-
Notifications
You must be signed in to change notification settings - Fork 50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow using multiple keys and selecting between them via kid #40
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
.vagrant | ||
.vagrant | ||
tmp |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,9 @@ services: | |
volumes: | ||
- ./example/haproxy/haproxy.cfg:/etc/haproxy/haproxy.cfg | ||
- ./example/haproxy/pem/pubkey.pem:/etc/haproxy/pem/pubkey.pem | ||
- ./example/haproxy/pem/pubkey2.pem:/etc/haproxy/pem/pubkey2.pem | ||
- ./example/haproxy/pem/test.com.pem:/etc/haproxy/pem/test.com.pem | ||
- ./lib/jwtverify.lua:/usr/local/share/lua/5.4/jwtverify.lua | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the existing repo, install_luaoauth() {
printf "\r[+] Installing haproxy-lua-oauth\n"
if [ ! -e $lua_dep_dir ]; then
mkdir -p $lua_dep_dir;
fi;
cp $CWD/lib/*.lua $lua_dep_dir
} which tests the install script. What's the reason to overwrite that installed lua file with a Docker volume? |
||
ports: | ||
- "80:80" | ||
- "443:443" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,14 @@ | ||
-----BEGIN PUBLIC KEY----- | ||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvIL8bebCh+pi68Rt0CCu | ||
104VqR10kuD0E1yzwaywvaEiyhfUeDDKAyKC8yS5ilu9xyWK/pg/84RiWq7WoqhU | ||
m8L06jtknn/ZCOuyUdkn1QcdOG10lbbrUF1AOduTIvFYyT4zHrIcKt6MyeQUO0kH | ||
cXQU7lvM2C62BboAasZFupDts1m1kPZMWaiSjLrE1eruhl8NrfipiPWMZJSJoYCQ | ||
cmtN3REXk9z8X7ZPgcMJ9hNN+Kv0fTYLZI4wS4TpHscVfbK18cL4uLrTCcip7jNe | ||
y2KZ/YdbeHgmmcQAdiB4veH4I2dAyqIdsy8Jk+KTs3Ae8qp+S3XtC8z/uXMbN7lR | ||
AwIDAQAB | ||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2Z1w2ZZ0oHnpxYX3Nnee | ||
zVwI4fHtOQTdNz9vek6QZlkl4JY9yxcVfG8ssJq0k55po4MelioNyybpR4+9AE/Q | ||
PwGUm7xEsAjlP/BAmpTz5PAeBAMd1sn+frHgKaICfQO6feob9/JaLo2ixRN0zzPd | ||
5dhUrRaW/Az5a4mcXLSEGUAtIgtCg+ZnXra5Bn503xSOqOJkp8R2qnozmsVidAeL | ||
/bOMe/Yb7QpDjJgv565G3gbbzcLn6+IG8IWnXNxLD7C6mcPA0yS3MrJUpRFRzWrW | ||
MMPWOvBHWCm/2NnNQGHGSpImQ/BZ1DOoFqXO6DRuZpYiBzE/H74ojaatfoxSzpJz | ||
618j4u3CcTKwYafkdXXbUoSMK9FWXdCsSVppXqBLIOtaS2MZMtnuUP23EucoK65l | ||
BSUiuQ7gztzuDTKSLjlm3oMS2Z6Z3j0e0dHXnNZQTZKvhjjTyi65n3Xq7EBhNsbp | ||
r6zN4RGbimoliRvyuNQpY9ottbB5Md2PkNRx2WwMtOEMCNCDvmSMwJ7SeUEcs0n+ | ||
J7v4WyEA5TH2OYdwRPOfyAbZbxyP8ZEICCe6Xhn8QKVZO5nTIHzuQuHW5z8Q9gA/ | ||
3waj/ksN9CGP211lRCxDf2iINw1EkPYjeWM5MAr1N1BwyhItqbP3DAIsajFb6CtY | ||
dZKcoUh45cl6d6DaXgWq4L8CAwEAAQ== | ||
-----END PUBLIC KEY----- |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
-----BEGIN PUBLIC KEY----- | ||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAu9TovLhy2PFHJ6U3+lDj | ||
8uiqtigAJ+cdBTbe4NotSJiNKikZlUSzJElIwAYXeHpRsDuCml76e0axB22XWGZ1 | ||
QPII/2Etd6ZjWu5mp27i9EqpJHnd5xpdeNnBUf1KWH2uVUJjPVEZWAU1rqVl/FhI | ||
owRyjqq3KtZo36u4ZD3264SOMXzIZIn4+dDuwNavGUen0mug+r3istTa5fQy8DVu | ||
DhSU24MBLKQwAlNOWfUUf/h9SqpE25w2mehJwhJ+qXP0OwlzCw0tJIjcSkycrB02 | ||
+xF9ucALZzZgX72et242gIak0p7NkRcjuYWPMhhmvrkVXgz4XSfszHIlgnvD+hG9 | ||
EZOTgq2hL7O1BHB4FIyQrZBtCT/Q7pcjlHb0VMiRW2tv4GYW1tiSnf2Tww3nJOwf | ||
YetCSuT0zhamEC7LEQFlju9ZZvQThTtXrEYhryp+Tw2UxEsUtiiVFcncX0St4lw8 | ||
XBzQkIUOsUDdVHenpdfPqTLsFZ1CwydX7DmQX1tVDHY2J2jQGK2ZBJrNWPD6SlWA | ||
7hDMaTZykTiVdQXGczVqHJi8YUB8YwBffLOHxF+TISF6Nj+6eB9DXmXmAyVsHaYu | ||
5TEZccPOGvSRKzUJo/vwusZ8pmSZigaaD42M+xqWKxYlvx1Mli8lBtA9Q3jMotSl | ||
YEAQiWgiAK/Vev1vo3i2sf8CAwEAAQ== | ||
-----END PUBLIC KEY----- |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,7 @@ if not config then | |
config = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please copy the updated |
||
debug = true, | ||
publicKey = nil, | ||
kid = nil, | ||
issuer = nil, | ||
audience = nil, | ||
hmacSecret = nil | ||
|
@@ -130,7 +131,23 @@ local function algorithmIsValid(token) | |
return true | ||
end | ||
|
||
local function rs256SignatureIsValid(token, publicKey) | ||
local function rs256SignatureIsValid(token, keys, kids) | ||
-- Check if a kid if provided, if so verify it exists in the kids array | ||
local token_kid = token.headerdecoded.kid | ||
local publicKey | ||
if kids ~= nil then | ||
if not contains(kids, token_kid) then | ||
log("The kid provided in the token (" .. token_kid .. ") does not match the kid provided in the configuration.") | ||
return false | ||
end | ||
|
||
-- get the key from the keys list at the index of the correct kid | ||
publicKey = keys[token_kid] | ||
else | ||
-- if no kid is provided, use the first key in the list | ||
publicKey = keys[next(keys)] | ||
end | ||
|
||
local digest = openssl.digest.new('SHA256') | ||
digest:update(token.header .. '.' .. token.payload) | ||
local vkey = openssl.pkey.new(publicKey) | ||
|
@@ -160,10 +177,10 @@ end | |
|
||
-- Checks if the audience in the token is listed in the | ||
-- OAUTH_AUDIENCE environment variable. Both the token audience | ||
-- and the environment variable can contain multiple audience values, | ||
-- and the environment variable can contain multiple audience values, | ||
-- separated by commas. Each value will be checked. | ||
local function audienceIsValid(token, expectedAudienceParam) | ||
|
||
-- Convert OAUTH_AUDIENCE environment variable to a table, | ||
-- even if it contains only one value | ||
local expectedAudiences = expectedAudienceParam | ||
|
@@ -172,8 +189,14 @@ local function audienceIsValid(token, expectedAudienceParam) | |
expectedAudiences = core.tokenize(expectedAudienceParam, " ") | ||
end | ||
|
||
-- Convert 'aud' claim to a table, even if it contains only one value | ||
local receivedAudiences = token.payloaddecoded.aud | ||
|
||
-- Check if 'aud' exists and handle cases where it's missing | ||
if receivedAudiences == nil then | ||
return false | ||
end | ||
|
||
-- Convert 'aud' claim to a table, even if it contains only one value | ||
if type(token.payloaddecoded.aud) == "string" then | ||
receivedAudiences ={} | ||
receivedAudiences[1] = token.payloaddecoded.aud | ||
|
@@ -195,7 +218,8 @@ local function setVariablesFromPayload(txn, decodedPayload) | |
end | ||
|
||
local function jwtverify(txn) | ||
local pem = config.publicKey | ||
local keys = config.publicKeys | ||
local kid = config.kid | ||
local issuer = config.issuer | ||
local audience = config.audience | ||
local hmacSecret = config.hmacSecret | ||
|
@@ -219,7 +243,7 @@ local function jwtverify(txn) | |
|
||
-- 3. Verify the signature with the certificate | ||
if token.headerdecoded.alg == 'RS256' then | ||
if rs256SignatureIsValid(token, pem) == false then | ||
if rs256SignatureIsValid(token, keys, kid) == false then | ||
log("Signature not valid.") | ||
goto out | ||
end | ||
|
@@ -271,18 +295,44 @@ end | |
core.register_init(function() | ||
config.issuer = os.getenv("OAUTH_ISSUER") | ||
config.audience = os.getenv("OAUTH_AUDIENCE") | ||
|
||
|
||
-- when using multiple keys, parse the kid list | ||
local kid = os.getenv("OAUTH_KID") | ||
if kid ~= nil then | ||
config.kid = core.tokenize(kid, " ") | ||
end | ||
|
||
-- when using an RS256 signature | ||
local publicKeyPath = os.getenv("OAUTH_PUBKEY_PATH") | ||
local publicKeyPath = os.getenv("OAUTH_PUBKEY_PATH") | ||
if publicKeyPath ~= nil then | ||
local pem = readAll(publicKeyPath) | ||
config.publicKey = pem | ||
-- tokenize the path in case multiple keys are provided | ||
keyPaths = core.tokenize(publicKeyPath, " ") | ||
|
||
-- Check if there is more than one file path then we must have kid identifiers | ||
if #keyPaths > 1 and config.kid == nil then | ||
log("Multiple public keys provided but no key identifiers.") | ||
return | ||
end | ||
|
||
-- Make sure that the kid size matches the keyPaths size | ||
if config.kid ~= nil and #config.kid ~= #keyPaths then | ||
log("The number of keys does not match the number of key identifiers.") | ||
return | ||
end | ||
|
||
-- Read all the keys and store them in the config | ||
config.publicKeys = {} | ||
for i, keyPath in ipairs(keyPaths) do | ||
local pem = readAll(keyPath) | ||
config.publicKeys[config.kid[i]] = pem | ||
end | ||
end | ||
|
||
-- when using an HS256 or HS512 signature | ||
config.hmacSecret = os.getenv("OAUTH_HMAC_SECRET") | ||
|
||
log("PublicKeyPath: " .. (publicKeyPath or "<none>")) | ||
log("KeyIdentifiers: " .. (kid or "<none>")) | ||
log("Issuer: " .. (config.issuer or "<none>")) | ||
log("Audience: " .. (config.audience or "<none>")) | ||
end) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace "audience values" with "key identifier values"?