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

add identity-platform bundle #274

Merged
merged 2 commits into from
Dec 12, 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
4 changes: 4 additions & 0 deletions common/ch_channel_map/any_series
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,9 @@ for c in ${CEPH_CHARMS[@]}; do
CHARM_CHANNEL[$c]=$ceph_release/edge
done

for c in ${IAM_CHARMS[@]}; do
CHARM_CHANNEL[$c]=latest/edge
done

CHARM_CHANNEL[pacemaker-remote]=${series}/edge
CHARM_CHANNEL[microk8s]=1.28/stable
11 changes: 11 additions & 0 deletions common/charm_lists
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,14 @@ prometheus
ro
zookeeper
)

declare -a IAM_CHARMS=(
hydra
identity-platform-login-ui-operator
kratos
kratos-external-idp-integrator
oathkeeper
postgresql-k8s
self-signed-certificates
traefik-k8s
)
21 changes: 21 additions & 0 deletions identity-platform/authentik-values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
authentik:
secret_key: "my-secure-secret-key"
error_reporting:
enabled: false
postgresql:
password: "my-secure-psql-password"
bootstrap_token: "my-secure-bootstrap-token"
bootstrap_password: "Passw0rd"
server:
ingress:
ingressClassName: nginx
enabled: false
hosts:
- authentik.secloud
postgresql:
enabled: true
auth:
password: "my-secure-psql-password"
redis:
enabled: true

1 change: 1 addition & 0 deletions identity-platform/common
74 changes: 74 additions & 0 deletions identity-platform/configure
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/bin/bash

# Reset
if [[ "$1" == "reset" ]]; then
helm uninstall authentik -n authentik
kubectl delete pvc -n authentik data-authentik-postgresql-0 redis-data-authentik-redis-master-0
fi

# Check for kratos-external-idp-integrator
if [ "$(juju status --format json| jq -r '.applications["kratos-external-idp-integrator"].units|to_entries[]|select(.value["leader"])|.key' 2> /dev/null)" == "" ]; then
echo 'ERROR: Cannot configure OIDC without kratos-external-idp-integrator!'
exit 1
fi

# Install Helm
if ! snap list | grep -q helm; then
sudo snap install helm --classic
fi

# Install authentik
kubectl get ns authentik &> /dev/null || kubectl create ns authentik
https_proxy=http://squid.internal:3128 helm repo add authentik https://charts.goauthentik.io
https_proxy=http://squid.internal:3128 helm repo update
helm install authentik authentik/authentik -f ./authentik-values.yaml -n authentik --version 2024.10.1

timeout=0
echo 'Waiting for Authentik to start...'
up=0
while [[ "$up" != 1 ]]; do
up="$(kubectl get deploy -n authentik authentik-server -o json | jq '.status.readyReplicas')"
if [[ $timeout == 600 ]]; then
echo 'ERROR: Authentik failed to start.'
exit 1
fi
sleep 1
((timeout++))
done

# Prepare port for API calls and wait
kubectl patch svc -n authentik authentik-server -p '{"spec": {"type": "NodePort"}}' || exit 1
AUTH_PORT=$(kubectl get svc -n authentik authentik-server -o jsonpath='{.spec.ports[].nodePort}')
AUTH_IP=$(kubectl get po -n authentik -o json | jq -r '.items[] | select(.metadata.name | test("authentik-server-")) | .status.hostIP')

# Configure OIDC
## get default values
until [ -n "$AUTH_FLOW" ]; do AUTH_FLOW=$(curl -s -X GET -H "accept: application/json" -H "Authorization: Bearer my-secure-bootstrap-token" "http://${AUTH_IP}:${AUTH_PORT}/api/v3/flows/instances/?search=default-authentication-flow" | jq -r '.results[0].pk'); done
until [ -n "$AUTHZ_FLOW" ]; do AUTHZ_FLOW=$(curl -s -X GET -H "accept: application/json" -H "Authorization: Bearer my-secure-bootstrap-token" "http://${AUTH_IP}:${AUTH_PORT}/api/v3/flows/instances/?search=default-provider-authorization-implicit-consent" | jq -r '.results[0].pk'); done
until [ -n "$INVALID_FLOW" ]; do INVALID_FLOW=$(curl -s -X GET -H "accept: application/json" -H "Authorization: Bearer my-secure-bootstrap-token" "http://${AUTH_IP}:${AUTH_PORT}/api/v3/flows/instances/?search=default-invalidation-flow" | jq -r '.results[0].pk'); done
until [ -n "$SIGN_KEY" ]; do SIGN_KEY=$(curl -s -X GET -H "accept: application/json" -H "Authorization: Bearer my-secure-bootstrap-token" "http://${AUTH_IP}:${AUTH_PORT}/api/v3/crypto/certificatekeypairs/" | jq -r '.results[0].pk'); done
until [ -n "$SCOPE" ]; do SCOPE=$(curl -s -X GET -H "accept: application/json" -H "Authorization: Bearer my-secure-bootstrap-token" "http://${AUTH_IP}:${AUTH_PORT}/api/v3/propertymappings/provider/scope/?search=email" | jq -r '.results[0].pk'); done

## create provider
curl -X POST "http://${AUTH_IP}:${AUTH_PORT}/api/v3/providers/oauth2/" -H "Authorization: Bearer my-secure-bootstrap-token" -H "accept: application/json" -H "content-type: application/json" -d "{\"name\":\"oidc-provider\",\"authentication_flow\":\"$AUTH_FLOW\",\"authorization_flow\":\"$AUTHZ_FLOW\",\"invalidation_flow\":\"$INVALID_FLOW\",\"client_type\":\"confidential\",\"client_id\":\"canonical-support\",\"client_secret\":\"my-secure-oidc-secret\",\"access_code_validity\":\"hours=3\",\"access_token_validity\":\"hours=3\",\"refresh_token_validity\":\"hours=3\",\"include_claims_in_id_token\":true,\"redirect_uris\":\"*\",\"sub_mode\":\"hashed_user_id\",\"issuer_mode\":\"per_provider\",\"signing_key\":\"$SIGN_KEY\",\"property_mappings\":[\"$SCOPE\"]}"

## create app
curl -H "Authorization: Bearer my-secure-bootstrap-token" -X POST "http://${AUTH_IP}:${AUTH_PORT}/api/v3/core/applications/" -H "accept: application/json" -H "content-type: application/json" -d '{"name":"canonical-support","slug":"canonical-support","provider":1,"policy_engine_mode":"all"}'

# Configure kratos
juju config kratos-external-idp-integrator provider=generic
juju config kratos-external-idp-integrator client_id=canonical-support
juju config kratos-external-idp-integrator client_secret=my-secure-oidc-secret
juju config kratos-external-idp-integrator issuer_url=http://"${AUTH_IP}:${AUTH_PORT}"/application/o/canonical-support/

echo "
Configuration is complete! You can test a login with the following credentials:

Authentik Dashboard: http://${AUTH_IP}:${AUTH_PORT}
OIDC User: akadmin
Password: Passw0rd"

grafana_url="$(juju run grafana/0 get-admin-password 2> /dev/null | grep url | sed -e 's/url: //')"
if [[ -n $grafana_url ]]; then
echo "Grafana Dashboard: $grafana_url"
fi
1 change: 1 addition & 0 deletions identity-platform/generate-bundle.sh
72 changes: 72 additions & 0 deletions identity-platform/iam.yaml.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
bundle: kubernetes
name: identity-platform
website: https://github.com/canonical/iam-bundle
issues: https://github.com/canonical/iam-bundle/issues
applications:
hydra:
charm: __CHARM_STORE____CHARM_CS_NS____CHARM_CH_PREFIX__hydra
scale: 1
series: jammy
trust: true
identity-platform-login-ui-operator:
charm: __CHARM_STORE____CHARM_CS_NS____CHARM_CH_PREFIX__identity-platform-login-ui-operator
scale: 1
series: jammy
trust: true
kratos:
charm: __CHARM_STORE____CHARM_CS_NS____CHARM_CH_PREFIX__kratos
scale: 1
series: jammy
options:
enforce_mfa: false
trust: true
oathkeeper:
charm: __CHARM_STORE____CHARM_CS_NS____CHARM_CH_PREFIX__oathkeeper
scale: 1
series: jammy
trust: true
postgresql-k8s:
charm: __CHARM_STORE____CHARM_CS_NS____CHARM_CH_PREFIX__postgresql-k8s
scale: 1
series: jammy
options:
plugin_btree_gin_enable: true
plugin_pg_trgm_enable: true
storage:
pgdata: kubernetes,1,1024M
trust: true
self-signed-certificates:
charm: __CHARM_STORE____CHARM_CS_NS____CHARM_CH_PREFIX__self-signed-certificates
scale: 1
traefik-admin:
charm: __CHARM_STORE____CHARM_CS_NS____CHARM_CH_PREFIX__traefik-k8s
scale: 1
series: focal
storage:
configurations: kubernetes,1,1024M
trust: true
traefik-public:
charm: __CHARM_STORE____CHARM_CS_NS____CHARM_CH_PREFIX__traefik-k8s
scale: 1
series: focal
options:
enable_experimental_forward_auth: true
storage:
configurations: kubernetes,1,1024M
trust: true
relations:
- [hydra:pg-database, postgresql-k8s:database]
- [kratos:pg-database, postgresql-k8s:database]
- [kratos:hydra-endpoint-info, hydra:hydra-endpoint-info]
- [hydra:admin-ingress, traefik-admin:ingress]
- [hydra:public-ingress, traefik-public:ingress]
- [kratos:admin-ingress, traefik-admin:ingress]
- [kratos:public-ingress, traefik-public:ingress]
- [identity-platform-login-ui-operator:ingress, traefik-public:ingress]
- [identity-platform-login-ui-operator:hydra-endpoint-info, hydra:hydra-endpoint-info]
- [identity-platform-login-ui-operator:ui-endpoint-info, hydra:ui-endpoint-info]
- [identity-platform-login-ui-operator:ui-endpoint-info, kratos:ui-endpoint-info]
- [identity-platform-login-ui-operator:kratos-info, kratos:kratos-info]
- [traefik-admin:certificates, self-signed-certificates:certificates]
- [traefik-public:certificates, self-signed-certificates:certificates]
9 changes: 9 additions & 0 deletions identity-platform/module_defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# This file must contain defaults for all variables used in bundles/overlays.
# They are used to render to final product in the event they are not provided
# elsewhere. It is inserted into the global context at the start of the
# pipeline.
#
# You can check that none are missing by running lint/check_var_defaults.sh
#
JUJU_DEPLOY_OPTS=" --trust"
CHARM_CHANNEL[postgresql-k8s]=14/stable
1 change: 1 addition & 0 deletions identity-platform/overlays
22 changes: 22 additions & 0 deletions identity-platform/pipeline/00setup
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash

# Globals
export MOD_NAME=identity-platform
export MOD_BASE_TEMPLATE=iam.yaml.template
export MOD_SSL_STATE_DIR=${MOD_NAME}
[ -n "${MASTER_OPTS[BUNDLE_NAME]}" ] && \
MOD_SSL_STATE_DIR="${MOD_SSL_STATE_DIR}-${MASTER_OPTS[BUNDLE_NAME]}"

# opts that 02configure does not recognise that get passed to the generator
export -a MOD_PASSTHROUGH_OPTS=()

# Collection of messages to display at the end
export -A MOD_MSGS=()
# Use order 0 to ensure this is first displayed
MOD_MSGS[0_common.0]="Ensure a LoadBalancer (e.g. MetalLB or Cilium) is enabled on k8s"
MOD_MSGS[0_common.2]="Configure a local user: juju run kratos/0 create-admin-account [email protected] password=Passw0rd username=admin"

# Array list of overlays to use with this deployment.
export -a MOD_OVERLAYS=()

export -A MOD_PARAMS=()
2 changes: 2 additions & 0 deletions identity-platform/pipeline/01import-config-defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Current module imports
. $MOD_DIR/module_defaults
33 changes: 33 additions & 0 deletions identity-platform/pipeline/02configure
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/bin/bash
# Global variables are first defined in 00setup and module
# dependencies are defined in 01import-config-defaults
#
# All overlay/bundle variables (MOD_PARAMS) defaults must go into
# the <module>/module_defaults file.

cloud="$(get_cloud_type)"
if [[ "$cloud" != "k8s" ]]; then
echo "ERROR: Must switch to a Kubernetes model first."
exit 1
fi

while (($# > 0))
do
case $1 in
--oidc)
MOD_OVERLAYS+=( "kubernetes/k8s-iam-oidc.yaml" )
MOD_MSGS[0_common.1]="Setup OIDC: ./configure"
;;
--grafana)
MOD_OVERLAYS+=( "kubernetes/k8s-iam-grafana.yaml" )
MOD_MSGS[grafana.0]="Get Grafana URL: juju run grafana/leader get-admin-password"
;;
*)
echo "ERROR: invalid input '$1'"
_usage
exit 1
;;
esac
shift
done

5 changes: 5 additions & 0 deletions identity-platform/pipeline/03build
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash
. $MOD_DIR/common/generate_bundle_base

print_msgs

11 changes: 11 additions & 0 deletions overlays/kubernetes/k8s-iam-grafana.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
applications:
grafana:
charm: __CHARM_STORE____CHARM_CS_NS____CHARM_CH_PREFIX__grafana-k8s
scale: 1
series: focal
storage:
database: kubernetes,1,1024M
relations:
- [grafana:ingress, traefik-public:traefik-route]
- [grafana:oauth, hydra:oauth]
- [grafana:receive-ca-cert, self-signed-certificates:send-ca-cert]
9 changes: 9 additions & 0 deletions overlays/kubernetes/k8s-iam-oidc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
applications:
kratos-external-idp-integrator:
charm: __CHARM_STORE____CHARM_CS_NS____CHARM_CH_PREFIX__kratos-external-idp-integrator
scale: 1
series: jammy
options:
provider: generic
relations:
- [kratos-external-idp-integrator:kratos-external-idp, kratos:kratos-external-idp]