Okta Authentication and Authorization for your AWS EKS
When deploying services to Kubernetes cluster, Istio is often used for handling request routing and user authentication and authorization. In this article we present Istio configuration together with Okta and AWS Application Load Balancer (ALB) on top of Elastic Kubernetes Service. JSON Web Token (JWT) with custom user group claim issued by Okta is used for authorization. The ALB enforces Okta authentication and is able to check the health of cluster instances.
Together with Jakub Snor and Vladimir Valouch.
Pre-requisites
kubectl
installed- running Kubernetes cluster in AWS
Istio Deployment
We showcase the role-based access on the simple httpbin
application used in Istio samples.
kubectl create ns foo
kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml) -n foo
Create New Application in Okta
You can use Okta dev account to do the set up as well as your production Okta. Open the Admin dashboard and make sure you are using Classic UI
as some options are not available in theDeveloper Console
. The first thing you need to create is a new application. Go to Applications
link in Applications
drop-down menu and click the Add Application
button. We are adding a custom application so click the Create New App
button. Keep platform as Web
and choose OpenID Connect
Sign on method. Confirm by clicking on the Create
button.
On the next page, type in your application name and optionally upload its icon. Configure Login redirect URIs to some temporary value, for example http://localhost
. We will change this value once we have an Application Load Balancer created. Confirm by clicking on the Save
button.
Create AWS Application Load Balancer with Okta Auth
We create an application load balancer using CloudFormation. See the whole CloudFormation template. Below we describe important parts of the template.
It configures AWS::ElasticLoadBalancingV2::Listener
to use Okta authentication in section AuthenticateOidcConfig
.
It allows “health check” endpoint communication on endpoint /status/200
. See the forward rule without Okta authentication in HealthCheckAllowRule
.
An important part of the load balancer is the HealthCheckPath
configuration of AWS::ElasticLoadBalancingV2::TargetGroup
. We use /status/200
where httpbin
responses with OK status. Later we will open this endpoint on the Istio level to be accessible without authentication.
In HttpPlainListener
we also redirect traffic from HTTP to HTTPS.
Parameters of the CloudFormation template
CertificateArn
— find it in AWS Certificate Manager in details of your certificate.authorizationEndpoint
,issuer
,tokenEndpoint
,userInfoEndpoint
— defaults of these are prepared, just replacedev-12345.okta.com
with your Okta account.clientId
andclientSecret
— find it Okta -> Applications -> your app -> General tab -> section Client Credentials.VpcId
,SubnetId
,SubnetIdB
,ELBSGId
— use same VPC, subnets and security group that classic load balancer created by kubernetes cluster.Instance1
,Instance2
— point the target group to the same instances that classic load balancer created by kubernetes cluster points to.TargetTransportPort
— the port whereistio-system
exposes port 80, runkubectl get svc istio-ingressgateway -n istio-system
to find it (usually 32252, 32280, …).
Configure Okta Application with the Load Balancer
Once you have the ALB ready, you can configure your Okta application.
- In the
Classic UI
of Okta Admin, open the application list and click on your application. - Switch to
General
tab and edit it by clicking onEdit
button. - Check
Implicit (Hybrid)
Allowed grant type. - Change Login redirect URIs to point to your load balancer. This value must be absolute URI and you can use either an A Record of your ALB or a CNAME you mapped to it. Do not forget to add the path
/oauth2/idpresponse
and to include a protocol (HTTPS). - Set Login initiated by to
Either Okta or App
. - Check both Display application icon to users and Display application icon in the Okta Mobile app.
- Set Initiate login URI to your entry point to the application. Confirm everything by clicking on the
Save
button.
Exposing Service in Istio
We expose our httpbin
service using Istio gateway.
Gateway
Configure a gateway on port 80 for HTTP traffic.
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: httpbin-gateway
namespace: foo
spec:
selector:
istio: ingressgateway
servers:
- hosts:
- '*'
port:
name: http
number: 80
protocol: HTTP
EOF
Virtual Service
Configure routes for traffic entering via the Gateway to go to our httpbin
service on port 8000.
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: httpbin
namespace: foo
spec:
hosts:
- "*"
gateways:
- httpbin-gateway
http:
- route:
- destination:
port:
number: 8000
host: httpbin.foo.svc.cluster.local
EOF
Intermediate Testing
At this point you should be able to access your application through the load balancer DNS name. It should redirect you to the Okta login screen and upon successful login to the httpbin
application.
If load balancer returns 504 Gateway Timeout, double-check that the target group shows that both instances are “healthy”.
You can use endpoint /headers
to inspect what headers Okta added to your request. You can find your JWT token in x-amzn-oidc-accesstoken
and you can decode it. Notice that so far there are no user groups present in your token. We will set it up in the next section and use it for Istio authorization.
Okta Groups and Custom JWT Token Claims
The application should be available to a managed set of users. We create an Okta group and add our users to this group to grant access to our application.
- In the
Classic UI
of Okta Admin, Go toGroups
link inDirectory
drop-down menu. - Create a new group by clicking on
Add Group
button. - Name your group and optionally write down some description for it and confirm by clicking on
Add Group
button. - Open the group and add people to this group by clicking on
Manage People
button. - Assign your application to this group by clicking on
Manage Apps
.
This completes the authentication part — only authenticated users can now access your application.
To do the authorization we are going to validate the JWT provided by Okta and verify the user is authorized to use our application. We will check that the user is indeed in the group that has access to our application. Before we can do that though, we need to ensure such information is included in one of JWT’s claims.
- In the
Classic UI
of Okta Admin, open the application list and click on your application. - Switch to
Sign On
tab. - Click on the
Edit
button in the OpenID Connect ID Token block. - Name your Groups claim filter and select
Equals
from the drop-down menu followed by the name of the group. Now this group is included in the JWT claim. You can also chooseMatches regex
followed by.*
to include all the groups of the user. - Confirm your configuration by clicking the
Save
button.
Verify — after the claims are set up, verify using the
/headers
endpoint that your JWT token in headerx-amzn-oidc-accesstoken
now contains the groups.
Istio Okta Authorization
Request Authentication
We configure RequestAuthentication
in our namespace foo
to verify that JWT token in the request is valid and issued by our Okta account.
Okta is using specific x-amzn-oidc-accesstoken
header for the JWT token. We need to instruct Istio in fromHeaders
to read the JWT token from the custom header, not from the default authentication header.
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
name: jwt-from-okta
namespace: foo
spec:
jwtRules:
- issuer: "https://dev-12345.okta.com/oauth2/default"
jwksUri: "https://dev-12345.okta.com/oauth2/default/v1/keys"
forwardOriginalToken: true
fromHeaders:
- name: x-amzn-oidc-accesstoken
EOF
Notice — at this point Istio refuses invalid tokens, but it still allows traffic without any tokens. We will enforce tokens below using
AuthorizationPolicy
. More in Istio documentation.
Authorization based on Okta Group
Above we instructed RequestAuthentication
to use x-amzn-oidc-accesstoken
header. Therefore we can now access the JWT token claims in AuthorizationPolicy
.
We open our “health check” endpoint /status/200
to everyone without JWT token. But for all other paths we require requestPrincipal
from our Okta account. Additionally, we enforce that the user belongs to “httpbin” Okta group that we read from request.auth.claims[groups]
.
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: httpbin-allow-health-policy
namespace: foo
spec:
selector:
matchLabels:
app: httpbin
action: ALLOW
rules:
- to:
- operation:
paths: ["/status/200"]
- from:
- source:
requestPrincipals: ["https://dev-12345.okta.com/oauth2/default/*"]
when:
- key: request.auth.claims[groups]
values: ["httpbin"]
EOF
Notice — you should have
RequestAuthentication
andAuthorizationPolicy
in the same namespace, otherwise the policy cannot correctly see therequest.auth.claims
that are filled from custom headerx-amzn-oidc-accesstoken
in theRequestAuthentication
.
Final Verification
- Everyone (even without Okta) should be able to access
/status/200
. - If your Okta user belongs to group “httpbin”, you should be able to access
httpbin
. - If Okta user is not in group “httpbin”, you should get
RBAC: Access denied
.
Troubleshooting
504 Gateway Timeout — check that the target group monitoring shows both instances as healthy. Double-check, that target group communicates with instances on correct TargetTransportPort
.
Always getting RBAC: Access Denied — double-check that your JWT token includes custom claim groups
and one of the groups is “httpbin”.