Skip to content

Commit

Permalink
Merge pull request #8 from appuio/feat/pull-secret-v2
Browse files Browse the repository at this point in the history
Refactor cluster pull-secret management
  • Loading branch information
simu authored Dec 8, 2022
2 parents 2034686 + 5d9de1b commit 73f02d7
Show file tree
Hide file tree
Showing 15 changed files with 478 additions and 29 deletions.
2 changes: 1 addition & 1 deletion .cruft.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"name": "openshift4-config",
"slug": "openshift4-config",
"parameter_key": "openshift4_config",
"test_cases": "defaults",
"test_cases": "defaults pull-secret",
"add_lib": "n",
"add_pp": "n",
"add_golden": "y",
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jobs:
matrix:
instance:
- defaults
- pull-secret
defaults:
run:
working-directory: ${{ env.COMPONENT_NAME }}
Expand All @@ -48,6 +49,7 @@ jobs:
matrix:
instance:
- defaults
- pull-secret
defaults:
run:
working-directory: ${{ env.COMPONENT_NAME }}
Expand Down
2 changes: 1 addition & 1 deletion Makefile.vars.mk
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ KUBENT_IMAGE ?= ghcr.io/doitintl/kube-no-trouble:latest
KUBENT_DOCKER ?= $(DOCKER_CMD) $(DOCKER_ARGS) $(root_volume) --entrypoint=/app/kubent $(KUBENT_IMAGE)

instance ?= defaults
test_instances = tests/defaults.yml
test_instances = tests/defaults.yml tests/pull-secret.yml
8 changes: 7 additions & 1 deletion class/defaults.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
parameters:
openshift4_config:
=_metadata: {}
globalPullSecret: null
globalPullSecrets: {}

images:
kubectl:
registry: docker.io
repository: bitnami/kubectl
tag: 1.25.4

# Fixes cluster upgrades on OCP4.10 clusters with custom `privileged` SCCs.
clusterUpgradeSCCPermissionFix:
Expand Down
35 changes: 24 additions & 11 deletions component/main.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,31 @@ local inv = kap.inventory();
// The hiera parameters for the component
local params = inv.parameters.openshift4_config;

local dockercfg = kube.Secret('pull-secret') {
metadata+: {
namespace: 'openshift-config',
},
stringData+: {
'.dockerconfigjson': params.globalPullSecret,
},
type: 'kubernetes.io/dockerconfigjson',
};
local legacyPullSecret = std.get(params, 'globalPullSecret', null);

local dockercfg = std.trace(
'Your config for openshift4-config uses the deprecated `globalPullSecret` parameter. '
+ 'Please migrate to `globalPullSecrets`. '
+ 'See https://hub.syn.tools/openshift4-config/how-to/migrate-v1.html for details.',
kube.Secret('pull-secret') {
metadata+: {
namespace: 'openshift-config',
annotations+: {
'argocd.argoproj.io/sync-options': 'Prune=false',
},
},
stringData+: {
'.dockerconfigjson': legacyPullSecret,
},
type: 'kubernetes.io/dockerconfigjson',
}
);

// Define outputs below
{
[if params.globalPullSecret != null then '01_dockercfg']: dockercfg,
[if params.clusterUpgradeSCCPermissionFix.enabled then '02_clusterUpgradeSCCPermissionFix']: (import 'privileged-scc.libsonnet'),
[if legacyPullSecret != null then '01_dockercfg']: dockercfg,
[if legacyPullSecret == null && std.length(std.objectFields(params.globalPullSecrets)) > 0 then '99_cluster_pull_secret']:
import 'pull-secret-sync-job.libsonnet',
[if params.clusterUpgradeSCCPermissionFix.enabled then '02_clusterUpgradeSCCPermissionFix']:
import 'privileged-scc.libsonnet',
}
167 changes: 167 additions & 0 deletions component/pull-secret-sync-job.libsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// Template for the ArgoCD sync job to manage the OpenShift cluster pull
// secret.
// The job is modelled after the instructions outlined in
// https://docs.openshift.com/container-platform/4.11/post_installation_configuration/cluster-tasks.html#images-update-global-pull-secret_post-install-cluster-tasks
local kap = import 'lib/kapitan.libjsonnet';
local kube = import 'lib/kube.libjsonnet';
local inv = kap.inventory();
// The hiera parameters for the component
local params = inv.parameters.openshift4_config;

// Jobs need get,update,patch for secret pull-secret in namespace openshift-config
// To ensure the unmanage job has the RBAC in place, all the RBAC objects are
// also in sync-wave -10.
local jobSA = kube.ServiceAccount('syn-cluster-pull-secret-manager') {
metadata+: {
annotations+: {
'argocd.argoproj.io/sync-wave': '-10',
},
namespace: 'openshift-config',
},
};
local jobRole = kube.Role('syn-cluster-pull-secret-manager') {
metadata+: {
annotations+: {
'argocd.argoproj.io/sync-wave': '-10',
},
},
rules: [
{
apiGroups: [ '' ],
resources: [ 'secrets' ],
verbs: [ 'get', 'update', 'patch' ],
resourceNames: [ 'pull-secret' ],
},
],
};
local jobRoleBinding = kube.RoleBinding('syn-cluster-pull-secret-manager') {
metadata+: {
annotations+: {
'argocd.argoproj.io/sync-wave': '-10',
},
},
roleRef_: jobRole,
subjects_: [ jobSA ],
};

local cleanJob = kube.Job('syn-unmanage-cluster-pull-secret') {
metadata+: {
annotations+: {
// run before the default sync wave, but after creating the Job RBAC so
// that we unmanage the cluster pull secret before patching it.
'argocd.argoproj.io/sync-wave': '-9',
'argocd.argoproj.io/hook': 'Sync',
'argocd.argoproj.io/hook-delete-policy': 'HookSucceeded',
},
},
spec+: {
template+: {
spec+: {
serviceAccountName: jobSA.metadata.name,
containers_: {
clean: {
image: '%(registry)s/%(repository)s:%(tag)s' % params.images.kubectl,
command: [
'bash',
'-ce',
'kubectl label secret pull-secret argocd.argoproj.io/instance-;' +
'kubectl annotate secret pull-secret kubectl.kubernetes.io/last-applied-configuration-;' +
'kubectl annotate secret pull-secret argocd.argoproj.io/sync-options-;',
],
},
},
},
},
},
};

local syncScript = kube.Secret('syn-update-cluster-pull-secret-script') {
stringData: {
// The shell script reads the secret `pull-secret`, base64-decodes the
// value of `.dockerconfigjson`, processes it with jq and updates the
// secret with the result of the JQ script (see below).
'sync-secret.sh': |||
#!/bin/bash
set -eu
pull_secret=$(
kubectl get secret pull-secret \
-o go-template='{{index .data ".dockerconfigjson"|base64decode}}'
)
patched_secret=$(
jq -cr '%(script)s' <<<"${pull_secret}"
)
kubectl -n openshift-config patch secret pull-secret \
-p "{\"data\": {\".dockerconfigjson\": \"$patched_secret\"}}"
||| % {
// We generate a JQ script which processes the pull-secret contents from
// params.globalPullSecrets. For each entry in the parameter, we
// generate a `.auths.[key]=[value]`. Jsonnet string formatting
// automatically formats objects as valid JSON when formatting them with
// %s. After processing each entry of the parameter, the script runs
// `del(..|nulls)` to drop any keys with `null` values and `@base64` to
// base64-encode the resulting object.
script:
// We transform the globalPullSecrets object into a list of objects
// representing the entries of the object...
local pullSecretKV = [
{
key: k,
value: params.globalPullSecrets[k],
}
for k in std.objectFields(params.globalPullSecrets)
];
// We use the transformed parameter to generate `.auths."[key]"=value`
// for each entry...
local auth_patches = std.foldl(function(str, cfg) str + '.auths."%(key)s"=%(value)s |' % cfg, pullSecretKV, '');
// and finally we append `del(..|nulls)|@base64` to the script.
auth_patches + 'del(..|nulls)|@base64',
},
},
};

local syncJob = kube.Job('syn-update-cluster-pull-secret') {
metadata+: {
annotations+: {
// run after the default sync wave since we depend on the script secret.
'argocd.argoproj.io/sync-wave': '10',
'argocd.argoproj.io/hook': 'Sync',
'argocd.argoproj.io/hook-delete-policy': 'HookSucceeded',
},
},
spec+: {
template+: {
spec+: {
serviceAccountName: jobSA.metadata.name,
containers_: {
update: kube.Container('update') {
image: '%(registry)s/%(repository)s:%(tag)s' % params.images.kubectl,
command: [ '/script/sync-secret.sh' ],
volumeMounts_: {
script: {
mountPath: '/script',
},
},
},
},
volumes_: {
script: {
secret: {
secretName: syncScript.metadata.name,
defaultMode: 504, // 0770
},
},
},
},
},
},
};

[
jobSA,
jobRole,
jobRoleBinding,
cleanJob,
syncScript,
syncJob,
]
33 changes: 33 additions & 0 deletions docs/modules/ROOT/pages/how-tos/migrate-v1.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
= Migrate from v0.x to v1.x

== Migrate cluster pull secret config

If your cluster config uses `globalPullSecret` parameter, you should migrate your customizations to parameter `globalPullSecrets`.

If you've added additional pull secrets, you can now configure them as

[source,yaml]
----
parameters:
openshift4_config:
globalPullSecrets:
registry.example.com:
auth: ?{vaultkv:${cluster:tenant}/${cluster:name}/openshift4-config/registry.example.com-pull-secret} <1>
email: docker@example.com <2>
----
<1> We strongly recommend that you store the `auth` config for the additional registry in Vault.
Please make sure you store the config as a base64-encoded string in Vault.
<2> Some registries require an email address for authenticated pulls.

If you've removed pull secrets, for example to https://docs.openshift.com/container-platform/4.11/support/remote_health_monitoring/opting-out-of-remote-health-reporting.html#insights-operator-new-pull-secret_opting-out-remote-health-reporting[disable telemetry], you can now remove them with

[source,yaml]
----
parameters:
openshift4_config:
globalPullSecrets:
cloud.openshift.com: null <1>
----
<1> Setting a registry hostname to `null` will remove any auth config for that registry if it's present in the `pull-secret` secret on the cluster.

See the xref:references/parameters.adoc#_globalPullSecrets[parameter docs] for more details.
34 changes: 33 additions & 1 deletion docs/modules/ROOT/pages/references/parameters.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ The parent key for all the following parameters is `openshift4_config`.

[horizontal]
type:: string
default:: null
default:: absent

[IMPORTANT]
====
This parameter is deprecated.
Please migrate your additional pull secrets to parameter `globalPullSecrets`.
====

A Vault reference pointing to the Vault secret containing the docker configuration file in JSON format.
If the parameter is null, the component doesn't manage the cluster's global pull secret.
Expand All @@ -21,6 +27,32 @@ You need to make sure that the existing pull secrets present on a cluster (deplo
Otherwise, OpenShift cluster services may stop working because their respective container images can't be downloaded anymore.
====

== `globalPullSecrets`

[horizontal]
type:: dict
default:: `{}`
example::
+
[source,yaml]
----
docker.io:
email: dockerhub@example.com <1>
auth: ?{vaultkv:${cluster:tenant}/${cluster:name}/openshift4-config/docker.io-pull-secret} <2>
----
<1> Some registries require an email address to be present for authenticated pulls.
<2> We strongly recommend that you store the `auth` value for the registry in Vault.

This parameter allows customizing the OpenShift cluster pull-secret without having to replicate the complete secret contents in Vault.
The component expects entries in the dict to be valid entries for the `.dockerconfigjson` `auths` field.
The component allows users to remove existing entries (also entries originally created by the OpenShift installer) by setting the value for a registry host to `null`.

[NOTE]
====
The component doesn't base64-encode the value provided for `auth`.
Please make sure that you store the `auth` value as base64 in Vault.
====

== `clusterUpgradeSCCPermissionFix`

[horizontal]
Expand Down
5 changes: 5 additions & 0 deletions docs/modules/ROOT/partials/nav.adoc
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
* xref:index.adoc[Home]
.How-to guides
* xref:how-tos/migrate-v1.adoc[]
.Technical reference
* xref:references/parameters.adoc[Parameters]
3 changes: 1 addition & 2 deletions tests/defaults.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
parameters:
openshift4_config:
globalPullSecret: ?{vaultkv:${cluster:tenant}/${cluster:name}/openshift4-config/dockercfg}
openshift4_config: {}

This file was deleted.

Empty file.
Loading

0 comments on commit 73f02d7

Please sign in to comment.