본문으로 건너뛰기
김신건의 로그

[Rails] Encrypted Credentials과 환경변수

· 수정 · 📖 약 1분 · 514자/단어 #rails #credentials #secrets #configuration
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
암호화OX (git 커밋 X)
git에 안전OX
편집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 로그 출력 등. 노출 시:

  1. 즉시 새 키 생성
  2. credentials 재암호화
  3. 모든 환경에 새 키 배포
  4. 노출된 비밀 (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

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기