Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(docker-jans-saml): unable to access KC admin console from browser #7336

Merged
merged 3 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions docker-jans-saml/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -161,17 +161,18 @@ ENV CN_MAX_RAM_PERCENTAGE=75.0 \
CN_AWS_SECRETS_ENDPOINT_URL="" \
CN_AWS_SECRETS_PREFIX=jans \
CN_AWS_SECRETS_REPLICA_FILE="" \
CN_SAML_PORT=8083 \
CN_SAML_HOST=0.0.0.0 \
CN_SAML_HTTP_PORT=8083 \
CN_SAML_HTTP_HOST=0.0.0.0 \
CN_SAML_JAVA_OPTIONS="" \
KC_HEALTH_ENABLED=true
# KC_METRICS_ENABLED=true
CN_SAML_KC_CREDENTIALS_FILE=/etc/jans/conf/kc_admin_creds \
KC_HEALTH_ENABLED=true \
KC_METRICS_ENABLED=true

# ==========
# misc stuff
# ==========

EXPOSE $CN_SAML_PORT
EXPOSE $CN_SAML_HTTP_PORT

LABEL org.opencontainers.image.url="ghcr.io/janssenproject/jans/saml" \
org.opencontainers.image.authors="Janssen Project <support@jans.io>" \
Expand Down
13 changes: 6 additions & 7 deletions docker-jans-saml/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ The following environment variables are supported by the container:
- `CN_COUCHBASE_KEEPALIVE_INTERVAL`: Keep-alive interval for Couchbase connection (default to `30000` milliseconds).
- `CN_COUCHBASE_KEEPALIVE_TIMEOUT`: Keep-alive timeout for Couchbase connection (default to `2500` milliseconds).
- `CN_SAML_JAVA_OPTIONS`: Java options passed to entrypoint, i.e. `-Xmx1024m` (default to empty-string).
- `CN_SAML_KC_CREDENTIALS_FILE`: File contains credentials for Keycloak admin user.
- `GOOGLE_APPLICATION_CREDENTIALS`: Optional JSON file (contains Google credentials) that can be injected into container for authentication. Refer to https://cloud.google.com/docs/authentication/provide-credentials-adc#how-to for supported credentials.
- `GOOGLE_PROJECT_ID`: ID of Google project.
- `CN_GOOGLE_SECRET_VERSION_ID`: Janssen secret version ID in Google Secret Manager. Defaults to `latest`, which is recommended.
Expand Down Expand Up @@ -117,18 +118,16 @@ As per v1.0.1, hybrid persistence supports all available persistence types. To c
}
```

### Keycloak Admin Credentials
### Keycloak Administration

Admin credentials are set in `/etc/jans/conf/kc_admin_creds` with the following format:
#### Admin Credentials

```
BASE64(username:password)
```
By defaults, Keycloak's admin username and password are self-generated during first install and saved as `kc_admin_username` (in configs layer) and `kc_admin_password` (in secrets layer) respectively.

Example:
The credentials will be rendered as `/etc/jans/conf/kc_admin_creds` file (can be changed via `CN_SAML_KC_CREDENTIALS_FILE` environment variable) with the following format:

```
echo admin:admin | base64 -w0 > /etc/jans/conf/kc_admin_creds
BASE64(username:password)
```

The credentials will be exported as `KEYCLOAK_ADMIN` and `KEYCLOAK_ADMIN_PASSWORD` environment variables for initial admin username and password.
30 changes: 29 additions & 1 deletion docker-jans-saml/scripts/bootstrap.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import base64
import logging.config
import os
import typing as _t
Expand Down Expand Up @@ -33,9 +34,13 @@

def render_keycloak_conf(ctx):
with open("/app/templates/jans-saml/keycloak.conf") as f:
tmpl = f.read()
defaults = f.read()

with open("/app/templates/jans-saml/keycloak.extra.conf") as f:
extras = f.read()

with open("/opt/keycloak/conf/keycloak.conf", "w") as f:
tmpl = "\n".join([defaults, extras])
f.write(tmpl % ctx)


Expand All @@ -44,6 +49,7 @@ def main():
persistence_setup = PersistenceSetup(manager)
persistence_setup.import_ldif_files()
render_keycloak_conf(persistence_setup.ctx)
render_keycloak_creds()


class PersistenceSetup:
Expand Down Expand Up @@ -116,6 +122,17 @@ def ctx(self) -> dict[str, _t.Any]:
).decode()
self.manager.secret.set("saml_scim_client_encoded_pw", ctx["saml_scim_client_encoded_pw"])

# keycloak credentials
ctx["kc_admin_username"] = self.manager.config.get("kc_admin_username")
if not ctx["kc_admin_username"]:
ctx["kc_admin_username"] = "admin"
self.manager.config.set("kc_admin_username", ctx["kc_admin_username"])

ctx["kc_admin_password"] = self.manager.secret.get("kc_admin_password")
if not ctx["kc_admin_password"]:
ctx["kc_admin_password"] = get_random_chars()
self.manager.secret.set("kc_admin_password", ctx["kc_admin_password"])

# finalized ctx
return ctx

Expand All @@ -132,5 +149,16 @@ def import_ldif_files(self) -> None:
self.client.create_from_ldif(file_, self.ctx)


def render_keycloak_creds():
creds_file = os.environ.get("CN_SAML_KC_CREDENTIALS_FILE", "/etc/jans/conf/kc_admin_creds")

if not os.path.isfile(creds_file):
with open(creds_file, "w") as f:
username = manager.config.get("kc_admin_username")
password = manager.secret.get("kc_admin_password")
creds_bytes = f"{username}:{password}".encode()
f.write(base64.b64encode(creds_bytes).decode())


if __name__ == "__main__":
main()
16 changes: 7 additions & 9 deletions docker-jans-saml/scripts/configure_kc.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ def __init__(self, admin_username, admin_password, base_dir, ctx):

@property
def server_url(self):
host = os.environ.get("CN_SAML_HOST", "0.0.0.0") # nosec: B104
port = os.environ.get("CN_SAML_PORT", "8083")
host = os.environ.get("CN_SAML_HTTP_HOST", "0.0.0.0") # nosec: B104
port = os.environ.get("CN_SAML_HTT_PORT", "8083")
return f"http://{host}:{port}/kc"

@property
Expand Down Expand Up @@ -172,13 +172,11 @@ def create_user(self):
def main():
manager = get_manager()

if os.path.isfile("/etc/jans/conf/kc_admin_creds"):
with open("/etc/jans/conf/kc_admin_creds") as f:
creds = f.read().strip()
admin_username, admin_password = base64.b64decode(creds).decode().strip().split(":")
else:
admin_username = os.environ.get("KEYCLOAK_ADMIN", "")
admin_password = os.environ.get("KEYCLOAK_ADMIN_PASSWORD", "")
creds_file = os.environ.get("CN_SAML_KC_CREDENTIALS_FILE", "/etc/jans/conf/kc_admin_creds")

with open(creds_file) as f:
creds = f.read().strip()
admin_username, admin_password = base64.b64decode(creds).decode().strip().split(":")

ctx = {
"jans_idp_realm": "jans-api",
Expand Down
27 changes: 10 additions & 17 deletions docker-jans-saml/scripts/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,19 @@ get_max_ram_percentage() {
}

export_keycloak_admin_creds() {
if [ -f /etc/jans/conf/kc_admin_creds ]; then
creds="$(base64 -d < /etc/jans/conf/kc_admin_creds)"
admin_username=$(echo "$creds" | awk -F ":" '{print $1}')
admin_password=$(echo "$creds" | awk -F ":" '{print $2}')
else
admin_username=${KEYCLOAK_ADMIN:-}
admin_password=${KEYCLOAK_ADMIN_PASSWORD:-}
fi
creds_file=${CN_SAML_KC_CREDENTIALS_FILE:-/etc/jans/conf/kc_admin_creds}
creds="$(base64 -d < ${creds_file})"
admin_username=$(echo "$creds" | awk -F ":" '{print $1}')
admin_password=$(echo "$creds" | awk -F ":" '{print $2}')
export KEYCLOAK_ADMIN="$admin_username"
export KEYCLOAK_ADMIN_PASSWORD="$admin_password"
}

export_keycloak_admin_creds
python3 "$basedir/wait.py"
python3 "$basedir/bootstrap.py"
python3 "$basedir/configure_kc.py" &
python3 "$basedir/upgrade.py"
export_keycloak_admin_creds

java_opts="$(get_max_ram_percentage) $(get_java_options)"
export JAVA_OPTS_APPEND="$java_opts"
Expand All @@ -46,19 +42,16 @@ export JAVA_OPTS_APPEND="$java_opts"
exec /opt/keycloak/bin/kc.sh start \
-Dlog.base=/opt/keycloak/logs/ \
-Djans.config.prop.path=/opt/keycloak/providers \
--health-enabled=true \
--metrics-enabled=true \
--http-host="${CN_SAML_HOST}" \
--http-port="${CN_SAML_PORT}" \
--http-host="${CN_SAML_HTTP_HOST}" \
--http-port=${CN_SAML_HTTP_PORT} \
--http-enabled=true \
--http-relative-path=/kc \
--hostname="localhost" \
--hostname-admin="localhost" \
--hostname-path=/kc \
--hostname-strict-https=false \
--hostname-strict-https=true \
--log=console \
--log-console-format='jans-saml - %d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n' \
--log-file=/opt/keycloak/logs/keycloak.log \
--log-level=INFO
--log-level=INFO \
--proxy=edge
# --db=dev-mem \
# --optimized
4 changes: 2 additions & 2 deletions docker-jans-saml/scripts/healthcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@


def run_healthcheck():
host = os.environ.get("CN_SAML_HOST", "0.0.0.0") # nosec: B104
port = os.environ.get("CN_SAML_PORT", "8083")
host = os.environ.get("CN_SAML_HTTP_HOST", "0.0.0.0") # nosec: B104
port = os.environ.get("CN_SAML_HTTP_PORT", "8083")
req = requests.get(f"http://{host}:{port}/kc/health", timeout=5)
if not req.ok:
return False
Expand Down
2 changes: 2 additions & 0 deletions docker-jans-saml/templates/jans-saml/keycloak.extra.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
hostname=%(hostname)s
hostname-admin=%(hostname)s