Okta Authentication and Authorization for your AWS EKS

Zdenek Papez
7 min readApr 22, 2020

--

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.

Okta Authentication and Authorization Overview

Pre-requisites

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.

Create new application in Okta
Create new application in Okta

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.

Configure new application in Okta

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.

CFN Template for Load Balancer with Okta Authentication

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 replace dev-12345.okta.com with your Okta account.
  • clientId and clientSecret — 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 where istio-system exposes port 80, run kubectl 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.

  1. In the Classic UI of Okta Admin, open the application list and click on your application.
  2. Switch to General tab and edit it by clicking on Edit button.
  3. Check Implicit (Hybrid) Allowed grant type.
  4. 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).
  5. Set Login initiated by to Either Okta or App.
  6. Check both Display application icon to users and Display application icon in the Okta Mobile app.
  7. Set Initiate login URI to your entry point to the application. Confirm everything by clicking on the Save button.
Configure your application in Okta

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.

  1. In the Classic UI of Okta Admin, Go to Groups link in Directory drop-down menu.
  2. Create a new group by clicking on Add Group button.
  3. Name your group and optionally write down some description for it and confirm by clicking on Add Group button.
  4. Open the group and add people to this group by clicking on Manage People button.
  5. Assign your application to this group by clicking on Manage Apps.

This completes the authentication part — only authenticated users can now access your application.

Add group in Okta

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.

  1. In the Classic UI of Okta Admin, open the application list and click on your application.
  2. Switch to Sign On tab.
  3. Click on the Edit button in the OpenID Connect ID Token block.
  4. 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 choose Matches regex followed by .* to include all the groups of the user.
  5. Confirm your configuration by clicking the Save button.
Configure Sign On of your application

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 and AuthorizationPolicy in the same namespace, otherwise the policy cannot correctly see the request.auth.claims that are filled from custom header x-amzn-oidc-accesstoken in the RequestAuthentication.

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”.

--

--