From 700adfd47d4973f1bfb88726c13a89a7cfb6d5f1 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Mon, 21 Oct 2024 15:38:19 +0200 Subject: [PATCH 01/10] Use configuration available in 4.2.x --- README.md | 48 +++++++++++++++++------------------ conf/auth0/rabbitmq.conf.tmpl | 2 ++ conf/entra/rabbitmq.conf.tmpl | 8 +++--- conf/okta/rabbitmq.conf.tmpl | 12 ++++++--- 4 files changed, 39 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 66149b1..0f5e573 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # RabbitMQ OAuth2 Tutorial -The instructions on how to configure and test OAuth 2.0 in RabbitMQ have been moved to [RabbitMQ documentation](https://www.rabbitmq.com/docs/oauth2-examples). This repository only maintains the configuration files and scripts referenced from the RabbitMQ documentation. +The instructions on how to configure and test OAuth 2.0 in RabbitMQ have been moved to [RabbitMQ documentation](https://www.rabbitmq.com/docs/next/oauth2-examples). This repository only maintains the configuration files and scripts referenced from the RabbitMQ documentation. **IMPORTANT** -This branch, `main`, of this repository is meant for the RabbitMQ docs with the version `4.0` or earlier. -For RabbitMQ docs with version `Next`, check the branch [next](https://github.com/rabbitmq/rabbitmq-oauth2-tutorial/tree/next). +This branch, `next`, of this repository is meant for the RabbitMQ docs with the version `Next`. For `4.0` or earlier, check the [main](https://github.com/rabbitmq/rabbitmq-oauth2-tutorial) branch. +This branch uses the latest docker image built from https://github.com/rabbitmq/rabbitmq-server `main` branch. That is when you run `make start-rabbitmq` it is deployed the latest RabbitMQ. **Table of Contents** @@ -29,33 +29,33 @@ When the example requires RabbitMQ with TLS enabled, the corresponding `conf` fo ### Management UI Access -* [Access management UI using OAuth 2.0 tokens](https://www.rabbitmq.com/docs/oauth2-examples#access-management-ui) -* [Service-Provider initiated logon](https://www.rabbitmq.com/docs/oauth2-examples#service-provider-initiated-logon) -* [Identity-Provider initiated logon](https://www.rabbitmq.com/docs/oauth2-examples#identity-provider-initiated-logon) +* [Access management UI using OAuth 2.0 tokens](https://www.rabbitmq.com/docs/next/oauth2-examples#access-management-ui) +* [Service-Provider initiated logon](https://www.rabbitmq.com/docs/next/oauth2-examples#service-provider-initiated-logon) +* [Identity-Provider initiated logon](https://www.rabbitmq.com/docs/next/oauth2-examples#identity-provider-initiated-logon) ### Using [JWT tokens in several protocols](#access-other-protocols) to access RabbitMQ -* [Management HTTP API](https://www.rabbitmq.com/docs/oauth2-examples#management-http-api) -* [AMQP 0-9-1](https://www.rabbitmq.com/docs/oauth2-examples#amqp-protocol) (and [scopes for topic exchanges](https://www.rabbitmq.com/docs/oauth2-examples#using-topic-exchanges) in a separate section) -* [AMQP 1.0](https://www.rabbitmq.com/docs/oauth2-examples#amqp10-protocol) -* [JMS](https://www.rabbitmq.com/docs/oauth2-examples#jms-clients) -* [MQTT](https://www.rabbitmq.com/docs/oauth2-examples#mqtt-protocol) +* [Management HTTP API](https://www.rabbitmq.com/docs/next/oauth2-examples#management-http-api) +* [AMQP 0-9-1](https://www.rabbitmq.com/docs/next/oauth2-examples#amqp-protocol) (and [scopes for topic exchanges](https://www.rabbitmq.com/docs/next/oauth2-examples#using-topic-exchanges) in a separate section) +* [AMQP 1.0](https://www.rabbitmq.com/docs/next/oauth2-examples#amqp10-protocol) +* [JMS](https://www.rabbitmq.com/docs/next/oauth2-examples#jms-clients) +* [MQTT](https://www.rabbitmq.com/docs/next/oauth2-examples#mqtt-protocol) ### Signing Keys, Scope Aliases, Rich Authorization Requests -* [How to Use Advanced OAuth 2.0 Configuration](https://www.rabbitmq.com/docs/oauth2-examples#advanced-configuration) -* [Using a custom scope field](https://www.rabbitmq.com/docs/oauth2-examples#using-custom-scope-field) -* [Using multiple asymmetrical signing keys](https://www.rabbitmq.com/docs/oauth2-examples#using-multiple-asymmetrical-signing-keys) -* [Using scope aliases](https://www.rabbitmq.com/docs/oauth2-examples#using-scope-aliases) -* [Preferred username claims](https://www.rabbitmq.com/docs/oauth2-examples#preferred-username-claims) -* [Using Rich Authorization Requests tokens](https://www.rabbitmq.com/docs/oauth2-examples#use-rar-tokens) +* [How to Use Advanced OAuth 2.0 Configuration](https://www.rabbitmq.com/docs/next/oauth2-examples#advanced-configuration) +* [Using a custom scope field](https://www.rabbitmq.com/docs/next/oauth2-examples#using-custom-scope-field) +* [Using multiple asymmetrical signing keys](https://www.rabbitmq.com/docs/next/oauth2-examples#using-multiple-asymmetrical-signing-keys) +* [Using scope aliases](https://www.rabbitmq.com/docs/next/oauth2-examples#using-scope-aliases) +* [Preferred username claims](https://www.rabbitmq.com/docs/next/oauth2-examples#preferred-username-claims) +* [Using Rich Authorization Requests tokens](https://www.rabbitmq.com/docs/next/oauth2-examples#use-rar-tokens) -### Examples for Specific OAuth 2.0 Identity Providers +### Examples for Specific OAuth 2.0 Identity Providers - * [Keycloak](https://www.rabbitmq.com/docs/oauth2-examples-keycloak) + * [Keycloak](https://www.rabbitmq.com/docs/next/oauth2-examples-keycloak) * [Auth0](https://www.rabbitmq.com/oauth2-examples-auth0) - * [Microsoft Entra ID](https://www.rabbitmq.com/docs/oauth2-examples-entra-id) (formerly known as Azure Active Directory) - * [OAuth2 Proxy](https://www.rabbitmq.com/docs/oauth2-examples-proxy) - * [Okta](https://www.rabbitmq.com/docs/oauth2-examples-okta) - * [Google](https://www.rabbitmq.com/docs/oauth2-examples-google) **NOT SUPPORTED** - * [Multiple OAuth 2.0 servers and/or audiences](https://www.rabbitmq.com/docs/oauth2-examples-multiresource) + * [Microsoft Entra ID](https://www.rabbitmq.com/docs/next/oauth2-examples-entra-id) (formerly known as Azure Active Directory) + * [OAuth2 Proxy](https://www.rabbitmq.com/docs/next/oauth2-examples-proxy) + * [Okta](https://www.rabbitmq.com/docs/next/oauth2-examples-okta) + * [Google](https://www.rabbitmq.com/docs/next/oauth2-examples-google) **NOT SUPPORTED** + * [Multiple OAuth 2.0 servers and/or audiences](https://www.rabbitmq.com/docs/next/oauth2-examples-multiresource) \ No newline at end of file diff --git a/conf/auth0/rabbitmq.conf.tmpl b/conf/auth0/rabbitmq.conf.tmpl index b3750c4..92a06a2 100644 --- a/conf/auth0/rabbitmq.conf.tmpl +++ b/conf/auth0/rabbitmq.conf.tmpl @@ -5,6 +5,8 @@ log.console.level = debug management.oauth_enabled = true management.oauth_client_id = {Client ID} management.oauth_scopes = openid profile rabbitmq.tag:administrator +management.oauth_authorization_endpoint_params.audience = rabbitmq +management.oauth_token_endpoint_params.audience = rabbitmq auth_oauth2.resource_server_id = rabbitmq auth_oauth2.issuer = {Domain} diff --git a/conf/entra/rabbitmq.conf.tmpl b/conf/entra/rabbitmq.conf.tmpl index 5e8a83c..dfcaef9 100644 --- a/conf/entra/rabbitmq.conf.tmpl +++ b/conf/entra/rabbitmq.conf.tmpl @@ -1,13 +1,13 @@ auth_backends.1 = rabbit_auth_backend_oauth2 -log.console.level = debug - management.oauth_enabled = true management.oauth_client_id = {Application(client) ID} management.oauth_scopes = openid profile api://{Application(client) ID}/rabbitmq auth_oauth2.resource_server_id = {Application(client) ID} auth_oauth2.additional_scopes_key = roles -auth_oauth2.jwks_url = https://login.microsoftonline.com/{Directory (tenant) ID}/discovery/v2.0/keys +auth_oauth2.issuer = https://login.microsoftonline.com/{Directory (tenant) ID}/v2.0 +#include the following line if your app has a custom signing key +#auth_oauth2.discovery_endpoint_params.appid = {Application(client) ID} auth_oauth2.preferred_username_claims.1 = name -auth_oauth2.preferred_username_claims.2 = preferred_username +auth_oauth2.preferred_username_claims.2 = preferred_username \ No newline at end of file diff --git a/conf/okta/rabbitmq.conf.tmpl b/conf/okta/rabbitmq.conf.tmpl index 6d27bb7..a4214a0 100644 --- a/conf/okta/rabbitmq.conf.tmpl +++ b/conf/okta/rabbitmq.conf.tmpl @@ -1,14 +1,20 @@ auth_backends.1 = rabbit_auth_backend_oauth2 +log.console.level = debug + management.oauth_enabled = true management.oauth_client_id = {okta_client_app_ID} management.oauth_scopes = admin monitoring -management.oauth_metadata_url = {okta-domain-name}/oauth2/default/.well-known/oauth-authorization-server -management.oauth_provider_url = {okta-domain-name}/oauth2/default auth_oauth2.resource_server_id = {okta_client_app_ID} -auth_oauth2.jwks_url = {okta-domain-name}/oauth2/default/v1/keys +auth_oauth2.issuer = {okta-domain-name}/oauth2/default +#Okta supports two openid discovery endpoints. The standard and .well-known/oauth-authorization-server +#Comment out this following line if the standard path does not work +#auth_oauth2.discovery_endpoint_path = .well-known/oauth-authorization-server auth_oauth2.additional_scopes_key = role auth_oauth2.verify_aud = false auth_oauth2.scope_prefix = okta. auth_oauth2.https.hostname_verification = wildcard + +auth_oauth2.scope_aliases.admin = okta.read:*/* okta.write:*/* okta.configure:*/* okta.tag:administrator +auth_oauth2.scope_aliases.monitoring = okta.tag:management okta.read:*/ \ No newline at end of file From 85985487a52d413d771d9d3a213982e684bb5e51 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Mon, 21 Oct 2024 15:57:48 +0200 Subject: [PATCH 02/10] Use pivotalrabbitmq/rabbitmq image --- bin/deploy-rabbit | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/deploy-rabbit b/bin/deploy-rabbit index a74bef6..5808f92 100755 --- a/bin/deploy-rabbit +++ b/bin/deploy-rabbit @@ -9,8 +9,8 @@ source $SCRIPT/common MODE=${MODE:-uaa} OAUTH_PROVIDER=${OAUTH_PROVIDER:-$MODE} ADVANCED=${ADVANCED:-advanced.config} -IMAGE_TAG=${IMAGE_TAG:-4.0.2-management} -IMAGE=${IMAGE:-rabbitmq} +IMAGE_TAG=${IMAGE_TAG:-main} +IMAGE=${IMAGE:-pivotalrabbitmq/rabbitmq} RABBITMQ_CONF=${RABBITMQ_CONF:-rabbitmq.conf} if [[ "${MODE}" == "uaa" ]]; then From c5b645486bc4a1608663fb8731f016fa1b3d1356 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Wed, 22 Jan 2025 11:15:28 +0100 Subject: [PATCH 03/10] Add commercial-only feature to support forward-proxy --- Makefile | 6 + README.md | 6 +- bin/common | 2 +- bin/deploy-rabbit | 20 ++-- bin/devkeycloak/deploy | 5 +- bin/forward-proxy/deploy | 38 ++++++ bin/keycloak/deploy | 7 +- bin/oauth2-proxy/deploy | 4 +- bin/prodkeycloak/deploy | 6 +- bin/uaa/deploy | 8 +- conf/forward-proxy/httpd/.htpasswd | 1 + conf/forward-proxy/httpd/httpd.conf | 165 +++++++++++++++++++++++++++ conf/forward-proxy/rabbitmq.conf | 19 +++ conf/forward-proxy/requires-tls | 0 conf/forward-proxy/tls.conf | 9 ++ conf/keycloak/import/test-realm.json | 4 +- 16 files changed, 279 insertions(+), 21 deletions(-) create mode 100755 bin/forward-proxy/deploy create mode 100644 conf/forward-proxy/httpd/.htpasswd create mode 100644 conf/forward-proxy/httpd/httpd.conf create mode 100644 conf/forward-proxy/rabbitmq.conf create mode 100644 conf/forward-proxy/requires-tls create mode 100644 conf/forward-proxy/tls.conf diff --git a/Makefile b/Makefile index 270b51e..905d418 100644 --- a/Makefile +++ b/Makefile @@ -21,12 +21,18 @@ start-uaa: ## Start uaa (remember to run make build-uaa if you have not done ) start-keycloak: ## Start keycloak @./bin/keycloak/deploy +start-forward-proxy: ## Start forward-proxy + @./bin/forward-proxy/deploy + stop-uaa: ## Stop uaa @docker kill uaa stop-keycloak: ## Stop keycloak @docker kill keycloak +stop-forward-proxy: ## Stop forward-proxy + @docker kill forward-proxy + stop-dev-keycloak: ## Stop dev keycloak @docker kill devkeycloak @docker rm devkeycloak diff --git a/README.md b/README.md index 0f5e573..ab8827a 100644 --- a/README.md +++ b/README.md @@ -58,4 +58,8 @@ When the example requires RabbitMQ with TLS enabled, the corresponding `conf` fo * [OAuth2 Proxy](https://www.rabbitmq.com/docs/next/oauth2-examples-proxy) * [Okta](https://www.rabbitmq.com/docs/next/oauth2-examples-okta) * [Google](https://www.rabbitmq.com/docs/next/oauth2-examples-google) **NOT SUPPORTED** - * [Multiple OAuth 2.0 servers and/or audiences](https://www.rabbitmq.com/docs/next/oauth2-examples-multiresource) \ No newline at end of file + * [Multiple OAuth 2.0 servers and/or audiences](https://www.rabbitmq.com/docs/next/oauth2-examples-multiresource) + + ### Commercial-only features + + * [Explicit forward proxy](https://techdocs.broadcom.com/us/en/vmware-tanzu/data-solutions/tanzu-rabbitmq-oci/4-0/tanzu-rabbitmq-oci-image/overview.html) diff --git a/bin/common b/bin/common index be3089e..180f559 100755 --- a/bin/common +++ b/bin/common @@ -45,7 +45,7 @@ kill_container_if_exist() { fi } ensure_docker_network() { - NETWORK=${DOCKER_NETWORK:-rabbitmq_net} + NETWORK=${1:?Requires first parameter as docker network} begin "Ensuring $NETWORK network ..." if [ ! "$(docker network ls | grep $NETWORK)" ]; then print "> DOCKER_NETWORK: $NETWORK created" diff --git a/bin/deploy-rabbit b/bin/deploy-rabbit index 5808f92..3b1d8e2 100755 --- a/bin/deploy-rabbit +++ b/bin/deploy-rabbit @@ -21,6 +21,7 @@ fi CONF_DIR=$SCRIPT/../conf/${MODE} CERTS_DIR=${CONF_DIR}/certs +RABBIT_NETWORK=${RABBIT_NETWORK:-rabbitmq_net} function generate-final-conf-dir { FINAL_CONF_DIR=`mktemp -d -t "oauth2"` @@ -52,31 +53,34 @@ function deploy { if [[ -f "${CONF_DIR}/requires-tls" ]]; then EXTRA_PORTS="-p 15671:15671 " fi - if [[ "${MODE}" == "oauth2-proxy" ]]; then - EXTRA_MOUNTS="${EXTRA_MOUNTS} -v $SCRIPT/../conf/keycloak/certs:/etc/keycloak/certs " - fi + EXTRA_MOUNTS="${EXTRA_MOUNTS} -v $SCRIPT/../conf/${OAUTH_PROVIDER}/certs:/etc/${OAUTH_PROVIDER}/certs " if [[ -n "${ADVANCED}" && -f "${CONF_DIR}/${ADVANCED}" ]]; then EXTRA_MOUNTS="${EXTRA_MOUNTS} -v ${CONF_DIR}/${ADVANCED}:/etc/rabbitmq/advanced.config:ro " USED_CONFIG="${USED_CONFIG} ${CONF_DIR}/${ADVANCED}" fi - echo "Running RabbitMQ ($IMAGE:$IMAGE_TAG) with Idp $MODE and configuration file(s) $USED_CONFIG" + echo "Running RabbitMQ ($IMAGE:$IMAGE_TAG) with" + echo " - Mode: ${MODE} " + echo " - OauthProvider: ${OAUTH_PROVIDER}" + echo " - configuration file(s): ${USED_CONFIG}" + echo " - mounts: ${EXTRA_MOUNTS}" + docker run -d --name rabbitmq \ - --net rabbitmq_net \ + --net ${RABBIT_NETWORK} \ -p 15672:15672 \ -p 5672:5672 \ -p 5552:5552 \ - --env RABBITMQ_CONFIG_FILES="/conf" \ ${EXTRA_PORTS} \ + --env RABBITMQ_CONFIG_FILES="/conf" \ ${EXTRA_MOUNTS} \ ${IMAGE}:${IMAGE_TAG} } generate-final-conf-dir generate-tls-certs-if-required -ensure_docker_network +ensure_docker_network ${RABBIT_NETWORK} kill_container_if_exist rabbitmq deploy -wait_for_message rabbitmq "Starting broker... completed" +wait_for_message rabbitmq "Server startup complete" print "RabbitMQ is running" diff --git a/bin/devkeycloak/deploy b/bin/devkeycloak/deploy index 74f6393..6946326 100755 --- a/bin/devkeycloak/deploy +++ b/bin/devkeycloak/deploy @@ -5,7 +5,8 @@ ROOT=$SCRIPT/../.. CONF_DIR=${ROOT}/conf/multi-keycloak CERTS_DIR=${CONF_DIR}/certs -ensure_docker_network +PROVIDER_NETWORK=${PROVIDER_NETWORK:-rabbitmq_net} +ensure_docker_network ${PROVIDER_NETWORK} kill_container_if_exist devkeycloak generate-ca-server-client-kpi devkeycloak $CERTS_DIR @@ -14,7 +15,7 @@ echo "Running devkeycloack docker image ..." docker run \ --detach \ - --name devkeycloak --net rabbitmq_net \ + --name devkeycloak --net ${PROVIDER_NETWORK} \ --publish 8081:8080 \ --publish 8443:8443 \ --env KEYCLOAK_ADMIN=admin \ diff --git a/bin/forward-proxy/deploy b/bin/forward-proxy/deploy new file mode 100755 index 0000000..f9a8912 --- /dev/null +++ b/bin/forward-proxy/deploy @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +set -x + +ROOT=$SCRIPT/../.. +CONF_DIR=${ROOT}/conf/forward-proxy +CERTS_DIR=${CONF_DIR}/certs + +source $SCRIPT/../common + +HTTPD_DOCKER_IMAGE=httpd:latest + +RABBIT_NETWORK=${RABBIT_NETWORK:-rabbitmq_net} +PROVIDER_NETWORK=${PROVIDER_NETWORK:-rabbitmq_net} +ensure_docker_network ${RABBIT_NETWORK} +ensure_docker_network ${PROVIDER_NETWORK} +kill_container_if_exist forward-proxy + +begin "Running forward-proxy docker image ${HTTPD_DOCKER_IMAGE} ..." + +if [ "${RABBIT_NETWORK}" != "${PROVIDER_NETWORK}" ]; then + NETWORKS="--network ${RABBIT_NETWORK} --network ${PROVIDER_NETWORK} " +else + NETWORKS="--network ${RABBIT_NETWORK} " +fi + +docker run \ + --detach \ + --name forward-proxy \ + --network ${RABBIT_NETWORK} --network ${PROVIDER_NETWORK} \ + --publish 9092:9092 \ + --mount "type=bind,source=${CONF_DIR}/httpd,target=/usr/local/apache2/conf" \ + ${HTTPD_DOCKER_IMAGE} + +wait_for_message forward-proxy "initializing worker proxy:forward local" +print "forward-proxy is ready" diff --git a/bin/keycloak/deploy b/bin/keycloak/deploy index b52518b..81e2303 100755 --- a/bin/keycloak/deploy +++ b/bin/keycloak/deploy @@ -2,13 +2,16 @@ SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +#set -x + ROOT=$SCRIPT/../.. CONF_DIR=${ROOT}/conf/keycloak CERTS_DIR=${CONF_DIR}/certs source $SCRIPT/../common -ensure_docker_network +PROVIDER_NETWORK=${PROVIDER_NETWORK:-rabbitmq_net} +ensure_docker_network ${PROVIDER_NETWORK} kill_container_if_exist keycloak generate-ca-server-client-kpi keycloak $CERTS_DIR @@ -17,7 +20,7 @@ begin "Running keycloack docker image ..." docker run \ --detach \ - --name keycloak --net rabbitmq_net \ + --name keycloak --net ${PROVIDER_NETWORK} \ --publish 8080:8080 \ --publish 8443:8443 \ --env KEYCLOAK_ADMIN=admin \ diff --git a/bin/oauth2-proxy/deploy b/bin/oauth2-proxy/deploy index 7fe1875..39e84b3 100755 --- a/bin/oauth2-proxy/deploy +++ b/bin/oauth2-proxy/deploy @@ -8,7 +8,9 @@ CERTS_DIR=${CONF_DIR}/certs source $SCRIPT/../common -ensure_docker_network +PROVIDER_NETWORK=${PROVIDER_NETWORK:-rabbitmq_net} +ensure_docker_network ${PROVIDER_NETWORK} + docker-compose -f $ROOT/conf/oauth2-proxy/compose.yml down 2>/dev/null || echo "oauth2-proxy was not running" generate-ca-server-client-kpi oauth2-proxy $CERTS_DIR diff --git a/bin/prodkeycloak/deploy b/bin/prodkeycloak/deploy index 9ac3106..64aaf5d 100755 --- a/bin/prodkeycloak/deploy +++ b/bin/prodkeycloak/deploy @@ -5,7 +5,9 @@ ROOT=$SCRIPT/../.. CONF_DIR=${ROOT}/conf/multi-keycloak CERTS_DIR=${CONF_DIR}/certs -ensure_docker_network +PROVIDER_NETWORK=${PROVIDER_NETWORK:-rabbitmq_net} +ensure_docker_network ${PROVIDER_NETWORK} + kill_container_if_exist prodkeycloak generate-ca-server-client-kpi prodkeycloak $CERTS_DIR @@ -14,7 +16,7 @@ echo "Running prodkeycloak docker image ..." docker run \ --detach \ - --name prodkeycloak --net rabbitmq_net \ + --name prodkeycloak --net ${PROVIDER_NETWORK} \ --publish 8082:8080 \ --publish 8442:8442 \ --env KEYCLOAK_ADMIN=admin \ diff --git a/bin/uaa/deploy b/bin/uaa/deploy index 0467c81..7a9c438 100755 --- a/bin/uaa/deploy +++ b/bin/uaa/deploy @@ -10,6 +10,8 @@ UAA_MODE=${UAA_MODE:-"uaa"} CONF_DIR=${ROOT}/conf/${UAA_MODE} CERTS_DIR=${CONF_DIR}/certs +PROVIDER_NETWORK=${PROVIDER_NETWORK:-rabbitmq_net} + source $SCRIPT/../common function generate-keystore-if-required { @@ -35,7 +37,7 @@ function deploy { docker run \ --detach \ --name uaa \ - --net rabbitmq_net \ + --net ${PROVIDER_NETWORK} \ --publish 8080:8080 \ --publish 8443:8443 \ -v ${CONF_DIR}:/uaa \ @@ -45,7 +47,9 @@ function deploy { "${UAA_IMAGE_NAME}:${UAA_IMAGE_TAG}" } -ensure_docker_network + +ensure_docker_network ${PROVIDER_NETWORK} + kill_container_if_exist uaa generate-ca-server-client-kpi uaa $CERTS_DIR generate-keystore-if-required diff --git a/conf/forward-proxy/httpd/.htpasswd b/conf/forward-proxy/httpd/.htpasswd new file mode 100644 index 0000000..1a63893 --- /dev/null +++ b/conf/forward-proxy/httpd/.htpasswd @@ -0,0 +1 @@ +guest:{SHA}NWdeaPS1r3uZXZIFrQ/EOELxZFA= diff --git a/conf/forward-proxy/httpd/httpd.conf b/conf/forward-proxy/httpd/httpd.conf new file mode 100644 index 0000000..3ff43b2 --- /dev/null +++ b/conf/forward-proxy/httpd/httpd.conf @@ -0,0 +1,165 @@ +# +# This is the main Apache HTTP server configuration file. It contains the +# configuration directives that give the server its instructions. +# See for detailed information. +# In particular, see +# +# for a discussion of each configuration directive. +# +# Do NOT simply read the instructions in here without understanding +# what they do. They're here only as hints or reminders. If you are unsure +# consult the online docs. You have been warned. +# +# Configuration and logfile names: If the filenames you specify for many +# of the server's control files begin with "/" (or "drive:/" for Win32), the +# server will use that explicit path. If the filenames do *not* begin +# with "/", the value of ServerRoot is prepended -- so "logs/access_log" +# with ServerRoot set to "/usr/local/apache2" will be interpreted by the +# server as "/usr/local/apache2/logs/access_log", whereas "/logs/access_log" +# will be interpreted as '/logs/access_log'. + +# +# ServerRoot: The top of the directory tree under which the server's +# configuration, error, and log files are kept. +# +# Do not add a slash at the end of the directory path. If you point +# ServerRoot at a non-local disk, be sure to specify a local disk on the +# Mutex directive, if file-based mutexes are used. If you wish to share the +# same ServerRoot for multiple httpd daemons, you will need to change at +# least PidFile. +# +ServerRoot "/usr/local/apache2" + +# +# Mutex: Allows you to set the mutex mechanism and mutex file directory +# for individual mutexes, or change the global defaults +# +# Uncomment and change the directory if mutexes are file-based and the default +# mutex file directory is not on a local disk or is not appropriate for some +# other reason. +# +# Mutex default:logs + +# +# Listen: Allows you to bind Apache to specific IP addresses and/or +# ports, instead of the default. See also the +# directive. +# +# Change this to Listen on specific IP addresses as shown below to +# prevent Apache from glomming onto all bound IP addresses. +# +#Listen 12.34.56.78:80 +Listen 9092 + +# +# Dynamic Shared Object (DSO) Support +# +# To be able to use the functionality of a module which was built as a DSO you +# have to place corresponding `LoadModule' lines at this location so the +# directives contained in it are actually available _before_ they are used. +# Statically compiled modules (those listed by `httpd -l') do not need +# to be loaded here. +# +# Example: +# LoadModule foo_module modules/mod_foo.so +# + +LoadModule mpm_event_module modules/mod_mpm_event.so +LoadModule access_compat_module modules/mod_access_compat.so +LoadModule log_config_module modules/mod_log_config.so +LoadModule auth_basic_module modules/mod_auth_basic.so +LoadModule authn_core_module modules/mod_authn_core.so +LoadModule authz_core_module modules/mod_authz_core.so +LoadModule authn_file_module modules/mod_authn_file.so +LoadModule authz_user_module modules/mod_authz_user.so +LoadModule proxy_module modules/mod_proxy.so +LoadModule proxy_connect_module modules/mod_proxy_connect.so +LoadModule proxy_http_module modules/mod_proxy_http.so +LoadModule ssl_module modules/mod_ssl.so +LoadModule unixd_module modules/mod_unixd.so + + +User www-data +Group www-data + + + +ServerAdmin you@example.com + +ServerName forward-proxy + +ErrorLog /proc/self/fd/2 + +LogLevel warn + + + # + # The following directives define some format nicknames for use with + # a CustomLog directive (see below). + # + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined + LogFormat "%h %l %u %t \"%r\" %>s %b" common + + + # You need to enable mod_logio.c to use %I and %O + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio + + + # + # The location and format of the access logfile (Common Logfile Format). + # If you do not define any access logfiles within a + # container, they will be logged here. Contrariwise, if you *do* + # define per- access logfiles, transactions will be + # logged therein and *not* in this file. + # + CustomLog logs/access_log common + + # + # If you prefer a logfile with access, agent, and referer information + # (Combined Logfile Format) you can use the following directive. + # + #CustomLog "logs/access_log" combined + + + + ProxyRequests On + ProxyVia On + + Allow from all + + + + + +# SSLEngine on +# SSLCertificateKeyFile /usr/local/apache2/conf/server_forward-proxy_key.pem +# SSLCertificateFile /usr/local/apache2/conf/server_forward-proxy_certificate.pem +# SSLCertificateChainFile /usr/local/apache2/conf/ca_keycloak_certificate.pem +# SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 +# SSLProxyVerify none + AllowCONNECT 8443 + +# SSLProxyEngine On + +# SSLProxyVerify none +# SSLProxyCheckPeerCN off +# SSLProxyCheckPeerName off +# SSLProxyCheckPeerExpire off +# SSLProxyProtocol +TLSv1.2 + + ProxyRequests On + ProxyVia On + LogLevel debug + ErrorLog /dev/stderr + CustomLog /dev/stdout combined + + + Allow from all + AuthType Basic + AuthName "Restricted Site" + AuthBasicProvider file + AuthUserFile /usr/local/apache2/conf/.htpasswd + Require valid-user + + + diff --git a/conf/forward-proxy/rabbitmq.conf b/conf/forward-proxy/rabbitmq.conf new file mode 100644 index 0000000..9cc6af5 --- /dev/null +++ b/conf/forward-proxy/rabbitmq.conf @@ -0,0 +1,19 @@ +auth_backends.1 = rabbit_auth_backend_oauth2 + +log.default.level = debug +log.console = true + +management.oauth_enabled = true +management.oauth_client_id = rabbitmq-client-code +management.oauth_scopes = openid profile rabbitmq.tag:administrator + +auth_oauth2.resource_server_id = rabbitmq +auth_oauth2.preferred_username_claims.1 = user_name +auth_oauth2.additional_scopes_key = extra_scope +auth_oauth2.issuer = https://keycloak:8443/realms/test +auth_oauth2.https.cacertfile = /etc/keycloak/certs/ca_keycloak_certificate.pem + +auth_oauth2.proxy.host = forward-proxy +auth_oauth2.proxy.port = 9092 +auth_oauth2.proxy.username = guest +auth_oauth2.proxy.password = guest diff --git a/conf/forward-proxy/requires-tls b/conf/forward-proxy/requires-tls new file mode 100644 index 0000000..e69de29 diff --git a/conf/forward-proxy/tls.conf b/conf/forward-proxy/tls.conf new file mode 100644 index 0000000..b157e3d --- /dev/null +++ b/conf/forward-proxy/tls.conf @@ -0,0 +1,9 @@ +management.ssl.port = 15671 +management.ssl.cacertfile = /certs/ca_rabbitmq_certificate.pem +management.ssl.certfile = /certs/server_rabbitmq_certificate.pem +management.ssl.keyfile = /certs/server_rabbitmq_key.pem +management.ssl.fail_if_no_peer_cert = false +management.ssl.client_renegotiation = false +management.ssl.secure_renegotiate = true +management.ssl.honor_ecc_order = true +management.ssl.honor_cipher_order = true \ No newline at end of file diff --git a/conf/keycloak/import/test-realm.json b/conf/keycloak/import/test-realm.json index 813a02f..af51ff8 100644 --- a/conf/keycloak/import/test-realm.json +++ b/conf/keycloak/import/test-realm.json @@ -1393,7 +1393,7 @@ "enabled" : true, "alwaysDisplayInConsole" : false, "clientAuthenticatorType" : "client-secret", - "redirectUris" : [ "http://localhost:15672/*" ], + "redirectUris" : [ "http://localhost:15672/*", "https://localhost:15671/*" ], "webOrigins" : [ "+" ], "notBefore" : 0, "bearerOnly" : false, @@ -1526,7 +1526,7 @@ "enabled" : true, "alwaysDisplayInConsole" : false, "clientAuthenticatorType" : "client-secret", - "redirectUris" : [ "http://localhost:15672/*" ], + "redirectUris" : [ "http://localhost:15672/*", "https://localhost:15671/*" ], "webOrigins" : [ "+" ], "notBefore" : 0, "bearerOnly" : false, From 077013fd3fa9c85c6b6798dd384ce7898850c12a Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Thu, 13 Mar 2025 09:33:04 +0100 Subject: [PATCH 04/10] Add idp-initiated with portal example --- Makefile | 14 ++++- README.md | 1 + bin/deploy-rabbit | 20 ++++-- bin/oauth2-proxy/deploy | 6 +- bin/oauth2-proxy/undeploy | 14 +++++ bin/portal/deploy | 58 ++++++++++++++++++ bin/proxy/deploy | 53 ++++++++++++++++ conf/oauth2-proxy/rabbitmq.conf | 9 +-- conf/portal/Dockerfile | 12 ++++ conf/portal/app.js | 85 ++++++++++++++++++++++++++ conf/portal/package.json | 32 ++++++++++ conf/portal/proxy.js | 70 +++++++++++++++++++++ conf/portal/rabbitmq.conf | 15 +++++ conf/portal/requires-tls | 0 conf/portal/tls.conf | 9 +++ conf/portal/views/rabbitmq.html | 15 +++++ conf/portal/views/unauthenticated.html | 18 ++++++ conf/uaa/uaa.yml | 8 +++ 18 files changed, 426 insertions(+), 13 deletions(-) create mode 100755 bin/oauth2-proxy/undeploy create mode 100755 bin/portal/deploy create mode 100755 bin/proxy/deploy create mode 100644 conf/portal/Dockerfile create mode 100644 conf/portal/app.js create mode 100644 conf/portal/package.json create mode 100644 conf/portal/proxy.js create mode 100644 conf/portal/rabbitmq.conf create mode 100644 conf/portal/requires-tls create mode 100644 conf/portal/tls.conf create mode 100644 conf/portal/views/rabbitmq.html create mode 100644 conf/portal/views/unauthenticated.html diff --git a/Makefile b/Makefile index 905d418..e83a53e 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,12 @@ start-keycloak: ## Start keycloak start-forward-proxy: ## Start forward-proxy @./bin/forward-proxy/deploy +start-portal: ## Start portal + @./bin/portal/deploy + +start-proxy: ## Start proxy + @./bin/proxy/deploy + stop-uaa: ## Stop uaa @docker kill uaa @@ -33,6 +39,12 @@ stop-keycloak: ## Stop keycloak stop-forward-proxy: ## Stop forward-proxy @docker kill forward-proxy +stop-portal: ## Stop portal + @docker kill portal + +stop-proxy: ## Stop proxy + @docker kill proxy + stop-dev-keycloak: ## Stop dev keycloak @docker kill devkeycloak @docker rm devkeycloak @@ -45,7 +57,7 @@ start-oauth2-proxy: ## Start oauth2-proxy @bin/oauth2-proxy/deploy stop-oauth2-proxy: ## Stop oauth2-proxy - @docker-compose -f conf/oauth2-proxy/compose.yml down + @bin/oauth2-proxy/undeploy start-rabbitmq: ## Run RabbitMQ Server @./bin/deploy-rabbit diff --git a/README.md b/README.md index ab8827a..9fe9ee6 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ When the example requires RabbitMQ with TLS enabled, the corresponding `conf` fo * [Okta](https://www.rabbitmq.com/docs/next/oauth2-examples-okta) * [Google](https://www.rabbitmq.com/docs/next/oauth2-examples-google) **NOT SUPPORTED** * [Multiple OAuth 2.0 servers and/or audiences](https://www.rabbitmq.com/docs/next/oauth2-examples-multiresource) + * [Identity Provider initiated logon with a web portal](https://www.rabbitmq.com/docs/next/oauth2-examples-idp-initiated) ### Commercial-only features diff --git a/bin/deploy-rabbit b/bin/deploy-rabbit index 3b1d8e2..32abcf9 100755 --- a/bin/deploy-rabbit +++ b/bin/deploy-rabbit @@ -1,6 +1,8 @@ #!/usr/bin/env bash -#set -x +if [[ ! -z "${DEBUG}" ]]; then + set -x +fi SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -9,8 +11,8 @@ source $SCRIPT/common MODE=${MODE:-uaa} OAUTH_PROVIDER=${OAUTH_PROVIDER:-$MODE} ADVANCED=${ADVANCED:-advanced.config} -IMAGE_TAG=${IMAGE_TAG:-main} -IMAGE=${IMAGE:-pivotalrabbitmq/rabbitmq} +IMAGE_TAG=${IMAGE_TAG:-4.1-rc} +IMAGE=${IMAGE:-rabbitmq} RABBITMQ_CONF=${RABBITMQ_CONF:-rabbitmq.conf} if [[ "${MODE}" == "uaa" ]]; then @@ -40,8 +42,8 @@ function generate-final-conf-dir { } function generate-tls-certs-if-required { - if [[ -f "${CONF_DIR}/requires-tls" && ! -f "${CERTS_DIR}" ]]; then - generate-ca-server-client-kpi rabbitmq $CERTS_DIR + if [[ -f "${CONF_DIR}/requires-tls" && ! -f "${CERTS_DIR}/server_rabbitmq_certificate.pem" ]]; then + generate-ca-server-client-kpi rabbitmq $CERTS_DIR fi } @@ -59,15 +61,21 @@ function deploy { EXTRA_MOUNTS="${EXTRA_MOUNTS} -v ${CONF_DIR}/${ADVANCED}:/etc/rabbitmq/advanced.config:ro " USED_CONFIG="${USED_CONFIG} ${CONF_DIR}/${ADVANCED}" fi - + echo "Running RabbitMQ ($IMAGE:$IMAGE_TAG) with" echo " - Mode: ${MODE} " echo " - OauthProvider: ${OAUTH_PROVIDER}" echo " - configuration file(s): ${USED_CONFIG}" echo " - mounts: ${EXTRA_MOUNTS}" + PLATFORM_ARGS="" + if [[ -n "${PLATFORM}" ]]; then + PLATFORM_ARGS="--platform ${PLATFORM} " + fi + docker run -d --name rabbitmq \ --net ${RABBIT_NETWORK} \ + ${PLATFORM_ARGS} \ -p 15672:15672 \ -p 5672:5672 \ -p 5552:5552 \ diff --git a/bin/oauth2-proxy/deploy b/bin/oauth2-proxy/deploy index 39e84b3..252aa35 100755 --- a/bin/oauth2-proxy/deploy +++ b/bin/oauth2-proxy/deploy @@ -8,16 +8,18 @@ CERTS_DIR=${CONF_DIR}/certs source $SCRIPT/../common +cp -rf ${ROOT}/conf/keycloak/certs/* ${CERTS_DIR} + PROVIDER_NETWORK=${PROVIDER_NETWORK:-rabbitmq_net} ensure_docker_network ${PROVIDER_NETWORK} docker-compose -f $ROOT/conf/oauth2-proxy/compose.yml down 2>/dev/null || echo "oauth2-proxy was not running" generate-ca-server-client-kpi oauth2-proxy $CERTS_DIR -echo "Running oauth2-proxy docker image ..." +print "Running oauth2-proxy docker image ..." export OAUTH2_PROXY_COOKIE_SECRET=`dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64 | tr -d -- '\n' | tr -- '+/' '-_' ; echo` -docker-compose -f $ROOT/conf/oauth2-proxy/compose.yml up -d +docker compose -f $ROOT/conf/oauth2-proxy/compose.yml up -d wait_for_message oauth2-proxy-oauth2-proxy-1 "Cookie settings" print "oauth2-proxy is running" diff --git a/bin/oauth2-proxy/undeploy b/bin/oauth2-proxy/undeploy new file mode 100755 index 0000000..8c5d403 --- /dev/null +++ b/bin/oauth2-proxy/undeploy @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +ROOT=$SCRIPT/../.. +CONF_DIR=${ROOT}/conf/oauth2-proxy +CERTS_DIR=${CONF_DIR}/certs + + +echo "Stopping oauth2-proxy ..." + +docker compose -f $ROOT/conf/oauth2-proxy/compose.yml down + + diff --git a/bin/portal/deploy b/bin/portal/deploy new file mode 100755 index 0000000..2330551 --- /dev/null +++ b/bin/portal/deploy @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +if [[ ! -z "${DEBUG}" ]]; then + set -x +fi + +ROOT=$SCRIPT/../.. +CONF_DIR=${ROOT}/conf/portal +CERTS_DIR=${CONF_DIR}/certs + +source $SCRIPT/../common + +if [ ! -f ${ROOT}/conf/uaa/certs ]; then + print "Deploy uaa first so that portal can reference its certificates" +fi + +print "Starting portal ..." + +DOCKER_NETWORK=${DOCKER_NETWORK:-rabbitmq_net} +ensure_docker_network ${DOCKER_NETWORK} +kill_container_if_exist portal + +image_tag=($(md5sum $CONF_DIR/package.json)) +if [[ $(docker images -q portal:$image_tag 2> /dev/null) == "" ]]; then + docker build -t portal:$image_tag --target test $CONF_DIR +fi + +generate-ca-server-client-kpi portal $CERTS_DIR + +begin "Running portal docker image portal:${image_tag} ..." + +rm -f ${CONF_DIR}/certs/ca_certs.pem +cat ${ROOT}/conf/uaa/certs/ca_uaa_certificate.pem ${CONF_DIR}/certs/ca_rabbitmq_certificate.pem \ + >> ${CONF_DIR}/certs/ca_certs.pem + +if [[ -f ${ROOT}/conf/portal/certs/ca_proxy_certificate.pem ]]; then + cat ${ROOT}/conf/portal/certs/ca_proxy_certificate.pem >> ${CONF_DIR}/certs/ca_certs.pem +fi + +docker run \ + --detach \ + --name portal \ + --net ${DOCKER_NETWORK} \ + --publish 3000:3000 \ + --env PORT=3000 \ + --env RABBITMQ_URL="https://localhost:15671" \ + --env UAA_URL="https://uaa:8443" \ + --env CLIENT_ID="rabbit_idp_user" \ + --env CLIENT_SECRET="rabbit_idp_user" \ + --env PROXIED_RABBITMQ_URL="https://proxy:9090" \ + --env NODE_EXTRA_CA_CERTS=/etc/portal/ca_certs.pem \ + -v ${CONF_DIR}/certs:/etc/portal \ + -v ${CONF_DIR}:/code/portal \ + portal:${image_tag} run portal + +print "portal is running" diff --git a/bin/proxy/deploy b/bin/proxy/deploy new file mode 100755 index 0000000..3c5d878 --- /dev/null +++ b/bin/proxy/deploy @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +if [[ ! -z "${DEBUG}" ]]; then + set -x +fi + +ROOT=$SCRIPT/../.. +CONF_DIR=${ROOT}/conf/portal +CERTS_DIR=${CONF_DIR}/certs + +source $SCRIPT/../common + +if [ ! -f ${ROOT}/conf/uaa/certs ]; then + print "Deploy uaa first so that portal can reference its certificates" +fi + +print "Starting proxy ..." + +DOCKER_NETWORK=${DOCKER_NETWORK:-rabbitmq_net} +ensure_docker_network ${DOCKER_NETWORK} +kill_container_if_exist proxy + +image_tag=($(md5sum $CONF_DIR/package.json)) +if [[ $(docker images -q portal:$image_tag 2> /dev/null) == "" ]]; then + docker build -t portal:$image_tag --target test $CONF_DIR +fi + +generate-ca-server-client-kpi proxy $CERTS_DIR + +begin "Running proxy docker image portal:${image_tag} ..." + +rm -f ${CONF_DIR}/certs/ca_certs.pem +cat ${ROOT}/conf/uaa/certs/ca_uaa_certificate.pem ${CONF_DIR}/certs/ca_rabbitmq_certificate.pem \ + >> ${CONF_DIR}/certs/ca_certs.pem + +docker run \ + --detach \ + --name proxy \ + --net ${DOCKER_NETWORK} \ + --publish 9090:9090 \ + --env PORT=9090 \ + --env RABBITMQ_URL="https://rabbitmq:15671" \ + --env UAA_URL="https://uaa:8443" \ + --env CLIENT_ID="rabbit_idp_user" \ + --env CLIENT_SECRET="rabbit_idp_user" \ + --env NODE_EXTRA_CA_CERTS=/etc/proxy/ca_certs.pem \ + -v ${CONF_DIR}/certs:/etc/proxy \ + -v ${CONF_DIR}:/code/portal \ + portal:${image_tag} run proxy + +print "proxy is running" diff --git a/conf/oauth2-proxy/rabbitmq.conf b/conf/oauth2-proxy/rabbitmq.conf index 72bcb70..8160537 100644 --- a/conf/oauth2-proxy/rabbitmq.conf +++ b/conf/oauth2-proxy/rabbitmq.conf @@ -1,14 +1,15 @@ auth_backends.1 = rabbit_auth_backend_oauth2 log.default.level = debug +log.console.level = debug management.oauth_enabled = true management.oauth_initiated_logon_type = idp_initiated -management.oauth_provider_url = https://localhost:8442 +management.oauth_provider_url = https://oauth2-proxy:8442 auth_oauth2.resource_server_id = rabbitmq -auth_oauth2.issuer = https://keycloak:8443/realms/test -auth_oauth2.end_session_endpoint = https://localhost:8442/oauth2/sign_out?rd=https://keycloak:8443/realms/test/protocol/openid-connect/logout -auth_oauth2.https.cacertfile = /etc/keycloak/certs/ca_keycloak_certificate.pem +auth_oauth2.jwks_uri = https://keycloak:8443/realms/test/protocol/openid-connect/certs +auth_oauth2.end_session_endpoint = https://oauth2-proxy:8442/oauth2/sign_out?rd=https://keycloak:8443/realms/test/protocol/openid-connect/logout +auth_oauth2.https.cacertfile = /etc/oauth2-proxy/certs/ca_keycloak_certificate.pem auth_oauth2.preferred_username_claims.1 = preferred_username auth_oauth2.verify_aud = false diff --git a/conf/portal/Dockerfile b/conf/portal/Dockerfile new file mode 100644 index 0000000..ce100de --- /dev/null +++ b/conf/portal/Dockerfile @@ -0,0 +1,12 @@ +# syntax=docker/dockerfile:1 +FROM atools/jdk-maven-node:mvn3-jdk11-node16 as base + +WORKDIR /code + +COPY package.json package.json + +FROM base as test +RUN npm install + +ENTRYPOINT [ "npm" ] +CMD [ "" ] diff --git a/conf/portal/app.js b/conf/portal/app.js new file mode 100644 index 0000000..d5d15f7 --- /dev/null +++ b/conf/portal/app.js @@ -0,0 +1,85 @@ +const express = require("express"); +const app = express(); +const fs = require('fs'); +const https = require('https'); +var path = require('path'); +const XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest + +const rabbitmq_url = process.env.RABBITMQ_URL; +const proxied_rabbitmq_url = process.env.PROXIED_RABBITMQ_URL; +const client_id = process.env.CLIENT_ID; +const client_secret = process.env.CLIENT_SECRET; +const uaa_url = process.env.UAA_URL; +const port = process.env.PORT || 3000; + +app.engine('.html', require('ejs').__express); +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'html'); + +app.get('/', function(req, res){ + let id = default_if_blank(req.query.client_id, client_id) + let secret = default_if_blank(req.query.client_secret, client_secret) + if (id == 'undefined' || secret == 'undefined') { + res.render('unauthenticated') + }else { + res.render('rabbitmq', { + proxied_url: proxied_rabbitmq_url, + url: rabbitmq_url.replace(/\/?$/, '/') + "login", + name: rabbitmq_url + " for " + id, + access_token: access_token(id, secret) + }) + } +}) + +app.get('/favicon.ico', (req, res) => res.status(204)); + +app.get('/logout', function(req, res) { + const redirectUrl = uaa_url + '/logout.do?client_id=' + client_id + "&redirect=https://portal:3000" + console.debug("Received /logout request -> redirect to " + redirectUrl) + res.redirect(redirectUrl); +}) + +https + .createServer( + { + cert: fs.readFileSync('/etc/portal/server_portal_certificate.pem'), + key: fs.readFileSync('/etc/portal/server_portal_key.pem') + }, + app + ) + .listen(port) + +console.log('Express started on port ' + port); + +function default_if_blank(value, defaultValue) { + if (typeof value === "undefined" || value === null || value == "") { + return defaultValue; + } else { + return value; + } +} + +function access_token(id, secret) { + const req = new XMLHttpRequest(); + const url = uaa_url + '/oauth/token'; + const params = 'client_id=' + id + + '&client_secret=' + secret + + '&grant_type=client_credentials' + + '&token_format=jwt' + + '&response_type=token'; + + console.debug("Sending " + url + " with params "+ params); + + req.open('POST', url, false); + req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + req.setRequestHeader('Accept', 'application/json'); + req.send(params); + if (req.status == 200) { + const token = JSON.parse(req.responseText).access_token; + console.log("Token => " + token) + return token + } else { + throw new Error(req.status + " : " + " : " + + req.response + " : " + req.responseText) + } +} diff --git a/conf/portal/package.json b/conf/portal/package.json new file mode 100644 index 0000000..a74090f --- /dev/null +++ b/conf/portal/package.json @@ -0,0 +1,32 @@ +{ + "name": "selenium", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "portal": "node portal/app.js", + "proxy": "node portal/proxy.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "chromedriver": "^132.0", + "ejs": "^3.1.8", + "express": "^4.18.2", + "geckodriver": "^3.0.2", + "http-proxy": "^1.18.1", + "mqtt": "^5.3.3", + "path": "^0.12.7", + "proxy": "^1.0.2", + "rhea": "^3.0.3", + "selenium-webdriver": "^4.26.0", + "xmlhttprequest": "^1.8.0" + }, + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.4.0", + "request": "^2.88.2", + "standard": "^17.0.0" + } +} diff --git a/conf/portal/proxy.js b/conf/portal/proxy.js new file mode 100644 index 0000000..f023fbc --- /dev/null +++ b/conf/portal/proxy.js @@ -0,0 +1,70 @@ +var http = require('http'), + httpProxy = require('http-proxy'), + fs = require('fs'); +const XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest + +const rabbitmq_url = process.env.RABBITMQ_URL || 'http://0.0.0.0:15672/'; +const client_id = process.env.CLIENT_ID; +const client_secret = process.env.CLIENT_SECRET; +const uaa_url = process.env.UAA_URL; +const port = process.env.PORT; + +// +// Create a proxy server with custom application logic +// +var proxy = httpProxy.createProxyServer({}); + +proxy.on('proxyReq', function(proxyReq, req, res, options) { + console.log("proxing " + req.url) + if (req.url.endsWith("bootstrap.js")) { + proxyReq.setHeader('Authorization', 'Bearer ' + access_token(client_id, client_secret)); + } + proxyReq.setHeader('origin', req.url) + proxyReq.setHeader('Access-Control-Allow-Origin', '*'); + proxyReq.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS'); + +}); +var server = http.createServer({ + ssl: { + key: fs.readFileSync('/etc/proxy/server_proxy_key.pem', 'utf8'), + cert: fs.readFileSync('/etc/proxy/server_proxy_certificate.pem', 'utf8') + }, + target: 'https://rabbitmq:15671', + secure: true +}); +console.log("Proxy listening on port " + port + ". RABBITMQ_URL=" + rabbitmq_url) +server.listen(port); + + +function default_if_blank(value, defaultValue) { + if (typeof value === "undefined" || value === null || value == "") { + return defaultValue; + } else { + return value; + } +} + +function access_token(id, secret) { + const req = new XMLHttpRequest(); + const url = uaa_url + '/oauth/token'; + const params = 'client_id=' + id + + '&client_secret=' + secret + + '&grant_type=client_credentials' + + '&token_format=jwt' + + '&response_type=token'; + + console.debug("Sending " + url + " with params "+ params); + + req.open('POST', url, false); + req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + req.setRequestHeader('Accept', 'application/json'); + req.send(params); + console.log("Ret " + req.status) + if (req.status == 200) { + const token = JSON.parse(req.responseText).access_token; + console.log("Token => " + token) + return token; + } else { + throw new Error(req.status + " : " + req.responseText); + } +} diff --git a/conf/portal/rabbitmq.conf b/conf/portal/rabbitmq.conf new file mode 100644 index 0000000..70e363e --- /dev/null +++ b/conf/portal/rabbitmq.conf @@ -0,0 +1,15 @@ +auth_backends.1 = rabbit_auth_backend_oauth2 + +log.console.level = debug + +management.oauth_enabled = true +management.oauth_client_id = rabbitmq-client-code +management.oauth_scopes = openid profile rabbitmq.tag:administrator +management.oauth_initiated_logon_type = idp_initiated +management.oauth_provider_url = https://localhost:3000 + +auth_oauth2.resource_server_id = rabbitmq +auth_oauth2.preferred_username_claims.1 = user_name +auth_oauth2.additional_scopes_key = extra_scope +auth_oauth2.issuer = https://uaa:8443 +auth_oauth2.https.cacertfile = /etc/uaa/certs/ca_uaa_certificate.pem diff --git a/conf/portal/requires-tls b/conf/portal/requires-tls new file mode 100644 index 0000000..e69de29 diff --git a/conf/portal/tls.conf b/conf/portal/tls.conf new file mode 100644 index 0000000..b157e3d --- /dev/null +++ b/conf/portal/tls.conf @@ -0,0 +1,9 @@ +management.ssl.port = 15671 +management.ssl.cacertfile = /certs/ca_rabbitmq_certificate.pem +management.ssl.certfile = /certs/server_rabbitmq_certificate.pem +management.ssl.keyfile = /certs/server_rabbitmq_key.pem +management.ssl.fail_if_no_peer_cert = false +management.ssl.client_renegotiation = false +management.ssl.secure_renegotiate = true +management.ssl.honor_ecc_order = true +management.ssl.honor_cipher_order = true \ No newline at end of file diff --git a/conf/portal/views/rabbitmq.html b/conf/portal/views/rabbitmq.html new file mode 100644 index 0000000..ccdbde2 --- /dev/null +++ b/conf/portal/views/rabbitmq.html @@ -0,0 +1,15 @@ +

Portal

+ +

This is a portal used to test Identity-Provider-based authentication. +This means users comes to RabbitMQ with a token already obtained without involving RabbitMQ +management ui. +

+ +

POST access_token to /login endpoint

+This mechanism is available for those portals which cannot inject the access token into the Authorization header. +Instead they submit the access token via the form field access_token to the RabbitMQ /login endpoint. +

+
+ + +
diff --git a/conf/portal/views/unauthenticated.html b/conf/portal/views/unauthenticated.html new file mode 100644 index 0000000..d857ae7 --- /dev/null +++ b/conf/portal/views/unauthenticated.html @@ -0,0 +1,18 @@ +

FakePortal

+ +

This is a portal used to test Identity-Provider-based authentication. +This means users comes to RabbitMQ with a token already obtained without involving RabbitMQ +management ui. +

+ +

This is the state of the Portal when the user is not authenticated yet.

+

To get the fakeportal fully authenticated, pass two request parameters: +

    +
  • client_id
  • +
  • client_secret
  • +
+ These credentitals are used to get an access token from UAA and send it to +RabbitMQ. +

+ + diff --git a/conf/uaa/uaa.yml b/conf/uaa/uaa.yml index 7a94142..0894924 100644 --- a/conf/uaa/uaa.yml +++ b/conf/uaa/uaa.yml @@ -207,6 +207,14 @@ oauth: secret: mgt_api_client authorized-grant-types: client_credentials authorities: rabbitmq.tag:monitoring + rabbit_idp_user: + id: rabbit_idp_user + secret: rabbit_idp_user + authorized-grant-types: client_credentials + authorities: uaa.resource,rabbitmq.tag:administrator + redirect-uri: https://localhost:3000 + autoapprove: true + allowpublic: true rabbit_client_code: id: rabbit_client_code secret: rabbit_client_code From 6b29cb253727d7c356bd166907d795736b3d87b0 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Thu, 13 Mar 2025 10:52:10 +0100 Subject: [PATCH 05/10] Fix url --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9fe9ee6..c0c05cc 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ When the example requires RabbitMQ with TLS enabled, the corresponding `conf` fo ### Examples for Specific OAuth 2.0 Identity Providers * [Keycloak](https://www.rabbitmq.com/docs/next/oauth2-examples-keycloak) - * [Auth0](https://www.rabbitmq.com/oauth2-examples-auth0) + * [Auth0](https://www.rabbitmq.com/docs/next/oauth2-examples-auth0) * [Microsoft Entra ID](https://www.rabbitmq.com/docs/next/oauth2-examples-entra-id) (formerly known as Azure Active Directory) * [OAuth2 Proxy](https://www.rabbitmq.com/docs/next/oauth2-examples-proxy) * [Okta](https://www.rabbitmq.com/docs/next/oauth2-examples-okta) From 7016495b6b164a4d406d4a457dd44f88f9d0bc1d Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Thu, 13 Mar 2025 11:18:05 +0100 Subject: [PATCH 06/10] Test --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c0c05cc..5622421 100644 --- a/README.md +++ b/README.md @@ -64,3 +64,5 @@ When the example requires RabbitMQ with TLS enabled, the corresponding `conf` fo ### Commercial-only features * [Explicit forward proxy](https://techdocs.broadcom.com/us/en/vmware-tanzu/data-solutions/tanzu-rabbitmq-oci/4-0/tanzu-rabbitmq-oci-image/overview.html) + +Test \ No newline at end of file From 4da7bfcdb8b05649773047c005de9d5fac49319c Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Thu, 13 Mar 2025 11:18:38 +0100 Subject: [PATCH 07/10] Test --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 5622421..9485339 100644 --- a/README.md +++ b/README.md @@ -65,4 +65,3 @@ When the example requires RabbitMQ with TLS enabled, the corresponding `conf` fo * [Explicit forward proxy](https://techdocs.broadcom.com/us/en/vmware-tanzu/data-solutions/tanzu-rabbitmq-oci/4-0/tanzu-rabbitmq-oci-image/overview.html) -Test \ No newline at end of file From 0140343856c413264edf010bed62337e60eed2c7 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Thu, 3 Apr 2025 06:41:04 +0200 Subject: [PATCH 08/10] Debug make start-keycloak --- bin/keycloak/deploy | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/keycloak/deploy b/bin/keycloak/deploy index 81e2303..c09ccae 100755 --- a/bin/keycloak/deploy +++ b/bin/keycloak/deploy @@ -1,8 +1,10 @@ #!/usr/bin/env bash -SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +if [[ ! -z "${DEBUG}" ]]; then + set -x +fi -#set -x +SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" ROOT=$SCRIPT/../.. CONF_DIR=${ROOT}/conf/keycloak From f3f6f00612ecba4141ba254dbf9d9a5e2b349edd Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Fri, 4 Apr 2025 08:30:20 +0200 Subject: [PATCH 09/10] Refactor Set permissions to config and certs so that there are no issues mounting those files in some distros Use /etc/rabbitmq to store configuration rather than an env variable --- bin/common | 1 + bin/deploy-rabbit | 10 +++++----- conf/keycloak/rabbitmq.conf | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/bin/common b/bin/common index 180f559..5e792de 100755 --- a/bin/common +++ b/bin/common @@ -90,5 +90,6 @@ function do_generate-ca-server-client-kpi { cp $ROOT/tls-gen/basic/result/server_${NAME}_certificate.pem $FOLDER cp $ROOT/tls-gen/basic/result/server_${NAME}_key.pem $FOLDER cp $ROOT/tls-gen/basic/result/server_${NAME}.p12 $FOLDER + chmod 777 $FOLDER/* end "SSL Certificates generated for $NAME under $FOLDER" } diff --git a/bin/deploy-rabbit b/bin/deploy-rabbit index 32abcf9..7f9dae2 100755 --- a/bin/deploy-rabbit +++ b/bin/deploy-rabbit @@ -26,7 +26,7 @@ CERTS_DIR=${CONF_DIR}/certs RABBIT_NETWORK=${RABBIT_NETWORK:-rabbitmq_net} function generate-final-conf-dir { - FINAL_CONF_DIR=`mktemp -d -t "oauth2"` + FINAL_CONF_DIR=`mktemp -d -t "oauth2XXXXX"` if [[ -z "${CONF_FILES}" ]]; then for i in $CONF_DIR/*.conf do @@ -48,8 +48,9 @@ function generate-tls-certs-if-required { } function deploy { - EXTRA_MOUNTS="${EXTRA_MOUNTS} -v ${SCRIPT}/../conf/enabled_plugins:/etc/rabbitmq/enabled_plugins " - EXTRA_MOUNTS="${EXTRA_MOUNTS} -v ${FINAL_CONF_DIR}:/conf -v ${CERTS_DIR}:/certs " + cp ${SCRIPT}/../conf/enabled_plugins ${FINAL_CONF_DIR} + EXTRA_MOUNTS="${EXTRA_MOUNTS} -v ${FINAL_CONF_DIR}:/etc/rabbitmq -v ${CERTS_DIR}:/certs " + chmod o+rwx ${FINAL_CONF_DIR} USED_CONFIG="${FINAL_CONF_DIR}/*.conf " if [[ -f "${CONF_DIR}/requires-tls" ]]; then @@ -80,7 +81,6 @@ function deploy { -p 5672:5672 \ -p 5552:5552 \ ${EXTRA_PORTS} \ - --env RABBITMQ_CONFIG_FILES="/conf" \ ${EXTRA_MOUNTS} \ ${IMAGE}:${IMAGE_TAG} } @@ -90,5 +90,5 @@ generate-tls-certs-if-required ensure_docker_network ${RABBIT_NETWORK} kill_container_if_exist rabbitmq deploy -wait_for_message rabbitmq "Server startup complete" +wait_for_message rabbitmq "complete" print "RabbitMQ is running" diff --git a/conf/keycloak/rabbitmq.conf b/conf/keycloak/rabbitmq.conf index 046e7b3..dfb91b1 100644 --- a/conf/keycloak/rabbitmq.conf +++ b/conf/keycloak/rabbitmq.conf @@ -1,6 +1,6 @@ auth_backends.1 = rabbit_auth_backend_oauth2 -log.default.level = debug +log.console.level = debug management.oauth_enabled = true management.oauth_client_id = rabbitmq-client-code From b2922b70243eb5f8e2f154325ad79916fb63deef Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Wed, 11 Jun 2025 11:41:04 +0200 Subject: [PATCH 10/10] Add var expansion to scopes And bump up rabbitmq docker image --- bin/deploy-rabbit | 4 ++-- bin/keycloak/deploy | 3 +++ conf/keycloak/import/test-realm.json | 32 ++++++++++++++++++++++++---- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/bin/deploy-rabbit b/bin/deploy-rabbit index 7f9dae2..7283b88 100755 --- a/bin/deploy-rabbit +++ b/bin/deploy-rabbit @@ -11,8 +11,8 @@ source $SCRIPT/common MODE=${MODE:-uaa} OAUTH_PROVIDER=${OAUTH_PROVIDER:-$MODE} ADVANCED=${ADVANCED:-advanced.config} -IMAGE_TAG=${IMAGE_TAG:-4.1-rc} -IMAGE=${IMAGE:-rabbitmq} +IMAGE_TAG=${IMAGE_TAG:-main-otp27} +IMAGE=${IMAGE:-pivotalrabbitmq/rabbitmq} RABBITMQ_CONF=${RABBITMQ_CONF:-rabbitmq.conf} if [[ "${MODE}" == "uaa" ]]; then diff --git a/bin/keycloak/deploy b/bin/keycloak/deploy index c09ccae..729be0b 100755 --- a/bin/keycloak/deploy +++ b/bin/keycloak/deploy @@ -33,5 +33,8 @@ docker run \ --https-certificate-file=/opt/keycloak/certs/server_keycloak_certificate.pem \ --https-certificate-key-file=/opt/keycloak/certs/server_keycloak_key.pem +print " Note: If you modify keycloak configuration. Make sure to run the following command to export the configuration." +print " docker exec -it keycloak /opt/keycloak/bin/kc.sh export --users realm_file --realm test --dir /opt/keycloak/data/import/" + wait_for_message keycloak "Running the server" print "keycloak is running" diff --git a/conf/keycloak/import/test-realm.json b/conf/keycloak/import/test-realm.json index af51ff8..3250851 100644 --- a/conf/keycloak/import/test-realm.json +++ b/conf/keycloak/import/test-realm.json @@ -96,6 +96,14 @@ "clientRole" : false, "containerId" : "test", "attributes" : { } + }, { + "id" : "70200494-09ed-425c-bee5-ba8730612f8b", + "name" : "test-var-expansion", + "description" : "", + "composite" : false, + "clientRole" : false, + "containerId" : "test", + "attributes" : { } }, { "id" : "af1bc955-6d4d-42e9-b0d4-343e7eb075d0", "name" : "rabbitmq-role", @@ -502,7 +510,7 @@ } ], "disableableCredentialTypes" : [ ], "requiredActions" : [ ], - "realmRoles" : [ "rabbitmq.tag:administrator", "default-roles-test" ], + "realmRoles" : [ "rabbitmq.tag:administrator", "test-var-expansion", "default-roles-test" ], "notBefore" : 0, "groups" : [ ] }, { @@ -643,6 +651,9 @@ }, { "clientScope" : "rabbitmq.tag:management", "roles" : [ "rabbitmq.tag:management" ] + }, { + "clientScope" : "rabbitmq.configure:*/q-{user_name}", + "roles" : [ "test-var-expansion" ] } ], "clientScopeMappings" : { "account" : [ { @@ -1612,7 +1623,7 @@ "jsonType.label" : "String" } } ], - "defaultClientScopes" : [ "web-origins", "acr", "rabbitmq.tag:administrator", "profile", "roles", "rabbitmq.tag:management", "email" ], + "defaultClientScopes" : [ "web-origins", "rabbitmq.configure:*/q-{user_name}", "acr", "rabbitmq.tag:administrator", "profile", "roles", "rabbitmq.tag:management", "email" ], "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] }, { "id" : "c265f3db-ed3a-4898-8800-af044b3c30f5", @@ -1773,7 +1784,8 @@ "included.client.audience" : "rabbitmq-proxy-client-tls", "id.token.claim" : "true", "access.token.claim" : "true", - "included.custom.audience" : "rabbitmq" + "included.custom.audience" : "rabbitmq", + "userinfo.token.claim" : "true" } } ], "defaultClientScopes" : [ "rabbitmq.read:*/*", "web-origins", "acr", "rabbitmq.write:*/*", "rabbitmq.tag:administrator", "profile", "roles", "rabbitmq.tag:management", "email", "rabbitmq.configure:*/*" ], @@ -2349,8 +2361,20 @@ "include.in.token.scope" : "true", "display.on.consent.screen" : "true" } + }, { + "id" : "f2495e2f-2d9a-44e2-b8da-a46b464f9534", + "name" : "rabbitmq.configure:*/q-{user_name}", + "description" : "", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "gui.order" : "", + "consent.screen.text" : "" + } } ], - "defaultDefaultClientScopes" : [ "role_list", "profile", "email", "roles", "web-origins", "acr" ], + "defaultDefaultClientScopes" : [ "role_list", "profile", "email", "roles", "web-origins", "acr", + "rabbitmq.configure:*/q-{user_name}" ], "defaultOptionalClientScopes" : [ "offline_access", "address", "phone", "microprofile-jwt" ], "browserSecurityHeaders" : { "contentSecurityPolicyReportOnly" : "",