diff --git a/ansible/playbooks/indri.yml b/ansible/playbooks/indri.yml index 3042366..90ea26c 100644 --- a/ansible/playbooks/indri.yml +++ b/ansible/playbooks/indri.yml @@ -116,6 +116,23 @@ no_log: true tags: [forgejo_actions_secrets] + # Zot OIDC client secret + - name: Fetch zot OIDC client secret + ansible.builtin.command: + cmd: op read "op://vg6xf6vvfmoh5hqjjhlhbeoaie/oor7os5kapczgpbwv7obkca4y4/zot-client-secret" + delegate_to: localhost + register: _zot_oidc_secret + changed_when: false + no_log: true + check_mode: false + tags: [zot] + + - name: Set zot OIDC client secret fact + ansible.builtin.set_fact: + zot_oidc_client_secret: "{{ _zot_oidc_secret.stdout }}" + no_log: true + tags: [zot] + # Caddy Gandi token for ACME DNS-01 challenges - name: Fetch Gandi PAT for Caddy ansible.builtin.command: diff --git a/ansible/roles/zot/tasks/main.yml b/ansible/roles/zot/tasks/main.yml index 20713b5..5c72577 100644 --- a/ansible/roles/zot/tasks/main.yml +++ b/ansible/roles/zot/tasks/main.yml @@ -46,6 +46,14 @@ mode: '0644' notify: Restart zot +- name: Deploy zot OIDC credentials + ansible.builtin.template: + src: oidc-credentials.json.j2 + dest: "{{ zot_config_dir }}/oidc-credentials.json" + mode: '0600' + notify: Restart zot + when: zot_oidc_client_secret is defined + - name: Deploy zot LaunchAgent plist ansible.builtin.template: src: zot.plist.j2 diff --git a/ansible/roles/zot/templates/oidc-credentials.json.j2 b/ansible/roles/zot/templates/oidc-credentials.json.j2 new file mode 100644 index 0000000..30ea3cb --- /dev/null +++ b/ansible/roles/zot/templates/oidc-credentials.json.j2 @@ -0,0 +1,4 @@ +{ + "clientid": "zot", + "clientsecret": "{{ zot_oidc_client_secret }}" +} diff --git a/argocd/manifests/authentik/configmap-blueprint.yaml b/argocd/manifests/authentik/configmap-blueprint.yaml index 4a001a4..7881614 100644 --- a/argocd/manifests/authentik/configmap-blueprint.yaml +++ b/argocd/manifests/authentik/configmap-blueprint.yaml @@ -154,3 +154,80 @@ data: enabled: true negate: false timeout: 30 + + zot.yaml: | + version: 1 + metadata: + name: BlumeOps Zot SSO + labels: + blueprints.goauthentik.io/description: "Zot OIDC provider, application, and CI identity" + entries: + # artifact-workloads group (CI push identity) + - model: authentik_core.group + id: artifact-workloads-group + identifiers: + name: artifact-workloads + attrs: + name: artifact-workloads + + # Service account for CI push (admin sets password via UI after deploy) + - model: authentik_core.user + id: zot-ci-user + identifiers: + username: zot-ci + attrs: + username: zot-ci + name: Zot CI Service Account + type: service_account + is_active: true + groups: + - !KeyOf artifact-workloads-group + + # OAuth2 provider for Zot + - model: authentik_providers_oauth2.oauth2provider + id: zot-provider + identifiers: + name: Zot + attrs: + name: Zot + authorization_flow: !Find [authentik_flows.flow, [slug, default-provider-authorization-implicit-consent]] + invalidation_flow: !Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]] + client_type: confidential + client_id: zot + client_secret: !Env AUTHENTIK_ZOT_CLIENT_SECRET + redirect_uris: + - matching_mode: strict + url: https://registry.ops.eblu.me/zot/auth/callback/oidc + signing_key: !Find [authentik_crypto.certificatekeypair, [name, authentik Self-signed Certificate]] + property_mappings: + - !Find [authentik_providers_oauth2.scopemapping, [scope_name, openid]] + - !Find [authentik_providers_oauth2.scopemapping, [scope_name, email]] + - !Find [authentik_providers_oauth2.scopemapping, [scope_name, profile]] + sub_mode: hashed_user_id + include_claims_in_id_token: true + + # Zot application — linked to the OAuth2 provider + - model: authentik_core.application + id: zot-app + identifiers: + slug: zot + attrs: + name: Zot Registry + slug: zot + provider: !KeyOf zot-provider + meta_launch_url: https://registry.ops.eblu.me + policy_engine_mode: any + + # Policy binding — restrict Zot to admins group + - model: authentik_policies.policybinding + identifiers: + order: 0 + target: !KeyOf zot-app + group: !Find [authentik_core.group, [name, admins]] + attrs: + target: !KeyOf zot-app + group: !Find [authentik_core.group, [name, admins]] + order: 0 + enabled: true + negate: false + timeout: 30 diff --git a/argocd/manifests/authentik/deployment-worker.yaml b/argocd/manifests/authentik/deployment-worker.yaml index 4144807..d4d9083 100644 --- a/argocd/manifests/authentik/deployment-worker.yaml +++ b/argocd/manifests/authentik/deployment-worker.yaml @@ -63,6 +63,11 @@ spec: secretKeyRef: name: authentik-config key: forgejo-client-secret + - name: AUTHENTIK_ZOT_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: authentik-config + key: zot-client-secret volumeMounts: - name: blueprints mountPath: /blueprints/custom diff --git a/argocd/manifests/authentik/external-secret.yaml b/argocd/manifests/authentik/external-secret.yaml index 097b640..0f4b639 100644 --- a/argocd/manifests/authentik/external-secret.yaml +++ b/argocd/manifests/authentik/external-secret.yaml @@ -45,3 +45,7 @@ spec: remoteRef: key: "Authentik (blumeops)" property: forgejo-client-secret + - secretKey: zot-client-secret + remoteRef: + key: "Authentik (blumeops)" + property: zot-client-secret diff --git a/docs/changelog.d/register-zot-oidc-client.feature.md b/docs/changelog.d/register-zot-oidc-client.feature.md new file mode 100644 index 0000000..2332f38 --- /dev/null +++ b/docs/changelog.d/register-zot-oidc-client.feature.md @@ -0,0 +1 @@ +Register Zot as an OIDC client in Authentik via blueprint, with artifact-workloads group, zot-ci service account, and OIDC credentials template for Ansible deployment.