From 653655d1d71850861f787a4b23eda8766ce9f705 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 14 May 2026 17:20:16 -0500 Subject: [PATCH 1/4] Add bootstrap blog post --- ...6-05-12-introducing-bootstrap-secrets.adoc | 238 ++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 content/blog/2026-05-12-introducing-bootstrap-secrets.adoc diff --git a/content/blog/2026-05-12-introducing-bootstrap-secrets.adoc b/content/blog/2026-05-12-introducing-bootstrap-secrets.adoc new file mode 100644 index 0000000000..2517dd69d9 --- /dev/null +++ b/content/blog/2026-05-12-introducing-bootstrap-secrets.adoc @@ -0,0 +1,238 @@ +--- + date: 2026-05-12 + title: Introducing "Bootstrap" Secrets + summary: Validated Patterns now includes the idea of "bootstrap" secrets that will be injected directly into your cluster during the secrets loading process. + author: Martin Jackson + blog_tags: + - patterns + - argocd + - gitops + - ansible + - secrets +--- +:toc: +:imagesdir: /images + +== Preamble + +We have recognized secrets as one of the challenges of GitOps since the beginning +of the Validated Patterns initiative. As a consequence, we have put thought and +effort into making the secrets management process with Validated Patterns as easy +as we can, while still modeling good security practices. + +One challenge that we have not fully addressed, though, is the occasional need to +inject secrets into a cluster in order to enable basic cluster functionality. One +example of when this might happen is when a cluster is attached to an enterprise +storage array which requires secrets to be present to access the storage. Another +good example is when the pattern itself is in a credential-protected repository, +such that ArgoCD, the OpenShift GitOps Operator, needs credentials to be able to +render the pattern. Previously, users had to use their own mechanisms to inject +secrets into the cluster, and the Validated Patterns framework did not help with +this. + +With this blog post, we are happy to announce that the Validated Patterns framework +now has a mechanism to help with this problem, which requires minimal effort to +use and offers some convenience mechanisms, including all of the convenience +functions (like path and ini_file parsing) that the Patterns secret loading system +already uses. Additionally, it works within the existing Validated Patterns workflow. + +== What are "bootstrap" secrets? + +The "bootstrap" secrets process is somewhat exceptional. The vast majority of secrets in +Validated Patterns usage are ordinary. But what if you need to do authentication to start +installing your pattern? Specific examples are when you are using an authenticated private +repository to hold the pattern itself, as well as any secrets you might need to create a +default storageclass. In such a case, these secrets must be injected before the pattern +object itself is created, and since it contains the list of namespaces to create, the early +phase secrets have a "chicken-and-egg" problem. Previous instructions have provided for the +manual creation of these objects, but this can be tedious and error prone. + +Bootstrap secrets are injected immediately after the initial cluster validations in the +ordinary installation flow. They are allowed to create their own namespaces, but to help with +idempotence, they check to see if the namespace exists first, and only create if it is missing. + +== How to use bootstrap secrets + +The Validated Patterns secrets v2.0 file format adds a new section, `boostrap_secrets`. These +secrets, and these only, will be considered in the "early" secrets installation phase. The +early secrets installation phase happens just prior to the Patterns Operator and CR creation. +This is because the pattern CR needs to be able to clone the repository in order to work. A +secret that is linked to the Pattern CR will automatically be copied to Patterns-managed +Argo instances. + +Bootstrap secrets have some special properties: + +1. They are always injected using the "none" injector. +2. They are allowed to create `targetNamespaces` if they do not already exist. (They check +before creating them, to avoid overwriting existing namespaces with annotations.) +3. Bootstrap secrets loading is always attempted if the section is present, even if the global +secrets loading feature is disabled via `global.secretsLoader.disabled` being set to `true`. + +Additionally, bootstrap secrets are checked and loaded during a `make load-secrets` run. + +== Kubernetes-specific secret injector features + +While it has long been possible, it has not been well documented (or advertized) that +Validated Patterns can inject secrets directly into kubernetes. The currently available injectors +that use kubernetes secret injection are `kubernetes` and `none`. (They were named based on the +External Secrets backend they are intended to support.) Some additional conveniences +are available in values-secret files for kubernetes secret injection, including: + +* `targetNamespaces`: The same secret can be injected into multiple namespaces. This is useful, +for example, when you have to inject the same ArgoCD secret into two or more different namespaces. The +loader treats each secret/namespace as a separate item for injection. Of course a list containing +one item is always acceptable as well. The loader will attempt to create a minimal namespace if it does +not already exist, but only during the early (boostrap) secrets loading phase. +* `Labels and annotations`: You can add arbitrary labels and/or annotations to your secret objects. +* `type`: Some secret types benefit from having type associated with them. The secret loader +defaults to `Opaque` when no type is specified, but will honor a type that is specified. + +== Example Bootstrap Secret file + +Given following file, ~/values-secret.yaml: + +[yaml,source] +---- +--- +version: "2.0" + +bootstrap_secrets: + - name: test-secret + annotations: + validatedpatterns.io/example: foo + labels: + validatedpatterns.io/injected-secrets: "true" + targetNamespaces: + - default + - vp-gitops + type: kubernetes.io/basic + fields: + - name: username + value: user + - name: password + value: password + +secrets: +# ... +# As before +---- + +The loader would generate and inject the following two objects: + +[yaml,source] +---- +apiVersion: v1 +data: + password: cGFzc3dvcmQ= + username: dXNlcg== +kind: Secret +metadata: + annotations: + validatedpatterns.io/example: foo + creationTimestamp: "2026-05-12T16:47:15Z" + labels: + validatedpatterns.io/injected-secrets: "true" + name: test-secret + namespace: default + resourceVersion: "158877" + uid: be74939e-6acc-41e8-96f3-0439f9e2e0af +type: kubernetes.io/basic +---- + +and + +[yaml,source] +---- +apiVersion: v1 +data: + password: cGFzc3dvcmQ= + username: dXNlcg== +kind: Secret +metadata: + annotations: + validatedpatterns.io/example: foo + creationTimestamp: "2026-05-12T16:48:02Z" + labels: + validatedpatterns.io/injected-secrets: "true" + name: test-secret + namespace: vp-gitops + resourceVersion: "158893" + uid: 311ba451-da6d-427b-8e0f-1293ef7b18a4 +type: kubernetes.io/basic +---- + +== Private Git Repositories - a Practical Use of this feature + +The primary use of this feature is to enable the use of private repositories to hold Validated Patterns. +These repositories need authentication. The patterns framework has included support for private repositories +for some time, but this feature introduces the ability to inject the secret necessary to run those patterns +without extra manual steps. + +Given the following repository definition in values-global.yaml: + +[yaml,source] +---- +--- +global: + pattern: private-pattern-test + options: + useCSV: false + syncPolicy: Automatic + installPlanApproval: Automatic +main: + git: + repoURL: git@github.com:mhjacks/private-pattern-test.git + revision: main + tokenSecret: private-repo + tokenSecretNamespace: patterns-operator + clusterGroupName: hub + multiSourceConfig: + enabled: true + clusterGroupChartVersion: "0.9.*" +---- + +The following secret file configuration will inject the private-repo secret needed into the +patterns-operator namespace (creating it if it does not already exist, which at the time it +is normally installed, it will not): + +[yaml,source] +---- +--- +version: "2.0" + +bootstrap_secrets: + - name: private-repo + targetNamespaces: + - patterns-operator + fields: + - name: type + value: git + - name: sshPrivateKey + path: ~/.ssh/id_ed25519 + - name: url + value: git@github.com:mhjacks/private-pattern-test.git +---- + +== Technical Details of the change + +The "bootstrap" loading process ignores the values-global secrets backend setting during the bootstrap +phase, forcing the use of the "none" provider instead, which will use the kubernetes secrets injection +roles with special flags enabled. It searches the discovered secrets file for the bootstrap secrets +section, loading them as described above. This step runs between cluster validation and pattern operator and object +creation. The regular secrets injection happens after pattern creation, as before. It is also possible +to force bootstrap secret loading via `./pattern.sh ansible-playbook rhvp.cluster_utils.load_bootstrap_secrets`, +but this is normally not necessary as it is integrated into the typical `make install` process, as +well as the `make load-secrets` process. + +The benefit to this approach is that all existing patterns can make use of this feature without +changing their Makefiles. Patterns without the bootstrap secrets section will skip bootstrap secrets +loading as they will have no bootstrap secrets to load. + +For each kubernetes secret that is generated, the loader will check for the namespace, and create a +minimal namespace if it does not already exist, and then create the secret. + +== Summary + +This change improves the Validated Patterns experience with secrets, streamlining the process of +installing private patterns. Additional cases, like injecting secrets for CSI drivers, is also +possible using this new mechanism. From c3b2dce5c35b01200deadbda4836a0f81b354109 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 14 May 2026 17:48:57 -0500 Subject: [PATCH 2/4] Add bootstrap section to private repos --- content/learn/private-repos.adoc | 47 ++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/content/learn/private-repos.adoc b/content/learn/private-repos.adoc index 902ae9261e..b09ef186c6 100644 --- a/content/learn/private-repos.adoc +++ b/content/learn/private-repos.adoc @@ -44,6 +44,30 @@ stringData: -----END OPENSSH PRIVATE KEY----- ---- +This secret can now be created with the `bootstrap_secrets` feature like so: + +[source,yaml] +---- +version: "2.0" + +bootstrap_secrets: + - name: private-repo + targetNamespaces: + - openshift-operators + fields: + - name: type + value: git + - name: sshPrivateKey + path: | + -----BEGIN OPENSSH PRIVATE KEY----- + a3... + ... + ... + -----END OPENSSH PRIVATE KEY----- + - name: url + value: git@github.com:mbaldessari/mcg-private.git +---- + === Deploy the pattern with the secret Reference the secret you created by passing `TOKEN_SECRET` and `TOKEN_NAMESPACE` to the install command: @@ -95,6 +119,29 @@ stringData: password: glpat-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ---- +Using the `bootstrap_secrets` feature, this can be created as follows: + +[source,yaml] +---- +version: "2.0" + +bootstrap_secrets: + - name: private-repo + targetNamespaces: + - openshift-operators + labels: + argocd.argoproj.io/secret-type: repository + fields: + - name: type + value: git + - name: url + value: https://gitlab.com/dminnear-rh/mcg-private.git + - name: username + value: oauth2 + - name: password + value: glpat-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +---- + NOTE: The username must be `oauth2`, not your GitLab handle. Then reference the secret in the install: From cbd263a32ff010ded53ff74e09887c5c5db68823 Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 14 May 2026 18:04:05 -0500 Subject: [PATCH 3/4] Add link to ESO in secrets doc --- content/learn/getting-started-secret-management.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/content/learn/getting-started-secret-management.adoc b/content/learn/getting-started-secret-management.adoc index 25ccd3ba4a..06147973d9 100644 --- a/content/learn/getting-started-secret-management.adoc +++ b/content/learn/getting-started-secret-management.adoc @@ -40,7 +40,8 @@ ESO [NOTE] ==== -As of November 15, 2024, ESO is not officially supported by Red Hat as a product. +As of November 11, 2025, ESO is officially supported by Red Hat as the External Secrets Operator for OpenShift. +The Validated Patterns initiative provides a link:https://charts.validatedpatterns.io/charts/openshift-external-secrets[chart] to configure the supported version, which is available via the Red Hat Ecosystem Catalog. ==== ESO's custom file format and utilities streamlines secret management by allowing file references and supporting encrypted secret storage. The design prioritizes security through multi-layer encryption and simplifies key management. In particular the ini key type is especially helpful for handling AWS credentials, where mismanagement could lead to unauthorized use and potential financial or operational issues. From 8b86f3f2d65ce69909705eddb691b275a73f13ed Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 14 May 2026 18:11:15 -0500 Subject: [PATCH 4/4] Update API version to match supported version and newer community ESO --- content/learn/getting-started-secret-management.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/learn/getting-started-secret-management.adoc b/content/learn/getting-started-secret-management.adoc index 06147973d9..d3df742f7e 100644 --- a/content/learn/getting-started-secret-management.adoc +++ b/content/learn/getting-started-secret-management.adoc @@ -163,7 +163,7 @@ $ vi mysecret-external-secret.yaml [source,yaml] ---- --- -apiVersion: "external-secrets.io/v1beta1" +apiVersion: "external-secrets.io/v1" kind: ExternalSecret metadata: name: config-demo-mysecret <1>