Set up external authentication
You can set up OpenID Connect (OIDC) authentication to secure access to your developer portal. To learn more about how external authentication works with your portal, see External authentication with the developer portal.
You can choose among the supported OIDC providers to secure your developer portal.
The example in this guide uses Keycloak as the OIDC provider.
Before you begin
-
Make sure that you created your developer portal and exposed the portal on the gateway.
-
Get the external address of the ingress gateway. If you deployed your ingress gateway in a different namespace or with a different version, update the command.
export INGRESS_GW_IP=$(kubectl get svc -n gloo-mesh-gateways istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') echo $INGRESS_GW_IP
export INGRESS_GW_IP=$(kubectl get svc -n gloo-mesh-gateways istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') echo $INGRESS_GW_IP
Note: Depending on your environment, you might see
<pending>
instead of an external IP address. For example, if you are testing locally in kind or minikube, or if you have insufficent permissions in your cloud platform, you can instead port-forward the service port of the ingress gateway:kubectl -n gloo-mesh-gateways port-forward deploy/istio-ingressgateway-1-18-2 8081
-
For local testing with your browser, update your
/etc/hosts
DNS file. Map the IP address of your ingress gateway to resolve to thedeveloper.example.com
host domain that you set in the route table.sudo nano /etc/hosts
Replace
${INGRESS_GW_IP}
with the value that you previously retrieved.${INGRESS_GW_IP} developer.example.com
Set up authentication with Keycloak
-
Follow the steps to install Keycloak.
-
Create a Kubernetes secret with the Keycloak OIDC secret.
kubectl apply -f - <<EOF apiVersion: v1 kind: Secret metadata: name: oauth namespace: gloo-mesh-addons type: extauth.solo.io/oauth data: client-secret: $(echo -n ${KEYCLOAK_SECRET} | base64) EOF
-
Create an external auth server to use for your policy.
kubectl apply -f - <<EOF apiVersion: admin.gloo.solo.io/v2 kind: ExtAuthServer metadata: name: ext-auth-server namespace: gloo-mesh-addons spec: destinationServer: ref: cluster: $CLUSTER_NAME name: ext-auth-service namespace: gloo-mesh-addons port: name: grpc EOF
-
Decide on the portal APIs that you want to enforce authentication for. In the following example, you want users to be authenticated before they can perform the
me
andapi-keys
actions. To apply an external auth policy to these APIs and enforce authentication, you add theoauth: "true"
label on all of the routes.kubectl apply -f - <<EOF apiVersion: networking.gloo.solo.io/v2 kind: RouteTable metadata: name: dev-portal-rt namespace: gloo-mesh-gateways spec: hosts: - developer.example.com virtualGateways: - name: istio-ingressgateway-portal namespace: gloo-mesh-gateways defaultDestination: port: number: 8080 ref: name: gloo-mesh-portal-server namespace: gloo-mesh-addons http: - forwardTo: {} labels: oauth: "true" matchers: - uri: prefix: /v1/api-keys - uri: prefix: /v1/me - headers: - name: Cookie value: ".*?id_token=.*" regex: true - forwardTo: {} labels: oauth: "false" matchers: - uri: prefix: /v1/apis - uri: prefix: /v1/usage-plans EOF
Setting Description spec.http.matchers.headers
If a Cookie
header is present in the client request, instruct the external auth server to retrieve the ID token from the header. Use.*?id_token=.*
if you decided to store the raw ID token from your OIDC provider in the cookie. If you decided to store the ID token in Redis and to return a session ID instead, enter.*?session=.*
. -
Create an external auth policy for the
oauth: "true"
routes to enforce authentication with the Keycloak OIDC provider that you set up. Make sure to replace$CLUSTER_NAME
,$KEYCLOAK_CLIENT
and$KEYCLOAK_URL
with the values for your cluster name, Keycloak client and URL.kubectl apply -f -<<EOF apiVersion: security.policy.gloo.solo.io/v2 kind: ExtAuthPolicy metadata: name: oidc-auth namespace: gloo-mesh-addons spec: applyToRoutes: - route: labels: oauth: "true" config: server: name: ext-auth-server namespace: gloo-mesh-addons cluster: $CLUSTER_NAME glooAuth: configs: - oauth2: oidcAuthorizationCode: appUrl: http://developer.example.com callbackPath: /v1/me clientId: $KEYCLOAK_CLIENT clientSecretRef: name: oauth namespace: gloo-mesh-addons issuerUrl: "${KEYCLOAK_URL}/realms/master/" scopes: - email session: failOnFetchFailure: true cookie: allowRefreshing: true cookieOptions: notSecure: true maxAge: 3600 headers: idTokenHeader: id_token EOF
Setting Description spec.applyToRoutes
Select the routes that you want to apply this policy to. In this example, you want to require external authentication for all routes with the oauth: "true"
label.spec.config.server
The external auth server to use for the policy. glooAuth.configs.oauth2.appUrl
The URL users are redirected to to authenticate with Keycloak. glooAuth.configs.oauth2.callbackPath
The callback path that users are getting redirected to after successful authentication. Gloo Gateway intercepts requests with this path to store the ID token that is returned from the OIDC provider. If you specified header matching of type ".*?id_token=.*"
in the route table for your developer portal, the raw ID token is stored in theSet-Cookie
response header. If you specified".*?session=.*"
, the ID token is stored in Redis and a session ID is set in theSet-Cookie
response header. Then, the request is forwarded to its original destination.The callback path must have a matching route in the route table that is associated with the external auth policy. In this example,/v1/me
is defined in the route table for the developer portal.glooAuth.configs.oauth2.clientId
The client ID token that you got when you registered the client in your OIDC provider. glooAuth.configs.oauth2.clientSecretRef
The Kubernetes secret that has the client secret that you got when you registered the client in your OIDC provider. The secret must exist on the same cluster as the ExtAuthServer resource that this policy refers to. glooAuth.configs.oauth2.issuerUrl
The URL of the OpenID Connect identity provider. Gloo Gateway automatically discovers OIDC configuration by querying the .well-known/openid-configuration
endpoint on theissuer_url
. In this example, Gloo Gateway expects to find OIDC discovery information at"${KEYCLOAK_URL}/realms/master/"
.glooAuth.configs.oauth2.scopes
Scopes to request in addition to the openid
scope, such asemail
in this example.glooAuth.configs.oauth2.idTokenHeader
Forward the ID token to the destination after successful authentication. This setting is required indepent of the OIDC provider that you use. -
Verify that the external auth policy is applied successfully.
- Review the status of the external auth policy and make sure that it shows
ACCEPTED
.kubectl get extauthpolicy oidc-auth -n gloo-mesh-addons -o yaml
- Get the authconfig resource that was created for your policy and make sure that it shows
ACCEPTED
.kubectl get authconfig -n gloo-mesh-addons -o yaml
- Review the status of the external auth policy and make sure that it shows
Verify external authentication
-
Send a request to the portal server. Verify that you get back a 302 HTTP redirect response. The redirect URL is stored in the
location
field of your CLI output.curl -vik --resolve developer.example.com:80:$INGRESS_GW_IP http://developer.example.com:80/v1/me
Example output:
* Mark bundle as not supporting multiuse < HTTP/1.1 302 Found HTTP/1.1 302 Found < location: http://34.145.136.160:8080/auth/realms/master/protocol/openid-connect/auth?client_id=4464fbac-ab29-4c6a-945b-14e4685c0ad4&redirect_uri=http%3A%2F%2Fdeveloper.example.com%3A80%2Fv1%2Flogin&response_type=code&scope=email+openid&state=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2ODA5MTk5OTYsImlhdCI6MTY4MDkxODE5Niwic3RhdGUiOiJodHRwOi8vZGV2ZWxvcGVyLmV4YW1wbGUuY29tL3YxL2xvZ2luIn0.ubqut8s4LZCHqHGtXN4TO2Esnp48FRK5ARufmKiFDjo location: http://34.145.136.160:8080/auth/realms/master/protocol/openid-connect/auth?client_id=4464fbac-ab29-4c6a-945b-14e4685c0ad4&redirect_uri=http%3A%2F%2Fdeveloper.example.com%3A80%2Fv1%2Flogin&response_type=code&scope=email+openid&state=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2ODA5MTk5OTYsImlhdCI6MTY4MDkxODE5Niwic3RhdGUiOiJodHRwOi8vZGV2ZWxvcGVyLmV4YW1wbGUuY29tL3YxL2xvZ2luIn0.ubqut8s4LZCHqHGtXN4TO2Esnp48FRK5ARufmKiFDjo < date: Sat, 08 Apr 2023 01:43:16 GMT date: Sat, 08 Apr 2023 01:43:16 GMT < server: istio-envoy server: istio-envoy < content-length: 0 content-length: 0
-
In your preferred web browser, enter the URL that was returned in the
location
field of your CLI output to open the Keycloak login screen. Make sure to also open the developer utilities for your web browser. You use these utilities to retrieve the ID token that Keycloak returns after successful login. For example, if you use Google Chrome, you can right-click on the page and click Inspect. Then, go to the Network tab. -
Log in to Keycloak by using one of the usernames that you set up in the beginning. For example, you can enter
user1@example.com
as the email andpassword
as the password. -
Make sure that after logging in, you are redirected to the
developer.example.com/v1/me
URL and that that user credentials are returned.Example output:
{"email":"user1@example.com","name":"","username":"user1"}
-
From the developer utilities Network tab, find the
me
request and find theCookie
header. -
Copy the
id_token
from theCookie
header and set it as an environment variable.export ID_TOKEN="id_token ey..."
-
Send another request to the
/v1/me
API. This time, you get back a 200 HTTP response because you are successfully authenticated.curl -vik -H "Cookie: ${ID_TOKEN}" --resolve developer.example.com:80:$INGRESS_GW_IP http://developer.example.com:80/v1/me
Example output:
* Mark bundle as not supporting multiuse < HTTP/1.1 200 OK HTTP/1.1 200 OK < date: Mon, 10 Apr 2023 16:33:30 GMT date: Mon, 10 Apr 2023 16:33:30 GMT < content-length: 0 content-length: 0 < x-envoy-upstream-service-time: 0 x-envoy-upstream-service-time: 0 < server: istio-envoy server: istio-envoy {"email":"user1@example.com","name":"","username":"user1"}
-
Continue with set up authorization for private APIs to create portal groups that let users access private APIs based on the claims in the ID token.
Troubleshoot issues
Use the following general steps if you encounter issues during your Keycloak setup.
-
Make sure that the status of the external auth policy shows
ACCEPTED
.kubectl get extauthpolicy oidc-auth -n gloo-mesh-addons -o yaml
-
Get the authconfig resource that was created for your policy and make sure that it shows
ACCEPTED
.kubectl get authconfig -n gloo-mesh-addons -o yaml
-
If you used environment variables, such as
$KEYCLOAK_CLIENT
or$KEYCLOAK_URL
, make sure that you entered the values of these environment variables in the external auth policy. If you used the variable names, the values might not be properly replaced. -
To get detailed logs for the external auth service, change the log level to
DEBUG
.-
Edit the external auth service.
kubectl edit deploy -n gloo-mesh-addons ext-auth-service
-
In the
spec.container.env
section, find theLOG_LEVEL
environment variable and set it toDEBUG
.... spec: containers: - env: - name: LOG_LEVEL value: DEBUG
-
Make sure that the external auth service pod restarts.
kubectl get pods -n gloo-mesh-addons | grep ext-auth-service
-
-
In a separate terminal, get the logs of the external auth service.
kubectl logs <external-auth-service-pod> -n gloo-mesh-addons
-
Send the curl request that is failing and review the logs that are returned by the external auth service.