Resource Protection (Keycloak)⚓︎
EOEPCA defines Building Blocks within a micro-service architecture. The services are subject to protection within an Identity and Access Management (IAM) approach that includes:
- Keycloak - Identity Service (Authorization Server)
- Gatekeeper - Policy Enforcement
Building Blocks that act as a Resource Server are individually protected by a dedicated Gatekeeper instance that enforces the authorization decision in collaboration with the Identity Service (Keycloak).
Gatekeeper ‘inserts itself’ into the request path of the target Resource Server using the auth_request
facility offered by Nginx. Thus, Gatekeeper deploys with an Ingress specification that:
- Configures the
auth_request
module to defer access authorization to thegatekeeper
service - Configures the ingress rules (host/path) for the target Resource Server
Helm Chart⚓︎
Each Gatekeeper is deployed via the identity-gatekeeper
helm chart from the EOEPCA Helm Chart Repository.
The chart is configured via values - the full set of available values can be seen at https://github.com/EOEPCA/helm-charts/blob/main/charts/application-hub/values.yaml.
It is expected to deploy multiple instances of the Gatekeeper
chart, one for each Resource Server to be protected.
helm install --version 1.0.12 --values myservice-gatekeeper-values.yaml \
--repo https://eoepca.github.io/helm-charts \
myservice-protection identity-gatekeeper
Values⚓︎
The helm chart is deployed with values that customise the service for the specific needs of the resource-server under protection and the deployment target platform.
Typical values to be specified include:
- Host/domain details for the Keycloak Identity Service, e.g.
keycloak.192-168-49-2.nip.io
- Credentials for the Keycloak client to be used by Gatekeeper (ideally via secret)
- TLS Certificate Provider, e.g.
letsencrypt-production
- Ingress rules definition for reverse-proxy to the target Resource Server
Example myservice-protection-values.yaml
…
nameOverride: myservice-protection
config:
client-id: myservice
discovery-url: https://keycloak.192-168-49-2.nip.io/realms/master
cookie-domain: 192-168-49-2.nip.io
targetService:
host: myservice.192-168-49-2.nip.io
name: myservice
port:
number: 80
secrets:
# Values for secret 'myservice-protection'
# Note - if ommitted, these can instead be set by creating the secret independently.
clientSecret: "changeme"
encryptionKey: "changemechangeme"
ingress:
enabled: true
className: nginx
annotations:
ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: letsencrypt-production
# Open access to some endpoints, including Swagger UI
openUri:
- ^/(docs|openapi.json|probe)
Client Credentials⚓︎
Gatekeeper requires Client Credentials for its interactions with the Keycloak identity-service
. These credentials must be supplied by the secret named <myservice>-protection
. The secret can be created directly by the helm chart - via the values secrets.clientSecret
and secrets.encryptionKey
- or perhaps more securely the secret can be created independently (e.g. via a SealedSecret
).
Client Registration⚓︎
The Keycloak client can be created directly in the Keycloak admin console - e.g. via https://keycloak.192-168-49-2.nip.io/admin.
As an aide there is a helper script create-client
. The script is available in the deployment-guide
repository, and can be obtained as follows…
The create-client
helper script requires some command-line arguments…
$ ./deploy/bin/create-client -h
Add a client with protected resources.
create-client [-h] [-a] [-i] [-u] [-p] [-c] [-s] [-t | --token t] [-r] --id id [--name name] (--secret secret | --public) [--default] [--authenticated] [--resource name] [--uris u1,u2] [--scopes s1,s2] [--users u1,u2] [--roles r1,r2]
where:
-h show help message
-a authorization server url - e.g. https://keycloak.192-168-49-2.nip.io
-i identity-api server url - e.g. https://identity-api.192-168-49-2.nip.io
-u username used for authentication
-p password used for authentication
-c client id (of the bootstrap client used in the create request)
-s client secret (of the bootstrap client used in the create request)
-t or --token access token used for authentication
-r realm
--id client id (of the created client)
--name client name (of the created client)
--secret client secret (of the created client)
--public public client (no client secret)
--default add default resource - /* authenticated
--authenticated allow access to the resource only when authenticated
--resource resource name
--uris resource uris - separated by comma (,)
--scopes resource scopes - separated by comma (,)
--users user names with access to the resource - separated by comma (,)
--roles role names with access to the resource - separated by comma (,)
For example…
./deploy/bin/create-client \
-a https://keycloak.192-168-49-2.nip.io \
-i https://identity-api.192-168-49-2.nip.io \
-r "master" \
-u "admin" \
-p "changeme" \
-c "admin-cli" \
--id=myservice \
--name="Gatekeeper for myservice" \
--secret="changeme" \
--description="Client to be used by Gatekeeper for myservice" \
--resource="eric" --uris='/eric/*' --scopes=view --users="eric" \
--resource="bob" --uris='/bob/*' --scopes=view --users="bob" \
--resource="alice" --uris='/alice/*' --scopes=view --users="alice"
User Tokens⚓︎
Requests to resource server endpoints that are protected by Gatekeeper must carry an Access Token that has been obtained on behalf of the requesting user. The access_token
is carried in the request header…
The Access Token for a given user can be obtained with a call to the token endpoint of the Keycloak Identity Service - supplying the credentials for the user and the pre-registered client…
curl -L -X POST 'https://keycloak.192-168-49-2.nip.io/realms/master/protocol/openid-connect/token' \
-H 'Cache-Control: no-cache' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'scope=openid profile email' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'username=<username>' \
--data-urlencode 'password=<password>' \
--data-urlencode 'client_id=admin-cli'
A json response is returned, in which the field access_token
provides the Access Token for the specified <username>
.
Additional Information⚓︎
Additional information regarding the Gatekeeper can be found at: