Skip to content

Commit

Permalink
Merge pull request #14 from projectsyn/application-based-certificate
Browse files Browse the repository at this point in the history
Encrypt cluster-internal traffic
  • Loading branch information
ccremer authored Jun 21, 2021
2 parents 0266775 + 520f96b commit ed9dc9d
Show file tree
Hide file tree
Showing 10 changed files with 354 additions and 22 deletions.
52 changes: 48 additions & 4 deletions class/defaults.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ parameters:
# This lookup table controls whether to enable the chart depending on the value of `keycloak.database.provider`
builtin: true
external: false
=_ingress_annotations:
# This lookup table controls whether to set specific annotations depending on the value of `keycloak.ingress.controller`,
# `keycloak.tls.termination` and `keycloak.tls.provider`
nginx:
passthrough:
certmanager:
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
vault:
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
reencrypt:
certmanager:
kubernetes.io/tls-acme: 'true'
cert-manager.io/cluster-issuer: ${keycloak:tls:certmanager:issuer:name}
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
vault:
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"

namespace: syn-${_instance}
release_name: keycloak
Expand All @@ -20,12 +36,32 @@ parameters:
password: '?{vaultkv:${customer:name}/${cluster:name}/${_instance}/admin-password}'
# Replica count
replicas: 2
# TLS configuration
tls:
provider: certmanager
termination: reencrypt
secretName: keycloak-tls
vault:
cert: '?{vaultkv:${customer:name}/${cluster:name}/${_instance}/keycloak-cert}'
certKey: '?{vaultkv:${customer:name}/${cluster:name}/${_instance}/keycloak-cert-key}'
certmanager:
apiVersion: cert-manager.io/v1alpha2
certName: ${keycloak:tls:secretName}
issuer:
name: letsencrypt-production
kind: ClusterIssuer
group: cert-manager.io
# Ingress or Route should be enabled on the distribution level
ingress:
enabled: false
annotations: {}
secretName: ${keycloak:fqdn}
enabled: true
controller: nginx
controllerNamespace: ingress-nginx
annotations: ${keycloak:_ingress_annotations:${keycloak:ingress:controller}:${keycloak:tls:termination}:${keycloak:tls:provider}}
tls:
secretName: ingress-tls
vault:
cert: '?{vaultkv:${customer:name}/${cluster:name}/${_instance}/ingress-cert}'
certKey: '?{vaultkv:${customer:name}/${cluster:name}/${_instance}/ingress-cert-key}'
route:
enabled: false
# Labels can be extended in the config hierarchy by providing further
Expand Down Expand Up @@ -118,24 +154,32 @@ parameters:
items:
- key: tls.crt
path: tls.crt
- name: keycloak-tls
secret:
secretName: ${keycloak:tls:secretName}
defaultMode: 420
extraVolumeMounts: |
- name: db-certs
readOnly: true
mountPath: /opt/jboss/certs
- name: keycloak-tls
readOnly: true
mountPath: /etc/x509/https
serviceAccount:
labels: ${keycloak:labels}
ingress:
enabled: ${keycloak:ingress:enabled}
annotations: ${keycloak:ingress:annotations}
labels: ${keycloak:labels}
servicePort: https
rules:
- host: ${keycloak:fqdn}
paths: ["/"]
tls:
- hosts:
- ${keycloak:fqdn}
secretName: ${keycloak:ingress:secretName}
secretName: ${keycloak:ingress:tls:secretName}
route:
enabled: ${keycloak:route:enabled}
labels: ${keycloak:labels}
Expand Down
52 changes: 52 additions & 0 deletions component/main.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,63 @@ local ns_patch =
}
);

local keycloak_cert_secret = kube.Secret(params.tls.secretName) {
metadata+: {
labels+: params.labels,
},
stringData: {
'tls.key': params.tls.vault.certKey,
'tls.crt': params.tls.vault.cert,
// CA is required by nginx in passthrough mode
'ca.crt': params.tls.vault.cert,
},
};

local cert_manager_cert = {
apiVersion: params.tls.certmanager.apiVersion,
kind: 'Certificate',
metadata: {
name: params.tls.certmanager.certName,
labels: params.labels,
},
spec: {
secretName: params.tls.secretName,
dnsNames: [
params.fqdn,
],
issuerRef: {
name: params.tls.certmanager.issuer.name,
kind: params.tls.certmanager.issuer.kind,
group: params.tls.certmanager.issuer.group,
},
},
};

local ingress_tls_secret = kube.Secret(params.ingress.tls.secretName) {
metadata+: {
labels+: params.labels,
},
stringData: {
'tls.key': params.ingress.tls.vault.certKey,
'tls.crt': params.ingress.tls.vault.cert,
},
};

local create_keycloak_cert_secret =
params.ingress.enabled && !(params.tls.termination == 'passthrough' && params.tls.provider == 'certmanager');
local create_ingress_cert_secret =
params.ingress.enabled && params.tls.termination == 'reencrypt' && params.tls.provider == 'vault';
local create_ingress_cert =
params.ingress.enabled && params.tls.termination == 'passthrough' && params.tls.provider == 'certmanager';

// Define outputs below
{
'00_namespace': namespace,
[if params.ingress.enabled && params.helm_values.networkPolicy.enabled then '01_ingress_controller_ns_patch']: ns_patch,
'10_admin_secret': admin_secret,
'11_db_secret': db_secret,
[if params.database.tls.enabled then '12_db_certs']: db_cert_secret,
[if create_keycloak_cert_secret then '13_keycloak_certs']: keycloak_cert_secret,
[if create_ingress_cert_secret then '14_ingress_certs']: ingress_tls_secret,
[if create_ingress_cert then '20_le_cert']: cert_manager_cert,
}
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/explanations/default-features.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ This page gives an overview over the defaults.
- Enabled network policy for database to protect from unexpected connections
- Prometheus ServiceMonitor to scrape metrics
- 2 replicas of Keycloak, with anti-affinity
- Enabled Ingress
- Enabled Ingress with re-encryption
- Configured requests and limits for CPU and memory resources

== Disabled features
Expand Down
75 changes: 75 additions & 0 deletions docs/modules/ROOT/pages/how-tos/configure-ingress.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
= Configure Keycloak ingress

Incoming network traffic to the Keycloak service is usually routed via an ingress.

[NOTE]
====
Currently, only NGINX ingress controller is supported and tested.
Others may work as well, but you may need to customize some parameters on your own (or contribute back to the component).
====

See also the following manual on how to setup the Keycloak encryption:

* https://github.com/keycloak/keycloak-containers/blob/master/server/README.md[Docker image configuration]
First, choose where the TLS session to Keycloak is terminated.

== Encryption mode: Re-encrypt

In re-encryption mode, the traffic is terminated at the NGINX ingress controller, and then re-encrypted when connecting to Keycloak pods.
By default, this component will use Let's Encrypt (cert-manager) so that NGINX terminates with valid certificates.
The connection to Keycloak is using self-signed certificates since Keycloak does not reload certificates when they have changed in the container.

Re-encryption is the default, so there's not much to configure.
Proceed with xref:how-tos/keycloak-tls.adoc[Setup a TLS certificate for Keycloak].

== Encryption mode: Pass-through

In the pass-through mode, the controller will not terminate the TLS session and instead directly forward the HTTPS traffic to Keycloak.
The controller needs to support pass-through mode.

[IMPORTANT]
====
This mode requires that the NGINX Controller is configured to pass through the TLS traffic.
This is not enabled by default.
With https://github.com/projectsyn/component-ingress-nginx[component-ingress-nginx], you can enable it like following:
[source,yaml]
----
parameters:
ingress_nginx:
helm_values:
controller:
extraArgs:
enable-ssl-passthrough: true
----
====

[WARNING]
====
When using certificates from Let's Encrypt (cert-manager), ensure that you regularly restart Keycloak.
Otherwise, you may end up serving expired certificates!
The default Keycloak container image does not reload the certificates when they have changed in the mounted filesystem.
====

. Terminate the TLS session directly in Keycloak
+
[source,yaml]
----
parameters:
keycloak:
tls:
termination: passthrough
----

. If you're using CA issued certificates, change the provider:
+
[source,yaml]
----
parameters:
keycloak:
tls:
provider: vault
----
+
See xref:how-tos/keycloak-tls.adoc[Setup a TLS certificate for Keycloak] for how to store a certificate in vault.
48 changes: 48 additions & 0 deletions docs/modules/ROOT/pages/how-tos/keycloak-tls.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
= Setup a TLS certificate for Keycloak

This guide provides an example how to setup a TLS certificate for Keycloak.

====
Requirements
* `commodore`
* `kubectl`
* `openssl`
* `vault`
====

. Prepare certificate files
+
.Self-signed certificates
[source,bash]
----
# Adjust the lifetime as necessary
lifetime=3650
openssl req -x509 -newkey rsa:4096 -nodes -keyout keycloak.key -out keycloak.crt -days ${lifetime} -subj '/CN=keycloak'
----
+
.CA issued certificates
[source,bash]
----
# Save the cert and key in these temporary files
editor keycloak.key
editor keycloak.crt
----

. Store certificate in Vault
+
[source,bash]
----
instance=keycloak
parent="clusters/kv/${TENANT_ID}/${CLUSTER_ID}"

# Use the 'patch' subcommand to add to existing secret
vault kv patch "${parent}/${instance}" [email protected] [email protected]
----

. Remove temporary files
+
[source,bash]
----
rm keycloak.{key,crt}
----
6 changes: 3 additions & 3 deletions docs/modules/ROOT/pages/how-tos/multi-instance.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ Using the `syn-` prefix might not be what you want when using multiple instances
Use the `namespace` parameter to customize the namespace, but be sure that each instance gets their own namespace.
====
. Set secrets
. Set secrets (don't forget to also xref:how-tos/keycloak-tls.adoc[store certificates for each Keycloak instance])
+
[source,bash]
----
parent="clusters/kv/${TENANT_ID}/${CLUSTER_ID}"

vault kv put -cas=0 "${parent}/keycloak-test" admin-password=$(pwgen -s 32 1) db-password=$(pwgen -s 32 1)
vault kv put -cas=0 "${parent}/keycloak-prod" admin-password=$(pwgen -s 32 1) db-password=<your-external-db-password>
vault kv put "${parent}/keycloak-test" admin-password=$(pwgen -s 32 1) db-password=$(pwgen -s 32 1)
vault kv put "${parent}/keycloak-prod" admin-password=$(pwgen -s 32 1) db-password=<your-external-db-password>
----
. Compile and push the cluster catalog
Expand Down
Loading

0 comments on commit ed9dc9d

Please sign in to comment.