[Rails] Encrypted Credentials과 환경변수
rails credentials, master.key, rails secrets, encrypted credentials, dotenv-rails
정의
Rails 5.2+ encrypted credentials는 비밀 정보를 암호화해 git에 안전하게 커밋. Rails 6+ 환경별 (development.yml.enc, production.yml.enc) 가능. .env 파일 + dotenv-rails는 대안.
기본 사용
bin/rails credentials:edit
# EDITOR 환경변수의 에디터로 plain YAML 열림
# 저장 시 자동 암호화
# credentials.yml.enc (편집 시 plain text)
secret_key_base: ...
aws:
access_key_id: ...
secret_access_key: ...
stripe:
publishable_key: pk_...
secret_key: sk_...
webhook_secret: whsec_...
sendgrid:
api_key: SG.xxx
Rails.application.credentials.aws[:access_key_id]
Rails.application.credentials.dig(:aws, :access_key_id) # 안전한 nested
Rails.application.credentials.stripe![:secret_key] # 누락 시 raise
파일 구조
config/
├── master.key # .gitignore (커밋 X)
└── credentials.yml.enc # 암호화 (커밋 O)
master.key 노출 → 모든 비밀 노출. 절대 git에 커밋 X.
환경별
bin/rails credentials:edit -e production
# config/credentials/production.yml.enc + production.key
bin/rails credentials:edit -e staging
Rails.env에 따라 자동 선택. 각 환경 다른 키.
config/
├── master.key # 기본 (development/test)
├── credentials.yml.enc # 공통
└── credentials/
├── production.yml.enc
├── production.key # .gitignore
├── staging.yml.enc
└── staging.key
환경변수로 키
RAILS_MASTER_KEY=xxx bin/rails server
CI/CD, Docker, k8s에서 master.key 파일 대신 RAILS_MASTER_KEY 환경변수.
코드에서
# config/initializers/stripe.rb
Stripe.api_key = Rails.application.credentials.stripe[:secret_key]
# config/database.yml
production:
password: <%= Rails.application.credentials.db[:password] %>
# Action Mailer
config.action_mailer.smtp_settings = {
user_name: Rails.application.credentials.smtp[:user],
password: Rails.application.credentials.smtp[:password],
}
.env (dotenv-rails)
# Gemfile
gem "dotenv-rails", groups: [:development, :test]
# .env (.gitignore)
DATABASE_URL=postgres://localhost/myapp_dev
STRIPE_SECRET=sk_test_...
SENDGRID_API_KEY=SG.xxx
# .env.example (git 커밋)
DATABASE_URL=
STRIPE_SECRET=
SENDGRID_API_KEY=
ENV["STRIPE_SECRET"]
ENV.fetch("STRIPE_SECRET") # 누락 시 raise
ENV.fetch("PORT", "3000") # 기본값
.env는 개발 편의. 프로덕션은 OS 환경변수 또는 secrets manager.
Credentials vs .env
| 항목 | Credentials | .env |
|---|---|---|
| 암호화 | O | X (git 커밋 X) |
| git에 안전 | O | X |
| 편집 | credentials:edit | 텍스트 |
| 키 관리 | master.key | 없음 |
| 동적 로드 | restart 필요 | restart 필요 |
Rails 5.2+ 신규는 credentials. legacy는 .env 그대로.
Vault / AWS Secrets Manager
대규모는 외부 KMS:
# config/initializers/secrets.rb
require "aws-sdk-secretsmanager"
client = Aws::SecretsManager::Client.new(region: "ap-northeast-2")
secrets = JSON.parse(client.get_secret_value(secret_id: "myapp/prod").secret_string)
Rails.application.config.x.stripe_key = secrets["stripe_key"]
키 회전, 권한, 감사 로그가 KMS 우위.
환경 분리 예시
# config/initializers/stripe.rb
api_key = if Rails.env.production?
Rails.application.credentials.dig(:stripe, :live_secret_key)
elsif Rails.env.staging?
Rails.application.credentials.dig(:stripe, :test_secret_key)
else
ENV["STRIPE_TEST_KEY"] || "sk_test_default"
end
Stripe.api_key = api_key
자주 보는 패턴
모든 secret 한 곳에
# config/initializers/00_secrets.rb (먼저 로드)
module AppSecrets
module_function
def stripe_key
Rails.application.credentials.dig(:stripe, :secret_key) || raise("Missing stripe key")
end
def aws_access
Rails.application.credentials.dig(:aws, :access_key_id) || raise("Missing AWS access")
end
end
# 사용
Stripe.api_key = AppSecrets.stripe_key
타입 안전 + 누락 명확.
Health check + 시작 시 검증
# config/initializers/validate_secrets.rb
required = [
[:stripe, :secret_key],
[:aws, :access_key_id],
[:sendgrid, :api_key],
]
missing = required.reject { |path| Rails.application.credentials.dig(*path).present? }
if missing.any?
raise "Missing credentials: #{missing.inspect}"
end
서버 시작 시 누락 즉시 발견.
12-factor 환경변수 우선
def secret(key, path:)
ENV[key] || Rails.application.credentials.dig(*path)
end
Stripe.api_key = secret("STRIPE_KEY", path: [:stripe, :secret_key])
ENV가 있으면 ENV, 없으면 credentials. 환경별 유연성.
함정
1. master.key 노출
git에 커밋, slack에 공유, CI 로그 출력 등. 노출 시:
- 즉시 새 키 생성
- credentials 재암호화
- 모든 환경에 새 키 배포
- 노출된 비밀 (API key, password) 모두 회전
2. credentials 충돌
여러 개발자가 동시 편집 → merge conflict. credentials.yml.enc는 사실상 바이너리 → 수동 병합 불가.
해결: 한 명만 편집 + share / lock 프로세스.
3. Heroku / 클라우드 master.key
RAILS_MASTER_KEY 환경변수 설정.
heroku config:set RAILS_MASTER_KEY=$(cat config/master.key)
4. CI/CD
# GitHub Actions
- name: Test
env:
RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
run: bundle exec rspec
master.key를 CI secret으로.
5. test 환경
테스트는 실제 비밀 X. 더미 credentials:
# config/credentials/test.yml.enc
stripe:
secret_key: sk_test_fake
또는 test에서 ENV / mock.
보안 모범
- master.key는 별도 안전한 저장소
- 키 회전 정기 (분기 1회 등)
- 누가 언제 변경했는지 audit log
- 환경 분리 (dev/staging/prod 다른 키)
- 코드에 비밀 하드코딩 X
- 로그 / 에러 / 응답에 비밀 X
💬 댓글