-
Notifications
You must be signed in to change notification settings - Fork 10
[WIP] Introduce Bootstrap secrets #125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 15 commits
3f0e3d8
9beb5f9
4d69e10
8a5fbae
b913b75
aadf421
a520daa
b8789ea
9823c80
0d6fb00
5c6eaf4
19cf684
1985d41
392baaf
fc8e80a
371089e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,3 +8,92 @@ The main purpose of this collections are to: | |
| loading local secrets files into VP secrets stores. | ||
|
|
||
| 2. Help manage imperative and other utility functions of the cluster | ||
|
|
||
| ## Secrets loading | ||
|
|
||
| Secrets are loaded from a **single primary** values-secret file (plus optional `values-secret.yaml.template` under the | ||
| pattern tree as a last-resort discovery path). There are **no** separate `*-bootstrap.yaml` files or `VALUES_SECRET_BOOTSTRAP` | ||
| paths; early cluster bootstrap uses **per-entry** `bootstrap` fields on v2 secrets in that same primary file. | ||
|
|
||
| ### Primary values-secret | ||
|
|
||
| - **Backing store** comes from `values-global.yaml`: `.global.secretStore.backend` (default `vault`). That drives parsing | ||
| and whether secrets go to Vault or Kubernetes. | ||
| - **Discovery order** when `VALUES_SECRET` is unset (first existing file wins): | ||
| `~/.config/hybrid-cloud-patterns/values-secret-<pattern>.yaml`, | ||
| `~/.config/validated-patterns/values-secret-<pattern>.yaml`, | ||
| `~/values-secret-<pattern>.yaml`, | ||
| `~/values-secret.yaml`, | ||
| then `<pattern_dir>/values-secret.yaml.template`. | ||
| - When `VALUES_SECRET` is set to an existing path, that file is used for the primary load. | ||
|
|
||
| Files may be plain YAML or `ansible-vault` encrypted. | ||
|
|
||
| ### Per-secret `bootstrap` in v2 primary files | ||
|
|
||
| On schema **2.0** primary values-secret files, each secret may set `bootstrap`: | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would make more sense to just have a separate "bootstrap_secrets" section and drop the bootstrap field entirely. If for whatever odd reason a user needs the same secret in both k8s and vault he can just duplicate them in both sections. Also code wise this is a lot simpler to reason about (especially for the UI as well). You also have less risk of breaking things, because the load_secrets bit can stay mostly unchanged and you just need to do some validation on the bootstrap secrets and then create them as k8s secrets normally. And it is a bit simpler for the user to reason about as well. What would be nice is to spell out somewhere the exact use cases for all this and how this will work exactly and why they should/must be bootstrap secrets. Right now we have:
So today for case 1. we pass this to a pattern CR: spec:
clusterGroupName: hub
gitSpec:
targetRepo: git@github.com:mbaldessari/mcg-private.git
targetRevision: private-repo
tokenSecret: private-repo
tokenSecretNamespace: patterns-operatorI assume that we will need to add docs to add an example to the bootstrap_secrets:
- name: private-repo
targetNamespaces:
- patterns-operator
fields:
- name: type
value: git
- name: sshPrivateKey
value: -----BEGIN OPENSSH PRIVATE KEY-----
- name: url
value: git@github.com:mbaldessari/mcg-private.gitIt will also need a label/annotations as well (no matter how we do that). I do wonder if we shouldn't come up with a more user-friendly to define the non secret bits of this (e.g. type, url)?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am opposed to creating a separate section of the file for this. I think in enough cases, people will also want to inject these secrets into their designated secret store, and duplicating them (by handling this with separate sections) is an invitation for the declarations to drift. Labels and annotations are already supported for the none injector.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We've had more internal/offline conversatons about this, and I'm now not opposed to creating a separate section. More work incoming :) |
||
| - **`bootstrap: true`** (or string equivalents such as `yes`, `both`) — the secret is included in the **early** | ||
| Kubernetes inject pass (`none` backend) and is **also** parsed in the **primary** pass into the configured backend | ||
| (Vault or Kubernetes as in `values-global.yaml`). It must not use `onMissingValue: generate` on any field (the early | ||
| pass cannot generate in Vault). | ||
| - **`bootstrap: only`** (or `early`) — the secret is **only** in the early inject pass; the primary pass **omits** it. | ||
| - **Unset / false** — normal primary-only secret. | ||
|
|
||
| Invalid `bootstrap` scalars fail parsing with a clear error. | ||
|
|
||
| Early inject runs **before** the primary backend load: during `playbooks/install.yml`, immediately after the | ||
| pattern-install manifests are applied (`operator_deploy.yml`), then again inside `load_secrets` unless that early pass | ||
| already completed (duplicate inject is skipped). | ||
|
|
||
| ### Playbooks and flows | ||
|
|
||
| - **`playbooks/load_secrets.yml`** | ||
| Respects `.global.secretLoader.disabled` in `values-global.yaml`. When enabled: `cluster_pre_check`, primary file | ||
| discovery, early Kubernetes inject for bootstrap-tagged v2 entries (when present), then parse and load the rest into | ||
| the configured backend. | ||
|
|
||
| - **`playbooks/load_bootstrap_secrets.yml`** | ||
| **Early bootstrap inject only**: `determine_pattern_dir`, `determine_pattern_name`, `pattern_settings`, then only the | ||
| Kubernetes inject for bootstrap-tagged secrets in the primary file (with retries). **Fails** if no primary file exists | ||
| or there are no bootstrap-tagged v2 entries. Does **not** read `secretLoader.disabled` or load into Vault / primary | ||
| backend. For the full early-then-primary flow, use `load_secrets.yml` (or `install.yml`). | ||
|
|
||
| - **`playbooks/display_secrets_info.yml`** | ||
| Loads and displays parsed secrets (using the backend from `values-global`). For v2 files with any bootstrap-tagged | ||
| entries, output is split into **`early_bootstrap_inject`** (none backend, early K8s view; includes `bootstrap: true` | ||
| and `bootstrap: only`) and **`primary_backend`** (configured backend; includes normal secrets and **`bootstrap: true`** | ||
| again so dual-mode entries appear in both groups). Otherwise a single parse is shown as before. | ||
|
|
||
| Typical usage passes the pattern checkout as `pattern_dir` (for example `-e pattern_dir=/path/to/pattern`). If you omit | ||
| it, the same resolution as `pattern_settings` applies: `PATTERN_DIR`, then `PWD`, then the `pwd` command. | ||
|
|
||
| `playbooks/install.yml` imports `load_secrets.yml` after the pattern install playbook. When secret loading is enabled, | ||
| early bootstrap inject from the primary file runs at the end of `operator_deploy.yml` (right after apply), then | ||
| `load_secrets.yml` continues without repeating that inject when it already succeeded. | ||
|
|
||
| ### Early bootstrap inject retries | ||
|
|
||
| Outer retries (parse plus Kubernetes apply) are controlled on the role defaults / extra-vars: | ||
|
|
||
| - `vp_secrets_bootstrap_retry_max` (default `20`) | ||
| - `vp_secrets_bootstrap_retry_delay` (seconds between attempts, default `30`) | ||
|
|
||
| These apply to the early inject path inside `load_secrets` and to `load_bootstrap_secrets.yml`. | ||
|
|
||
| Per-secret namespace readiness (before each `kubernetes.core.k8s` apply) uses role defaults on `k8s_secret_utils`: | ||
|
|
||
| - `k8s_secret_namespace_check_retries` (default `5`) and `k8s_secret_namespace_check_delay` (seconds between attempts, default `45`). | ||
|
|
||
| If the namespace still does not exist after those attempts, the inject fails and the **outer** retry re-runs parse plus | ||
| all secret injections from the start. | ||
|
|
||
| ### Roles (implementation notes) | ||
|
|
||
| - `roles/load_secrets/tasks/main.yml` implements the **combined** flow (early inject from primary file, then primary | ||
| backend load). | ||
| - `roles/load_secrets/tasks/bootstrap_only.yml` is used when you invoke the `load_secrets` role with | ||
| `tasks_from: bootstrap_only.yml` (as `playbooks/load_bootstrap_secrets.yml` does). | ||
| - `roles/find_vp_secrets` resolves the primary file (`tasks/main.yml`). | ||
| - v2 parsing and phase filters (`bootstrap_only`, `exclude_bootstrap`, `all`) are implemented in | ||
| `plugins/module_utils/parse_secrets_v2.py` (single `bootstrap` normalizer: off / dual / early-only). | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,24 @@ | ||
| --- | ||
| # Resolves pattern_dir the same way as the pattern_settings role (extra-vars, PATTERN_DIR, PWD, pwd), | ||
| # then fails if still unset. Used by display_secrets_info, load_values_global, load_bootstrap_secrets, etc. | ||
| - name: Determine pattern dir | ||
| hosts: localhost | ||
| connection: local | ||
| gather_facts: false | ||
| become: false | ||
| vars: | ||
| pattern_dir: '' | ||
| tasks: | ||
| - name: Fail if directory is not set | ||
| - name: Resolve pattern_dir from extra-vars, PATTERN_DIR, PWD, or pwd | ||
| ansible.builtin.include_role: | ||
| name: pattern_settings | ||
| tasks_from: resolve_overrides.yml | ||
|
|
||
| - name: Fail if pattern directory is not set after resolution | ||
| ansible.builtin.fail: | ||
| msg: "pattern_dir variable must be set" | ||
| when: pattern_dir | length == 0 | ||
| msg: >- | ||
| pattern_dir is not set. Pass -e pattern_dir=/path/to/pattern, export PATTERN_DIR to that path, | ||
| or run the playbook from the pattern directory so PWD is correct. | ||
| when: pattern_dir | default('') | string | trim | length == 0 | ||
|
|
||
| - name: Set pattern_dir fact for future plays | ||
| ansible.builtin.set_fact: | ||
| pattern_dir: '{{ pattern_dir }}' | ||
| pattern_dir: "{{ pattern_dir | string | trim }}" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| --- | ||
| # Inject only bootstrap-tagged secrets from the primary values-secret file (none backend, with retries). | ||
| # Does not load secrets into Vault or the primary Kubernetes backend. Does not honor | ||
| # secretLoader.disabled from values-global. Fails if no primary file exists or there are no | ||
| # bootstrap-tagged v2 entries. | ||
| # Optional extra-vars: vp_secrets_bootstrap_retry_max, vp_secrets_bootstrap_retry_delay (seconds). | ||
| - name: Determine pattern directory | ||
| ansible.builtin.import_playbook: ./determine_pattern_dir.yml | ||
|
|
||
| - name: Determine pattern name | ||
| ansible.builtin.import_playbook: ./determine_pattern_name.yml | ||
|
|
||
| - name: Load bootstrap secrets | ||
| hosts: localhost | ||
| connection: local | ||
| gather_facts: false | ||
| become: false | ||
| roles: | ||
| - pattern_settings | ||
|
|
||
| tasks: | ||
| - name: Run bootstrap-only secrets load | ||
| ansible.builtin.include_role: | ||
| name: load_secrets | ||
| tasks_from: bootstrap_only.yml |
Uh oh!
There was an error while loading. Please reload this page.