When I originally created this, I was usingkubernasty.micahrl.com
for the cluster; I’ve moved to usingmicahrl.me
instead. This should be almost completely cleaned up now, but remnants of the old DNS name may lurk in the corners.
Deploying Keycloak #
- The Helm chart is pretty magical (pejorative), and I had to do a lot of investigating to get it working for me. Aside from this page, you may need the Troubleshooting document as well.
- The Helm chart we use creates the ingress for us
(as we specify in our override config),
so we have to adapt that section of
♘
kubernasty/manifests/crust/keycloak/configmaps/keycloak.overrides.yaml
to look like other Ingress objects we’ve created.- It was useful to compare the YAML for working ingresses I’d already made
with the one that the chart generates.
You can get any object in YAML format with
kubectl get OBJECT_TYPE -n NAMESPACE OBJECT_NAME -o yaml
.
- It was useful to compare the YAML for working ingresses I’d already made
with the one that the chart generates.
You can get any object in YAML format with
- You should avoid having users with the same name in both the Keycloak and LLDAP databases.
(Note your administrative users - don’t call them
admin
in both places. I use the usernamekeymaker
for the Keycloak admin user, and vanilla0p3r4t0r
for the LLDAP admin user.)- If there are users of the same name created independently, syncing users in Keycloak will show “1 user failed”, and there will be an error in the container logs naming the user.
Create keycloak-credentials.secret.yaml
#
gopass generate kubernasty/keycloak-admin-user-keymaster 64
adminpw="$(gopass cat kubernasty/keycloak-admin-user-keymaster)"
mkdir -p manifests/crust/keycloak/secrets
cat > manifests/crust/keycloak/secrets/keycloak-credentials.secret.yaml <<EOF
apiVersion: v1
kind: Secret
metadata:
name: keycloak-credentials
namespace: keycloak
type: generic
stringData:
adminpw: $adminpw
EOF
sops --encrypt --in-place manifests/crust/keycloak/secrets/keycloak-credentials.secret.yaml
Create keycloakdb-pass.secret.yaml
#
kcpass="$(pwgen 64)"
pgpass="$(pwgen 64)"
mkdir -p manifests/crust/keycloak/secrets
cat > manifests/crust/keycloak/secrets/keycloak-pass.secret.yaml <<EOF
apiVersion: v1
kind: Secret
metadata:
name: keycloakdb-pass
namespace: keycloak
type: generic
stringData:
# The password for the 'keycloak' user
password: $kcpass
# The password for the 'postgres' database admin user
postgres-password: $pgpass
EOF
sops --encrypt --in-place manifests/crust/keycloak/secrets/keycloak-pass.secret.yaml
Configuring LLDAP #
- Create a regular user for our own use, and add him to the group we just created.
I call mine
mrled
. Save its password in your regular password manager.
Configuring KeyCloak #
- Log in to LLDAP
- Create the Keycloak user
- User named
keycloak
- Add it to the
lldap_admin
group. If you don’t do this, it’ll connect, but sync won’t work. - Store password in gopass,
gopass generate kubernasty/lldap/keycloak 64
- User named
- Create the Keycloak user
- Log in to Keycloak
- Configure LDAP
- https://github.com/nitnelave/lldap/blob/main/example_configs/keycloak.md
- Connection URL:
ldap://lldap.lldap:389
- TODO: Have Keycloak connect to LLDAP over TLS
- Bind DN:
uid=keycloak,ou=people,dc=kubernasty,dc=micahrl,dc=com
- When it says to “sync users”, this is done from the action dropdown in the upper right of the LDAP integration page.
- TODO: I can’t get it to sync only changed users –
when I try, LLDAP logs
🚨 [error]: [LDAP] Service Error: while handling incoming messages: while receiving LDAP op: ldapmsg invalid
– but my directory will be small enough that I just have it do a full sync every 30 seconds. - Make sure to enable the
Trust email
option, so that Keycloak considers email address in LLDAP as “verified”
- Configure LDAP
Deploying traefik-forward-auth
#
traefik-forward-auth
is a service that provides SSO for traefik.
With it, we can protect all our apps behind one (hopefully) secure provider.
For most apps, this is better than any username/password authentication they may do natively,
since Keycloak is designed for this and gets a lot of security attention.
Note the WHITELIST
environment variable in
♘
kubernasty/manifests/crust/keycloak/deployments/tfa.deployment.yaml
.
List the email addresses if the users you want there.
Configure Keycloak #
- See also https://geek-cookbook.funkypenguin.co.nz/recipes/keycloak/setup-oidc-provider/
- Log in to Keycloak
- Configure OIDC
Master
realm -> Clients -> Create- Client type:
openid-connect
- Client ID:
kubernasty-tfa
- Save (moves to second page of create action)
- Client Authentication:
On
- Save (creates the client and shows the full settings page)
- Valid redirect URIs:
https://*.micahrl.me/*
- Valid post logout redirect URIs:
https://*.micahrl.me/*
- Save
- Credentials -> Client secret -> copy
- Configure OIDC
Now we can create the tfa-secrets
secret.
Create tfa-secrets.secret.yaml
manifest
#
# Must match what was entered into Keycloak above
clientid="kubernasty-tfa"
# The key we copied above
clientsecret="... SECRET VALUE ..."
# We generate a random value here
authsecret="$(pwgen 64)"
cat > manifests/crust/keycloak/secrets/tfa-secrets.secret.yaml <<EOF
apiVersion: v1
kind: Secret
metadata:
name: tfa-secrets
namespace: keycloak
labels:
app: traefik-forward-auth
type: Opaque
stringData:
tfa-oidc-client-id: $clientid
tfa-oidc-client-secret: $clientsecret
tfa-auth-secret: $authsecret
EOF
sops --encrypt --in-place manifests/crust/keycloak/secrets/tfa-secrets.secret.yaml
Testing traefik-forward-auth
#
- We deploy
tfawhoami
pod for this purpose - Note that in the
traefik-forward-auth-mw
at ♘kubernasty/manifests/crust/keycloak/middlewares/tfa.middleware.yaml
, we create the middleware in thekube-system
namespace. I think this is required, but I don’t remember for sure. - Note that in the
twawhoami
ingress at ♘kubernasty/manifests/crust/keycloak/ingresses/tfawhoami.ingress.yaml
, we add a labeltraefik.ingress.kubernetes.io/router.middlewares: kube-system-traefik-forward-auth-mw@kubernetescrd
. See that the middleware specifier isNAMESPACE-NAME@kubernetescrd
. - Adding that label will protect other ingresses behind Keycloak.
- Be careful: if you don’t add that label, an ingress is open to anyone who can talk to your cluster.