From 0d3269e8d6c8bdf5e8b91907792cfb24cd593f15 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Wed, 18 Feb 2026 19:40:21 -0800 Subject: [PATCH] Add 1Password Connect + External Secrets to ringtail k3s Deploy the full ESO stack on ringtail, matching the indri pattern: - 4 ArgoCD apps (1password-connect, external-secrets-crds, external-secrets, external-secrets-config) targeting ringtail k3s cluster - ExternalSecret for forgejo-runner-amd64 token (replaces Ansible-managed secret) - Ansible playbook bootstraps 1Password Connect credentials instead of directly managing runner tokens Co-Authored-By: Claude Opus 4.6 --- ansible/playbooks/ringtail.yml | 54 ++++++++++--------- argocd/apps/1password-connect-ringtail.yaml | 32 +++++++++++ .../external-secrets-config-ringtail.yaml | 24 +++++++++ .../apps/external-secrets-crds-ringtail.yaml | 24 +++++++++ argocd/apps/external-secrets-ringtail.yaml | 32 +++++++++++ .../forgejo-runner-amd64/external-secret.yaml | 27 ++++++++++ .../forgejo-runner-amd64/kustomization.yaml | 1 + .../feature-k3s-ringtail-runner.feature.md | 2 +- docs/reference/infrastructure/ringtail.md | 6 +++ 9 files changed, 176 insertions(+), 26 deletions(-) create mode 100644 argocd/apps/1password-connect-ringtail.yaml create mode 100644 argocd/apps/external-secrets-config-ringtail.yaml create mode 100644 argocd/apps/external-secrets-crds-ringtail.yaml create mode 100644 argocd/apps/external-secrets-ringtail.yaml create mode 100644 argocd/manifests/forgejo-runner-amd64/external-secret.yaml diff --git a/ansible/playbooks/ringtail.yml b/ansible/playbooks/ringtail.yml index 3cc1a0b..de5826a 100644 --- a/ansible/playbooks/ringtail.yml +++ b/ansible/playbooks/ringtail.yml @@ -4,10 +4,18 @@ become: true pre_tasks: - - name: Fetch Forgejo runner registration token from 1Password + - name: Fetch 1Password Connect credentials from 1Password ansible.builtin.command: - cmd: op read "op://vg6xf6vvfmoh5hqjjhlhbeoaie/Forgejo Secrets/runner_reg" - register: _runner_token + cmd: op read "op://vg6xf6vvfmoh5hqjjhlhbeoaie/1Password Connect/credentials-file" + register: _op_credentials + changed_when: false + delegate_to: localhost + become: false + + - name: Fetch 1Password Connect token from 1Password + ansible.builtin.command: + cmd: op read "op://vg6xf6vvfmoh5hqjjhlhbeoaie/1Password Connect/token" + register: _op_token changed_when: false delegate_to: localhost become: false @@ -56,36 +64,32 @@ delay: 5 until: _k3s_ready.rc == 0 - - name: Create forgejo-runner namespace - ansible.builtin.command: k3s kubectl create namespace forgejo-runner + - name: Create 1password namespace + ansible.builtin.command: k3s kubectl create namespace 1password register: _ns changed_when: _ns.rc == 0 failed_when: _ns.rc != 0 and 'AlreadyExists' not in _ns.stderr - - name: Check if forgejo-runner-env secret exists - ansible.builtin.command: k3s kubectl get secret forgejo-runner-env -n forgejo-runner - register: _secret_exists - changed_when: false - failed_when: false - - - name: Create forgejo-runner-env secret - ansible.builtin.command: > - k3s kubectl create secret generic forgejo-runner-env - --namespace=forgejo-runner - --from-literal=RUNNER_TOKEN={{ _runner_token.stdout }} - changed_when: true - when: _secret_exists.rc != 0 - no_log: true - - - name: Update forgejo-runner-env secret + - name: Create or update op-credentials secret ansible.builtin.shell: cmd: | set -o pipefail - k3s kubectl create secret generic forgejo-runner-env \ - --namespace=forgejo-runner \ - --from-literal=RUNNER_TOKEN={{ _runner_token.stdout }} \ + k3s kubectl create secret generic op-credentials \ + --namespace=1password \ + --from-literal=1password-credentials.json='{{ _op_credentials.stdout }}' \ + --dry-run=client -o yaml | k3s kubectl apply -f - + executable: /bin/bash + changed_when: true + no_log: true + + - name: Create or update onepassword-token secret + ansible.builtin.shell: + cmd: | + set -o pipefail + k3s kubectl create secret generic onepassword-token \ + --namespace=1password \ + --from-literal=token={{ _op_token.stdout }} \ --dry-run=client -o yaml | k3s kubectl apply -f - executable: /bin/bash - when: _secret_exists.rc == 0 changed_when: true no_log: true diff --git a/argocd/apps/1password-connect-ringtail.yaml b/argocd/apps/1password-connect-ringtail.yaml new file mode 100644 index 0000000..408eb23 --- /dev/null +++ b/argocd/apps/1password-connect-ringtail.yaml @@ -0,0 +1,32 @@ +# 1Password Connect for ringtail k3s cluster +# Same chart/values as indri, different destination +# +# Prerequisites: +# 1. Bootstrap secrets via ansible (provision-ringtail creates 1password namespace, +# op-credentials and onepassword-token secrets) +# 2. Sync BEFORE external-secrets-ringtail +# +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: 1password-connect-ringtail + namespace: argocd +spec: + project: default + sources: + - repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/connect-helm-charts.git + targetRevision: connect-2.3.0 + path: charts/connect + helm: + releaseName: onepassword-connect + valueFiles: + - $values/argocd/manifests/1password-connect/values.yaml + - repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git + targetRevision: main + ref: values + destination: + server: https://ringtail.tail8d86e.ts.net:6443 + namespace: 1password + syncPolicy: + syncOptions: + - CreateNamespace=true diff --git a/argocd/apps/external-secrets-config-ringtail.yaml b/argocd/apps/external-secrets-config-ringtail.yaml new file mode 100644 index 0000000..d3f9e58 --- /dev/null +++ b/argocd/apps/external-secrets-config-ringtail.yaml @@ -0,0 +1,24 @@ +# External Secrets Configuration for ringtail k3s cluster +# Same ClusterSecretStore manifests as indri, different destination +# +# Prerequisites: +# - 1password-connect-ringtail is deployed and healthy +# - external-secrets-ringtail operator is deployed and CRDs are installed +# +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: external-secrets-config-ringtail + namespace: argocd +spec: + project: default + source: + repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git + targetRevision: main + path: argocd/manifests/external-secrets + destination: + server: https://ringtail.tail8d86e.ts.net:6443 + namespace: external-secrets + syncPolicy: + syncOptions: + - CreateNamespace=true diff --git a/argocd/apps/external-secrets-crds-ringtail.yaml b/argocd/apps/external-secrets-crds-ringtail.yaml new file mode 100644 index 0000000..a23eae3 --- /dev/null +++ b/argocd/apps/external-secrets-crds-ringtail.yaml @@ -0,0 +1,24 @@ +# External Secrets Operator CRDs for ringtail k3s cluster +# Same CRDs source as indri, different destination +# +# Must be synced BEFORE external-secrets-ringtail operator app. +# +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: external-secrets-crds-ringtail + namespace: argocd +spec: + project: default + source: + repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/external-secrets.git + targetRevision: helm-chart-2.0.0 + path: config/crds/bases + directory: + exclude: 'kustomization.yaml' + destination: + server: https://ringtail.tail8d86e.ts.net:6443 + syncPolicy: + syncOptions: + - ServerSideApply=true + - CreateNamespace=false diff --git a/argocd/apps/external-secrets-ringtail.yaml b/argocd/apps/external-secrets-ringtail.yaml new file mode 100644 index 0000000..c54c51b --- /dev/null +++ b/argocd/apps/external-secrets-ringtail.yaml @@ -0,0 +1,32 @@ +# External Secrets Operator for ringtail k3s cluster +# Same chart/values as indri, different destination +# +# Prerequisites: +# - 1password-connect-ringtail must be deployed and healthy +# - external-secrets-crds-ringtail must be synced first +# +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: external-secrets-ringtail + namespace: argocd +spec: + project: default + sources: + - repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/external-secrets.git + targetRevision: helm-chart-2.0.0 + path: deploy/charts/external-secrets + helm: + releaseName: external-secrets + valueFiles: + - $values/argocd/manifests/external-secrets/values.yaml + - repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git + targetRevision: main + ref: values + destination: + server: https://ringtail.tail8d86e.ts.net:6443 + namespace: external-secrets + syncPolicy: + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/argocd/manifests/forgejo-runner-amd64/external-secret.yaml b/argocd/manifests/forgejo-runner-amd64/external-secret.yaml new file mode 100644 index 0000000..3d0ac98 --- /dev/null +++ b/argocd/manifests/forgejo-runner-amd64/external-secret.yaml @@ -0,0 +1,27 @@ +# ExternalSecret for Forgejo Runner token (amd64) +# +# 1Password item: "Forgejo Secrets" in blumeops vault +# Field: runner_reg (runner registration token) +# +# Non-secret env vars (FORGEJO_URL, RUNNER_NAME, RUNNER_LABELS) live in the +# deployment spec so that changes (e.g. image version bumps) trigger a rollout +# automatically. +# +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: forgejo-runner-env + namespace: forgejo-runner +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: onepassword-blumeops + target: + name: forgejo-runner-env + creationPolicy: Owner + data: + - secretKey: RUNNER_TOKEN + remoteRef: + key: Forgejo Secrets + property: runner_reg diff --git a/argocd/manifests/forgejo-runner-amd64/kustomization.yaml b/argocd/manifests/forgejo-runner-amd64/kustomization.yaml index 6f6f6e5..1793b51 100644 --- a/argocd/manifests/forgejo-runner-amd64/kustomization.yaml +++ b/argocd/manifests/forgejo-runner-amd64/kustomization.yaml @@ -4,3 +4,4 @@ resources: - namespace.yaml - configmap.yaml - deployment.yaml + - external-secret.yaml diff --git a/docs/changelog.d/feature-k3s-ringtail-runner.feature.md b/docs/changelog.d/feature-k3s-ringtail-runner.feature.md index 5d69fda..9dcb385 100644 --- a/docs/changelog.d/feature-k3s-ringtail-runner.feature.md +++ b/docs/changelog.d/feature-k3s-ringtail-runner.feature.md @@ -1 +1 @@ -K3s cluster on ringtail with Forgejo Actions runner (`k8s-amd64` label) for native amd64 container builds, managed via ArgoCD multi-cluster. +K3s cluster on ringtail with Forgejo Actions runner (`k8s-amd64` label) for native amd64 container builds, managed via ArgoCD multi-cluster. Includes 1Password Connect + External Secrets Operator for automated secret management, matching the indri pattern. diff --git a/docs/reference/infrastructure/ringtail.md b/docs/reference/infrastructure/ringtail.md index 686c0eb..ee5a6da 100644 --- a/docs/reference/infrastructure/ringtail.md +++ b/docs/reference/infrastructure/ringtail.md @@ -55,6 +55,12 @@ Ringtail runs a single-node k3s cluster for native amd64 workloads, registered i - **Token:** `/etc/k3s/token` (generated on first provision) - **Kubeconfig:** `/etc/rancher/k3s/k3s.yaml` (world-readable via `--write-kubeconfig-mode=644`) +### Secrets Management + +1Password Connect + External Secrets Operator syncs secrets from 1Password to k8s, matching the [[1password|indri pattern]]. Bootstrap credentials (`op-credentials`, `onepassword-token`) are provisioned by Ansible; ArgoCD manages the operator stack. + +Sync order: `1password-connect-ringtail` -> `external-secrets-crds-ringtail` -> `external-secrets-ringtail` -> `external-secrets-config-ringtail` + ### Workloads | Workload | Namespace | Label |