Skip to content

lukaszbudnik/haproxy-auth-gateway

main
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 

haproxy-auth-gateway Docker

haproxy-auth-gateway is an authentication and authorization gateway for cloud native apps.

haproxy-auth-gateway features include:

  • parsing JWT token from the HTTP Authorization header
  • Keycloak realm roles support
  • RS256, HS256, HS512 signature verification
  • expiration time verification
  • issuer verification
  • audience verification

haproxy-auth-gateway can be configured with the following env variables:

  • OAUTH_PUBKEY_PATH - contains location to issuer public key (mandatory)
  • OAUTH_ISSUER - contains name of the issuer (optional)
  • OAUTH_AUDIENCE - contains name of the audience (optional)

Docker image

haproxy-auth-gateway is available on docker hub:

docker pull lukasz/haproxy-auth-gateway

or on ghcr.io:

docker pull ghcr.io/lukaszbudnik/haproxy-auth-gateway

Example

The below example shows how to deploy & configure lukasz/haproxy-auth-gateway in Kubernetes. It also shows how to invoke the Lua verify script and write ACLs.

If you are interested in running a complete distributed demo app on Kubernetes check out: lukaszbudnik/keycloak-kubernetes. This demo app uses Keycloak as Identity and Access Management solution and haproxy-auth-gateway for transparent authentication and authorization for backend services.

Kubernetes deployment

haproxy-auth-gateway requires:

  • your haproxy config (file)
  • public key of the JWT issuer (file)
  • OAUTH_PUBKEY_PATH set to the path of the public key of the JWT issuer (env variable)
  • OAUTH_ISSUER and OAUTH_AUDIENCE are optional should you want a more fine-grained JWT verification (env variable)

You can create haproxy config and public key files as config maps:

kubectl create configmap haproxy-auth-gateway-iss-cert --from-file=config/hotel.pem
kubectl create configmap haproxy-auth-gateway-haproxy-cfg --from-file=config/haproxy.cfg

Then you can map them to volumes and then mount them into haproxy-auth-gateway container. In the container spec you also set the env variables:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: gateway
  labels:
    app.kubernetes.io/name: gateway
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: gateway
  template:
    metadata:
      labels:
        app.kubernetes.io/name: gateway
    spec:
      containers:
        - name: gateway
          image: lukasz/haproxy-auth-gateway
          env:
            - name: OAUTH_PUBKEY_PATH
              value: /etc/certs/hotel.pem
            - name: OAUTH_ISSUER
              value: issuer_is_optional
            - name: OAUTH_AUDIENCE
              value: audience_is_optional
          ports:
            - containerPort: 80
          volumeMounts:
            - name: iss-cert
              mountPath: /etc/certs
            - name: haproxy-cfg
              mountPath: /usr/local/etc/haproxy
      volumes:
        - name: haproxy-cfg
          configMap:
            name: haproxy-auth-gateway-haproxy-cfg
        - name: iss-cert
          configMap:
            name: haproxy-auth-gateway-iss-cert
---
apiVersion: v1
kind: Service
metadata:
  name: gateway
  labels:
    app.kubernetes.io/name: gateway
spec:
  type: ClusterIP
  clusterIP: None
  selector:
    app.kubernetes.io/name: gateway
  ports:
    - protocol: TCP
      port: 80

Then we are ready to deploy haproxy-auth-gateway:

kubectl apply -f gateway.yaml

haproxy ACL

haproxy-auth-gateway will verify passed JWT and will (if all good):

  • set txn.authorized variable to true
  • set txn.roles variable to a comma separated list of realm_access.roles

Above variables can be used in haproxy ACLs.

For example:

# deny if no Authorization header sent
http-request deny unless { req.hdr(authorization) -m found }
# invoke the jwtverify Lua script
http-request lua.jwtverify
# check if authorized successfully
http-request deny unless { var(txn.authorized) -m bool }
# check roles
http-request deny if PATH_camarero ! { var(txn.roles) -m sub camarero }

Troubleshooting

The script outputs many useful debug messages. To enable debug add the following configuration to you haproxy.cfg:

global
    log stdout local0 debug

defaults
    log global

Sample JWT

A sample JWT token generated by Keycloak looks like this:

eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJXejFuaDNCWDI4UHMxVEMzSDRoOW52Q1VWRXpjVVBzQms4Z1NmeEp4ZS1JIn0.eyJleHAiOjE2MTM4NTQ3OTgsImlhdCI6MTYxMzg1Mzg5OCwiYXV0aF90aW1lIjoxNjEzODUzNjk2LCJqdGkiOiIxMmI1YTMxYS1hYjM1LTQxMDMtYTkxNC0wZjRlODUzMzg4ZjUiLCJpc3MiOiJodHRwczovL2F1dGgubG9jYWx0ZXN0Lm1lL2F1dGgvcmVhbG1zL2hvdGVsIiwic3ViIjoiMWE1NWUxMjktZjliYi00ZDYwLWJlZDEtMGJhYmIwOWJlZTNlIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoicmVhY3QiLCJub25jZSI6Ijg4NGNiMzY1LTRjMTQtNDZhYS04ZTBjLWViM2Q3ZjBjYTRmMCIsInNlc3Npb25fc3RhdGUiOiI3NDZhNDZhZC1hY2Y3LTRhMTYtYWI2Yy1iMWZhNWE1YTgxZDMiLCJhY3IiOiIwIiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHA6Ly9sb2NhbGhvc3Q6MzAwMCJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiY29jaW5lcmEiLCJkb25jZWxsYSIsImNhbWFyZXJvIl19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoiQW5nZWxhIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYW5nZWxhIiwiZ2l2ZW5fbmFtZSI6IkFuZ2VsYSJ9.kkv2K-XYpHexnKmCoyNED_pO7G8hNI8hi2WCUzhpErkvrazNNZmUYZ8ZAjiybpi1u6ouc2EsHGykTNhUBD2jRhb2dWHYqcEEDaIn9MUq62B-nbTIcB-6vf1SrKnY_Vdnq_olmV_MhIJSQjPbDfcCVKKiUxHYmSBc9Vuno-enPehfUb_EpoRaM24SfJ0WDU281rTPxsgAJBdB4Yg0E9KMfCgaXkwRaHXMEGVpzHHqdi8S1lWwxs12Par-Qz4HqP-Tsw6KqNPU11dG3v6H_Q2fWmDsX5vvMqnmWkMQOFzco2fffsx7lcClPxNw3VghSVT-qB_7dMKUoT-DfyIo1Rcbqw

When parsed and decoded becomes the following.

Header:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "Wz1nh3BX28Ps1TC3H4h9nvCUVEzcUPsBk8gSfxJxe-I"
}

Payload:

{
  "exp": 1613854798,
  "iat": 1613853898,
  "auth_time": 1613853696,
  "jti": "12b5a31a-ab35-4103-a914-0f4e853388f5",
  "iss": "https://auth.localtest.me/auth/realms/hotel",
  "sub": "1a55e129-f9bb-4d60-bed1-0babb09bee3e",
  "typ": "Bearer",
  "azp": "react",
  "nonce": "884cb365-4c14-46aa-8e0c-eb3d7f0ca4f0",
  "session_state": "746a46ad-acf7-4a16-ab6c-b1fa5a5a81d3",
  "acr": "0",
  "allowed-origins": ["http://localhost:3000"],
  "realm_access": {
    "roles": ["cocinera", "doncella", "camarero"]
  },
  "scope": "openid email profile",
  "email_verified": false,
  "name": "Angela",
  "preferred_username": "angela",
  "given_name": "Angela"
}

Public key:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyn1SYrKiXgJembEG1emG
lUw/NliK0tOTeKr8eBp7TZxI8D9k9FUkfzEeQyWekShPt3yTG9boZ9Sq/K7FAfs7
vXFG+kTKYYXysvfdkHHKJnPWEAJgqj3vDEpHB/Xqw5OtqOkSNPNYOxJ65ZmmZVNB
77NpGK5xW5s7xc7XXvLuILhfbOQXlObPbMnjVcnQSGHjmfbtTKsQ/im6ayxtShsL
FQgEJycplJU21WRy3T9cDHpGOMF3LehFIOmsxspcuC/idS0Nber3Fuw9QndSHZQL
KPTkDlyacPu9SyOJiMmD9S4QOZo9UVQWA8JlKa+KuL6TXyZ1OZdSkPSX1o1xeH7L
ewIDAQAB
-----END PUBLIC KEY-----

Original project

haproxy-auth-gateway is based on great project from haproxytech folks: https://github.com/haproxytech/haproxy-lua-oauth.

haproxy-auth-gateway contains changes to support Keycloak realm roles out of the box.