Main purpose of this article is to configure Keycloak Server to be proxied from Apache Web Server. Below you can find the steps:
Configure Keycloak for reverse proxy
You have to open the file KEYCLOAK_HOME/standalone/configuration/standalone.xml and do the changes shown below:
<subsystem xmlns="urn:jboss:domain:undertow:3.0"> ... <server name="default-server"> <!------- Change this line -----> <http-listener name="default" socket-binding="http" proxy-address-forwarding="true" redirect-socket="proxy-https" /> ... <socket-binding-group name="standard-sockets" default-interface=" ... ... <!------- Add this line -----> <socket-binding name="proxy-https" port="443"/> ...
Set up Keycloak as a service
Create a file named keycloak-defaults with the following content (adjust the paths for your installation) and copy it to /etc/default/keycloak
## Location of JDK JAVA_HOME="/usr/lib/jvm/jdk8" ## Location of WildFly JBOSS_HOME="/opt/keycloak" ## The username who should own the process. JBOSS_USER=keycloak ## The mode WildFly should start, standalone or domain JBOSS_MODE=standalone ## Configuration for standalone mode JBOSS_CONFIG=standalone.xml ## Configuration for domain mode JBOSS_DOMAIN_CONFIG=domain.xml JBOSS_HOST_CONFIG=host-master.xml ## The amount of time to wait for startup # If this is too low, the service might fail STARTUP_WAIT=60 ## The amount of time to wait for shutdown SHUTDOWN_WAIT=60 ## Location to keep the console log JBOSS_CONSOLE_LOG="/var/log/keycloak/console.log" ## Additionals args to include in startup JBOSS_OPTS="-b 127.0.0.1"
Create file name keycloak-init.d with the following content and copy it to /etc/init.d/keycloak
#!/bin/sh # # /etc/init.d/keycloak -- startup script for Keycloak # # Original written by Jorge Solorzano for /etc/init.d/Wildfly # Modified for Keycloak by Markus Lehtonen ### BEGIN INIT INFO # Provides: keycloak # Required-Start: $remote_fs $network # Required-Stop: $remote_fs $network # Should-Start: $named # Should-Stop: $named # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Keycloak Application Server # Description: Provide Keycloak startup/shutdown script ### END INIT INFO NAME=keycloak DESC="Keycloak Application Server" DEFAULT="/etc/default/$NAME" # Check privileges if [ `id -u` -ne 0 ]; then echo "You need root privileges to run this script" exit 1 fi # Make sure keycloak is started with system locale if [ -r /etc/default/locale ]; then . /etc/default/locale export LANG fi . /lib/lsb/init-functions if [ -r /etc/default/rcS ]; then . /etc/default/rcS fi # Overwrite settings from default file if [ -f "$DEFAULT" ]; then . "$DEFAULT" fi # Location of JDK if [ -n "$JAVA_HOME" ]; then export JAVA_HOME fi # Setup the JVM if [ -z "$JAVA" ]; then if [ -n "$JAVA_HOME" ]; then JAVA="$JAVA_HOME/bin/java" else JAVA="java" fi fi # Location of keycloak if [ -z "$JBOSS_HOME" ]; then JBOSS_HOME="/opt/keycloak/" fi export JBOSS_HOME # Check if keycloak is installed if [ ! -f "$JBOSS_HOME/jboss-modules.jar" ]; then log_failure_msg "$NAME is not installed in \"$JBOSS_HOME\"" exit 1 fi # Run as keycloak user # Example of user creation for Debian based: # adduser --system --group --no-create-home --home $JBOSS_HOME --disabled-login keycloak if [ -z "$JBOSS_USER" ]; then JBOSS_USER=keycloak fi # Check keycloak user id $JBOSS_USER > /dev/null 2>&1 if [ $? -ne 0 -o -z "$JBOSS_USER" ]; then log_failure_msg "User \"$JBOSS_USER\" does not exist..." exit 1 fi # Check owner of JBOSS_HOME if [ ! $(stat -L -c "%U" "$JBOSS_HOME") = $JBOSS_USER ]; then log_failure_msg "The user \"$JBOSS_USER\" is not owner of \"$JBOSS_HOME\"" exit 1 fi # Startup mode of keycloak if [ -z "$JBOSS_MODE" ]; then JBOSS_MODE=standalone fi # Startup mode script if [ "$JBOSS_MODE" = "standalone" ]; then JBOSS_SCRIPT="$JBOSS_HOME/bin/standalone.sh" if [ -z "$JBOSS_CONFIG" ]; then JBOSS_CONFIG=standalone.xml fi else JBOSS_SCRIPT="$JBOSS_HOME/bin/domain.sh" if [ -z "$JBOSS_DOMAIN_CONFIG" ]; then JBOSS_DOMAIN_CONFIG=domain.xml fi if [ -z "$JBOSS_HOST_CONFIG" ]; then JBOSS_HOST_CONFIG=host.xml fi fi # Check startup file if [ ! -x "$JBOSS_SCRIPT" ]; then log_failure_msg "$JBOSS_SCRIPT is not an executable!" exit 1 fi # Check cli file JBOSS_CLI="$JBOSS_HOME/bin/jboss-cli.sh" if [ ! -x "$JBOSS_CLI" ]; then log_failure_msg "$JBOSS_CLI is not an executable!" exit 1 fi # The amount of time to wait for startup if [ -z "$STARTUP_WAIT" ]; then STARTUP_WAIT=30 fi # The amount of time to wait for shutdown if [ -z "$SHUTDOWN_WAIT" ]; then SHUTDOWN_WAIT=30 fi # Location to keep the console log if [ -z "$JBOSS_CONSOLE_LOG" ]; then JBOSS_CONSOLE_LOG="/var/log/$NAME/console.log" fi export JBOSS_CONSOLE_LOG # Location to set the pid file JBOSS_PIDFILE="/var/run/$NAME/$NAME.pid" export JBOSS_PIDFILE # Launch keycloak in background LAUNCH_JBOSS_IN_BACKGROUND=1 export LAUNCH_JBOSS_IN_BACKGROUND # Helper function to check status of keycloak service check_status() { pidofproc -p "$JBOSS_PIDFILE" "$JAVA" >/dev/null 2>&1 } case "$1" in start) log_daemon_msg "Starting $DESC" "$NAME" check_status status_start=$? if [ $status_start -eq 3 ]; then mkdir -p $(dirname "$JBOSS_PIDFILE") mkdir -p $(dirname "$JBOSS_CONSOLE_LOG") chown $JBOSS_USER $(dirname "$JBOSS_PIDFILE") || true cat /dev/null > "$JBOSS_CONSOLE_LOG" if [ "$JBOSS_MODE" = "standalone" ]; then start-stop-daemon --start --user "$JBOSS_USER" \ --chuid "$JBOSS_USER" --chdir "$JBOSS_HOME" --pidfile "$JBOSS_PIDFILE" \ --exec "$JBOSS_SCRIPT" -- -c $JBOSS_CONFIG $JBOSS_OPTS >> "$JBOSS_CONSOLE_LOG" 2>&1 & else start-stop-daemon --start --user "$JBOSS_USER" \ --chuid "$JBOSS_USER" --chdir "$JBOSS_HOME" --pidfile "$JBOSS_PIDFILE" \ --exec "$JBOSS_SCRIPT" -- --domain-config=$JBOSS_DOMAIN_CONFIG \ --host-config=$JBOSS_HOST_CONFIG $JBOSS_OPTS >> "$JBOSS_CONSOLE_LOG" 2>&1 & fi count=0 launched=0 until [ $count -gt $STARTUP_WAIT ] do grep 'WFLYSRV0025:' "$JBOSS_CONSOLE_LOG" > /dev/null if [ $? -eq 0 ] ; then launched=1 break fi sleep 1 count=$((count + 1)); done if check_status; then log_end_msg 0 else log_end_msg 1 fi if [ $launched -eq 0 ]; then log_warning_msg "$DESC hasn't started within the timeout allowed" log_warning_msg "please review file \"$JBOSS_CONSOLE_LOG\" to see the status of the service" fi elif [ $status_start -eq 1 ]; then log_failure_msg "$DESC is not running but the pid file exists" exit 1 elif [ $status_start -eq 0 ]; then log_success_msg "$DESC (already running)" fi ;; stop) check_status status_stop=$? if [ $status_stop -eq 0 ]; then read kpid < "$JBOSS_PIDFILE" log_daemon_msg "Stopping $DESC" "$NAME" children_pids=$(pgrep -P $kpid) start-stop-daemon --stop --quiet --pidfile "$JBOSS_PIDFILE" \ --user "$JBOSS_USER" --retry=TERM/$SHUTDOWN_WAIT/KILL/5 \ >/dev/null 2>&1 if [ $? -eq 2 ]; then log_failure_msg "$DESC can't be stopped" exit 1 fi for child in $children_pids; do /bin/kill -9 $child >/dev/null 2>&1 done log_end_msg 0 elif [ $status_stop -eq 1 ]; then log_action_msg "$DESC is not running but the pid file exists, cleaning up" rm -f $JBOSS_PIDFILE elif [ $status_stop -eq 3 ]; then log_action_msg "$DESC is not running" fi ;; restart) check_status status_restart=$? if [ $status_restart -eq 0 ]; then $0 stop fi $0 start ;; reload|force-reload) check_status status_reload=$? if [ $status_reload -eq 0 ]; then log_daemon_msg "Reloading $DESC config" "$NAME" if [ "$JBOSS_MODE" = "standalone" ]; then RELOAD_CMD=":reload"; else RELOAD_CMD=":reload-servers"; fi start-stop-daemon --start --chuid "$JBOSS_USER" \ --exec "$JBOSS_CLI" -- --connect --command=$RELOAD_CMD >/dev/null 2>&1 if [ $? -eq 0 ]; then log_end_msg 0 else log_end_msg 1 fi else log_failure_msg "$DESC is not running" fi ;; status) check_status status=$? if [ $status -eq 0 ]; then read pid < $JBOSS_PIDFILE log_action_msg "$DESC is running with pid $pid" exit 0 elif [ $status -eq 1 ]; then log_action_msg "$DESC is not running and the pid file exists" exit 1 elif [ $status -eq 3 ]; then log_action_msg "$DESC is not running" exit 3 else log_action_msg "Unable to determine $NAME status" exit 4 fi ;; *) log_action_msg "Usage: $0 {start|stop|restart|reload|force-reload|status}" exit 2 ;; esac exit 0
Set the permissions and enable services
sudo chown root:root /etc/init.d/wildfly sudo chmod +X /etc/init.d/keycloak # Enable the new service update-rc.d keycloak defaults update-rc.d keycloak enable # Create logging directory sudo mkdir -p /var/log/keycloak # Create user to run keycloak as sudo useradd --system --shell /bin/false keycloak # Set permissions for the new user sudo chown -R keycloak /opt/keycloak/ sudo chown -R keycloak /var/log/keycloak/ # Finally, start the service sudo service keycloak start
Configure Apache to act as a reverse proxy
Configure your site under /etc/apache2/sites-available/yoursite.comf and ssl virtual host the following:
ProxyPreserveHost On SSLProxyEngine On SSLProxyCheckPeerCN on SSLProxyCheckPeerExpire on RequestHeader set X-Forwarded-Proto "https" RequestHeader set X-Forwarded-Port "443" ProxyPass / http://127.0.0.1:8080/ ProxyPassReverse / http://127.0.0.1:8080/
If you want to keep admin console accessible only from localhost , you can set the ProxyPass parameter like this:
ProxyPass /auth/realms/ http://127.0.0.1:8080/
The site has to be https in order keycloak to work properly.
Great post! Thanks so much for sharing. I was running into a number of issues with retrieving resources over SSL/non-SSL connections (blocked mixed-content). The main piece I was missing was the first step that tells Keycloak that it is sitting behind a reverse proxy. Made the suggested edits to my standalone.xml and all of my problems are resolved and I have a working application. Thanks again for sharing. I spent days trying to figure this out.