Embedded Radius Server in Keycloak SSO
Run radius server inside keycloak. features:
- Embedded radius server in keycloak
- use keycloak authentication and authorization for the embedded RADIUS server
- radius oidc password
- webAuthn authentication. Radius Authentication using your fingerprint or FIDO2 security key
- radius OTP password (TOTP/HOTP via Google Authenticator or FreeOTP)
- use Keycloak user credentials, if radius access-request protocol is PAP Otherwise is using Keycloak Radius credentials or OTP
- use Kerberos/ldap credentials(only if Radius client use PAP authorization)
- can work as radius proxy
- support Radsec Protocol (Radius over TLS)
- Map Keycloak authorization , Role, Group and User Attributes to Radius Attributes
- conditional attributes for authorization/Role/Group/User
- reject attribute for authorization/Role/Group/User
- dynamically assign attributes based on keycloak policies(Role, javascript, Time, User)
- start/stop Keycloak Session
- BackChannel logout(Disconnect-message request)
- Service to Service communication
- Mikrotik plugin
- Cisco plugin (thanks vbkunin)
- Chillispot plugin
- Social Hotspot Login
- PPTP VPN with Radsec
- L2TP IPSec VPN with Radius and Radsec
- Specify Realm in username: username@realm
- support docker arm64 and arm/v7
- Assign attributes dynamically using javascript policy
- Reject and Accept by condition example
- Radius and OIDC integration example
- OTP Password example
- Ldap Password with OTP example
- Default Realm example(if the radius client does not support realm attribute)
- An example of a call from backend service to radius using a service account (Service to Service communication)
- WebAuthn authentication. Radius Authentication using your fingerprint or FIDO2 security key
- Download keycloak-radius.zip asset from github releases
- unzip release
unzip keycloak-radius.zip -d keycloak-radius
- run keycloak
sh keycloak-radius/bin/standalone.sh -c standalone.xml -b 0.0.0.0 -Djboss.bind.address.management=0.0.0.0 --debug 8190 -Djboss.http.port=8090
- open http://localhost:8090
- initialize keycloak master realm
requirements: java jdk 11 and above, maven 3.5 and above
cd keycloak-plugins
mvn clean install
requirements: keycloak 18.0.1
cp ${SOURCE}/keycloak-plugins/radius-plugin/target/radius-plugin-1.4.6-SNAPSHOT.jar ${KEYCLOAK_PATH}/providers/radius-plugin-1.4.6-SNAPSHOT.jar
cp ${SOURCE}/keycloak-plugins/rad-sec-plugin/target/rad-sec-plugin-1.4.6-SNAPSHOT.jar ${KEYCLOAK_PATH}/providers/rad-sec-plugin-1.4.6-SNAPSHOT.jar
cp ${SOURCE}/keycloak-plugins/mikrotik-radius-plugin/target/mikrotik-radius-plugin-1.4.6-SNAPSHOT.jar ${KEYCLOAK_PATH}/providers/mikrotik-radius-plugin-1.4.6-SNAPSHOT.jar
cp ${SOURCE}/keycloak-plugins/cisco-radius-plugin/target/cisco-radius-plugin-1.4.6-SNAPSHOT.jar ${KEYCLOAK_PATH}/providers/cisco-radius-plugin-1.4.6-SNAPSHOT.jar
cp ${SOURCE}/keycloak-plugins/chillispot-radius-plugin/target/chillispot-radius-plugin-1.4.6-SNAPSHOT.jar ${KEYCLOAK_PATH}/providers/chillispot-radius-plugin-1.4.6-SNAPSHOT.jar
cp ${SOURCE}/keycloak-plugins/radius-disconnect-plugin/target/radius-disconnect-plugin-1.4.6-SNAPSHOT.jar ${KEYCLOAK_PATH}/providers/radius-disconnect-plugin-1.4.6-SNAPSHOT.jar
cp ${SOURCE}/keycloak-plugins/proxy-radius-plugin/target/proxy-radius-plugin-1.4.6-SNAPSHOT.jar ${KEYCLOAK_PATH}/providers/proxy-radius-plugin-1.4.6-SNAPSHOT.jar
cp ${SOURCE}/keycloak-radius-plugin/keycloak-plugins/radius-theme/target/radius-theme-1.4.6-SNAPSHOT.zip ${KEYCLOAK_PATH}/providers/radius-theme-1.4.6-SNAPSHOT.jar
where
- KEYCLOAK_PATH - Path where you are unpacked keycloak-18.0.1.zip (you can use RADIUS_CONFIG_PATH instead of KEYCLOAK_PATH)
- SOURCE - Path where you checked out the code and built the project
requirements: keycloak 18.0.1
- setup radius-plugin
${KEYCLOAK_PATH}/bin/jboss-cli.sh --command="module add --name=keycloak.plugins.radius --resources=${SOURCE}/keycloak-plugins/radius-plugin/target/radius-plugin-1.4.6-SNAPSHOT.jar --dependencies=org.jboss.logging,org.keycloak.keycloak-core,org.keycloak.keycloak-services,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private,org.apache.commons.io,javax.activation.api,javax.servlet.api,org.jboss.resteasy.resteasy-jaxrs,javax.ws.rs.api,com.fasterxml.jackson.core.jackson-databind,org.keycloak.keycloak-common,com.fasterxml.jackson.core.jackson-core,javax.transaction.api,org.hibernate,io.netty,org.slf4j,javax.xml.bind.api,org.apache.commons.codec,org.apache.commons.lang3"
- setup rad-sec plugin
${KEYCLOAK_PATH}/bin/jboss-cli.sh --command="module add --name=keycloak.plugins.rad.sec --resources=${SOURCE}/keycloak-plugins/rad-sec-plugin/target/rad-sec-plugin-1.4.6-SNAPSHOT.jar --dependencies=org.jboss.logging,org.keycloak.keycloak-core,org.keycloak.keycloak-services,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private,org.apache.commons.io,javax.activation.api,com.fasterxml.jackson.core.jackson-databind,org.keycloak.keycloak-common,com.fasterxml.jackson.core.jackson-core,javax.transaction.api,org.hibernate,io.netty,org.slf4j,javax.xml.bind.api,org.apache.commons.codec,keycloak.plugins.radius,org.apache.commons.lang3"
- setup mikrotik plugin
${KEYCLOAK_PATH}/bin/jboss-cli.sh --command="module add --name=keycloak.plugins.radius.mikrotik --resources=${SOURCE}/keycloak-plugins/mikrotik-radius-plugin/target/mikrotik-radius-plugin-1.4.6-SNAPSHOT.jar --dependencies=org.jboss.logging,org.keycloak.keycloak-core,org.keycloak.keycloak-services,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private,org.apache.commons.io,javax.activation.api,com.fasterxml.jackson.core.jackson-databind,org.keycloak.keycloak-common,com.fasterxml.jackson.core.jackson-core,javax.transaction.api,org.hibernate,io.netty,org.slf4j,javax.xml.bind.api,org.apache.commons.codec,keycloak.plugins.radius,org.apache.commons.lang3"
- setup cisco plugin
${KEYCLOAK_PATH}/bin/jboss-cli.sh --command="module add --name=keycloak.plugins.radius.cisco --resources=${SOURCE}/keycloak-plugins/cisco-radius-plugin/target/cisco-radius-plugin-1.4.6-SNAPSHOT.jar --dependencies=org.jboss.logging,org.keycloak.keycloak-core,org.keycloak.keycloak-services,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private,org.apache.commons.io,javax.activation.api,com.fasterxml.jackson.core.jackson-databind,org.keycloak.keycloak-common,com.fasterxml.jackson.core.jackson-core,javax.transaction.api,org.hibernate,io.netty,org.slf4j,javax.xml.bind.api,org.apache.commons.codec,keycloak.plugins.radius,org.apache.commons.lang3"
- setup chillispot plugin
${KEYCLOAK_PATH}/bin/jboss-cli.sh --command="module add --name=keycloak.plugins.radius.chillispot --resources=${SOURCE}/keycloak-plugins/chillispot-radius-plugin/target/chillispot-radius-plugin-1.4.6-SNAPSHOT.jar --dependencies=org.jboss.logging,org.keycloak.keycloak-core,org.keycloak.keycloak-services,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private,org.apache.commons.io,javax.activation.api,com.fasterxml.jackson.core.jackson-databind,org.keycloak.keycloak-common,com.fasterxml.jackson.core.jackson-core,javax.transaction.api,org.hibernate,io.netty,org.slf4j,javax.xml.bind.api,org.apache.commons.codec,keycloak.plugins.radius,org.apache.commons.lang3"
- setup radius-disconnect plugin
${KEYCLOAK_PATH}/bin/jboss-cli.sh --command="module add --name=keycloak.plugins.radius.dm --resources=${SOURCE}/keycloak-plugins/radius-disconnect-plugin/target/radius-disconnect-plugin-1.4.6-SNAPSHOT.jar --dependencies=org.jboss.logging,org.keycloak.keycloak-core,javax.ws.rs.api,org.keycloak.keycloak-services,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private,org.apache.commons.io,javax.activation.api,com.fasterxml.jackson.core.jackson-databind,org.keycloak.keycloak-common,com.fasterxml.jackson.core.jackson-core,javax.transaction.api,org.hibernate,io.netty,org.slf4j,javax.xml.bind.api,org.apache.commons.codec,keycloak.plugins.radius,org.keycloak.keycloak-model-jpa,javax.persistence.api,org.hibernate,org.apache.commons.lang3"
- setup proxy-radius plugin
${KEYCLOAK_PATH}/bin/jboss-cli.sh --command="module add --name=keycloak.plugins.radius.proxy --resources=${SOURCE}/keycloak-plugins/proxy-radius-plugin/target/proxy-radius-plugin-1.4.6-SNAPSHOT.jar --dependencies=org.jboss.logging,org.keycloak.keycloak-core,org.keycloak.keycloak-services,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private,org.apache.commons.io,javax.activation.api,com.fasterxml.jackson.core.jackson-databind,org.keycloak.keycloak-common,com.fasterxml.jackson.core.jackson-core,javax.transaction.api,org.hibernate,io.netty,org.slf4j,javax.xml.bind.api,org.apache.commons.codec,keycloak.plugins.radius,org.apache.commons.lang3"
- setup radius theme
${KEYCLOAK_PATH}/bin/jboss-cli.sh --command="module add --name=keycloak.plugins.radius.theme --resources=${SOURCE}/keycloak-radius-plugin/keycloak-plugins/radius-theme/target/radius-theme-1.4.6-SNAPSHOT.zip
- run script for standalone
${KEYCLOAK_PATH}/bin/jboss-cli.sh --file=${SOURCE}/cli/radius.cli
- run script for standalone-ha where
${KEYCLOAK_PATH}/bin/jboss-cli.sh --file=${SOURCE}/cli/radius-ha.cli
- KEYCLOAK_PATH - Path where you are unpacked keycloak-18.0.1.zip (you can use RADIUS_CONFIG_PATH instead of KEYCLOAK_PATH)
- SOURCE - Path where you checked out the code and built the project
Variable Name | Variable Value | Config file Location |
---|---|---|
KEYCLOAK_PATH | Path where you are unpacked keycloak | ${KEYCLOAK_PATH}/config/radius.config |
RADIUS_CONFIG_PATH | Path where you store radius.config | ${RADIUS_CONFIG_PATH}/radius.config |
Examples:
export RADIUS_CONFIG_PATH= /opt/keycloak/radius/config
or
export KEYCLOAK_PATH= /opt/keycloak/
-
create file ${KEYCLOAK_PATH}config/radius.config or ${RADIUS_CONFIG_PATH}/radius.config
-
example
where{ "sharedSecret": "radsec", "authPort": 1812, "accountPort": 1813, "numberThreads": 8, "useUdpRadius": true, "externalDictionary": "/opt/dictionary", "otp": false, "radsec": { "privateKey": "config/private.key", "certificate": "config/public.crt", "numberThreads": 8, "useRadSec": true }, "coa":{ "port":3799, "useCoA":true } }
-
sharedSecret - Used to secure communication between a RADIUS server and a RADIUS client.
-
authPort - Authentication and authorization port
-
accountPort - Accounting port
-
useUdpRadius - if true, then listen to authPort and accountPort
-
radsec - radsec configuration
-
privateKey - private SSL key (https://netty.io/wiki/sslcontextbuilder-and-private-key.html)
-
certificate - certificates chain
-
useRadSec - if true, then listen radsec port
-
numberThreads - number of connection threads
-
coa - CoA request configuration
-
port - CoA port (Mikrotik:3799, Cisco:1700)
-
useCoA - use CoA request
-
otp - use OTP without password
-
externalDictionary - path to the dictionary file in freeradius format
Run Keycloak Locally
#!/usr/bin/env bash
set -e
cd keycloak-18.0.1
sh bin/kc.sh --debug 8190 start-dev --http-port=8090
Radius Protocol | Keycloak credentials | Keycloak credentials with OTP | Kerberos credentials | Ldap credentials | Keycloak Radius credentials | Keycloak Radius credentials with OTP | Keycloak OTP(if config file contains "otp":true) |
---|---|---|---|---|---|---|---|
PAP | Yes | Yes | Yes | Yes | Yes | Yes | NO |
CHAP | No | No | No | No | Yes | Yes | Yes |
MSCHAPV2 | No | No | No | No | Yes | Yes | Yes |
NOTE: Composite roles supported
if conditional Attribute is present and has valid value then all other attributes will be applied. (Example: apply role attributes only if NAS-IP-Address= 192.168.88.1)
Structure of Attribute:
<PREFIX><ATTRIBUTE_NAME>=<values>
- PREFIX =
COND_
- ATTRIBUTE_NAME attribute name from access-request
- VALUES Comma-separated list of attribute values
Example:
COND_NAS-IP-Address = "192.168.88.1, 192.168.88.2"
The role will only be applied if the NAS server address is 192.168.88.1 or 192.168.88.2.
Role REJECT Attributes (Example)
if reject Attribute is present and has valid value then access request will be rejected. (Example: reject user request if access request contains attribute NAS-IP-Address= 192.168.88.1)
Structure of Attribute:
<PREFIX><ATTRIBUTE_NAME>=<values>
- PREFIX =
REJECT_
- ATTRIBUTE_NAME attribute name from access-request
- VALUES Comma-separated list of attribute values
Example:
REJECT_NAS-IP-Address = "192.168.88.2"
The role will only be applied if the NAS server address is not 192.168.88.2, otherwise request will be rejected
If Reject Attribute is present then access request will be rejected.
Structure of Attribute: REJECT_RADIUS=<ANY VALUE>
Example:
REJECT_RADIUS = "true"
Role ACCEPT Attributes (Example)
if accept Attribute is present and has valid value then access request will be accepted, otherwise rejected. (Example: accept user request if access request contains attribute NAS-IP-Address= 192.168.88.1,192.168.88.2)
Structure of Attribute:
<PREFIX><ATTRIBUTE_NAME>=<values>
- PREFIX =
ACCEPT_
- ATTRIBUTE_NAME attribute name from access-request
- VALUES Comma-separated list of attribute values
Example:
ACCEPT_NAS-IP-Address = "192.168.88.1"
The role will only be applied if the NAS server address is not 192.168.88.2, otherwise request will be rejected
if conditional Attribute is present and has valid value then all other attributes will be applied. (Example: apply group attributes only if NAS-IP-Address= 192.168.88.1)
Structure of Attribute:
<PREFIX><ATTRIBUTE_NAME>=<values>
- PREFIX =
COND_
- ATTRIBUTE_NAME attribute name from access-request
- VALUES Comma-separated list of attribute values
Example: Role Conditional Attributes/README.md:1
if reject Attribute is present and has valid value then access request will be rejected. (Example: reject user request if access request contains attribute NAS-IP-Address= 192.168.88.1)
Structure of Attribute:
<PREFIX><ATTRIBUTE_NAME>=<values>
- PREFIX =
REJECT_
- ATTRIBUTE_NAME attribute name from access-request
- VALUES Comma-separated list of attribute values
Example: Role REJECT Attributes
If Reject Attribute is present then access request will be rejected.
Structure of Attribute: REJECT_RADIUS=<ANY VALUE>
Example:
REJECT_RADIUS = "true"
if accept Attribute is present and has valid value then access request will be accepted, otherwise rejected. (Example: accept user request if access request contains attribute NAS-IP-Address= 192.168.88.1,192.168.88.2)
Structure of Attribute:
<PREFIX><ATTRIBUTE_NAME>=<values>
- PREFIX =
ACCEPT_
- ATTRIBUTE_NAME attribute name from access-request
- VALUES Comma-separated list of attribute values
Example: Role ACCEPT Attributes
if conditional Attribute is present and has valid value then all other attributes will be applied. (Example: apply user attributes only if NAS-IP-Address= 192.168.88.1)
Structure of Attribute:
<PREFIX><ATTRIBUTE_NAME>=<values>
- PREFIX =
COND_
- ATTRIBUTE_NAME attribute name from access-request
- VALUES Comma-separated list of attribute values
Example: Role Conditional Attributes/README.md:1
if reject Attribute is present and has valid value then access request will be rejected. (Example: reject user request if access request contains attribute NAS-IP-Address= 192.168.88.1)
Structure of Attribute:
<PREFIX><ATTRIBUTE_NAME>=<values>
- PREFIX =
REJECT_
- ATTRIBUTE_NAME attribute name from access-request
- VALUES Comma-separated list of attribute values
Example: Role REJECT Attributes
if accept Attribute is present and has valid value then access request will be accepted, otherwise rejected. (Example: accept user request if access request contains attribute NAS-IP-Address= 192.168.88.1,192.168.88.2)
Structure of Attribute:
<PREFIX><ATTRIBUTE_NAME>=<values>
- PREFIX =
ACCEPT_
- ATTRIBUTE_NAME attribute name from access-request
- VALUES Comma-separated list of attribute values
Example: Role ACCEPT Attributes
if conditional Attribute is present and has valid value then all other attributes will be applied. (Example: apply user attributes only if NAS-IP-Address= 192.168.88.1)
Structure of Attribute:
<PREFIX><ATTRIBUTE_NAME>=<values>
- PREFIX =
COND_
- ATTRIBUTE_NAME attribute name from access-request
- VALUES Comma-separated list of attribute values
Example: Role Conditional Attributes/README.md:1
if reject Attribute is present and has valid value then access request will be rejected. (Example: reject user request if access request contains attribute NAS-IP-Address= 192.168.88.1)
Structure of Attribute:
<PREFIX><ATTRIBUTE_NAME>=<values>
- PREFIX =
REJECT_
- ATTRIBUTE_NAME attribute name from access-request
- VALUES Comma-separated list of attribute values
Example: Role REJECT Attributes
If Reject Attribute is present then access request will be rejected.
Structure of Attribute: REJECT_RADIUS=<ANY VALUE>
Example:
REJECT_RADIUS = "true"
if accept Attribute is present and has valid value then access request will be accepted, otherwise rejected. (Example: accept user request if access request contains attribute NAS-IP-Address= 192.168.88.1,192.168.88.2)
Structure of Attribute:
<PREFIX><ATTRIBUTE_NAME>=<values>
- PREFIX =
ACCEPT_
- ATTRIBUTE_NAME attribute name from access-request
- VALUES Comma-separated list of attribute values
Example: Role ACCEPT Attributes
Hotspot Example (with Facebook login)
- Setup Radius Credentials during first time login
- enable Otp Password on Keycloak side. https://www.keycloak.org/docs/latest/server_admin/
- password in request must contain the password and otp.
- Structure Password in request:
- PAP password:
<Keycloak Password/RADIUS Password><OTP>
example: testPassword123456, where testPassword is password, 123456 is otp - MSCHAP/CHAP:
<RADIUS Password><OTP>
example: testPassword123456, where testPassword is password, 123456 is otp - PAP password with Otp (if config file contains "otp":true) :
<OTP>
example: 123456, where 123456 is otp
- PAP password:
- create dictionary Fortinet.dictionary:
VENDOR 12356 Fortinet
VENDORATTR 12356 Fortinet-Group-Name 1 string
VENDORATTR 12356 Fortinet-Client-IP-Address 2 ipaddr
VENDORATTR 12356 Fortinet-Vdom-Name 3 string
VENDORATTR 12356 Fortinet-Client-IPv6-Address 4 octets
VENDORATTR 12356 Fortinet-Interface-Name 5 string
VENDORATTR 12356 Fortinet-Access-Profile 6 string
- run as docker container
docker run -p 8090:8080 -e -server -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true" -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -e RADIUS_DICTIONARY=/opt/dictionary -v `pwd`/Fortinet.dictionary:/opt/dictionary vassio/keycloak-radius-plugin