Browse Source

certificate registration

master
Nicolas Massé 8 years ago
parent
commit
fe335425c1
  1. 9
      .s2i/bin/run
  2. 277
      nginx.env.conf

9
.s2i/bin/run

@ -9,11 +9,14 @@ set -e
# Default values are set here
export "LOG_LEVEL=${LOG_LEVEL:=info}"
export "NGINX_CONF=${NGINX_CONF:=/opt/app-root/etc/}"
export "BACKEND_ENDPOINT_OVERRIDE=${BACKEND_ENDPOINT_OVERRIDE:=https://su1.3scale.net}"
export "SSO_REALMS=${SSO_REALMS:=3scale}"
export "SSO_CLIENT_ID=${SSO_CLIENT_ID:=admin-cli}"
# Process the environment variables in the nginx configuration file
if [ -f "$NGINX_CONF/nginx.env.conf" ]; then
envsubst '$LOG_LEVEL:$RESOLVER:$APP_ROOT:$SSO_SERVICE_HOSTNAME:$PROXY_ROUTE_HOSTNAME' < "$NGINX_CONF/nginx.env.conf" > "$NGINX_CONF/nginx.conf"
envsubst '$LOG_LEVEL:$RESOLVER:$APP_ROOT:$SSO_SERVICE_HOSTNAME:$PROXY_ROUTE_HOSTNAME:$PROXY_ROUTE_HOSTNAME:$BACKEND_ENDPOINT_OVERRIDE:$THREESCALE_PORTAL_ENDPOINT:$SSO_REALMS:$THREESCALE_SERVICE_TOKEN:$THREESCALE_SERVICE_ID:$THREESCALE_ACCESS_TOKEN:$SSO_SERVICE_USERNAME:$SSO_SERVICE_PASSWORD:$SSO_CLIENT_ID' < "$NGINX_CONF/nginx.env.conf" > "$NGINX_CONF/nginx.conf"
fi
# Run nginx with our custom config file
exec nginx -g "daemon off;" -c "$NGINX_CONF/nginx.conf"
# Run openresty with our custom config file
exec openresty -g "daemon off;" -c "$NGINX_CONF/nginx.conf"

277
nginx.env.conf

@ -20,14 +20,285 @@ http {
ssl_certificate ${APP_ROOT}/etc/serving-cert/tls.crt;
ssl_certificate_key ${APP_ROOT}/etc/serving-cert/tls.key;
location / {
# Enable SSL/TLS Client Certificates Authentication
ssl_verify_client optional;
ssl_client_certificate ${APP_ROOT}/etc/ca-certs/ca-bundle.pem;
ssl_crl ${APP_ROOT}/etc/ca-certs/crl.pem;
location ~ ^/auth/realms/(${SSO_REALMS})/register$ {
access_by_lua_block {
ngx.log(ngx.INFO, "VERIFY: ", ngx.var.ssl_client_verify)
if ngx.var.ssl_client_verify ~= "SUCCESS" 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.INFO, "Authenticated Client : ", ngx.var.ssl_client_s_dn)
}
content_by_lua_block {
local realm = ngx.var[1]; -- captured from the location regex
ngx.log(ngx.INFO, "Received a registration request for realm " .. realm)
if ngx.req.get_method() ~= "POST" then
ngx.status = ngx.HTTP_NOT_ALLOWED
ngx.header['Content-Type'] = 'application/json'
ngx.say('{"error":"invalid_request","error_description":"Only POST requests are accepted."}')
ngx.exit(ngx.HTTP_OK)
end
if ngx.req.get_headers()["Content-Type"] ~= "application/x-www-form-urlencoded" then
ngx.status = 415 -- Unsupported Media
ngx.header['Content-Type'] = 'application/json'
ngx.say('{"error":"invalid_request","error_description":"Wrong content-type. Must be application/x-www-form-urlencoded."}')
ngx.exit(ngx.HTTP_OK)
end
ngx.req.read_body()
local args, err = ngx.req.get_post_args()
if not args then
ngx.status = ngx.HTTP_BAD_REQUEST
ngx.header['Content-Type'] = 'application/json'
ngx.say('{"error":"invalid_request","error_description":"No post parameters found."}')
ngx.exit(ngx.HTTP_OK)
end
if not args['apikey'] or args['apikey'] == '' then
ngx.status = ngx.HTTP_BAD_REQUEST
ngx.header['Content-Type'] = 'application/json'
ngx.say('{"error":"invalid_request","error_description":"No apikey parameter found in the request."}')
ngx.exit(ngx.HTTP_OK)
end
if not args['client_id'] or args['client_id'] == '' then
ngx.status = ngx.HTTP_BAD_REQUEST
ngx.header['Content-Type'] = 'application/json'
ngx.say('{"error":"invalid_request","error_description":"No client_id parameter found in the request."}')
ngx.exit(ngx.HTTP_OK)
end
local apikey = args['apikey']
local client_id = args['client_id']
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri("${BACKEND_ENDPOINT_OVERRIDE}/transactions/authrep.xml", {
query = {
service_token = "${THREESCALE_SERVICE_TOKEN}",
service_id = "${THREESCALE_SERVICE_ID}",
user_key = apikey
},
method = "GET",
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 the 3scale backend."}')
ngx.exit(ngx.HTTP_OK)
end
if res.status ~= ngx.HTTP_OK then
ngx.status = ngx.HTTP_FORBIDDEN
ngx.header['Content-Type'] = 'application/json'
ngx.say('{"error":"server_error","error_description":"Denied by the 3scale backend."}')
ngx.exit(ngx.HTTP_OK)
end
res, err = httpc:request_uri("${THREESCALE_PORTAL_ENDPOINT}/admin/api/applications/find.json", {
query = {
access_token = "${THREESCALE_ACCESS_TOKEN}",
user_key = apikey
},
method = "GET",
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 the 3scale API."}')
ngx.exit(ngx.HTTP_OK)
end
local cjson = require 'cjson'
local json_body = cjson.decode(res.body)
local apikey_account_id = json_body['application'] and json_body['application']['account_id']
res, err = httpc:request_uri("${THREESCALE_PORTAL_ENDPOINT}/admin/api/applications/find.json", {
query = {
access_token = "${THREESCALE_ACCESS_TOKEN}",
app_id = client_id
},
method = "GET",
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 the 3scale API."}')
ngx.exit(ngx.HTTP_OK)
end
json_body = cjson.decode(res.body)
local client_account_id = json_body['application'] and json_body['application']['account_id']
if client_account_id ~= apikey_account_id then
ngx.status = ngx.HTTP_FORBIDDEN
ngx.header['Content-Type'] = 'application/json'
ngx.say('{"error":"invalid_request","error_description":"The apikey and client_id do not belong to the same account."}')
ngx.exit(ngx.HTTP_OK)
end
res, err = httpc:request_uri("http://${SSO_SERVICE_HOSTNAME}/auth/realms/"..realm.."/protocol/openid-connect/token", {
body = ngx.encode_args({
client_id = "${SSO_CLIENT_ID}",
username = "${SSO_SERVICE_USERNAME}",
password = "${SSO_SERVICE_PASSWORD}",
grant_type = "password"
}),
headers = {
["Content-Type"] = "application/x-www-form-urlencoded",
},
method = "POST",
ssl_verify = false
})
if not res or res.status ~= ngx.HTTP_OK then
ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR
ngx.header['Content-Type'] = 'application/json'
ngx.say('{"error":"server_error","error_description":"Could not get an access token with admin privileges on RH-SSO."}')
ngx.exit(ngx.HTTP_OK)
end
json_body = cjson.decode(res.body)
local access_token = json_body['access_token']
res, err = httpc:request_uri("http://${SSO_SERVICE_HOSTNAME}/auth/admin/realms/"..realm.."/clients", {
query = {
clientId = client_id
},
headers = {
["Authorization"] = "Bearer " .. access_token
},
method = "GET",
ssl_verify = false
})
if not res or res.status ~= ngx.HTTP_OK then
ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR
ngx.header['Content-Type'] = 'application/json'
ngx.say('{"error":"server_error","error_description":"Could not retrieve the list of clients in RH-SSO."}')
ngx.exit(ngx.HTTP_OK)
end
json_body = cjson.decode(res.body)
local client_id_rhssoid = json_body[1] and json_body[1]['id']
if not client_id_rhssoid then
ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR
ngx.header['Content-Type'] = 'application/json'
ngx.say('{"error":"server_error","error_description":"Could not find the client in RH-SSO."}')
ngx.exit(ngx.HTTP_OK)
end
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
},
ssl_verify = false
})
if not res or res.status ~= ngx.HTTP_OK then
ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR
ngx.header['Content-Type'] = 'application/json'
ngx.say('{"error":"server_error","error_description":"Could not check client certificates in RH-SSO."}')
ngx.exit(ngx.HTTP_OK)
end
json_body = cjson.decode(res.body)
local existing_certificate = json_body['certificate']
if existing_certificate then
ngx.status = ngx.HTTP_FORBIDDEN
ngx.header['Content-Type'] = 'application/json'
ngx.say('{"error":"invalid_request","error_description":"Already registered."}')
ngx.exit(ngx.HTTP_OK)
end
local certificate = ngx.var.ssl_client_raw_cert
local start, len = string.find(certificate, "[-]+BEGIN CERTIFICATE[-]+")
start = start + len
local finish = string.find(certificate, "[-]+END CERTIFICATE[-]+")
certificate = string.sub(certificate, start, finish-1)
local raw_certificate = ""
for i in string.gmatch(certificate, "[a-zA-Z0-9+/=]+") do
raw_certificate = raw_certificate .. i
end
local boundary = string.format("--------------------------%s", ngx.var.request_id)
local content_type = "multipart/form-data; boundary=" .. boundary
body = string.format('--%s\r\nContent-Disposition: form-data; name="file"\r\nContent-Type: application/octet-stream\r\n\r\n%s\r\n--%s\r\nContent-Disposition: form-data; name="keystoreFormat"\r\n\r\n%s\r\n--%s--\r\n', boundary, raw_certificate, boundary, "Certificate PEM", boundary)
ngx.say(body)
res, err = httpc:request_uri("http://${SSO_SERVICE_HOSTNAME}/auth/admin/realms/"..realm.."/clients/" .. client_id_rhssoid .. "/certificates/jwt.credential/upload", {
method = "POST",
body = body,
headers = {
["Authorization"] = "Bearer " .. access_token,
["Content-Type"] = content_type
},
ssl_verify = false
})
if not res or res.status ~= ngx.HTTP_OK then
ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR
ngx.header['Content-Type'] = 'application/json'
ngx.say('{"error":"server_error","error_description":"Could not register certificate in RH-SSO."}')
ngx.exit(ngx.HTTP_OK)
end
ngx.status = ngx.HTTP_OK
ngx.header['Content-Type'] = 'application/json'
ngx.say('{ "status": "registered" }')
ngx.exit(ngx.HTTP_OK)
}
}
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
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)
}
# 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;
}
}
}
location ~ ^/.* {
# 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;
}
}
}

Loading…
Cancel
Save