Skip to content

Commit 9a6ca91

Browse files
authored
Merge pull request #22 from appuio/fix/custom-cert-manager-cert
Create cert-manager Certificates in namespace `openshift-console`
2 parents 4c37a22 + 7c250b3 commit 9a6ca91

File tree

7 files changed

+220
-29
lines changed

7 files changed

+220
-29
lines changed

component/main.jsonnet

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,48 @@ local consoleRoutePatch =
153153

154154
local tls = import 'tls.libsonnet';
155155

156+
// If we deploy cert-manager Certificates, we annotate namespace
157+
// openshift-config with the `kyvernoAnnotation` defined in `tls.libsonnet`
158+
// through a ResourceLocker patch. This triggers the the Kyverno policy to
159+
// copy the cert-manager TLS secrets into namespace openshift-config.
160+
//
161+
// We add the ResourceLocker patch to ArgoCD sync-wave 5, so it's guaranteed
162+
// to be applied in the cluster after the certificate has been issued and
163+
// before the custom openshift console route config is applied.
164+
//
165+
// NOTE: Due to the current implementation of the resource locker component
166+
// library this prevents other components from also providing ResourceLocker
167+
// patches for the `openshift-config` namespace.
168+
local openshiftConfigNsAnnotationPatch =
169+
local needsPatch = hostname != null && std.length(tls.certs) > 0;
170+
if needsPatch then
171+
local target = kube.Namespace('openshift-config');
172+
local patch = {
173+
metadata: {
174+
annotations: tls.kyvernoAnnotation,
175+
},
176+
};
177+
[
178+
if obj.kind == 'ResourceLocker' then
179+
obj {
180+
metadata+: {
181+
annotations+: {
182+
// Annotate namespace openshift-config before we configure the
183+
// route certificate, see patch above
184+
'argocd.argoproj.io/sync-wave': '5',
185+
},
186+
},
187+
}
188+
else
189+
obj
190+
for obj in
191+
rl.Patch(
192+
target,
193+
patch,
194+
patchstrategy='application/merge-patch+json'
195+
)
196+
];
197+
156198
{
157199
'00_namespace': kube.Namespace(params.namespace) {
158200
metadata+: {
@@ -184,4 +226,6 @@ local tls = import 'tls.libsonnet';
184226
},
185227
[if !oldConfig && consoleRoutePatch != null then '20_ingress_config_patch']:
186228
consoleRoutePatch,
229+
[if openshiftConfigNsAnnotationPatch != null then '20_openshift_config_ns_annotation_patch']:
230+
openshiftConfigNsAnnotationPatch,
187231
}

component/tls.libsonnet

Lines changed: 65 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ local cm = import 'lib/cert-manager.libsonnet';
22
local com = import 'lib/commodore.libjsonnet';
33
local kap = import 'lib/kapitan.libjsonnet';
44
local kube = import 'lib/kube.libjsonnet';
5+
local kyverno = import 'lib/kyverno.libsonnet';
56

67
local inv = kap.inventory();
78
local params = inv.parameters.openshift4_console;
@@ -35,25 +36,73 @@ local secrets = std.filter(
3536
]
3637
);
3738

38-
local certs = std.filter(
39-
function(it) it != null,
39+
local kyvernoAnnotation = {
40+
'syn.tools/openshift4-console': 'secret-target-namespace',
41+
};
42+
43+
local makeCert(c, cert) =
44+
assert
45+
std.member(inv.applications, 'kyverno') :
46+
'You need to add component `kyverno` to the cluster to be able to deploy cert-manager Certificate resources for the the openshift web console.';
4047
[
41-
local cert = params.cert_manager_certs[c];
42-
if cert != null then
43-
cm.cert(c) {
44-
metadata+: {
45-
// Certificates must be deployed in namespace openshift-config
46-
namespace: 'openshift-config',
47-
},
48-
spec+: {
49-
secretName: '%s' % c,
50-
},
51-
} + com.makeMergeable(cert)
52-
for c in std.objectFields(params.cert_manager_certs)
53-
]
54-
);
48+
cm.cert(c) {
49+
metadata+: {
50+
// Certificate must be deployed in the same namespace as the web
51+
// console, otherwise OpenShift won't admit the HTTP01 solver route.
52+
// We copy the resulting secret to namespace 'openshift-config' with
53+
// Kyverno, see below.
54+
namespace: params.namespace,
55+
},
56+
spec+: {
57+
secretName: '%s' % c,
58+
},
59+
} + com.makeMergeable(cert),
60+
kyverno.ClusterPolicy('openshift4-console-sync-' + c) {
61+
spec: {
62+
rules: [
63+
{
64+
name: 'Sync "%s" certificate secret to openshift-config' % c,
65+
match: {
66+
resources: {
67+
kinds: [ 'Namespace' ],
68+
// We copy the created TLS secret into all namespaces which
69+
// have the annotation specified in `kyvernoAnnotation`.
70+
annotations: kyvernoAnnotation,
71+
},
72+
},
73+
generate: {
74+
kind: 'Secret',
75+
name: c,
76+
namespace: '{{request.object.metadata.name}}',
77+
synchronize: true,
78+
clone: {
79+
namespace: params.namespace,
80+
name: c,
81+
},
82+
},
83+
},
84+
],
85+
},
86+
},
87+
];
88+
89+
local certs =
90+
std.foldl(
91+
function(arr, e) arr + e,
92+
std.filter(
93+
function(it) it != null,
94+
[
95+
local cert = params.cert_manager_certs[c];
96+
if cert != null then
97+
makeCert(c, cert)
98+
for c in std.objectFields(params.cert_manager_certs)
99+
],
100+
),
101+
[]
102+
);
55103

56104
{
57105
certs: certs,
58106
secrets: secrets,
107+
kyvernoAnnotation: kyvernoAnnotation,
59108
}

docs/modules/ROOT/pages/references/parameters.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ This allows users to remove certificates which were configured higher up in the
117117
The dictionary keys are used as `metadata.name` and `spec.secretName` for the resulting `Certificate` resources.
118118
The dictionary values are then directly directly merged into the mostly empty `Certificate` resources.
119119

120+
OpenShift won't admit the route for the HTTP01 solver pod unless the `Certificate` resources are deployed in the same namespace as the web console.
121+
This behavior is caused by a security feature in the OpenShift ingress controller operator to not allow malicious actors to abuse hostnames which are already in use in other namespaces.
122+
123+
However, since OpenShift requires that custom TLS secrets for the OpenShift console are stored in namespace `openshift-config`, we deploy a Kyverno policy to clone the TLS secret created by cert-manager into namespace `openshift-config` for each `Certificate` resource.
124+
Because of that, the component requires that Kyverno is installed on the cluster via the https://hub.syn.tools/kyverno/[Commodore component `kyverno`], when `Certificate` resources are configured in the hierarchy.
125+
120126

121127
== Example: Custom hostname in cluster's app domain
122128

tests/custom-route-managed-tls.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1+
applications:
2+
- kyverno
13
parameters:
24
kapitan:
35
dependencies:
46
- type: https
57
source: https://raw.githubusercontent.com/projectsyn/component-cert-manager/v2.2.0/lib/cert-manager.libsonnet
68
output_path: vendor/lib/cert-manager.libsonnet
79
- type: https
8-
source: https://raw.githubusercontent.com/projectsyn/component-resource-locker/v2.1.0/lib/resource-locker.libjsonnet
10+
source: https://raw.githubusercontent.com/projectsyn/component-resource-locker/v2.3.2/lib/resource-locker.libjsonnet
911
output_path: vendor/lib/resource-locker.libjsonnet
12+
- type: https
13+
source: https://raw.githubusercontent.com/projectsyn/component-kyverno/v1.4.0/lib/kyverno.libsonnet
14+
output_path: vendor/lib/kyverno.libsonnet
1015

1116
resource_locker:
1217
namespace: syn-resource-locker

tests/golden/custom-route-managed-tls/openshift4-console/openshift4-console/01_certs.yaml

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,36 @@ metadata:
55
labels:
66
name: console-cluster-example-org-tls
77
name: console-cluster-example-org-tls
8-
namespace: openshift-config
8+
namespace: openshift-console
99
spec:
1010
dnsNames:
1111
- console.cluster.example.org
1212
issuerRef:
1313
kind: ClusterIssuer
1414
name: letsencrypt-staging
1515
secretName: console-cluster-example-org-tls
16+
---
17+
apiVersion: kyverno.io/v1
18+
kind: ClusterPolicy
19+
metadata:
20+
annotations: {}
21+
labels:
22+
name: openshift4-console-sync-console-cluster-example-org-tls
23+
name: openshift4-console-sync-console-cluster-example-org-tls
24+
spec:
25+
rules:
26+
- generate:
27+
clone:
28+
name: console-cluster-example-org-tls
29+
namespace: openshift-console
30+
kind: Secret
31+
name: console-cluster-example-org-tls
32+
namespace: '{{request.object.metadata.name}}'
33+
synchronize: true
34+
match:
35+
resources:
36+
annotations:
37+
syn.tools/openshift4-console: secret-target-namespace
38+
kinds:
39+
- Namespace
40+
name: Sync "console-cluster-example-org-tls" certificate secret to openshift-config

tests/golden/custom-route-managed-tls/openshift4-console/openshift4-console/20_ingress_config_patch.yaml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ apiVersion: v1
22
kind: ServiceAccount
33
metadata:
44
labels:
5-
name: cluster-manager
6-
name: cluster-manager
5+
name: ingress-config-openshift-io-cluster-manager
6+
name: ingress-config-openshift-io-cluster-manager
77
namespace: syn-resource-locker
88
---
99
apiVersion: rbac.authorization.k8s.io/v1
1010
kind: ClusterRole
1111
metadata:
1212
labels:
13-
name: syn-resource-locker-cluster-manager
14-
name: syn-resource-locker-cluster-manager
13+
name: syn-resource-locker-ingress-config-openshift-io-cluster-manager
14+
name: syn-resource-locker-ingress-config-openshift-io-cluster-manager
1515
rules:
1616
- apiGroups:
1717
- config.openshift.io
@@ -27,15 +27,15 @@ apiVersion: rbac.authorization.k8s.io/v1
2727
kind: ClusterRoleBinding
2828
metadata:
2929
labels:
30-
name: syn-resource-locker-cluster-manager
31-
name: syn-resource-locker-cluster-manager
30+
name: syn-resource-locker-ingress-config-openshift-io-cluster-manager
31+
name: syn-resource-locker-ingress-config-openshift-io-cluster-manager
3232
roleRef:
3333
apiGroup: rbac.authorization.k8s.io
3434
kind: ClusterRole
35-
name: syn-resource-locker-cluster-manager
35+
name: syn-resource-locker-ingress-config-openshift-io-cluster-manager
3636
subjects:
3737
- kind: ServiceAccount
38-
name: cluster-manager
38+
name: ingress-config-openshift-io-cluster-manager
3939
namespace: syn-resource-locker
4040
---
4141
apiVersion: redhatcop.redhat.io/v1alpha1
@@ -45,8 +45,8 @@ metadata:
4545
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
4646
argocd.argoproj.io/sync-wave: '10'
4747
labels:
48-
name: cluster
49-
name: cluster
48+
name: ingress-config-openshift-io-cluster
49+
name: ingress-config-openshift-io-cluster
5050
namespace: syn-resource-locker
5151
spec:
5252
patches:
@@ -60,4 +60,4 @@ spec:
6060
kind: Ingress
6161
name: cluster
6262
serviceAccountRef:
63-
name: cluster-manager
63+
name: ingress-config-openshift-io-cluster-manager
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
apiVersion: v1
2+
kind: ServiceAccount
3+
metadata:
4+
labels:
5+
name: namespace-openshift-config-manager
6+
name: namespace-openshift-config-manager
7+
namespace: syn-resource-locker
8+
---
9+
apiVersion: rbac.authorization.k8s.io/v1
10+
kind: ClusterRole
11+
metadata:
12+
labels:
13+
name: syn-resource-locker-namespace-openshift-config-manager
14+
name: syn-resource-locker-namespace-openshift-config-manager
15+
rules:
16+
- apiGroups:
17+
- ''
18+
resources:
19+
- namespaces
20+
verbs:
21+
- get
22+
- list
23+
- patch
24+
- watch
25+
---
26+
apiVersion: rbac.authorization.k8s.io/v1
27+
kind: ClusterRoleBinding
28+
metadata:
29+
labels:
30+
name: syn-resource-locker-namespace-openshift-config-manager
31+
name: syn-resource-locker-namespace-openshift-config-manager
32+
roleRef:
33+
apiGroup: rbac.authorization.k8s.io
34+
kind: ClusterRole
35+
name: syn-resource-locker-namespace-openshift-config-manager
36+
subjects:
37+
- kind: ServiceAccount
38+
name: namespace-openshift-config-manager
39+
namespace: syn-resource-locker
40+
---
41+
apiVersion: redhatcop.redhat.io/v1alpha1
42+
kind: ResourceLocker
43+
metadata:
44+
annotations:
45+
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
46+
argocd.argoproj.io/sync-wave: '5'
47+
labels:
48+
name: namespace-openshift-config
49+
name: namespace-openshift-config
50+
namespace: syn-resource-locker
51+
spec:
52+
patches:
53+
- id: patch1
54+
patchTemplate: "\"metadata\":\n \"annotations\":\n \"syn.tools/openshift4-console\"\
55+
: \"secret-target-namespace\""
56+
patchType: application/merge-patch+json
57+
targetObjectRef:
58+
apiVersion: v1
59+
kind: Namespace
60+
name: openshift-config
61+
serviceAccountRef:
62+
name: namespace-openshift-config-manager

0 commit comments

Comments
 (0)