Add k3s, 1Password Connect, and systemd nix-container-builder to ringtail #209

Merged
eblume merged 7 commits from feature/k3s-ringtail-runner into main 2026-02-18 21:15:31 -08:00
9 changed files with 173 additions and 23 deletions
Showing only changes of commit 0d3269e8d6 - Show all commits

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 <noreply@anthropic.com>
Erich Blume 2026-02-18 19:40:21 -08:00

View file

@ -4,10 +4,18 @@
become: true become: true
pre_tasks: pre_tasks:
- name: Fetch Forgejo runner registration token from 1Password - name: Fetch 1Password Connect credentials from 1Password
ansible.builtin.command: ansible.builtin.command:
cmd: op read "op://vg6xf6vvfmoh5hqjjhlhbeoaie/Forgejo Secrets/runner_reg" cmd: op read "op://vg6xf6vvfmoh5hqjjhlhbeoaie/1Password Connect/credentials-file"
register: _runner_token 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 changed_when: false
delegate_to: localhost delegate_to: localhost
become: false become: false
@ -56,36 +64,32 @@
delay: 5 delay: 5
until: _k3s_ready.rc == 0 until: _k3s_ready.rc == 0
- name: Create forgejo-runner namespace - name: Create 1password namespace
ansible.builtin.command: k3s kubectl create namespace forgejo-runner ansible.builtin.command: k3s kubectl create namespace 1password
register: _ns register: _ns
changed_when: _ns.rc == 0 changed_when: _ns.rc == 0
failed_when: _ns.rc != 0 and 'AlreadyExists' not in _ns.stderr failed_when: _ns.rc != 0 and 'AlreadyExists' not in _ns.stderr
- name: Check if forgejo-runner-env secret exists - name: Create or update op-credentials secret
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
ansible.builtin.shell: ansible.builtin.shell:
cmd: | cmd: |
set -o pipefail set -o pipefail
k3s kubectl create secret generic forgejo-runner-env \ k3s kubectl create secret generic op-credentials \
--namespace=forgejo-runner \ --namespace=1password \
--from-literal=RUNNER_TOKEN={{ _runner_token.stdout }} \ --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 - --dry-run=client -o yaml | k3s kubectl apply -f -
executable: /bin/bash executable: /bin/bash
when: _secret_exists.rc == 0
changed_when: true changed_when: true
no_log: true no_log: true

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -4,3 +4,4 @@ resources:
- namespace.yaml - namespace.yaml
- configmap.yaml - configmap.yaml
- deployment.yaml - deployment.yaml
- external-secret.yaml

View file

@ -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.

View file

@ -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) - **Token:** `/etc/k3s/token` (generated on first provision)
- **Kubeconfig:** `/etc/rancher/k3s/k3s.yaml` (world-readable via `--write-kubeconfig-mode=644`) - **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 ### Workloads
| Workload | Namespace | Label | | Workload | Namespace | Label |