Skip to content

Commit dcb191d

Browse files
authored
Merge pull request #387 from Ygnas/feat/kustomize-openshift-overlay
Feat: add OpenShift overlay with AuthBridge configs, SCC, and Istio shared trust
2 parents 1b4e2bc + 6290e08 commit dcb191d

17 files changed

Lines changed: 591 additions & 4 deletions

kagenti-operator/Makefile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ docker-buildx: ## Build and push docker image for the manager for cross-platform
193193
- $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} .
194194
- $(CONTAINER_TOOL) buildx rm kagenti-operator-builder
195195

196+
.PHONY: manifests-openshift
197+
manifests-openshift: manifests kustomize ## Build OpenShift standalone kustomize overlay manifests.
198+
$(KUSTOMIZE) build config/openshift
199+
196200
.PHONY: build-installer
197201
build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment.
198202
mkdir -p dist
@@ -229,10 +233,19 @@ deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in
229233
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
230234
$(KUSTOMIZE) build config/default | $(KUBECTL) apply --server-side -f -
231235

236+
.PHONY: deploy-openshift
237+
deploy-openshift: manifests kustomize ## Deploy controller with OpenShift extras (istio-trust, authbridge, SCC).
238+
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
239+
$(KUSTOMIZE) build config/openshift | $(KUBECTL) apply --server-side -f -
240+
232241
.PHONY: undeploy
233242
undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
234243
$(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f -
235244

245+
.PHONY: undeploy-openshift
246+
undeploy-openshift: kustomize ## Undeploy controller with OpenShift extras. Call with ignore-not-found=true to ignore resource not found errors.
247+
$(KUSTOMIZE) build config/openshift | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f -
248+
236249
.PHONY: chart
237250
chart: manifests kustomize
238251
cd config/manager && $(KUSTOMIZE) edit set image controller=$(shell echo ${IMG} | sed 's/\(:.*\)v/\1/')

kagenti-operator/cmd/main.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,9 @@ func init() {
7676
}
7777

7878
// getOperatorNamespace returns the namespace the operator is running in.
79-
// Reads from POD_NAMESPACE environment variable (set via downward API in deployment),
80-
// falling back to kagenti-system if not set.
79+
// In production, the manager_webhook_patch.yaml injects POD_NAMESPACE via
80+
// the downward API, so the fallback is effectively dead code. It exists for
81+
// local development and test runs where the webhook patch is not applied.
8182
func getOperatorNamespace() string {
8283
if ns := os.Getenv("POD_NAMESPACE"); ns != "" {
8384
return ns
@@ -309,6 +310,11 @@ func main() {
309310
})
310311
}
311312

313+
// ========================================
314+
// Operator namespace resolution
315+
// ========================================
316+
controller.SetClusterDefaultsNamespace(getOperatorNamespace())
317+
312318
cmCacheNamespaces := buildConfigMapCacheNamespaces(
313319
requireA2ASignature, spireTrustBundleConfigMapName, spireTrustBundleConfigMapNS,
314320
)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# AuthBridge runtime configuration template.
2+
# The operator copies this to agent namespaces via ensureNamespaceConfigMaps.
3+
apiVersion: v1
4+
kind: ConfigMap
5+
metadata:
6+
name: authbridge-runtime-config
7+
namespace: kagenti-operator-system
8+
labels:
9+
app.kubernetes.io/name: kagenti-operator
10+
app.kubernetes.io/component: authbridge
11+
app.kubernetes.io/managed-by: kustomize
12+
data:
13+
config.yaml: |
14+
spiffe: {}
15+
pipeline:
16+
inbound:
17+
plugins:
18+
- name: jwt-validation
19+
config:
20+
issuer: "http://keycloak.localtest.me:8080/realms/kagenti"
21+
keycloak_url: "http://keycloak-service.keycloak.svc:8080"
22+
keycloak_realm: "kagenti"
23+
outbound:
24+
plugins:
25+
- name: token-exchange
26+
config:
27+
keycloak_url: "http://keycloak-service.keycloak.svc:8080"
28+
keycloak_realm: "kagenti"
29+
default_policy: "passthrough"
30+
identity:
31+
type: "client-secret"
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# Envoy proxy configuration template for envoy-sidecar mode.
2+
# The operator copies this to agent namespaces via ensureNamespaceConfigMaps.
3+
apiVersion: v1
4+
kind: ConfigMap
5+
metadata:
6+
name: envoy-config
7+
namespace: kagenti-operator-system
8+
labels:
9+
app.kubernetes.io/name: kagenti-operator
10+
app.kubernetes.io/component: authbridge
11+
app.kubernetes.io/managed-by: kustomize
12+
data:
13+
envoy.yaml: |
14+
admin:
15+
address:
16+
socket_address:
17+
protocol: TCP
18+
address: 127.0.0.1
19+
port_value: 9901
20+
21+
static_resources:
22+
listeners:
23+
- name: outbound_listener
24+
address:
25+
socket_address:
26+
protocol: TCP
27+
address: 0.0.0.0
28+
port_value: 15123
29+
listener_filters:
30+
- name: envoy.filters.listener.original_dst
31+
typed_config:
32+
"@type": type.googleapis.com/envoy.extensions.filters.listener.original_dst.v3.OriginalDst
33+
- name: envoy.filters.listener.tls_inspector
34+
typed_config:
35+
"@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector
36+
filter_chains:
37+
- filter_chain_match:
38+
transport_protocol: tls
39+
filters:
40+
- name: envoy.filters.network.tcp_proxy
41+
typed_config:
42+
"@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
43+
stat_prefix: outbound_tls_passthrough
44+
cluster: original_destination
45+
- filter_chain_match:
46+
transport_protocol: raw_buffer
47+
filters:
48+
- name: envoy.filters.network.http_connection_manager
49+
typed_config:
50+
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
51+
stat_prefix: outbound_http
52+
codec_type: AUTO
53+
route_config:
54+
name: outbound_routes
55+
virtual_hosts:
56+
- name: catch_all
57+
domains: ["*"]
58+
routes:
59+
- match:
60+
prefix: "/"
61+
route:
62+
cluster: original_destination
63+
timeout: 300s
64+
http_filters:
65+
- name: envoy.filters.http.ext_proc
66+
typed_config:
67+
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_proc.v3.ExternalProcessor
68+
grpc_service:
69+
envoy_grpc:
70+
cluster_name: ext_proc_cluster
71+
timeout: 300s
72+
allow_mode_override: true
73+
processing_mode:
74+
request_header_mode: SEND
75+
response_header_mode: SEND
76+
request_body_mode: NONE
77+
response_body_mode: NONE
78+
- name: envoy.filters.http.router
79+
typed_config:
80+
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
81+
82+
- name: inbound_listener
83+
address:
84+
socket_address:
85+
protocol: TCP
86+
address: 0.0.0.0
87+
port_value: 15124
88+
listener_filters:
89+
- name: envoy.filters.listener.original_dst
90+
typed_config:
91+
"@type": type.googleapis.com/envoy.extensions.filters.listener.original_dst.v3.OriginalDst
92+
filter_chains:
93+
- filters:
94+
- name: envoy.filters.network.http_connection_manager
95+
typed_config:
96+
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
97+
stat_prefix: inbound_http
98+
codec_type: AUTO
99+
route_config:
100+
name: inbound_routes
101+
virtual_hosts:
102+
- name: local_app
103+
domains: ["*"]
104+
routes:
105+
- match:
106+
prefix: "/"
107+
route:
108+
cluster: original_destination
109+
timeout: 300s
110+
http_filters:
111+
- name: envoy.filters.http.lua
112+
typed_config:
113+
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
114+
inline_code: |
115+
function envoy_on_request(request_handle)
116+
request_handle:headers():add("x-authbridge-direction", "inbound")
117+
end
118+
- name: envoy.filters.http.ext_proc
119+
typed_config:
120+
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_proc.v3.ExternalProcessor
121+
grpc_service:
122+
envoy_grpc:
123+
cluster_name: ext_proc_cluster
124+
timeout: 300s
125+
allow_mode_override: true
126+
processing_mode:
127+
request_header_mode: SEND
128+
response_header_mode: SEND
129+
request_body_mode: NONE
130+
response_body_mode: NONE
131+
- name: envoy.filters.http.router
132+
typed_config:
133+
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
134+
135+
clusters:
136+
- name: original_destination
137+
connect_timeout: 30s
138+
type: ORIGINAL_DST
139+
lb_policy: CLUSTER_PROVIDED
140+
original_dst_lb_config:
141+
use_http_header: false
142+
143+
- name: ext_proc_cluster
144+
connect_timeout: 5s
145+
type: STATIC
146+
lb_policy: ROUND_ROBIN
147+
http2_protocol_options: {}
148+
load_assignment:
149+
cluster_name: ext_proc_cluster
150+
endpoints:
151+
- lb_endpoints:
152+
- endpoint:
153+
address:
154+
socket_address:
155+
address: 127.0.0.1
156+
port_value: 9090
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
resources:
2+
- authbridge-runtime-config.yaml
3+
- envoy-config.yaml
4+
- spiffe-helper-config.yaml
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# SPIFFE helper configuration template.
2+
# The operator copies this to agent namespaces via ensureNamespaceConfigMaps.
3+
apiVersion: v1
4+
kind: ConfigMap
5+
metadata:
6+
name: spiffe-helper-config
7+
namespace: kagenti-operator-system
8+
labels:
9+
app.kubernetes.io/name: kagenti-operator
10+
app.kubernetes.io/component: authbridge
11+
app.kubernetes.io/managed-by: kustomize
12+
data:
13+
helper.conf: |
14+
agent_address = "/spiffe-workload-api/spire-agent.sock"
15+
cmd = ""
16+
cmd_args = ""
17+
svid_file_name = "/opt/svid.pem"
18+
svid_key_file_name = "/opt/svid_key.pem"
19+
svid_bundle_file_name = "/opt/svid_bundle.pem"
20+
cert_file_mode = 0644
21+
key_file_mode = 0640
22+
jwt_svids = [{jwt_audience="http://keycloak.localtest.me:8080/realms/kagenti", jwt_svid_file_name="/opt/jwt_svid.token"}]
23+
jwt_svid_file_mode = 0644
24+
include_federated_domains = true

kagenti-operator/config/default/manager_webhook_patch.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ spec:
1212
- containerPort: 9443
1313
name: webhook-server
1414
protocol: TCP
15+
env:
16+
- name: POD_NAMESPACE
17+
valueFrom:
18+
fieldRef:
19+
fieldPath: metadata.namespace
1520
volumeMounts:
1621
- mountPath: /tmp/k8s-webhook-server/serving-certs
1722
name: cert
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# cert-manager Certificates for multi-mesh Istio shared trust.
2+
# Root CA in cert-manager namespace, intermediate CAs per Istio control plane.
3+
#
4+
# Dependency chain: Root CA (istio-mesh-root-ca, 10-year duration) issues all
5+
# intermediate CAs via the istio-mesh-ca ClusterIssuer. If the root CA secret
6+
# (istio-mesh-root-ca-secret) is accidentally deleted, all intermediates become
7+
# orphaned and workload mTLS will break.
8+
# Recovery: re-create this Certificate to generate a new root CA, then restart
9+
# cert-manager to re-issue the intermediate CA certificates.
10+
---
11+
apiVersion: cert-manager.io/v1
12+
kind: Certificate
13+
metadata:
14+
name: istio-mesh-root-ca
15+
namespace: cert-manager
16+
labels:
17+
app.kubernetes.io/name: kagenti-operator
18+
app.kubernetes.io/component: shared-trust
19+
app.kubernetes.io/managed-by: kustomize
20+
spec:
21+
isCA: true
22+
commonName: istio-mesh-root-ca
23+
duration: 87600h
24+
renewBefore: 720h
25+
secretName: istio-mesh-root-ca-secret
26+
privateKey:
27+
algorithm: RSA
28+
size: 4096
29+
issuerRef:
30+
name: istio-mesh-root-selfsigned
31+
kind: ClusterIssuer
32+
---
33+
apiVersion: cert-manager.io/v1
34+
kind: Certificate
35+
metadata:
36+
name: istio-cacerts-default
37+
namespace: istio-system
38+
labels:
39+
app.kubernetes.io/name: kagenti-operator
40+
app.kubernetes.io/component: shared-trust
41+
app.kubernetes.io/managed-by: kustomize
42+
spec:
43+
isCA: true
44+
commonName: istio-ca-default
45+
duration: 8760h
46+
renewBefore: 720h
47+
secretName: istio-cacerts-default-cert
48+
privateKey:
49+
algorithm: RSA
50+
size: 2048
51+
issuerRef:
52+
name: istio-mesh-ca
53+
kind: ClusterIssuer
54+
---
55+
apiVersion: cert-manager.io/v1
56+
kind: Certificate
57+
metadata:
58+
name: istio-cacerts-openshift-gateway
59+
namespace: openshift-ingress
60+
labels:
61+
app.kubernetes.io/name: kagenti-operator
62+
app.kubernetes.io/component: shared-trust
63+
app.kubernetes.io/managed-by: kustomize
64+
spec:
65+
isCA: true
66+
commonName: istio-ca-openshift-gateway
67+
duration: 8760h
68+
renewBefore: 720h
69+
secretName: istio-cacerts-og-cert
70+
privateKey:
71+
algorithm: RSA
72+
size: 2048
73+
issuerRef:
74+
name: istio-mesh-ca
75+
kind: ClusterIssuer
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# ClusterIssuers for multi-mesh Istio shared trust via cert-manager.
2+
---
3+
apiVersion: cert-manager.io/v1
4+
kind: ClusterIssuer
5+
metadata:
6+
name: istio-mesh-root-selfsigned
7+
labels:
8+
app.kubernetes.io/name: kagenti-operator
9+
app.kubernetes.io/component: shared-trust
10+
app.kubernetes.io/managed-by: kustomize
11+
spec:
12+
selfSigned: {}
13+
---
14+
apiVersion: cert-manager.io/v1
15+
kind: ClusterIssuer
16+
metadata:
17+
name: istio-mesh-ca
18+
labels:
19+
app.kubernetes.io/name: kagenti-operator
20+
app.kubernetes.io/component: shared-trust
21+
app.kubernetes.io/managed-by: kustomize
22+
spec:
23+
ca:
24+
secretName: istio-mesh-root-ca-secret
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
resources:
2+
- cluster-issuers.yaml
3+
- certificates.yaml

0 commit comments

Comments
 (0)