APIcast icon indicating copy to clipboard operation
APIcast copied to clipboard

THREESCALE-11015 - Support client_secret_jwt and private_key_jwt as authentication type for token introspection policy

Open tkan145 opened this issue 1 year ago • 0 comments

Known issue

Some time failed with the following error

/opt/app-root/src/gateway/src/apicast/policy_loader.lua:98: Invalid config for policy: failed to validate dependent schema for "auth_type": value should match only one schema, but matches none

~~This is due to the use of oneOf and seems like a bug. I will need to investigate this further.~~

The original intention was to add support for a single algorithm and then upgrade lua-retsy-jwt afterwards and have a minimal schema structure change. However, the jsonschema validation fails when the emum field only contains a single value so I decided to remove it for now.

~~@eguzki if you know a better way to build apicast-config.json or how to solve this problem, please let me know~~


What

This PR mainly adding 2 new authentication method for token introspection policy, client_secret_jwt and private_key_jwt.

JIRA: https://issues.redhat.com/browse/THREESCALE-11015 Reference: https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication

Why only HS256 and RS256 are supported?

This is due to the version of lua-resty-jwt (0.20) that we use

Support sign algorithm

RHKB 24 lua-resty-jwt 0.20 lua-resty-0.2.2 lua-resty-0.2.3
client_secret_jwt HS256HS384HS512 HS256HS512 HS256HS512 HS256HS512
private_key_jwt RS256RS382RS512ES256ES384ES512PS256PS384PS512 RS256 RS256ES256 RS256RS512ES256ES512

Highest we can go is 0.2.2 but this only add support for ES256 0.2.3 introduce a new dependency lua-resty-openssl. See https://github.com/3scale/APIcast/issues/1375#issuecomment-1281071040

Eventually we will need an update but given the amount of work involved, I'd like to keep version 0.20 for now

Verification steps:

Validate client_secret_jwt authentication method

  • Checkout this PR
  • Build a new runtime image
$ make rumtime-image IMAGE_NAME=apicast-test
  • Navigate to keycloak dev-environment
$ cd dev-environments/keycloak-env
  • Update apicast-config.json with the following
diff --git a/dev-environments/keycloak-env/apicast-config.json b/dev-environments/keycloak-env/apicast-config.json
index 071296cd..2ee79be1 100644
--- a/dev-environments/keycloak-env/apicast-config.json
+++ b/dev-environments/keycloak-env/apicast-config.json
@@ -90,6 +90,17 @@
               "auth_type": "use_3scale_oidc_issuer_endpoint"
             }
           },
+          {
+            "name": "token_introspection",
+            "version": "builtin",
+            "configuration": {
+              "auth_type": "client_secret_jwt",
+              "client_id": "oidc-issuer-for-3scale",
+              "client_secret": "oidc-issuer-for-3scale-secret",
+              "client_jwt_assertion_audience": "http://keycloak:8080/realms/basic/protocol/openid-connect/token",
+              "introspection_url": "http://keycloak:8080/realms/basic/protocol/openid-connect/token/introspect"
+            }
+          },
           {
             "name": "apicast",
             "version": "builtin",
  • Start dev-environment
$ make gateway IMAGE_NAME=apicast-test
$ make keycloak-data
  • Configure keycloak client to use client-secret-jwt
    • Open keycloak dashboard in the browser at `http://127.0.0.1:9090
    • Login with admin/adminpass
    • Navigate to basic realm
    • Navigate to Client -> oidc-issuer-for-3scale -> Credentials
    • Change Client Authenticator to Signed JWT with Client Secret and use HS256 algorithm
    • Click Save
  • Get the access token
$ export ACCESS_TOKEN=$(make token)
  • Run request
$ curl -v --resolve stg.example.com:8080:127.0.0.1 -H "Authorization: Bearer ${ACCESS_TOKEN}" "http://stg.example.com:8080"
  • The response should be 200

Validate private_key_jwt authentication method

  • Configure keycloak client to use signed_jwt
    • Navigate to the keycloak dashboard Client -> oidc-issuer-for-3scale -> Credentials
    • Change Client Authenticator to Signed JWT with RS256 signature algorithm
    • Navigate to Key tab and click generate a new key
    • Select archive format PCKS12 and fill in required fill and save keystore file to local disk
    • From the downloaded keystore.p12 file, run the following command to extract private key
openssl pkcs12 -in keystore.p12 -nodes -nocerts | openssl rsa -out privatekey.rsa
  • Encode certificate as base64
$ openssl base64 -A -in privatekey.rsa
  • Stop dev-environment
CTRL-C
  • Update apicast-config.json
diff --git a/dev-environments/keycloak-env/apicast-config.json b/dev-environments/keycloak-env/apicast-config.json
index 071296cd..a5ff3ffb 100644
--- a/dev-environments/keycloak-env/apicast-config.json
+++ b/dev-environments/keycloak-env/apicast-config.json
@@ -90,6 +90,19 @@
               "auth_type": "use_3scale_oidc_issuer_endpoint"
             }
           },
+          {
+            "name": "token_introspection",
+            "version": "builtin",
+            "configuration": {
+              "auth_type": "private_key_jwt",
+              "client_id": "oidc-issuer-for-3scale",
+              "client_secret": "oidc-issuer-for-3scale-secret",
+              "client_jwt_assertion_audience": "http://keycloak:8080/realms/basic/protocol/openid-connect/token",
+              "introspection_url": "http://keycloak:8080/realms/basic/protocol/openid-connect/token/introspect",
+              "certificate_type": "embedded",
+              "certificate": "data:application/octet-stream;name=privatekey.rsa;base64,LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2d0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktrd2dnU2xBZ0VBQW9JQkFRRDQwNWl2OG5GNlNpTmIKUFVIOTdqVjVxNXliOVlDd3hXVGdQZVF1Y0l0c1RJWDNMcDF1aSs5TXZueDUzYStERGhmUjJ2SE5hcS90aXE1aQpsalNGL3V1TEZZdjhPOGc0eFVZUTVFTjFjR1VDSld1NkRTNmtDRFI2VjM0MVgxb0xpWUp4M05zbHdLdUcwcm5qCmpyZURpK2tXaHdrMWZYVE5LczJRSlI1U
npsbW9YMFN5Wnh5YzUyN2ZZYmdjRGlQUFI3RTZrT2wvdXdEMlI4bk0KY28xcE5EKzJvL2svMWtQV2dLNXlZeEswYmRRd2JpcENoKzBUaUlnSXBxS3h0WVlZVHZyalVoRjNUNHpCYXErNQpNZ1ljRjdFSlRJM3ZFaklodmFSN1ZjUjNsYW9QUU9KcCtQMmErUkZCN3IwMEVOTHduUmdyWXhHa1JtK3dUeDA5CjZnTnozTFh6QWdNQkFBRUNnZ0VBTmxjN29yMC9WQllwMTR5dXcwNkpCaXZZMVdTTnVNMDdKUS9QSytjdlE3VUkKa3IxdTYwd0xORWJyZDAvWE96ZFNNMlh0NWM4VllicW1MK2lleXQ2cndTR3hBeUpwTFNERUZ2OUt6alNBRXJKcQpidVRmR1RxamYwNXBS
UzJ3UkJIQlY2MkVmSit4dGcyQ1JEU1FWbDJ4UjFheFI2bkEzdWVvb2dEQk9OdG9VREVqCjJhMDNQd2M0ano1Qlo4RmZsQ3JPcmE5M2dlTlRpLzRRTUwrZDdlbWIrQjN3a2R4aFF4ZnUwdWtxeUl6c1VXTy8KV2plMzFQbWZRL0hKRUhtM3BUMklySTIva21lQjN4NzBkUmRLYnJEK2taMXFDK2JpOWhoZjUvZThVbzBWM0d5RgoxeDd5RXQ2VHY5aWk2UjZESVlUMDV1T1V1MUU2aXVOQmN3SzFBSWYwRlFLQmdRRDlrV3pOMzNUZVpKSFBFdjdMCmlQNmFEbjdDS1liWVRwcDVOT0dkL3Vab2Q4dTV1ZkZJRW1NV00yc0RMZEp3cGRIWVRpZENxSktlQlRkbC85ODUKcU9
WNHRLbmxNV2NZam96bysrT0JPc2U0cnd0NDNCMVRLNzRDdHRCYlZWVElPbnVqdlk4d2EvU0hiaDlESlNFLwpEcjdOeUxiWDloY3BLeHVIaHF0NWJqV1hiUUtCZ1FEN05vZWU5eVFSYklQbUxHRjI3QnFmenBXdjhERDNuUkdBCnFBWnY2VHdTblpUOTM5UVlzRi9ZLzIxdjVPamdGcGJERmt4S3lNMkJHV09sZ1RMaXZHUnIzZ2o3TGxMRUh6OTYKejRVSEUzVlpJbDVIY2hQemZHakpVVVJscXVxbk1MRHJ5ME1uZ1RIREhuME5KdXhJTm9xUXdPaHJpQ1FRSzVOMwoxelJoSVI1RzN3S0JnUURqd3VPYmpMTWFLL1c0cmRSR0dIaXhBb0lqZjArTExoZWM5YjRPdis1UU
9nSzVnZWJUCm1RaDk0Wk9tMkZybEtsenlVVWo4bkJTT2Noc1B1S1RXMHZuRDBXdWwzaGsvdXBPaGx0Z0V0VHEramlUYzI4SlAKZWNRRUJoZmpZaU4wY3V1cDZWUWI1MnhPMWNDby9Fbi9yUXdBSmVEdTNUSnluVER1TEM0TU5jMVhoUUtCZ1FDcwphVUZ0UGFzNGRpU1ViYk02dmxLTGlXbzhoUG5taDV0Q2xJOU9jV0cwV1FpdnNOWE5XQWVBVTlZVkxLTVRZUTE1CnVTMEZTb21ZYUFkMnlKUlcvdnRnK05OcktPRFBENjh1cDR4aVRkMkZIa3hjZHBQdzBWck5pSVFMenVFYmZCU0EKMEZFM3BMaTFkSkJZM1hUZkh1ZTg3MWpVckd3cjJPeHVISG9yaTJKUE93S0JnU
UNoTUxUUXFBNlE2Qk9CUFAxYQpsQlNSMG05RUJld0k2Y0s0QmhUNVl0U21JUjN3Kzk3OWN5eEJJQ0JVcHRMelEyVzMwM0syc1l2RXhkYzRTU2hVCkFuV01jZE5WWDhvQmc4bEFpZTFuaUFuTmFqb213SGxCV25OUUVUZjdTYmR6NHFFUFVFbFJRaXRId0VhUjdIbmQKMG1tQzRXcllqQUJVRDFUMlVUWXdQYWJmbWc9PQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg=="
+            }
+          },
           {
             "name": "apicast",
             "version": "builtin",

Replace the content after base64 with the based64 string of your certificate from previous step.

  • Start gateway
$ make gateway IMAGE_NAME=apicast-test
  • Run request
$ curl -v --resolve stg.example.com:8080:127.0.0.1 -H "Authorization: Bearer ${ACCESS_TOKEN}" "http://stg.example.com:8080"
  • The response should be 200

tkan145 avatar May 30 '24 06:05 tkan145