feat: sync runtime secrets from doppler
This commit is contained in:
@@ -230,6 +230,7 @@ jobs:
|
|||||||
-e "tailscale_tailnet=${{ secrets.TAILSCALE_TAILNET }}" \
|
-e "tailscale_tailnet=${{ secrets.TAILSCALE_TAILNET }}" \
|
||||||
-e "tailscale_oauth_client_id=${{ secrets.TAILSCALE_OAUTH_CLIENT_ID }}" \
|
-e "tailscale_oauth_client_id=${{ secrets.TAILSCALE_OAUTH_CLIENT_ID }}" \
|
||||||
-e "tailscale_oauth_client_secret=${{ secrets.TAILSCALE_OAUTH_CLIENT_SECRET }}" \
|
-e "tailscale_oauth_client_secret=${{ secrets.TAILSCALE_OAUTH_CLIENT_SECRET }}" \
|
||||||
|
-e "doppler_hetznerterra_service_token=${{ secrets.DOPPLER_HETZNERTERRA_SERVICE_TOKEN }}" \
|
||||||
-e "grafana_admin_password=${{ secrets.GRAFANA_ADMIN_PASSWORD }}" \
|
-e "grafana_admin_password=${{ secrets.GRAFANA_ADMIN_PASSWORD }}" \
|
||||||
-e "cluster_name=k8s-cluster"
|
-e "cluster_name=k8s-cluster"
|
||||||
env:
|
env:
|
||||||
|
|||||||
14
README.md
14
README.md
@@ -169,6 +169,7 @@ Set these in your Gitea repository settings (**Settings** → **Secrets** → **
|
|||||||
| `TAILSCALE_TAILNET` | Tailnet domain (e.g., `yourtailnet.ts.net`) |
|
| `TAILSCALE_TAILNET` | Tailnet domain (e.g., `yourtailnet.ts.net`) |
|
||||||
| `TAILSCALE_OAUTH_CLIENT_ID` | Tailscale OAuth client ID for Kubernetes Operator |
|
| `TAILSCALE_OAUTH_CLIENT_ID` | Tailscale OAuth client ID for Kubernetes Operator |
|
||||||
| `TAILSCALE_OAUTH_CLIENT_SECRET` | Tailscale OAuth client secret for Kubernetes Operator |
|
| `TAILSCALE_OAUTH_CLIENT_SECRET` | Tailscale OAuth client secret for Kubernetes Operator |
|
||||||
|
| `DOPPLER_HETZNERTERRA_SERVICE_TOKEN` | Doppler service token for `hetznerterra` runtime secrets |
|
||||||
| `GRAFANA_ADMIN_PASSWORD` | Optional admin password for Grafana (auto-generated if unset) |
|
| `GRAFANA_ADMIN_PASSWORD` | Optional admin password for Grafana (auto-generated if unset) |
|
||||||
| `RUNNER_ALLOWED_CIDRS` | Optional CIDR list for CI runner access if you choose to pass it via tfvars/secrets |
|
| `RUNNER_ALLOWED_CIDRS` | Optional CIDR list for CI runner access if you choose to pass it via tfvars/secrets |
|
||||||
| `SSH_PUBLIC_KEY` | SSH public key content |
|
| `SSH_PUBLIC_KEY` | SSH public key content |
|
||||||
@@ -178,6 +179,19 @@ Set these in your Gitea repository settings (**Settings** → **Secrets** → **
|
|||||||
|
|
||||||
This repo now includes a Flux GitOps layout for phased migration from imperative Ansible applies to continuous reconciliation.
|
This repo now includes a Flux GitOps layout for phased migration from imperative Ansible applies to continuous reconciliation.
|
||||||
|
|
||||||
|
### Runtime secrets
|
||||||
|
|
||||||
|
Runtime cluster secrets are moving to Doppler + External Secrets Operator.
|
||||||
|
|
||||||
|
- Doppler project: `hetznerterra`
|
||||||
|
- Initial auth: service token via `DOPPLER_HETZNERTERRA_SERVICE_TOKEN`
|
||||||
|
- First synced secrets:
|
||||||
|
- `GRAFANA_ADMIN_PASSWORD`
|
||||||
|
- `WEAVE_GITOPS_ADMIN_USERNAME`
|
||||||
|
- `WEAVE_GITOPS_ADMIN_PASSWORD_BCRYPT_HASH`
|
||||||
|
|
||||||
|
Terraform/bootstrap secrets remain in Gitea Actions secrets and are not managed by Doppler.
|
||||||
|
|
||||||
### Repository layout
|
### Repository layout
|
||||||
|
|
||||||
- `clusters/prod/`: cluster entrypoint and Flux reconciliation objects
|
- `clusters/prod/`: cluster entrypoint and Flux reconciliation objects
|
||||||
|
|||||||
@@ -54,9 +54,14 @@ Add these secrets in your Gitea repository settings:
|
|||||||
|
|
||||||
### Application Secrets
|
### Application Secrets
|
||||||
|
|
||||||
|
#### `DOPPLER_HETZNERTERRA_SERVICE_TOKEN`
|
||||||
|
- Doppler service token for the `hetznerterra` project runtime secrets
|
||||||
|
- Used by External Secrets Operator bootstrap
|
||||||
|
- Recommended scope: `hetznerterra` project, `prod` config only
|
||||||
|
|
||||||
#### `GRAFANA_ADMIN_PASSWORD`
|
#### `GRAFANA_ADMIN_PASSWORD`
|
||||||
- Admin password for Grafana
|
- Transitional fallback only while migrating observability secrets to Doppler
|
||||||
- Generate a strong password: `openssl rand -base64 32`
|
- In steady state, store this in Doppler as `GRAFANA_ADMIN_PASSWORD`
|
||||||
|
|
||||||
## Setting Up Secrets
|
## Setting Up Secrets
|
||||||
|
|
||||||
@@ -82,6 +87,7 @@ Check the workflow logs to verify all secrets are being used correctly.
|
|||||||
|
|
||||||
- Never commit secrets to the repository
|
- Never commit secrets to the repository
|
||||||
- Use strong, unique passwords for Grafana and other services
|
- Use strong, unique passwords for Grafana and other services
|
||||||
|
- Prefer Doppler for runtime app/platform secrets after cluster bootstrap
|
||||||
- Rotate Tailscale auth keys periodically
|
- Rotate Tailscale auth keys periodically
|
||||||
- Review OAuth client permissions regularly
|
- Review OAuth client permissions regularly
|
||||||
- The workflow automatically opens SSH/API access only for the runner's IP during deployment
|
- The workflow automatically opens SSH/API access only for the runner's IP during deployment
|
||||||
|
|||||||
17
ansible/roles/doppler-bootstrap/tasks/main.yml
Normal file
17
ansible/roles/doppler-bootstrap/tasks/main.yml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
- name: Ensure Doppler service token is provided
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- doppler_hetznerterra_service_token | length > 0
|
||||||
|
fail_msg: doppler_hetznerterra_service_token must be provided for External Secrets bootstrap.
|
||||||
|
|
||||||
|
- name: Ensure external-secrets namespace exists
|
||||||
|
shell: kubectl create namespace external-secrets --dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
changed_when: true
|
||||||
|
|
||||||
|
- name: Apply Doppler service token secret
|
||||||
|
shell: >-
|
||||||
|
kubectl -n external-secrets create secret generic doppler-hetznerterra-service-token
|
||||||
|
--from-literal=dopplerToken='{{ doppler_hetznerterra_service_token }}'
|
||||||
|
--dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
changed_when: true
|
||||||
@@ -123,6 +123,13 @@
|
|||||||
roles:
|
roles:
|
||||||
- private-access
|
- private-access
|
||||||
|
|
||||||
|
- name: Bootstrap Doppler access for External Secrets
|
||||||
|
hosts: control_plane[0]
|
||||||
|
become: true
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- doppler-bootstrap
|
||||||
|
|
||||||
- name: Finalize
|
- name: Finalize
|
||||||
hosts: localhost
|
hosts: localhost
|
||||||
connection: local
|
connection: local
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
apiVersion: external-secrets.io/v1
|
||||||
|
kind: ClusterSecretStore
|
||||||
|
metadata:
|
||||||
|
name: doppler-hetznerterra
|
||||||
|
spec:
|
||||||
|
provider:
|
||||||
|
doppler:
|
||||||
|
auth:
|
||||||
|
secretRef:
|
||||||
|
dopplerToken:
|
||||||
|
name: doppler-hetznerterra-service-token
|
||||||
|
key: dopplerToken
|
||||||
|
namespace: external-secrets
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||||
|
kind: HelmRelease
|
||||||
|
metadata:
|
||||||
|
name: external-secrets
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
interval: 10m
|
||||||
|
targetNamespace: external-secrets
|
||||||
|
chart:
|
||||||
|
spec:
|
||||||
|
chart: external-secrets
|
||||||
|
version: 2.1.0
|
||||||
|
sourceRef:
|
||||||
|
kind: HelmRepository
|
||||||
|
name: external-secrets
|
||||||
|
namespace: flux-system
|
||||||
|
install:
|
||||||
|
createNamespace: true
|
||||||
|
remediation:
|
||||||
|
retries: 3
|
||||||
|
upgrade:
|
||||||
|
remediation:
|
||||||
|
retries: 3
|
||||||
|
values:
|
||||||
|
installCRDs: true
|
||||||
|
serviceMonitor:
|
||||||
|
enabled: false
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
apiVersion: source.toolkit.fluxcd.io/v1
|
||||||
|
kind: HelmRepository
|
||||||
|
metadata:
|
||||||
|
name: external-secrets
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
interval: 1h
|
||||||
|
url: https://charts.external-secrets.io
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
resources:
|
||||||
|
- namespace.yaml
|
||||||
|
- helmrepository-external-secrets.yaml
|
||||||
|
- helmrelease-external-secrets.yaml
|
||||||
|
- clustersecretstore-doppler-hetznerterra.yaml
|
||||||
4
infrastructure/addons/external-secrets/namespace.yaml
Normal file
4
infrastructure/addons/external-secrets/namespace.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: external-secrets
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
apiVersion: external-secrets.io/v1
|
||||||
|
kind: ExternalSecret
|
||||||
|
metadata:
|
||||||
|
name: cluster-user-auth
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
refreshInterval: 1h
|
||||||
|
secretStoreRef:
|
||||||
|
name: doppler-hetznerterra
|
||||||
|
kind: ClusterSecretStore
|
||||||
|
target:
|
||||||
|
name: cluster-user-auth
|
||||||
|
creationPolicy: Owner
|
||||||
|
template:
|
||||||
|
type: Opaque
|
||||||
|
data:
|
||||||
|
username: "{{ .fluxAdminUsername }}"
|
||||||
|
password: "{{ .fluxAdminPasswordHash }}"
|
||||||
|
data:
|
||||||
|
- secretKey: fluxAdminUsername
|
||||||
|
remoteRef:
|
||||||
|
key: WEAVE_GITOPS_ADMIN_USERNAME
|
||||||
|
- secretKey: fluxAdminPasswordHash
|
||||||
|
remoteRef:
|
||||||
|
key: WEAVE_GITOPS_ADMIN_PASSWORD_BCRYPT_HASH
|
||||||
@@ -27,9 +27,8 @@ spec:
|
|||||||
adminUser:
|
adminUser:
|
||||||
create: true
|
create: true
|
||||||
createClusterRole: true
|
createClusterRole: true
|
||||||
createSecret: true
|
createSecret: false
|
||||||
username: admin
|
username: admin
|
||||||
passwordHash: "$2b$12$iVSpwZxP98Y1T4AOwj.TAeMsrOuQ6vWfhXfG4Gan9ay.qGMaRNdrC"
|
|
||||||
rbac:
|
rbac:
|
||||||
create: true
|
create: true
|
||||||
impersonationResourceNames:
|
impersonationResourceNames:
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
|
- cluster-user-auth-externalsecret.yaml
|
||||||
- gitrepository-weave-gitops.yaml
|
- gitrepository-weave-gitops.yaml
|
||||||
- helmrelease-weave-gitops.yaml
|
- helmrelease-weave-gitops.yaml
|
||||||
- traefik-helmchartconfig-flux-entrypoint.yaml
|
- traefik-helmchartconfig-flux-entrypoint.yaml
|
||||||
|
|||||||
15
infrastructure/addons/kustomization-external-secrets.yaml
Normal file
15
infrastructure/addons/kustomization-external-secrets.yaml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||||
|
kind: Kustomization
|
||||||
|
metadata:
|
||||||
|
name: addon-external-secrets
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
interval: 10m
|
||||||
|
prune: true
|
||||||
|
sourceRef:
|
||||||
|
kind: GitRepository
|
||||||
|
name: platform
|
||||||
|
path: ./infrastructure/addons/external-secrets
|
||||||
|
wait: true
|
||||||
|
timeout: 5m
|
||||||
|
suspend: false
|
||||||
@@ -10,6 +10,8 @@ spec:
|
|||||||
kind: GitRepository
|
kind: GitRepository
|
||||||
name: platform
|
name: platform
|
||||||
path: ./infrastructure/addons/flux-ui
|
path: ./infrastructure/addons/flux-ui
|
||||||
|
dependsOn:
|
||||||
|
- name: addon-external-secrets
|
||||||
wait: true
|
wait: true
|
||||||
timeout: 5m
|
timeout: 5m
|
||||||
suspend: false
|
suspend: false
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ spec:
|
|||||||
kind: GitRepository
|
kind: GitRepository
|
||||||
name: platform
|
name: platform
|
||||||
path: ./infrastructure/addons/observability
|
path: ./infrastructure/addons/observability
|
||||||
|
dependsOn:
|
||||||
|
- name: addon-external-secrets
|
||||||
wait: true
|
wait: true
|
||||||
timeout: 5m
|
timeout: 5m
|
||||||
suspend: false
|
suspend: false
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ kind: Kustomization
|
|||||||
resources:
|
resources:
|
||||||
- kustomization-ccm.yaml
|
- kustomization-ccm.yaml
|
||||||
- kustomization-csi.yaml
|
- kustomization-csi.yaml
|
||||||
|
- kustomization-external-secrets.yaml
|
||||||
- kustomization-flux-ui.yaml
|
- kustomization-flux-ui.yaml
|
||||||
- kustomization-tailscale-operator.yaml
|
- kustomization-tailscale-operator.yaml
|
||||||
- kustomization-observability.yaml
|
- kustomization-observability.yaml
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
apiVersion: external-secrets.io/v1
|
||||||
|
kind: ExternalSecret
|
||||||
|
metadata:
|
||||||
|
name: grafana-admin
|
||||||
|
namespace: observability
|
||||||
|
spec:
|
||||||
|
refreshInterval: 1h
|
||||||
|
secretStoreRef:
|
||||||
|
name: doppler-hetznerterra
|
||||||
|
kind: ClusterSecretStore
|
||||||
|
target:
|
||||||
|
name: grafana-admin-credentials
|
||||||
|
creationPolicy: Owner
|
||||||
|
template:
|
||||||
|
type: Opaque
|
||||||
|
data:
|
||||||
|
admin-user: admin
|
||||||
|
admin-password: "{{ .grafanaAdminPassword }}"
|
||||||
|
data:
|
||||||
|
- secretKey: grafanaAdminPassword
|
||||||
|
remoteRef:
|
||||||
|
key: GRAFANA_ADMIN_PASSWORD
|
||||||
@@ -24,6 +24,10 @@ spec:
|
|||||||
values:
|
values:
|
||||||
grafana:
|
grafana:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
admin:
|
||||||
|
existingSecret: grafana-admin-credentials
|
||||||
|
userKey: admin-user
|
||||||
|
passwordKey: admin-password
|
||||||
grafana.ini:
|
grafana.ini:
|
||||||
server:
|
server:
|
||||||
root_url: http://observability/grafana/
|
root_url: http://observability/grafana/
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1
|
|||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
- namespace.yaml
|
- namespace.yaml
|
||||||
|
- grafana-admin-externalsecret.yaml
|
||||||
- traefik-tailscale-service.yaml
|
- traefik-tailscale-service.yaml
|
||||||
- grafana-ingress.yaml
|
- grafana-ingress.yaml
|
||||||
- prometheus-ingress.yaml
|
- prometheus-ingress.yaml
|
||||||
|
|||||||
Reference in New Issue
Block a user