Secrets Manager GraphQL API
GitLab v19.5요약
Secrets Manager는 ee/app/graphql/ 아래의 GraphQL 타입, 뮤테이션, 리졸버로 데이터와 동작을 노출합니다. 타입은 ee/app/graphql/types/secrets_management/에 있습니다.
Secrets Manager는 ee/app/graphql/ 아래의 GraphQL 타입, 뮤테이션, 리졸버로 데이터와 동작을 노출합니다.
모든 뮤테이션과 리졸버는 아래에 설명하는 오류 처리와 인가 관련 사항을 공통으로 따릅니다.
타입#
타입은 ee/app/graphql/types/secrets_management/에 있습니다.
| 타입 | GraphQL 이름 | 기반 |
|---|---|---|
ProjectSecretsManagerType |
ProjectSecretsManager |
ProjectSecretsManager 레코드. effective_status에서 가져온 status와 엄격한 읽기 전용 모드를 위한 read_only 필드를 노출합니다. |
GroupSecretsManagerType |
GroupSecretsManager |
GroupSecretsManager 레코드. |
ProjectSecretType |
ProjectSecret |
OpenBao 응답으로 만든 ActiveModel::Model 시크릿. |
GroupSecretType |
GroupSecret |
그룹 시크릿용으로 같은 방식. |
SecretRotationInfoType |
SecretRotationInfo |
ProjectSecretRotationInfo 또는 GroupSecretRotationInfo. 인가는 이 타입이 아니라 부모 시크릿 타입이 처리합니다. |
ProjectSecretsPermissionType |
ProjectSecretsPermission |
프로젝트 시크릿 권한. SecretsPermissionInterface를 포함합니다. |
GroupSecretsPermissionType |
GroupSecretsPermission |
그룹 권한용으로 같은 방식. |
EntitlementType |
SecretsManagerEntitlement |
SecretsManagement::Entitlement 값 객체. 자격 상태를 참고합니다. |
EnrollmentType |
SecretsManagerEnrollment |
SecretsManagement::NamespaceEnrollment 또는 인스턴스 전체 등록. |
ee/app/graphql/types/secrets_management/secrets_permission_interface.rb의 SecretsPermissionInterface는 두 권한 타입에 principal, actions, grantedBy, expiredAt 필드를 추가하는 공유 ActiveSupport::Concern입니다.
권한 관련 지원 타입은 ee/app/graphql/types/secrets_management/permissions/에 있습니다. PrincipalType, PrincipalInputType, PrincipalTypeEnum, ActionEnum, SecretPermissionType입니다.
열거형#
| 열거형 | 값 |
|---|---|
ProjectSecretsManagerStatusEnum, GroupSecretsManagerStatusEnum |
BaseSecretsManagerStatusEnum을 기반으로 합니다. |
SecretStatusEnum |
타임스탬프로부터 계산한 시크릿의 라이프사이클 상태. |
SecretRotationStatusEnum |
OVERDUE, APPROACHING, OK. |
EntitlementStateEnum |
SecretsManagement::Entitlement 상태를 그대로 반영합니다. |
EntitlementBlockedReasonEnum |
자격의 blocked_reason 값을 그대로 반영합니다. |
WriteActionDenialReasonEnum |
자격 문제로 뮤테이션이 거부될 때 reason 필드에 반환되며, ProjectSecretsManagerType과 GroupSecretsManagerType의 writeDenialReason 필드에도 반환됩니다. |
타입의 인가#
타입은 authorize로 Rails 정책 권한을 선언합니다. 예를 들어 ProjectSecretType에는 authorize :read_project_secrets가 있습니다.
일부 타입은 authorize_granular_token도 선언합니다. 이는 개인 액세스 토큰이 GraphQL API로 해당 타입을 읽으려면 가져야 하는 세분화된 토큰 권한을 지정합니다. 예를 들어 ProjectSecretsManagerType은 authorize_granular_token permissions: :read_secrets_manager, boundary: :project, boundary_type: :project를 선언합니다.
SecretRotationInfoType처럼 객체별 정책이 없는 타입은 rubocop:disable Graphql/AuthorizeTypes 주석으로 인가 검사를 비활성화하고 부모 타입의 인가에 의존합니다.
뮤테이션#
뮤테이션은 ee/app/graphql/mutations/secrets_management/에 영역별로 묶여 있습니다.
프로젝트 및 그룹 Secrets Manager#
| 뮤테이션 파일 | GraphQL 이름 | Rails 권한 | 서비스 |
|---|---|---|---|
project_secrets_managers/initialize.rb |
ProjectSecretsManagerInitialize |
:provision_secrets_manager |
ProjectSecretsManagers::InitializeService |
project_secrets_managers/deprovision.rb |
ProjectSecretsManagerDeprovision |
:admin_project_secrets_manager |
ProjectSecretsManagers::InitiateDeprovisionService |
group_secrets_managers/initialize.rb |
GroupSecretsManagerInitialize |
:provision_secrets_manager |
GroupSecretsManagers::InitializeService |
group_secrets_managers/deprovision.rb |
GroupSecretsManagerDeprovision |
:deprovision_secrets_manager |
GroupSecretsManagers::InitiateDeprovisionService |
프로젝트 및 그룹 시크릿#
| 뮤테이션 파일 | GraphQL 이름 | Rails 권한 |
|---|---|---|
project_secrets/create.rb |
ProjectSecretCreate |
:create_project_secrets |
project_secrets/update.rb |
ProjectSecretUpdate |
:update_project_secrets |
project_secrets/delete.rb |
ProjectSecretDelete |
:delete_project_secrets |
group_secrets/create.rb |
GroupSecretCreate |
:create_secret |
group_secrets/update.rb |
GroupSecretUpdate |
:update_secret |
group_secrets/delete.rb |
GroupSecretDelete |
:delete_secret |
프로젝트 시크릿 뮤테이션과 그룹 시크릿 뮤테이션은 몇 가지 사소한 점이 다릅니다.
| 차이점 | 프로젝트 | 그룹 |
|---|---|---|
| 리졸버 include | include ResolvesProject |
include ResolvesGroup |
find_object 인수 |
project_path: |
group_path: |
| 범위별 인수 | branch (String) |
protected (Boolean) |
| 반환 필드 | project_secret |
group_secret |
| 생성 시 내부 이벤트 이름 | create_ci_secret |
create_group_ci_secret |
시크릿 값을 받는 GraphQL 인수의 이름은 secret이지만 서비스 호출 매개변수의 이름은 value입니다.
이름 변경은 resolve에서 이루어지며, 예를 들어 project_secrets/create.rb의 .execute(value: secret, ...)가 그렇습니다.
시크릿 값을 받는 새 뮤테이션에는 모두 이 관례를 따릅니다.
권한#
| 뮤테이션 파일 | GraphQL 이름 |
|---|---|
project_secrets_permissions/update.rb |
ProjectSecretsPermissionUpdate |
project_secrets_permissions/delete.rb |
ProjectSecretsPermissionDelete |
group_secrets_permissions/update.rb |
GroupSecretsPermissionUpdate |
group_secrets_permissions/delete.rb |
GroupSecretsPermissionDelete |
자격, 체험판, 등록#
| 뮤테이션 파일 | GraphQL 이름 |
|---|---|
enable_add_on.rb |
SecretsManagerEnableAddOn |
instance_enable_add_on.rb |
SecretsManagerInstanceEnableAddOn |
start_trial.rb |
SecretsManagerStartTrial |
instance_start_trial.rb |
SecretsManagerInstanceStartTrial |
refresh_entitlement.rb |
SecretsManagerRefreshEntitlement |
instance_refresh_entitlement.rb |
SecretsManagerInstanceRefreshEntitlement |
enrollment/namespace_enroll.rb |
NamespaceSecretsManagerEnroll |
enrollment/namespace_unenroll.rb |
NamespaceSecretsManagerUnenroll |
enrollment/instance_enroll.rb |
InstanceSecretsManagerEnroll |
enrollment/instance_unenroll.rb |
InstanceSecretsManagerUnenroll |
이 뮤테이션이 무엇을 바꾸는지는 각 상태가 허용하는 동작을 참고합니다.
리졸버#
리졸버는 ee/app/graphql/resolvers/secrets_management/에 있습니다.
| 리졸버 | 용도 |
|---|---|
ProjectSecretsManagerResolver, GroupSecretsManagerResolver |
프로젝트 또는 그룹의 Secrets Manager 상태를 가져옵니다. |
ProjectSecretsResolver, GroupSecretsResolver |
시크릿을 나열합니다. lookahead를 사용해 쿼리가 선택하지 않으면 로테이션 정보 로딩을 건너뜁니다. |
ProjectSecretResolver, GroupSecretResolver |
시크릿 하나를 가져옵니다. |
ProjectSecretsCountResolver, GroupSecretsCountResolver |
시크릿 개수를 셉니다. |
ProjectSecretsPermissionsResolver, GroupSecretsPermissionsResolver |
권한을 나열합니다. |
ProjectSecrets::ListNeedingRotationResolver, GroupSecrets::ListNeedingRotationResolver |
로테이션 기한이 되었거나 임박한 시크릿을 나열합니다. |
Permissions::SecretPermissionsResolver |
더 새로운 권한 타입을 통해 시크릿 권한을 나열합니다. |
OpenbaoHealthResolver |
OpenBao 서버 상태를 확인합니다. |
NamespaceEnrollmentResolver, InstanceEnrollmentResolver |
등록 상태를 가져옵니다. |
InstanceEntitlementResolver |
인스턴스 수준의 자격을 가져옵니다. |
Lookahead 최적화#
ProjectSecretsResolver와 GroupSecretsResolver는 extras [:lookahead]를 받으며, 쿼리가 요청할 때만 목록 서비스에 로테이션 정보를 포함하도록 요청합니다.
def resolve(lookahead:, project_path:)
result = ListService.new(project, current_user)
.execute(include_rotation_info: include_rotation_info?(lookahead))
end
def include_rotation_info?(lookahead)
lookahead.selection(:nodes).selects?(:rotation_info) ||
lookahead.selection(:edges).selection(:node).selects?(:rotation_info)
end
공유 뮤테이션 및 리졸버 concern#
모든 뮤테이션과 리졸버는 오류 처리 concern을 포함해야 합니다.
두 concern 모두 ee/lib/secrets_management/graphql_error_handling.rb의 SecretsManagement::GraphqlErrorHandling에 한 번 정의된 같은 ErrorWrapper로 resolve를 감쌉니다.
| Concern | 모듈 | 포함하는 곳 |
|---|---|---|
| 뮤테이션 오류 처리 | SecretsManagement::MutationErrorHandling (ee/app/graphql/mutations/concerns/secrets_management/mutation_error_handling.rb) |
OpenBao 또는 Secrets Manager 서비스를 사용하는 뮤테이션. CustomersDot만 호출하는 체험판, 추가 기능, 자격 새로 고침 뮤테이션은 포함하지 않습니다. |
| 리졸버 오류 처리 | SecretsManagement::ResolverErrorHandling (ee/app/graphql/resolvers/concerns/secrets_management/resolver_error_handling.rb) |
모든 리졸버. |
쓰기 뮤테이션은 두 가지 concern을 더 포함합니다.
| Concern | 모듈 | 동작 |
|---|---|---|
| 활성 네임스페이스 확인 | SecretsManagement::RequiresActiveNamespace (ee/app/graphql/mutations/concerns/secrets_management/requires_active_namespace.rb) |
raise_if_namespace_inactive!를 추가하며, 보관되었거나 삭제 대기 중인 그룹 또는 프로젝트에 대한 쓰기를 차단합니다. |
| 쓰기 자격 적용 | SecretsManagement::EnforcesWriteEntitlement (ee/app/graphql/mutations/concerns/secrets_management/enforces_write_entitlement.rb) |
자격이 쓰기를 거부할 때 구조화된 reason과 함께 쓰기를 즉시 중단하는 ready? 훅을 추가합니다. enforces_write_entitlement_for :payload_key, find_by: :project_path(또는 :group_path)로 사용을 선택합니다. |
시크릿 삭제 뮤테이션은 EnforcesWriteEntitlement를 포함하지 않습니다. 권한 삭제 뮤테이션과 프로비저닝 해제 뮤테이션은 포함합니다.
인가#
뮤테이션이나 리졸버의 인가는 최대 세 가지 검사를 조합합니다.
authorize :ability_name.resolve메서드의authorized_find!로 검사하는 Rails 정책 권한입니다.authorize_granular_token permissions: ..., boundary_argument: ..., boundary_type: ...(:instance처럼 고정된 경계에는boundary:). 개인 액세스 토큰이 가져야 하는 세분화된 토큰 권한을 지정합니다. 여러 권한을 나열할 수 있으며, 예를 들어instance_enable_add_on.rb는 페이로드가 자격도 반환하므로:enable_secrets_manager_add_on과:read_secrets_manager를 모두 요구합니다.- 쓰기 뮤테이션의
enforces_write_entitlement_for. 앞에서 설명했습니다.
SecretsPermissionInterface에서 볼 수 있듯이 필드도 타입 자체의 인가와 별개로 고유한 authorize를 가질 수 있습니다.
오류가 클라이언트에 전달되는 방식#
ee/lib/secrets_management/error_mapping.rb의 SecretsManagement::ErrorMapping은 GraphqlErrorHandling이 포함하며, 원시 OpenBao 오류를 클라이언트에 노출하지 않습니다.
- 권한 오류나 정확히
"not found"인 메시지는raise_resource_not_available_error!가 됩니다. - check-and-set 불일치는 "This resource was recently modified. Refresh the page and try again to avoid overwriting newer changes."가 됩니다.
- 그 밖의 오류는 "Internal server error."가 되며, 메시지에서 시크릿 경로를 먼저 가린 뒤
Gitlab::ErrorTracking.track_exception으로 보고됩니다.
뮤테이션의 자체 resolve 메서드도 이 rescue 경로와 별개로, 사용자에게 보여 주는 구조화된 오류를 GraphQL 응답에 직접 반환합니다.
if result.success?
{ project_secret: result.payload[:secret], errors: [] }
else
{ project_secret: nil, errors: error_messages(result, [:secret]) }
end
프론트엔드 GraphQL 문서#
프론트엔드 쿼리와 뮤테이션은 ee/app/assets/javascripts/ci/secrets/graphql/ 아래에 있습니다.
- 쿼리:
ee/app/assets/javascripts/ci/secrets/graphql/queries/. 예를 들어get_project_secrets.query.graphql과get_project_secret_manager_status.query.graphql이 있습니다. - 뮤테이션:
ee/app/assets/javascripts/ci/secrets/graphql/mutations/. 예를 들어create_project_secret.mutation.graphql과enable_secret_manager.mutation.graphql이 있습니다.
이 문서를 사용하는 Vue 컴포넌트는 Secrets Manager 프론트엔드 개발을 참고합니다.
새 뮤테이션 체크리스트#
- 뮤테이션이 호출하는 서비스가 아직 없다면 만듭니다.
ee/app/graphql/mutations/secrets_management/{scope}/{action}.rb에 뮤테이션 파일을 만듭니다.- 필요한 concern을 포함합니다.
ResolvesProject또는ResolvesGroup, 뮤테이션이 OpenBao 또는 Secrets Manager 서비스를 사용한다면SecretsManagement::MutationErrorHandling, 쓰기라면SecretsManagement::RequiresActiveNamespace와SecretsManagement::EnforcesWriteEntitlement입니다. - 다른 진입점도 서비스를 실행한다면 서비스에서 내부 이벤트를 추적합니다. 예를 들어
InitializeServiceHelpers가 활성화 이벤트를 추적하므로 체험판 및 추가 기능 뮤테이션도 이를 발생시킵니다. - 뮤테이션을 가장 가까운 기존 대응 뮤테이션과 비교합니다. 예를 들어 그룹 뮤테이션의 프로젝트 버전이나 과금 뮤테이션의
EnableAddOn입니다. 같은 concern, 인가, 기능 검사를 포함하거나, 포함하지 않는 이유를 기록합니다. EE::ProjectPolicy또는EE::GroupPolicy의 기존 권한으로authorize :ability_name을 설정하거나 새 권한을 추가합니다.- 개인 액세스 토큰에 필요한 권한과 그
boundary_type으로authorize_granular_token을 설정합니다. - 쓰기 뮤테이션이라면
enforces_write_entitlement_for :payload_key, find_by: :project_path(또는:group_path)를 호출합니다. ee/app/graphql/ee/types/mutation_type.rb에서 다른SecretsManagement뮤테이션 옆에mount_mutation으로 뮤테이션을 마운트합니다.- 뮤테이션이 새 이벤트를 추적한다면
config/events/아래에 내부 이벤트 정의를 추가합니다. ee/app/assets/javascripts/ci/secrets/graphql/mutations/아래에 프론트엔드.mutation.graphql파일을 만듭니다.bundle exec rake gitlab:graphql:compile_docs로 GraphQL 참조 문서와 스키마를 다시 생성하며, 스키마 덤프도 업데이트하려면bundle exec rake gitlab:graphql:update_all을 사용합니다.ee/spec/requests/api/graphql/secrets_management/아래에 요청 스펙을 추가합니다. Secrets Manager 테스트를 참고합니다.ee/spec/frontend/ci/secrets/아래에 프론트엔드 스펙을 추가합니다.