Skip to content

Instantly share code, notes, and snippets.

@german
Last active January 27, 2025 13:44
Show Gist options
  • Save german/1a0ff1a9710237f48315fa0978642bf6 to your computer and use it in GitHub Desktop.
Save german/1a0ff1a9710237f48315fa0978642bf6 to your computer and use it in GitHub Desktop.
Stripe API keys rotation
# lib/tasks/stripe_key_rotation.rake
require 'stripe'
require 'aws-sdk-secretsmanager'
namespace :stripe do
desc 'Automate Stripe API key rotation process'
task rotate_keys: :environment do
class StripeKeyRotator
def initialize
@secrets = Aws::SecretsManager::Client.new(
region: ENV['AWS_REGION'],
access_key_id: ENV['AWS_ACCESS_KEY_ID'],
secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
)
@slack_notifier = Slack::Notifier.new(ENV['SLACK_WEBHOOK_URL'])
@rotation_logger = Logger.new(Rails.root.join('log', 'key_rotation.log'))
end
def rotate
begin
rotation_start = Time.current
@rotation_logger.info("Starting API key rotation at #{rotation_start}")
# Step 1: Create new key in Stripe
new_key = create_new_stripe_key
# Step 2: Verify new key works
verify_new_key(new_key)
# Step 3: Store new key in AWS Secrets Manager
store_new_key(new_key)
# Step 4: Update application configuration
update_application_config(new_key)
# Step 5: Monitor for errors
monitor_key_transition(new_key)
# Step 6: Revoke old key if everything is stable
revoke_old_key
notify_success
rescue StandardError => e
handle_rotation_error(e)
end
end
private
def create_new_stripe_key
@rotation_logger.info("Creating new Stripe API key")
# Note: This is a placeholder as Stripe doesn't have a direct API for key creation
# In practice, you'll need to create the key manually in Stripe Dashboard
# and store it securely for the rotation process
# Simulate key creation for demonstration
new_key = "sk_test_#{SecureRandom.hex(24)}"
@rotation_logger.info("New key created successfully")
new_key
end
def verify_new_key(new_key)
@rotation_logger.info("Verifying new key functionality")
Stripe.api_key = new_key
# Perform test API call
Stripe::Customer.list(limit: 1)
@rotation_logger.info("New key verified successfully")
end
def store_new_key(new_key)
@rotation_logger.info("Storing new key in AWS Secrets Manager")
@secrets.put_secret_value(
secret_id: 'stripe/api_key',
secret_string: new_key
)
@rotation_logger.info("New key stored in AWS Secrets Manager")
end
def update_application_config(new_key)
@rotation_logger.info("Updating application configuration")
# Update credentials in Rails credentials
system("EDITOR='echo #{new_key} >' rails credentials:edit")
# Reload configuration in running application
Rails.application.credentials.reload
@rotation_logger.info("Application configuration updated")
end
def monitor_key_transition(new_key)
@rotation_logger.info("Monitoring key transition")
monitoring_period = 1.hour
start_time = Time.current
error_threshold = 5
error_count = 0
while Time.current - start_time < monitoring_period
begin
# Monitor API calls and error rates
error_rate = check_error_rate
if error_rate > error_threshold
error_count += 1
if error_count >= 3
raise "High error rate detected during transition"
end
end
sleep 300 # Check every 5 minutes
rescue => e
@rotation_logger.error("Monitoring error: #{e.message}")
raise e if error_count >= 3
end
end
@rotation_logger.info("Key transition monitoring completed successfully")
end
def check_error_rate
# Implement your error rate checking logic here
# This could involve checking your error tracking service
# or monitoring Stripe API response codes
# Example implementation using Rails cache
errors = Rails.cache.read('stripe_api_errors') || 0
total_calls = Rails.cache.read('stripe_api_calls') || 1
(errors.to_f / total_calls) * 100
end
def revoke_old_key
@rotation_logger.info("Revoking old API key")
old_key = Rails.application.credentials.stripe[:old_api_key]
# Note: Stripe doesn't provide direct API for key revocation
# This would typically be done manually in the Stripe Dashboard
# Here we're just logging the action
@rotation_logger.info("Old key revoked successfully")
end
def notify_success
message = <<~MSG
🔄 Stripe API Key Rotation Completed Successfully
Time: #{Time.current}
Environment: #{Rails.env}
Status: Success
Next rotation scheduled: #{90.days.from_now}
MSG
@slack_notifier.ping(message)
@rotation_logger.info("Key rotation completed successfully")
end
def handle_rotation_error(error)
error_message = <<~MSG
❌ Stripe API Key Rotation Failed
Time: #{Time.current}
Environment: #{Rails.env}
Error: #{error.message}
Backtrace: #{error.backtrace.first(5).join("\n")}
MSG
@slack_notifier.ping(error_message)
@rotation_logger.error("Key rotation failed: #{error.message}")
# Trigger rollback if necessary
rollback_rotation
raise error
end
def rollback_rotation
@rotation_logger.info("Initiating rollback procedure")
# Implement your rollback logic here
# This could involve restoring the old key from backup
# and reverting application configuration
end
end
# Execute the rotation
StripeKeyRotator.new.rotate
end
end
# config/initializers/stripe_monitoring.rb
module StripeMonitoring
class ApiCallMonitor
def self.track_request
Rails.cache.increment('stripe_api_calls')
end
def self.track_error
Rails.cache.increment('stripe_api_errors')
end
end
end
# config/schedule.rb (if using whenever gem)
every 90.days do
rake "stripe:rotate_keys"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment