diff --git a/README.md b/README.md index 473ec718..6d2fdb5c 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,10 @@ GitOps repository for managing Kubernetes deployments via ArgoCD. All changes to │ ├── user-exchange-topology/ # Helm chart │ ├── users-v1/ # UOWS REST API v1 (legacy, master-v1 branch) │ ├── users-v2/ # UOWS REST API v2 (current, main branch) -│ └── vault-operator-config/ # Per-cluster Vault connection config +│ └── vault-operator-config/ # Per-cluster, per-namespace Vault connection config +│ └── dev/ +│ ├── apps/ # VaultConnection, VaultAuth, ServiceAccount + RBAC for `apps` +│ └── cron-jobs/ # Same, for `cron-jobs` (ServiceAccount: cron-jobs-vault-op) │ └── .github/workflows/ └── create-release-pr.yaml # Called by source repos to create deploy PRs @@ -95,6 +98,25 @@ generators: #- name: prod-fallback ``` +The `vault-operator-config` ApplicationSet is the exception: it uses a matrix generator (cluster × namespace), because a `VaultConnection` and `VaultAuth` must exist in every namespace that consumes Vault secrets: + +```yaml +generators: +- matrix: + generators: + - list: + elements: + - name: dev-v3 + path: dev + + - list: + elements: + - namespace: apps + - namespace: cron-jobs +``` + +This produces one Application per cluster/namespace pair,
named `{{.name}}-{{.namespace}}-vault-operator-config`,
sourced from `components/ua/vault-operator-config/{{.path}}/{{.namespace}}`
and deployed into `{{.namespace}}`. If you enable the fallback cluster,
the matching `{{.path}}/apps` and `{{.path}}/cron-jobs` directories must exist first, otherwise the Application will fail to sync. + See [moving UA apps to the fallback cluster](https://github.com/isisbusapps/ISISBusApps/wiki/Moving-UA-apps-to-the-fallback-cluster) for details. ### Clusters @@ -118,7 +140,25 @@ spec: name: user-office-web-service-v2 # K8s Secret name ``` -The Vault operator and its per-cluster config (`vault-operator-config/`) are deployed at sync-wave `-10` and `-1` respectively, before any application workloads. +Each namespace that uses Vault needs its own `VaultConnection` and `VaultAuth` (`static-auth`), defined in `components/ua/vault-operator-config/{env}/{namespace}/`. +
A `VaultStaticSecret` must live in the same namespace as the `VaultAuth` it references. Each `VaultAuth` authenticates with a ServiceAccount in its own namespace, created alongside it in `vault-auth-service-account.yaml` together with the RBAC it needs (including a `system:auth-delegator` ClusterRoleBinding): + +| Namespace | ServiceAccount | Config directory (dev) | +|-----------|----------------|------------------------| +| `apps` | `vault-op` | `vault-operator-config/dev/apps/` | +| `cron-jobs` | `cron-jobs-vault-op` | `vault-operator-config/dev/cron-jobs/` | + +> **Note:** the per-namespace layout (`apps/`, `cron-jobs/`) currently applies to `dev` only. `prod` still uses a single flat `vault-operator-config/prod/` directory deployed into `apps`. + +The `VaultAuth` uses the `cluster` Kubernetes auth role on the `submissions` mount. That role is configured in Vault, not in this repo, and its `bound_service_account_names` and `bound_service_account_namespaces` must include each ServiceAccount and namespace above. + +The Vault operator and its per-cluster, per-namespace config (`vault-operator-config/`) are deployed at sync-wave `-10` and `-1` respectively, before any application workloads. + +### Adding Vault access for a new namespace + +1. Copy `components/ua/vault-operator-config/dev/cron-jobs/` to `.../dev/{namespace}/` and update the namespace and ServiceAccount name in all three files. Keep the ServiceAccount name identical across the `ServiceAccount`, the `RoleBinding` and `ClusterRoleBinding` subjects, and the `VaultAuth`. +2. Add `- namespace: {namespace}` to the second list in `argocd/dev/ua/vault-operator-configs/app.yaml`. +3. Add the new ServiceAccount name and namespace to the bound lists of the Vault auth role. ## How Deployments Happen @@ -146,6 +186,6 @@ See [docs/uows-release-guide.md](docs/uows-release-guide.md) for the UOWS-specif | Wave | Resources | |------|-----------| | -10 | Vault Operator (Helm) | -| -1 | Vault Operator Config, UA Shared Config | +| -1 | Vault Operator Config (per namespace. e.g. `apps`, `cron-jobs`), UA Shared Config | | 0 | All application workloads | -| 1 | Health Check Service (dev only) | +| 1 | Health Check Service (dev only) | \ No newline at end of file diff --git a/argocd/dev/ua/vault-operator-configs/app.yaml b/argocd/dev/ua/vault-operator-configs/app.yaml index 8eb54969..cacf1df6 100644 --- a/argocd/dev/ua/vault-operator-configs/app.yaml +++ b/argocd/dev/ua/vault-operator-configs/app.yaml @@ -7,27 +7,33 @@ spec: goTemplate: true goTemplateOptions: ["missingkey=error"] generators: - - list: - elements: - # Names of clusters to deploy the app to - - name: dev-v3 - path: dev - # Uncomment if you want to deploy to dev-microk8s-alternative - - name: dev-microk8s-alternative - path: dev-fallback + - matrix: + generators: + - list: + elements: + # Names of clusters to deploy the app to + - name: dev-v3 + path: dev + # Uncomment if you want to deploy to dev-microk8s-alternative + # - name: dev-microk8s-alternative + # path: dev-fallback + - list: + elements: + - namespace: apps + - namespace: cron-jobs template: metadata: - name: '{{.name}}-vault-operator-config' + name: '{{.name}}-{{.namespace}}-vault-operator-config' annotations: argocd.argoproj.io/sync-wave: "-1" spec: project: u-a-project source: - path: components/ua/vault-operator-config/{{.path}} + path: components/ua/vault-operator-config/{{.path}}/{{.namespace}} repoURL: 'https://github.com/isisbusapps/gitops' targetRevision: main destination: - namespace: apps + namespace: '{{.namespace}}' name: '{{.name}}' syncPolicy: automated: diff --git a/components/ua/vault-operator-config/dev/vault-auth-service-account.yaml b/components/ua/vault-operator-config/dev/apps/vault-auth-service-account.yaml similarity index 100% rename from components/ua/vault-operator-config/dev/vault-auth-service-account.yaml rename to components/ua/vault-operator-config/dev/apps/vault-auth-service-account.yaml diff --git a/components/ua/vault-operator-config/dev/vault-auth.yaml b/components/ua/vault-operator-config/dev/apps/vault-auth.yaml similarity index 85% rename from components/ua/vault-operator-config/dev/vault-auth.yaml rename to components/ua/vault-operator-config/dev/apps/vault-auth.yaml index 21743fa8..9779694d 100644 --- a/components/ua/vault-operator-config/dev/vault-auth.yaml +++ b/components/ua/vault-operator-config/dev/apps/vault-auth.yaml @@ -15,4 +15,4 @@ spec: serviceAccount: vault-op audiences: - vault - - https://kubernetes.default.svc.cluster.local \ No newline at end of file + - https://kubernetes.default.svc.cluster.local diff --git a/components/ua/vault-operator-config/dev/apps/vault-connection.yaml b/components/ua/vault-operator-config/dev/apps/vault-connection.yaml new file mode 100644 index 00000000..2054bdfb --- /dev/null +++ b/components/ua/vault-operator-config/dev/apps/vault-connection.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultConnection +metadata: + namespace: apps + name: secrets-isis-connection +spec: + address: "https://secrets.isis.rl.ac.uk" diff --git a/components/ua/vault-operator-config/dev/cron-jobs/vault-auth-service-account.yaml b/components/ua/vault-operator-config/dev/cron-jobs/vault-auth-service-account.yaml new file mode 100644 index 00000000..99e243f3 --- /dev/null +++ b/components/ua/vault-operator-config/dev/cron-jobs/vault-auth-service-account.yaml @@ -0,0 +1,49 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + namespace: cron-jobs + name: cron-jobs-vault-op +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: service-account-read + namespace: cron-jobs +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: service-account-read +subjects: + - kind: ServiceAccount + name: cron-jobs-vault-op + namespace: cron-jobs +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: service-account-read-cron-jobs +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: + - kind: ServiceAccount + name: cron-jobs-vault-op + namespace: cron-jobs +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: service-account-read + namespace: cron-jobs +rules: + - apiGroups: [""] + resources: ["serviceaccounts"] + verbs: ["get", "watch", "list"] + - apiGroups: ["authentication.k8s.io"] + resources: ["tokenreviews"] + verbs: ["create"] + - apiGroups: [ "authentication.k8s.io" ] + resources: [ "subjectaccessreviews" ] + verbs: [ "create" ] \ No newline at end of file diff --git a/components/ua/vault-operator-config/dev/cron-jobs/vault-auth.yaml b/components/ua/vault-operator-config/dev/cron-jobs/vault-auth.yaml new file mode 100644 index 00000000..f5353442 --- /dev/null +++ b/components/ua/vault-operator-config/dev/cron-jobs/vault-auth.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultAuth +metadata: + name: static-auth + namespace: cron-jobs +spec: + vaultConnectionRef: secrets-isis-connection + method: kubernetes + mount: submissions + kubernetes: + role: cluster + serviceAccount: cron-jobs-vault-op + audiences: + - vault + - https://kubernetes.default.svc.cluster.local \ No newline at end of file diff --git a/components/ua/vault-operator-config/dev/vault-connection.yaml b/components/ua/vault-operator-config/dev/cron-jobs/vault-connection.yaml similarity index 87% rename from components/ua/vault-operator-config/dev/vault-connection.yaml rename to components/ua/vault-operator-config/dev/cron-jobs/vault-connection.yaml index ad933107..b8fddeff 100644 --- a/components/ua/vault-operator-config/dev/vault-connection.yaml +++ b/components/ua/vault-operator-config/dev/cron-jobs/vault-connection.yaml @@ -2,7 +2,7 @@ apiVersion: secrets.hashicorp.com/v1beta1 kind: VaultConnection metadata: - namespace: apps + namespace: cron-jobs name: secrets-isis-connection spec: address: "https://secrets.isis.rl.ac.uk" \ No newline at end of file