Skip to content

Commit 6a088d2

Browse files
authored
Add kratos oidc provider secret support (#162)
* zero#203 : Add support for OIDC provider secret and documentation around kratos and oathkeeper * Add support for creating and using JWKS file for oathkeeper * Changed oidc comment in kratos values file * Disable secret loading if user auth is not enabled * Fixed json structure in oidc docs * Removed unnecessary comment
1 parent 40fb4b9 commit 6a088d2

5 files changed

Lines changed: 98 additions & 35 deletions

File tree

templates/Makefile

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ export AWS_DEFAULT_REGION = <% index .Params `region` %>
66
export AWS_PAGER =
77
KUBE_CONTEXT := $(PROJECT)-$(ENVIRONMENT)-$(AWS_DEFAULT_REGION)
88

9+
.EXPORT_ALL_VARIABLES:
10+
911
apply: apply-remote-state apply-shared-remote-state apply-secrets apply-shared-env apply-env update-k8s-conf pre-k8s apply-k8s-utils post-apply-setup
1012

1113
apply-remote-state:
@@ -40,12 +42,7 @@ apply-env:
4042
terraform apply $(AUTO_APPROVE)
4143

4244
pre-k8s:
43-
@aws secretsmanager describe-secret --region $(AWS_DEFAULT_REGION) --secret-id $(PROJECT)-$(ENVIRONMENT)-vpn-wg-privatekey-<% index .Params `randomSeed` %> > /dev/null 2>&1 || ( \
44-
echo "Creating VPN private key..." && \
45-
kubectl run --context $(KUBE_CONTEXT) -i --tty zero-k8s-utilities --image=commitdev/zero-k8s-utilities:0.0.3 --restart=Never -- wg genkey | \
46-
xargs aws secretsmanager create-secret --region $(AWS_DEFAULT_REGION) --name $(PROJECT)-$(ENVIRONMENT)-vpn-wg-privatekey-<% index .Params `randomSeed` %> --description "Auto-generated Wireguard VPN private key" --tags '[{"Key":"wg","Value":"$(PROJECT)-$(ENVIRONMENT)"}]' --secret-string && \
47-
kubectl delete --context $(KUBE_CONTEXT) pod/zero-k8s-utilities && \
48-
echo "Done VPN private key creation" )
45+
cd scripts && sh pre-k8s.sh
4946

5047
apply-k8s-utils:
5148
cd kubernetes/terraform/environments/$(ENVIRONMENT) && \
@@ -56,7 +53,7 @@ update-k8s-conf:
5653
aws eks --region $(AWS_DEFAULT_REGION) update-kubeconfig --role "arn:aws:iam::<% index .Params `accountId` %>:role/$(PROJECT)-kubernetes-$(ROLE)-$(ENVIRONMENT)" --name $(KUBE_CONTEXT) --alias $(KUBE_CONTEXT)
5754

5855
post-apply-setup:
59-
cd scripts && ENVIRONMENT=$(ENVIRONMENT) PROJECT=$(PROJECT) sh post-apply.sh
56+
cd scripts && sh post-apply.sh
6057

6158
teardown: teardown-k8s-utils teardown-env teardown-shared-env teardown-secrets teardown-remote-state teardown-shared-remote-state
6259

templates/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,39 @@ A database user will automatically be created for a backend application with a r
9595

9696
_Note: the user creation only happens once during `zero apply`. The creation happens in the script `zero-aws-eks-stack/dp-ops/create-db-user.sh`. This script should most likely not be run again as it could remove any subsequent changes to the db user or kubernetes secret._
9797

98+
#### User management / authentication
99+
If you initialized your infrastructure with the userAuth parameter, Ory [Kratos][kratos] and [Oathkeeper][oathkeeper] will be configured in the Kubernetes cluster.
100+
101+
Kratos is an OIDC-compliant user management tool, it manages the users in your system, including signup, login, password reset, Single Sign-On, etc.
102+
Oathkeeper is a zero-trust Identity & Access Proxy which sits in front of your application and handles authentication, passing traffic on to your backend only when a user is logged in, or in cases where you want to expose something publicly.
103+
104+
All these components should be working out of the box and you can style a front-end for Kratos however you like.
105+
To enable SSO support for Kratos (to log in with Github, Google, etc.) you will need to set up the OIDC providers you want to support. See [the Kratos documentation][kratos-oidc] for a guide.
106+
107+
To specify your provider config you need to create a secret containing your provider configuration in JSON format like so:
108+
```sh
109+
kubectl create secret generic oidc-providers -n user-auth --from-literal=SELFSERVICE_METHODS_OIDC='{"enabled":true,"config":{"providers":[{"id":"github","provider":"github","client_id":"<id>","client_secret":"<secret>","mapper_url":"http://your-url/github.data-mapper.jsonnet","scope":["user:email"]}]}}'
110+
```
111+
112+
This also requires specifying a mapper file which maps claims from the provider to fields which will be exposed to your app. This must be provided in the `mapper_url` field in the config above. It can be provided as a HTTP URL, a file, or a base64 encoded string.
113+
Here is a simple example for GitHub:
114+
```js
115+
local claims = std.extVar('claims');
116+
117+
{
118+
identity: {
119+
traits: {
120+
email: claims.email, // If email is not set the Jsonnet snippet will fail with an error.
121+
[if "website" in claims then "website" else null]: claims.website, // The website claim is optional.
122+
},
123+
},
124+
}
125+
```
126+
127+
Next, edit `kubernetes/terraform/modules/kubernetes/files/kratos-values.yml` and set `deployment.environmentSecretsName` to the name of the secret you created above.
128+
After restarting kratos, you should see a button appear on the login / signup pages to use the providers you set up.
129+
130+
_Note: Oathkeeper requires a `JWKS` file which should have been created automatically during project setup and saved into a secret in AWS secret manager._
98131

99132
# Resources
100133
### Infrastructure
@@ -150,6 +183,10 @@ make teardown-remote-state
150183
<!-- Links -->
151184
[tf-workflow]: https://www.terraform.io/guides/core-workflow.html
152185
[why-infra-as-code]: https://www.oreilly.com/library/view/terraform-up-and/9781491977071/ch01.html
186+
[kratos]: https://www.ory.sh/kratos/
187+
[oathkeeper]: https://www.ory.sh/oathkeeper/
188+
[kratos-oidc]: https://www.ory.sh/kratos/docs/guides/sign-in-with-github-google-facebook-linkedin
189+
153190
<!-- code -->
154191
[tf-remote-state]: ./terraform/bootstrap/remote-state
155192
[tf-secrets]: ./terraform/bootstrap/secrets

templates/kubernetes/terraform/modules/kubernetes/files/kratos-values.yml

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
image:
22
repository: oryd/kratos
3-
# tag: v0.4.6-alpha.1-sqlite
3+
tag: v0.5.4-alpha.1-sqlite
44
pullPolicy: IfNotPresent
55

66
ingress:
@@ -26,6 +26,12 @@ ingress:
2626
tls:
2727
- secretName: kratos-admin-tls-secret
2828

29+
# To set up OIDC you can uncomment this and create a secret which contains the
30+
# OIDC provider configuration in JSON format in an env var called SELFSERVICE_METHODS_OIDC
31+
# See README.md for more info
32+
# deployment:
33+
# environmentSecretsName: secretName
34+
2935
kratos:
3036
development: false
3137
autoMigrate: true
@@ -82,26 +88,13 @@ kratos:
8288
default_schema_url: file:///etc/config/identity.default.schema.json
8389

8490
selfservice:
91+
# To set up OIDC (Github, Google login, etc.) the 'oidc' method section can be provided via env var, see the deployment section above
8592
methods:
8693
# TODO: Look in to error when this is enabled
8794
# link:
8895
# enabled: true
8996
password:
9097
enabled: true
91-
oidc: # TODO: allow methods to be configured with parameters
92-
enabled: true
93-
# config:
94-
# TODO: use SELFSERVICE_METHODS_OIDC_CONFIG_PROVIDERS to set via secrets
95-
# [{"id":"github","provider":"github","client_id":"id","client_secret":"secret","mapper_url":"http://zombism.ca/github.data-mapper.jsonnet","scope":["user:email"]}]
96-
# providers:
97-
# - id: github
98-
# provider: github
99-
# client_id: # TODO: load secret
100-
# client_secret: # TODO : load secret
101-
# # TODO: Change to static or deploy somewhere within infra
102-
# mapper_url: http://zombism.ca/github.data-mapper.jsonnet
103-
# scope:
104-
# - user:email
10598

10699
default_browser_return_url: / # Overridden by user_auth.tf
107100
whitelisted_return_urls:

templates/kubernetes/terraform/modules/kubernetes/user_auth.tf

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,18 @@ locals {
44
"postgres" : "postgres",
55
"mysql" : "mysql",
66
}
7-
db_type = local.type_map[data.aws_db_instance.database.engine]
7+
db_type = local.type_map[data.aws_db_instance.database.engine]
8+
jwks_secret_name = "${var.project}-${var.environment}-oathkeeper-jwks-${var.random_seed}" # Created in pre-k8s.sh
9+
}
10+
11+
## Get generated JWKS content from secret
12+
data "aws_secretsmanager_secret" "jwks_content" {
13+
count = var.auth_enabled ? 1 : 0
14+
name = local.jwks_secret_name
15+
}
16+
data "aws_secretsmanager_secret_version" "jwks_content" {
17+
count = var.auth_enabled ? 1 : 0
18+
secret_id = data.aws_secretsmanager_secret.jwks_content[0].id
819
}
920

1021
resource "kubernetes_namespace" "user_auth" {
@@ -32,16 +43,10 @@ resource "helm_release" "oathkeeper" {
3243
value = "https://${var.backend_service_domain}"
3344
}
3445

35-
# This will read the local jwks file and
36-
# Nope, this won't work. Need to create the secret OOB.
37-
# set {
38-
# name = "oathkeeper.mutatorIdTokenJWKs"
39-
# value = file("${path.module}/files/id_token.jwks.json")
40-
# }
41-
42-
set {
43-
name = "oathkeeper.config.mutators.id_token.config.jwks_url"
44-
value = "http://zombism.ca/id_token.jwks.json"
46+
# Clean up and set the JWKS content. This will become a secret mounted into the pod
47+
set_sensitive {
48+
name = "oathkeeper.mutatorIdTokenJWKs"
49+
value = replace(jsonencode(jsondecode(data.aws_secretsmanager_secret_version.jwks_content[0].secret_string)), "/([,\\[\\]{}])/", "\\$1")
4550
}
4651

4752
set {
@@ -206,9 +211,9 @@ resource "helm_release" "kratos" {
206211
data "template_file" "oathkeeper_kratos_proxy_rules" {
207212
template = file("${path.module}/files/oathkeeper_kratos_proxy_rules.yaml.tpl")
208213
vars = {
209-
backend_service_domain = var.backend_service_domain
214+
backend_service_domain = var.backend_service_domain
210215
public_selfserve_endpoint = "/.ory/kratos/public"
211-
admin_selfserve_endpoint = "/.ory/kratos"
216+
admin_selfserve_endpoint = "/.ory/kratos"
212217
}
213218
}
214219

templates/scripts/pre-k8s.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/bin/sh
2+
ZERO_K8S_UTILS_VERSION=0.0.3
3+
OATHKEEPER_VERSION=v0.38.4-beta.1-alpine
4+
5+
VPN_SECRET_NAME=${PROJECT}-${ENVIRONMENT}-vpn-wg-privatekey-<% index .Params `randomSeed` %>
6+
OATHKEEPER_SECRET_NAME=${PROJECT}-${ENVIRONMENT}-oathkeeper-jwks-<% index .Params `randomSeed` %>
7+
8+
# Create VPN private key if the secret doesn't already exist
9+
aws secretsmanager describe-secret --region ${AWS_DEFAULT_REGION} --secret-id ${VPN_SECRET_NAME} > /dev/null 2>&1
10+
if [[ $? -ne 0 ]]; then
11+
echo "Creating VPN private key..."
12+
secret=$(kubectl run --rm --quiet --attach=true --context ${KUBE_CONTEXT} zero-k8s-utilities --image=commitdev/zero-k8s-utilities:${ZERO_K8S_UTILS_VERSION} --restart=Never -- wg genkey)
13+
aws secretsmanager create-secret --region ${AWS_DEFAULT_REGION} --name ${VPN_SECRET_NAME} --description "Auto-generated Wireguard VPN private key" --tags "[{\"Key\":\"wg\",\"Value\":\"${PROJECT}-${ENVIRONMENT}\"}]" --secret-string "${secret}"
14+
15+
if [[ $? -eq 0 ]]; then
16+
echo "Done VPN private key creation"
17+
fi
18+
fi
19+
20+
<% if eq (index .Params `userAuth`) "yes" %>
21+
# Create oathkeeper JWKS file if the secret doesn't already exist
22+
aws secretsmanager describe-secret --region ${AWS_DEFAULT_REGION} --secret-id ${OATHKEEPER_SECRET_NAME} > /dev/null 2>&1
23+
if [[ $? -ne 0 ]]; then
24+
echo "Creating Oathkeeper JWKS file..."
25+
secret=$(kubectl run --rm --quiet --attach=true --context ${KUBE_CONTEXT} oathkeeper --image=oryd/oathkeeper:${OATHKEEPER_VERSION} --restart=Never -- credentials generate --alg RS256)
26+
aws secretsmanager create-secret --region ${AWS_DEFAULT_REGION} --name ${OATHKEEPER_SECRET_NAME} --description "Auto-generated Oathkeeper JWKS file" --tags "[{\"Key\":\"oathkeeper-jwks\",\"Value\":\"${PROJECT}-${ENVIRONMENT}\"}]" --secret-string "${secret}"
27+
if [[ $? -eq 0 ]]; then
28+
echo "Done Oathkeeper JWKS file creation..."
29+
fi
30+
fi
31+
<% end %>

0 commit comments

Comments
 (0)