diff --git a/nginx.env.conf b/nginx.env.conf index 2d7f26c..8226671 100644 --- a/nginx.env.conf +++ b/nginx.env.conf @@ -162,6 +162,7 @@ http { }), headers = { ["Content-Type"] = "application/x-www-form-urlencoded", + ["Host"] = ngx.var.host }, method = "POST", ssl_verify = false @@ -182,7 +183,8 @@ http { clientId = client_id }, headers = { - ["Authorization"] = "Bearer " .. access_token + ["Authorization"] = "Bearer " .. access_token, + ["Host"] = ngx.var.host }, method = "GET", ssl_verify = false @@ -208,7 +210,8 @@ http { res, err = httpc:request_uri("http://${SSO_SERVICE_HOSTNAME}/auth/admin/realms/"..realm.."/clients/" .. client_id_rhssoid .. "/certificates/jwt.credential", { method = "GET", headers = { - ["Authorization"] = "Bearer " .. access_token + ["Authorization"] = "Bearer " .. access_token, + ["Host"] = ngx.var.host }, ssl_verify = false }) @@ -251,7 +254,8 @@ http { body = body, headers = { ["Authorization"] = "Bearer " .. access_token, - ["Content-Type"] = content_type + ["Content-Type"] = content_type, + ["Host"] = ngx.var.host }, ssl_verify = false }) @@ -274,21 +278,95 @@ http { location ~ ^/auth/realms/(${SSO_REALMS})/protocol/openid-connect/token$ { access_by_lua_block { ngx.log(ngx.INFO, "VERIFY: ", ngx.var.ssl_client_verify) - if ngx.var.ssl_client_verify ~= "SUCCESS" then + if ngx.var.ssl_client_verify ~= "SUCCESS" or not ngx.var.ssl_client_raw_cert then ngx.status = ngx.HTTP_FORBIDDEN ngx.header['Content-Type'] = 'application/json' ngx.say('{"error":"invalid_request","error_description":"You need to authenticate using an SSL/TLS Client Certificate."}') ngx.exit(ngx.HTTP_OK) end - ngx.log(ngx.ERR, "Authenticated Client : ", ngx.var.ssl_client_s_dn) + ngx.log(ngx.INFO, "Authenticated Client : ", ngx.var.ssl_client_s_dn) } - # Beware: no slash at the end of the proxy_pass directive since we don't want to rewrite URLs - proxy_pass http://${SSO_SERVICE_HOSTNAME}; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; + content_by_lua_block { + local jwt = require "resty.jwt" + local http = require "resty.http" + + ngx.req.read_body() + local form, err = ngx.req.get_post_args() + if not form then + ngx.status = ngx.HTTP_BAD_REQUEST + ngx.header['Content-Type'] = 'application/json' + ngx.say('{"error":"invalid_request","error_description":"You need to pass the token request arguments in the post body."}') + ngx.exit(ngx.HTTP_OK) + end + + local client_assertion = form['client_assertion'] + local client_assertion_type = form['client_assertion_type'] + if not client_assertion_type or client_assertion_type ~= "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" then + ngx.status = ngx.HTTP_BAD_REQUEST + ngx.header['Content-Type'] = 'application/json' + ngx.say('{"error":"invalid_request","error_description":"Only JWT is allowed for client authentication. See RFC 7523."}') + ngx.exit(ngx.HTTP_OK) + end + + if not client_assertion then + ngx.status = ngx.HTTP_BAD_REQUEST + ngx.header['Content-Type'] = 'application/json' + ngx.say('{"error":"invalid_request","error_description":"No client_assertion found."}') + ngx.exit(ngx.HTTP_OK) + end + + local certificate = ngx.var.ssl_client_raw_cert + local jwt_obj = jwt:load_jwt(client_assertion) + if not jwt_obj.valid then + ngx.status = ngx.HTTP_BAD_REQUEST + ngx.header['Content-Type'] = 'application/json' + ngx.say('{"error":"invalid_request","error_description":"client_assertion is not a valid JWT."}') + ngx.exit(ngx.HTTP_OK) + end + + if jwt_obj.header.alg ~= "RS256" then + ngx.status = ngx.HTTP_BAD_REQUEST + ngx.header['Content-Type'] = 'application/json' + ngx.say('{"error":"invalid_request","error_description":"client_assertion must be signed using the RS256 algorithm."}') + ngx.exit(ngx.HTTP_OK) + end + + jwt_obj = jwt:verify_jwt_obj(certificate, jwt_obj, {}) + if not jwt_obj.verified then + ngx.status = ngx.HTTP_FORBIDDEN + ngx.header['Content-Type'] = 'application/json' + ngx.say('{"error":"invalid_request","error_description":"client_assertion signature cannot be verified."}') + ngx.log(ngx.WARN, "JWT validation error: ", jwt_obj.reason) + ngx.exit(ngx.HTTP_OK) + end + + local httpc = http.new() + + local body = ngx.req.get_body_data() + local headers = {} + local h = ngx.req.get_headers() + for k, v in pairs(h) do + headers[k] = v + end + local res, err = httpc:request_uri("http://${SSO_SERVICE_HOSTNAME}" .. ngx.var.uri, { + body = body, + headers = headers, + method = "POST", + ssl_verify = false + }) + + if not res then + ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR + ngx.header['Content-Type'] = 'application/json' + ngx.say('{"error":"server_error","error_description":"Could not get a response from RH-SSO."}') + ngx.exit(ngx.HTTP_OK) + end + + ngx.status = res.status; + ngx.header['Content-Type'] = res.headers['Content-Type'] + ngx.say(res.body); + } } location ~ ^/.* { diff --git a/tests/sign.sh b/tests/sign.sh new file mode 100755 index 0000000..367a05c --- /dev/null +++ b/tests/sign.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +if [ "$#" -ne 2 ]; then + echo "Usage: $0 subject audience" + exit 1 +fi + +ISSUER="$1" +SUBJECT="$1" +AUDIENCE="$2" +EXPIRATION=$(date -v+1d "+%s") +ISSUED_AT=$(date "+%s") +NOT_BEFORE=$(date -v-1H "+%s") +TOKENID=$(openssl rand -hex 8) + +jwt -claim "iss=$ISSUER" \ + -claim "sub=$SUBJECT" \ + -claim "aud=$AUDIENCE" \ + -claim "exp=$EXPIRATION" \ + -claim "iat=$ISSUED_AT" \ + -claim "nbf=$NOT_BEFORE" \ + -claim "jti=$TOKENID" \ + -sign + \ + -key "client.key" \ + -alg "RS256" + + +