At $WORK we needed to expose some apps that don’t have a login flow (eg alertmanager, prometheus), these are running in kubernetes.
Since I found the documentation a little bit confusing I decided to create this blogpost to document to others (here myself from the future included).
Most examples here will be in terraform, which can be easily translated to the UI.
Requirements
- ExternalDNS setup
- A domain you already know
- All required permissions to create all necessary stuff
First let’s create a user pool and a domain.
Pay attention to the domain name, since it will be used later on.
1. user pool + domain
1
2
3
4
5
6
7
8
| resource "aws_cognito_user_pool" "myproject" {
name = "myproject"
}
resource "aws_cognito_user_pool_domain" "myproject" {
domain = "myproject"
user_pool_id = aws_cognito_user_pool.myproject.id
}
|
2. google client
Then at google UI let’s create a Client ID
There at Authorized JavaScript origins you specify your domain name you created previously
1
| https://myproject.auth.us-east-1.amazoncognito.com
|
And for Authorized redirect URIs you specify the same thing plus the callback url
1
| https://myproject.auth.us-east-1.amazoncognito.com/oauth2/idpresponse
|
Copy the client id
and client secret
.
3. Identity provider
Now you create the google provider, the client id and secret is the one you copied in a previous step.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| resource "aws_cognito_identity_provider" "google_provider" {
user_pool_id = aws_cognito_user_pool.myproject.id
provider_name = "Google"
provider_type = "Google"
provider_details = {
authorize_scopes = "profile email openid"
client_id = "YOUR_CLIENT_ID"
client_secret = "YOUR_CLIENT_SECRET"
}
attribute_mapping = {
email = "email"
}
}
|
ignore changes
As you login state will be updated, which terraform is not aware about. To make terraform ignore it use:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| resource "aws_cognito_identity_provider" "google_provider" {
(...)
lifecycle {
ignore_changes = [
attribute_mapping["username"],
provider_details["attributes_url"],
provider_details["attributes_url_add_attributes"],
provider_details["oidc_issuer"],
provider_details["token_request_method"],
provider_details["token_url"],
provider_details["authorize_url"],
]
}
}
|
3. aws cognito client
Now that we have the google provider we can create a client. Pay attention to the callback URL, since you need to append /oauth2/idpresponse
.
1
2
3
4
5
6
7
8
9
10
11
| resource "aws_cognito_user_pool_client" "test_client" {
name = "google_client"
user_pool_id = aws_cognito_user_pool.myproject.id
callback_urls = ["https://MY_EXPOSED_WEBSITE/oauth2/idpresponse"]
allowed_oauth_flows_user_pool_client = true
allowed_oauth_flows = ["code"]
allowed_oauth_scopes = ["email", "openid"]
supported_identity_providers = ["Google"]
generate_secret = true
}
|
4. The kubernetes bit
Let’s create a simple nginx just to test this. This part should be straightforward (if you know k8s).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
type: ClusterIP
ports:
- port: 80
targetPort: 80
|
Then the ingress part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: "nginx-alb-ingress"
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: "ip"
alb.ingress.kubernetes.io/subnets: YOUR_SUBNETS
alb.ingress.kubernetes.io/listen-ports: "[{\"HTTP\": 80}, {\"HTTPS\": 443}]"
alb.ingress.kubernetes.io/ssl-redirect: "443"
alb.ingress.kubernetes.io/certificate-arn: YOUR_CERTIFICATE
external-dns.alpha.kubernetes.io/hostname: YOUR_HOSTNAME
# Cognito stuff
alb.ingress.kubernetes.io/auth-type: cognito
alb.ingress.kubernetes.io/auth-scope: openid
alb.ingress.kubernetes.io/auth-session-timeout: '3600'
alb.ingress.kubernetes.io/auth-session-cookie: AWSELBAuthSessionCookie
alb.ingress.kubernetes.io/auth-on-unauthenticated-request: authenticate
alb.ingress.kubernetes.io/auth-idp-cognito: '{"UserPoolArn":"YOUR_POOL", "UserPoolClientId":"YOUR_CLIENT_ID", "UserPoolDomain":"YOUR_DOMAIN"}'
spec:
rules:
- host: YOUR_HOSTNAME
http:
paths:
- path: /*
backend:
serviceName: "nginx-service"
servicePort: 80
|
I think here everything should be straightforward, except the auth-idp-cognito
part
1
| alb.ingress.kubernetes.io/auth-idp-cognito: '{"UserPoolArn":"YOUR_POOL", "UserPoolClientId":"YOUR_CLIENT_ID", "UserPoolDomain":"YOUR_DOMAIN"}'
|
It’s a JSON, so let’s break it down
| |
---|
"UserPoolArn":"YOUR_POOL" | you can get that via the UI by accessing General Settings |
"UserPoolClientId":"YOUR_CLIENT_ID" | same thing, but under App Integration -> App Client Settings |
"UserPoolDomain":"YOUR_DOMAIN" | thing here is that we only want the prefix, which is what we set in aws_cognito_user_pool_domain.myproject.domain |
And that should be it.