애플리케이션 시크릿
GitLab v19.1GitLab은 기능을 수행하기 위해 액세스 토큰 및 기타 자격 증명과 같은 다양한 시크릿에 접근할 수 있어야 합니다. 크게 보면 두 가지 종류의 시크릿이 있습니다: 애플리케이션 시크릿. 운영 시크릿. 직접 설치(self-compiled)의 경우 직접 저장됩니다.
GitLab은 기능을 수행하기 위해 액세스 토큰 및 기타 자격 증명과 같은 다양한 시크릿에 접근할 수 있어야 합니다. 이러한 시크릿은 암호화되어 저장되며, 용도에 따라 다양한 데이터 저장소에 보관될 수 있습니다. 이 가이드를 통해 다양한 종류의 시크릿이 어떻게 저장되고 관리되는지 이해하세요.
애플리케이션 시크릿과 운영 시크릿#
크게 보면 두 가지 종류의 시크릿이 있습니다:
-
애플리케이션 시크릿. GitLab 애플리케이션이 특정 기능이나 함수를 구현하는 데 사용합니다. 예시로는 암호화 서명을 생성하기 위한 액세스 토큰이나 개인 키가 있습니다. 이러한 시크릿은 암호화된 칼럼을 통해 데이터베이스에 저장됩니다. 보안 코딩 가이드라인: 저장 중 데이터를 참고하세요.
-
운영 시크릿. 다른 시크릿을 읽고 저장하거나 애플리케이션을 부트스트랩하는 데 사용됩니다. 이러한 이유로 데이터베이스에 저장할 수 없습니다. 이러한 시크릿은
config/secrets.yml파일의 Rails 자격 증명으로 저장됩니다:
직접 설치(self-compiled)의 경우 직접 저장됩니다.
애플리케이션 시크릿#
애플리케이션 시크릿은 ActiveRecord::Encryption을 사용하여 PostgreSQL에 저장해야 합니다:
class MyModel < ApplicationRecord
encrypts :my_secret
end
최근까지는 ActiveRecord::Encryption 대신 attr_encrypted를 사용했습니다. 현재 모든 칼럼을 새로운 Rails 네이티브 암호화 프레임워크로 마이그레이션하는 작업이 진행 중입니다(에픽 15420 참고).
기존 attr_encrypted 속성 마이그레이션에 대한 지침은 attr_encrypted에서 ActiveRecord::Encryption으로 마이그레이션을 참고하세요.
선례가 있더라도 애플리케이션 시크릿을 ApplicationSetting으로 저장해서는 안 됩니다.
이 시크릿이 디코딩에 실패하면 전체 애플리케이션이 오작동할 수 있습니다. 다른 기능과의
결합도를 줄이기 위해 시크릿은 전용 테이블에 격리하세요.
경우에 따라 데이터베이스에 시크릿을 저장하는 것이 바람직하지 않을 수도 있습니다. 예를 들어, Rails 애플리케이션을 부트스트랩하기 위해 시크릿이 필요한 경우, 이니셜라이저에서 데이터베이스에 접근해야 할 수 있으며, 데이터베이스 연결 자체가 아직 준비되지 않은 상황에서 초기화 경쟁 조건이 발생할 수 있습니다. 이 경우에는 시크릿을 운영 시크릿으로 저장하세요.
attr_encrypted에서 ActiveRecord::Encryption으로 마이그레이션#
attr_encrypted 젬에서 Rails 네이티브 ActiveRecord::Encryption 프레임워크로 모든 암호화된 속성을 마이그레이션하고 있습니다. 이 마이그레이션은 전환 기간 동안 하위 호환성을 유지하면서 보안, 성능, 유지보수성을 향상시킵니다.
migrate_to_encrypts 메서드#
migrate_to_encrypts 메서드는 attr_encrypted에서 ActiveRecord::Encryption으로의 원활한 마이그레이션 경로를 제공합니다. 전환 기간 동안 두 가지 암호화 형식 모두에 데이터를 임시로 저장합니다.
사용법:
class MyModel < ApplicationRecord
include Gitlab::EncryptedAttribute
# Replace attr_encrypted with migrate_to_encrypts
# Keep the same encryption options (mode, key, algorithm etc.) during migration
migrate_to_encrypts :my_secret_attribute,
mode: :per_attribute_iv,
key: :db_key_base_truncated,
algorithm: 'aes-256-cbc',
insecure_mode: true
end
작동 방식:
-
이중 암호화: 속성이 설정될 때, 이전(
attr_encrypted) 형식과 새(ActiveRecord::Encryption) 형식 모두로 저장됩니다. -
폴백 읽기: 데이터를 검색할 때 시스템은 먼저 새 형식(
tmp_<attribute>칼럼)을 확인하고, 필요한 경우 이전 형식으로 폴백합니다. -
하위 호환성: 기존 암호화된 데이터는 마이그레이션 과정 전반에 걸쳐 계속 접근 가능합니다.
생성되는 메서드:
migrate_to_encrypts 메서드는 여러 헬퍼 메서드를 생성합니다:
-
attr_encrypted_<attribute>: 원래attr_encrypted값에 접근합니다. -
tmp_<attribute>: 새ActiveRecord::Encryption값에 접근합니다. -
<attribute>: 새 형식에서 먼저 읽고 이전 형식으로 폴백하는 기본 접근자입니다.
마이그레이션 프로세스#
마이그레이션은 무중단 배포를 보장하기 위해 4개의 마일스톤 프로세스를 따릅니다:
마일스톤 M (초기 마이그레이션):
- 임시 칼럼 추가:
:jsonb타입으로tmp_<attribute>칼럼을 생성합니다:
class AddTmpSecretKeyToMyModel < Gitlab::Database::Migration[2.3]
milestone '18.4'
def change
add_column :my_models, :tmp_secret_key, :jsonb
end
end
- 모델 업데이트:
attr_encrypted를migrate_to_encrypts로 교체합니다:
class MyModel < ApplicationRecord
include Gitlab::EncryptedAttribute
# Before:
# attr_encrypted :secret_key, mode: :per_attribute_iv, key: :db_key_base_truncated, algorithm: 'aes-256-cbc', insecure_mode: true
# After:
# Keep the same encryption options (mode, key, algorithm etc.) during migration
migrate_to_encrypts :secret_key,
mode: :per_attribute_iv,
key: :db_key_base_truncated,
algorithm: 'aes-256-cbc',
insecure_mode: true
end
- 데이터 마이그레이션 생성: 새 칼럼을 채우기 위한 배포 후 마이그레이션을 생성합니다:
class MigrateSecretKeyToNewEncryptionFramework < Gitlab::Database::Migration[2.3]
milestone '18.1'
restrict_gitlab_migration gitlab_schema: :gitlab_main
class MigrationMyModel < MigrationRecord
include Gitlab::EncryptedAttribute
self.table_name = 'my_models'
migrate_to_encrypts :secret_key,
mode: :per_attribute_iv,
key: :db_key_base_truncated,
algorithm: 'aes-256-cbc',
insecure_mode: true
end
def up
MigrationMyModel.find_each do |record|
next if record.secret_key.blank?
record.secret_key = record.attr_encrypted_secret_key
record.save!
end
end
def down
execute "UPDATE my_models SET tmp_secret_key = NULL"
end
end
대형 테이블의 경우 일반 배포 후 마이그레이션 대신 배치 백그라운드 마이그레이션이 필요할 수 있습니다.
마일스톤 M+1 (칼럼 이름 변경):
-
마이그레이션 완료: 백그라운드 마이그레이션이 완료되었는지 확인합니다.
-
칼럼 이름 변경:
tmp_<attribute>칼럼을<attribute>로 이름을 변경합니다.
-
모델 업데이트:
migrate_to_encrypts메서드 호출을 네이티브encryptsRails 메서드로 교체합니다. -
이전 칼럼 무시:
encrypted_<attribute>,encrypted_<attribute>_iv,encrypted_<attribute>_salt칼럼에ignore_columns추가
마일스톤 M+2 (정리):
-
이전 칼럼 삭제:
encrypted_<attribute>,encrypted_<attribute>_iv,encrypted_<attribute>_salt칼럼 삭제 -
무시 규칙 제거:
tmp_<attribute>에 대한ignore_column제거
마일스톤 M+3 (최종 정리):
- 무시 규칙 제거:
encrypted_<attribute>,encrypted_<attribute>_iv,encrypted_<attribute>_salt칼럼에 대한ignore_columns제거
마이그레이션 테스트#
속성이 두 프레임워크 모두에서 올바르게 암호화되었는지 테스트하기 위해 제공된 공유 예제를 사용하세요:
RSpec.describe MyModel, feature_category: :my_feature do
let(:record) { build(:my_model) }
it_behaves_like 'encrypted attribute being migrated to the new encryption framework',
:secret_key do
let(:record) { build(:my_model) }
end
end
이 공유 예제는 다음을 검증합니다:
-
속성 값이 올바르게 저장되고 검색됩니다.
-
두 암호화 프레임워크가 동일한 복호화된 값을 저장합니다.
-
암호화된 값은 평문과 다릅니다 (암호화가 작동 중임을 보장).
-
attr_encrypted_<attribute>와tmp_<attribute>접근자 모두 올바르게 작동합니다.
모범 사례#
-
JSONB 칼럼 사용: 새 암호화 칼럼에는
:text대신 항상:jsonb타입을 사용하세요. -
암호화 옵션 유지: 마이그레이션 중에는 동일한 암호화 옵션(모드, 키, 알고리즘)을 유지하세요.
-
철저한 테스트: 제공된 공유 예제를 사용하여 두 암호화 방법 모두 작동하는지 확인하세요.
-
성능 모니터링: 대형 테이블의 경우 일반 배포 후 마이그레이션 대신 배치 백그라운드 마이그레이션이 필요할 수 있습니다.
-
데이터 무결성 검증: 마이그레이션 후 마이그레이션된 데이터가 원본과 일치하는지 항상 확인하세요.
구현 예제#
완전한 구현 예제는 다음을 참고하세요:
-
MR !191926:
migrate_to_encrypts메서드 도입 -
MR !189940:
ApplicationSetting.asset_proxy_secret_key마이그레이션 사용 예제 -
에픽 &15420: 104개 이상의 속성 마이그레이션 전체 프로젝트 추적
운영 시크릿#
다른 시크릿을 관리하기 위해 주로 config/secrets.yml에 여러 운영 시크릿을 유지합니다. 역사적으로 GitLab은
애플리케이션 시크릿을 포함한 모든 시크릿에 이 방법을 사용했지만, 이후 대부분의 시크릿을 PostgreSQL로 이전했습니다.
유일한 예외는 openid_connect_signing_key로, 데이터베이스가 준비되기 전에 Rails 이니셜라이저에서 접근해야 하기 때문입니다.
시크릿 항목#
| 항목 | 설명 |
|---|---|
| secret_key_base | 다양한 시크릿을 생성하는 데 사용되는 기본 키 |
| otp_key_base | 사용자 관리에 설명된 일회용 비밀번호(One Time Passwords)의 기본 키 |
| db_key_base | attr_encrypted 칼럼의 데이터를 암호화하기 위한 기본 키 |
| openid_connect_signing_key | OpenID Connect의 서명 키 |
| encrypted_settings_key_base | 설정 파일을 암호화하기 위한 기본 키 |
| active_record_encryption_primary_key | ActiveRecord::Encryption 암호화 칼럼의 데이터를 비결정론적으로 암호화하기 위한 기본 키 |
| active_record_encryption_deterministic_key | ActiveRecord::Encryption 암호화 칼럼의 데이터를 결정론적으로 암호화하기 위한 기본 키 |
| active_record_encryption_key_derivation_salt | ActiveRecord::Encryption 암호화 칼럼의 데이터를 암호화하기 위한 파생 솔트 |
시크릿이 저장되는 위치#
| 설치 유형 | 위치 |
|---|---|
| Linux 패키지 | /etc/gitlab/gitlab-secrets.json |
| Cloud Native GitLab Charts | 쿠버네티스 시크릿 |
| 직접 설치(Self-compiled) |
경고: 애플리케이션 시크릿에 새 시크릿을 추가하기 전에#
Omnibus GitLab 및 Cloud Native GitLab Charts 지원 추가#
config/initializers/2_secret_token.rb에 새 시크릿을 추가하기 전에,
GitLab Linux 패키지와 Cloud Native GitLab Charts도 함께 업데이트해야 합니다. 그렇지 않으면 업데이트가 실패합니다.
두 설치 방법 모두 config/secrets.yml 파일 작성을 담당합니다.
시크릿에 대해 알지 못하면 Rails가 파일에 쓰려고 시도하지만, 쓰기 권한이 없기 때문에 실패합니다.
예제
라이브 환경에서 시크릿 채우기#
또한, 모든 노드에서 시크릿의 값이 동일해야 하는 경우(일반적으로 그렇습니다), 이 파일을 변경하기 전에 모든 라이브 환경(GitLab.com, 스테이징, 사전 환경)에 대해 구성되어 있는지 확인해야 합니다.
새 시크릿 문서화#
-
이 문서 파일에 새 시크릿을 추가합니다.
-
다음 릴리즈 업그레이드 노트에 새 시크릿을 언급합니다. 예를 들어, 17.8 릴리즈의 경우, 노트는
data/release_posts/17_8/17-8-upgrade.yml에 들어가며 다음과 같은 내용을 포함합니다:
---
upgrades:
- reporter: <your username> # item author username
description: |
In Gitlab 17.8, three new secrets have been added to support the upcoming encryption framework:
- `active_record_encryption_primary_key`
- `active_record_encryption_deterministic_key`
- `active_record_encryption_key_derivation_salt`
**If you have a multi-node configuration, you should ensure these secrets are the same on all nodes.** Otherwise, the application will automatically generate the missing secrets.
If you use the [GitLab helm chart](https://docs.gitlab.com/charts/) and disabled the [shared-secrets chart](https://docs.gitlab.com/charts/charts/shared-secrets/), you will need to [manually create these secrets](https://docs.gitlab.com/charts/installation/secrets/#gitlab-rails-secret).
- 다음 Cloud Native GitLab Charts 업그레이드 노트에 새 시크릿을 언급합니다. 예를 들어, 8.8의 경우 https://docs.gitlab.com/charts/releases/8_0/에 새 시크릿을 문서화해야 합니다.
추가 반복 작업#
향후 config/initializers/2_secret_token.rb에서 수행하는 이 자동 시크릿 생성을 폐지하거나 제거할 수 있습니다.
자세한 내용은 이슈 #222690을 참고하세요.