22 Commits
Author SHA1 Message Date
gitea_admin 1a1e37ffdf feat(freeradius): add dynamic tenant VLAN assignment via unlang policy 2026-05-12 15:50:18 +02:00
gitea_admin 328ff7511c feat: enforce Framed-IP-Address presence in access decisions
Add hard validation to reject requests without assigned IP address,
including clear rejection message for audit trail.
2026-04-22 19:15:04 +02:00
gitea_admin 064bda9e20 feat(freeradius): log Reply-Message in radpostauth instead of only packet type
- Store Reply-Message when available, fallback to Packet-Type otherwise
- Improves visibility of authentication failures (e.g. "already logged in")
2026-04-21 12:51:12 +02:00
gitea_admin 69a4e3a3b8 feat(freeradius): implement node pinning and dynamic IP allocation
- add node pinning logic in authorize (reject if wrong node)
- optimize SQL queries using control variables
- assign Pool-Name dynamically for engineers
- integrate sqlippool for dynamic IP allocation (engineers only)
- add Session-Timeout aligned with lease_duration
- fix duplicate sqlippool execution in post-auth/accounting
- configure sqlippool with NAS-IP scoping for multi-node isolation

Ensures correct routing, tenant isolation, and scalable IP management.
2026-04-20 16:15:55 +02:00
gitea_admin fca66dfa52 Add RADIUS node pinning to enforce client connection to assigned OpenVPN node 2026-04-14 16:24:35 +02:00
gitea_admin 8c1289f92e Disable strict realm validation to allow username format user@tenant (non-DNS realm). 2026-04-13 18:50:15 +02:00
gitea_admin c245ab29af feat(freeradius): enable sqlippool for centralized IP allocation
- Add sqlippool module configuration
- Integrate sqlippool into authorize, post-auth, and accounting sections
- Add radippool table schema (InnoDB)
- Update Helm ConfigMap and Deployment templates
- Enable centralized IP management for OpenVPN cluster
- Bump Helm chart version to 1.0.12
2026-03-03 11:31:41 +01:00
gitea_admin 629a51a905 security(freeradius): enforce require_message_authenticator globally
- Set require_message_authenticator = yes in security block
- Set limit_proxy_state = yes
- Mitigate BLASTRADIUS vulnerability
- Harden RADIUS request validation
2026-02-27 21:59:10 +01:00
gitea_admin fc35d35b00 fix(radius/openvpn): activate SQL authorize pipeline to enforce static IP allocation
- Enable `sql` in sites-enabled/default authorize section
- Allow processing of radreply attributes (e.g. Framed-IP-Address)
- Fix static IP assignment from RADIUS for OpenVPN clients
- Bump Helm chart version to 1.0.11
2026-02-26 16:34:51 +01:00
gitea_admin 35e0f2f419 feat(freeradius): add radius.conf and queries.conf ConfigMaps and bump chart version
- Move FreeRADIUS SQL and radius.conf into ConfigMaps
- Add /etc/freeradius/mods-config/sql/main/mysql/queries.conf and /etc/freeradius/radius.conf templates for Helm deployment
- Update SQL post-auth query formatting
- Enable suppress_secrets in radius.conf
- Bump Helm chart version to 1.0.10
- Adjust GitLab CI and Deployment to use new config structure
2026-02-03 15:33:08 +01:00
gitea_admin 7d90707deb update chart files and remove old tgz packages , update version 1.0.9 2025-11-25 14:17:58 +01:00
gitea_admin e50df4ba36 add manual job to push Helm chart to Gitea OCI registry 2025-10-28 11:13:04 +01:00
gitea_admin a08dcf6983 feat(radius): enable dynamic client support via SQL nas table
- Activated `dynamic_clients` module to allow loading RADIUS clients dynamically from the database
- Configured SQL query to fetch client secret, shortname, and type based on Packet-Src-IP-Address
- Integrated `dynamic_clients` into the `authorize` section for real-time NAS resolution
- Eliminated need to restart FreeRADIUS when adding new NAS entries
2025-07-01 12:13:43 +02:00
gitea_admin aad17e7b3f enable SQL accounting and disable detail logging in FreeRADIUS 2025-07-01 10:35:07 +02:00
gitea_admin 4885bc1eb4 edit mods-availabe/eap form default_eap_type= md5 to default_eap_type=peap 2025-06-30 13:19:44 +02:00
gitea_admin 3ccf50ed57 feat: add EAP module support with dynamic CA file path and config injection
- Added `files/mods-available/eap` with environment-based `ca_file` reference
- Updated `mods-enabled.yaml` to include EAP module via Helm `.Files.Glob`
- Modified `Deployment.yaml` to mount EAP config into /mods-enabled/eap
2025-06-30 12:35:39 +02:00
gitea_admin f025437c6a new version 1.0.4 2025-06-25 11:43:25 +02:00
gitea_admin bb0a10b2bf Disable Client Certificate Requirement 2025-06-24 16:43:25 +02:00
gitea_admin a7e7202761 comment out clients = radsec on freeradius/files/sites-available/tls 2025-06-24 15:49:19 +02:00
gitea_admin 763d743c16 add .helmignore 2025-06-24 12:53:59 +02:00
gitea_admin 642f747b48 comment out the “dh_file” configuration element from files/sites-available/tls 2025-06-24 12:04:48 +02:00
gitea_admin 98c2fa6240 Initial commit
- update else condition on the line 239 in templates/Deployment
- update  st-common version from 0.1.10 to 0.1.12 on Chart.yaml file
- add gitlab ci/cd pipeline to  package the Helm chart into a .tgz. and Publish it to GitLab’s Helm package registry
2025-06-24 11:20:34 +02:00
28 changed files with 3698 additions and 143 deletions
+1
View File
@@ -0,0 +1 @@
charts/
+24 -2
View File
@@ -8,9 +8,13 @@ stages:
variables: variables:
CHART_NAME: "freeradius" CHART_NAME: "freeradius"
CHART_VERSION: "1.0.3" CHART_VERSION: "1.0.12"
PACKAGE_PATH: "packages" PACKAGE_PATH: "packages"
ST_COMMON_PROJECT_ID: "270"
COMMON_PROJECT_URL: "${CI_API_V4_URL}/projects/${ST_COMMON_PROJECT_ID}/packages/helm/stable"
HELM_EXPERIMENTAL_OCI: "1" HELM_EXPERIMENTAL_OCI: "1"
GITEA_URL: "gitea.infrastructure.helmholz.cloud"
GITEA_REPO: "oci://gitea.infrastructure.helmholz.cloud/gitea_admin/helm"
package_chart: package_chart:
stage: package stage: package
@@ -18,7 +22,7 @@ package_chart:
name: alpine/helm:3.14.0 name: alpine/helm:3.14.0
entrypoint: [""] entrypoint: [""]
script: script:
- helm repo add startechnica https://startechnica.github.io/apps - helm repo add st-common ${COMMON_PROJECT_URL} --username gitlab-ci-token --password $CI_JOB_TOKEN
- helm dependency build . - helm dependency build .
- mkdir -p $PACKAGE_PATH - mkdir -p $PACKAGE_PATH
- helm package . --destination $PACKAGE_PATH - helm package . --destination $PACKAGE_PATH
@@ -38,3 +42,21 @@ publish_chart:
"${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/helm/api/stable/charts" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/helm/api/stable/charts"
only: only:
- main - main
push_chart_to_gitea:
stage: publish
image:
name: alpine/helm:3.14.0
entrypoint: [""]
variables:
GITEA_URL: "https://gitea.infrastructure.helmholz.cloud"
GITEA_USERNAME: "gitea_admin"
GITEA_PASSWORD: "$GITEA_PASSWORD"
script:
- echo "$GITEA_PASSWORD" | helm registry login $GITEA_URL --username $GITEA_USERNAME --password-stdin
- helm push ${PACKAGE_PATH}/${CHART_NAME}-${CHART_VERSION}.tgz $GITEA_REPO
when: manual
only:
- main
dependencies:
- package_chart
+1
View File
@@ -0,0 +1 @@
.git/
+3 -6
View File
@@ -1,9 +1,6 @@
dependencies: dependencies:
- name: st-common - name: st-common
repository: https://startechnica.github.io/apps repository: https://gitlab/api/v4/projects/270/packages/helm/stable
version: 0.1.12 version: 0.1.12
- name: mariadb digest: sha256:effc27041fec14dd47ce67b96daae936455482afe0a2d3d6069ebffd7e33ba2c
repository: oci://registry-1.docker.io/bitnamicharts generated: "2026-02-03T12:45:51.347388527+01:00"
version: 20.5.9
digest: sha256:03a311ace58596d28267595eab6c85c5d39af13a5864dbe04be8931a2262b6c7
generated: "2025-06-16T16:20:04.252816273+02:00"
+2 -6
View File
@@ -4,12 +4,8 @@ apiVersion: v2
appVersion: 3.2.7 appVersion: 3.2.7
dependencies: dependencies:
- name: st-common - name: st-common
repository: https://startechnica.github.io/apps repository: https://gitlab/api/v4/projects/270/packages/helm/stable
version: 0.1.12 version: 0.1.12
- condition: mariadb.enabled
name: mariadb
repository: oci://registry-1.docker.io/bitnamicharts
version: 20.x.x
description: FreeRADIUS is a modular, high performance free RADIUS suite developed description: FreeRADIUS is a modular, high performance free RADIUS suite developed
and distributed under the GNU General Public License, version 2, and is free for and distributed under the GNU General Public License, version 2, and is free for
download and use. download and use.
@@ -31,4 +27,4 @@ sources:
- https://freeradius.org/ - https://freeradius.org/
- https://github.com/FreeRADIUS/freeradius-server - https://github.com/FreeRADIUS/freeradius-server
type: application type: application
version: 1.0.3 version: 1.0.12
+1 -1
View File
@@ -211,7 +211,7 @@ server radsec {
# Require a client certificate. # Require a client certificate.
# #
require_client_cert = yes require_client_cert = no
# #
# As of version 2.1.10, client certificates can be validated via an external command. This allows dynamic CRLs or OCSP to be used. # As of version 2.1.10, client certificates can be validated via an external command. This allows dynamic CRLs or OCSP to be used.
Binary file not shown.
Binary file not shown.
+41
View File
@@ -0,0 +1,41 @@
# -*- text -*-
#
# $Id: cc2bd5fd22aa473b98af5dde3fac7a66e39a9e9d $
# This module loads RADIUS clients as needed, rather than when the server
# starts.
#
# There are no configuration entries for this module. Instead, it
# relies on the "client" configuration. You must:
#
# 1) link raddb/sites-enabled/dynamic_clients to
# raddb/sites-available/dynamic_clients
#
# 2) Define a client network/mask (see top of the above file)
#
# 3) uncomment the "directory" entry in that client definition
#
# 4) list "dynamic_clients" in the "authorize" section of the
# "dynamic_clients' virtual server. The default example already
# does this.
#
# 5) put files into the above directory, one per IP.
# e.g. file "192.0.2.1" should contain a normal client definition
# for a client with IP address 192.0.2.1.
#
# For more documentation, see the file:
#
# raddb/sites-available/dynamic-clients
#
dynamic_clients {
sql = "SELECT secret, shortname, type FROM nas WHERE nasname = '%{Packet-Src-IP-Address}'"
key = "%{Packet-Src-IP-Address}"
client {
ipaddr = "%{Packet-Src-IP-Address}"
secret = "%{reply:secret}"
shortname = "%{reply:shortname}"
nastype = "%{reply:type}"
}
}
File diff suppressed because it is too large Load Diff
+110
View File
@@ -0,0 +1,110 @@
# Configuration for the SQL based IP Pool module (rlm_sqlippool)
#
# The database schemas are available at:
#
# raddb/mods-config/sql/ippool/<DB>/schema.sql
#
# $Id: f17a9898e906d3db0ad5871d8683f731f7a6baab $
sqlippool {
# SQL instance to use (from mods-available/sql)
#
# If you have multiple sql instances, such as "sql sql1 {...}",
# use the *instance* name here: sql1.
sql_module_instance = "sql"
# This is duplicative of info available in the SQL module, but
# we have to list it here as we do not yet support nested
# reference expansions.
dialect = $ENV{FREERADIUS_MODS_SQL_DIALECT}
# Name of the check item attribute to be used as a key in the SQL queries
pool_name = "Pool-Name"
# SQL table to use for ippool range and lease info
ippool_table = $ENV{FREERADIUS_MODS_SQL_TABLE_RADIPPOOL}
# IP lease duration. (Leases expire even if Acct Stop packet is lost)
#
# Note that you SHOULD also set Session-Timeout to this value!
# That way the NAS will automatically kick the user offline when the
# lease expires.
#
lease_duration = 18000
#
# Timeout between each consecutive 'allocate_clear' queries (default: 1s)
# This will avoid having too many deadlock issues, especially on MySQL backend.
#
allocate_clear_timeout = 1
#
# The attribute to use for IP address assignment. The
# default is Framed-IP-Address. You can change this to any
# attribute which is IPv4 or IPv6.
#
# e.g. Framed-IPv6-Prefix, or Delegated-IPv6-Prefix.
#
# All of the default queries use this attribute_name. So you
# can do IPv6 address assignment simply by putting IPv6
# addresses into the pool, and changing the following line to
# "Framed-IPv6-Prefix"
#
# Note that you MUST use separate pools for each attribute. i.e. one pool
# for Framed-IP-Address, a different one for Framed-IPv6-prefix, etc.
#
# This means configuring separate "sqlippool" instances, and different
# "ippool_table" in SQL. Then, populate the pool with addresses and
# it will all just work.
#
attribute_name = Framed-IP-Address
#
# Assign the IP address, even if the above attribute already exists
# in the reply.
#
# allow_duplicates = no
# The attribute in which an IP address hint may be supplied
req_attribute_name = Framed-IP-Address
# Attribute which should be considered unique per NAS
#
# Using NAS-Port gives behaviour similar to rlm_ippool. (And ACS)
# Using Calling-Station-Id works for NAS that send fixed NAS-Port
# ONLY change this if you know what you are doing!
# pool_key = "%{NAS-Port}"
# pool_key = "%{Calling-Station-Id}"
pool_key = "%{User-Name}"
nas_ip_address = "%{NAS-IP-Address}"
################################################################
#
# WARNING: MySQL (MyISAM) has certain limitations that means it can
# hand out the same IP address to 2 different users.
#
# We suggest using an SQL DB with proper transaction
# support, such as PostgreSQL, or using MySQL
# with InnoDB.
#
################################################################
# These messages are added to the "control" items, as
# Module-Success-Message. They are not logged anywhere else,
# unlike previous versions. If you want to have them logged
# to a file, see the "linelog" module, and create an entry
# which writes Module-Success-Message message.
#
messages {
exists = "Existing IP: %{reply:${..attribute_name}} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
success = "Allocated IP: %{reply:${..attribute_name}} from %{control:${..pool_name}} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
clear = "Released IP %{request:${..attribute_name}} (did %{Called-Station-Id} cli %{Calling-Station-Id} user %{User-Name})"
failed = "IP Allocation FAILED from %{control:${..pool_name}} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
nopool = "No ${..pool_name} defined (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
}
$INCLUDE ${modconfdir}/sql/ippool/${dialect}/queries.conf
}
+694
View File
@@ -0,0 +1,694 @@
# -*- text -*-
#
# main/mysql/queries.conf-- MySQL configuration for default schema (schema.sql)
#
# $Id: b31ae9c3a1bf41f030a2112d31bf3a678e76f23c $
# Use the driver specific SQL escape method.
#
# If you enable this configuration item, the "safe_characters"
# configuration is ignored. FreeRADIUS then uses the MySQL escape
# functions to escape input strings. The only downside to making this
# change is that the MySQL escaping method is not the same the one
# used by FreeRADIUS. So characters which are NOT in the
# "safe_characters" list will now be stored differently in the database.
#
#auto_escape = yes
# Safe characters list for sql queries. Everything else is replaced
# with their mime-encoded equivalents.
# The default list should be ok
# Using 'auto_escape' is preferred
safe_characters = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"
#######################################################################
# Connection config
#######################################################################
# The character set is not configurable. The default character set of
# the mysql client library is used. To control the character set,
# create/edit my.cnf (typically in /etc/mysql/my.cnf or /etc/my.cnf)
# and enter
# [freeradius]
# default-character-set = utf8
#
#######################################################################
# Query config: Username
#######################################################################
# This is the username that will get substituted, escaped, and added
# as attribute 'SQL-User-Name'. '%{SQL-User-Name}' should be used below
# everywhere a username substitution is needed so you you can be sure
# the username passed from the client is escaped properly.
#
# Uncomment the next line, if you want the sql_user_name to mean:
#
# Use Stripped-User-Name, if it's there.
# Else use User-Name, if it's there,
# Else use hard-coded string "DEFAULT" as the user name.
#sql_user_name = "%{%{Stripped-User-Name}:-%{%{User-Name}:-DEFAULT}}"
#
sql_user_name = "%{User-Name}"
#######################################################################
# Query config: Event-Timestamp
#######################################################################
# event_timestamp_epoch is the basis for the time inserted into
# accounting records. Typically this will be the Event-Timestamp of the
# accounting request, which is usually provided by a NAS.
#
# Uncomment the next line, if you want the timestamp to be based on the
# request reception time recorded by this server, for example if you
# distrust the provided Event-Timestamp.
#event_timestamp_epoch = "%l"
event_timestamp_epoch = "%{%{integer:Event-Timestamp}:-%l}"
# event_timestamp is the SQL snippet for converting an epoch timestamp
# to an SQL date.
event_timestamp = "FROM_UNIXTIME(${event_timestamp_epoch})"
#######################################################################
# Query config: Class attribute
#######################################################################
#
# 3.0.22 and later have a "class" column in the accounting table.
#
# However, we do NOT want to break existing configurations by adding
# the Class attribute to the default queries. If we did that, then
# systems using newer versions of the server would fail, because
# there is no "class" column in their accounting tables.
#
# The solution to that is the following "class" subsection. If your
# database has a "class" column for the various tables, then you can
# uncomment the configuration items here. The queries below will
# then automatically insert the Class attribute into radacct,
# radpostauth, etc.
#
class {
#
# Delete the '#' character from each of the configuration
# items in this section. This change puts the Class
# attribute into the various tables. Leave the double-quoted
# string there, as the value for the configuration item.
#
# See also policy.d/accounting, and the "insert_acct_class"
# policy. You will need to list (or uncomment)
# "insert_acct_class" in the "post-auth" section in order to
# create a Class attribute.
#
column_name = # ", class"
packet_xlat = # ", '%{Class}'"
reply_xlat = # ", '%{reply:Class}'"
}
#######################################################################
# Default profile
#######################################################################
# This is the default profile. It is found in SQL by group membership.
# That means that this profile must be a member of at least one group
# which will contain the corresponding check and reply items.
# This profile will be queried in the authorize section for every user.
# The point is to assign all users a default profile without having to
# manually add each one to a group that will contain the profile.
# The SQL module will also honor the User-Profile attribute. This
# attribute can be set anywhere in the authorize section (ie the users
# file). It is found exactly as the default profile is found.
# If it is set then it will *overwrite* the default profile setting.
# The idea is to select profiles based on checks on the incoming packets,
# not on user group membership. For example:
# -- users file --
# DEFAULT Service-Type == Outbound-User, User-Profile := "outbound"
# DEFAULT Service-Type == Framed-User, User-Profile := "framed"
#
# By default the default_user_profile is not set
#
#default_user_profile = "DEFAULT"
#######################################################################
# NAS Query
#######################################################################
# This query retrieves the radius clients
#
# 0. Row ID (currently unused)
# 1. Name (or IP address)
# 2. Shortname
# 3. Type
# 4. Secret
# 5. Server
#######################################################################
client_query = "\
SELECT id, nasname, shortname, type, secret, server \
FROM ${client_table}"
#######################################################################
# Authorization Queries
#######################################################################
# These queries compare the check items for the user
# in ${authcheck_table} and setup the reply items in
# ${authreply_table}. You can use any query/tables
# you want, but the return data for each row MUST
# be in the following order:
#
# 0. Row ID (currently unused)
# 1. UserName/GroupName
# 2. Item Attr Name
# 3. Item Attr Value
# 4. Item Attr Operation
#######################################################################
# Use these for case sensitive usernames.
#authorize_check_query = "\
# SELECT id, username, attribute, value, op \
# FROM ${authcheck_table} \
# WHERE username = BINARY '%{SQL-User-Name}' \
# ORDER BY id"
#authorize_reply_query = "\
# SELECT id, username, attribute, value, op \
# FROM ${authreply_table} \
# WHERE username = BINARY '%{SQL-User-Name}' \
# ORDER BY id"
#
# The default queries are case insensitive. (for compatibility with
# older versions of FreeRADIUS)
#
authorize_check_query = "\
SELECT id, username, attribute, value, op \
FROM ${authcheck_table} \
WHERE username = '%{SQL-User-Name}' \
ORDER BY id"
authorize_reply_query = "\
SELECT id, username, attribute, value, op \
FROM ${authreply_table} \
WHERE username = '%{SQL-User-Name}' \
ORDER BY id"
#
# Use these for case sensitive usernames.
#
#group_membership_query = "\
# SELECT groupname \
# FROM ${usergroup_table} \
# WHERE username = BINARY '%{SQL-User-Name}' \
# ORDER BY priority"
group_membership_query = "\
SELECT groupname \
FROM ${usergroup_table} \
WHERE username = '%{SQL-User-Name}' \
ORDER BY priority"
authorize_group_check_query = "\
SELECT id, groupname, attribute, \
Value, op \
FROM ${groupcheck_table} \
WHERE groupname = '%{${group_attribute}}' \
ORDER BY id"
authorize_group_reply_query = "\
SELECT id, groupname, attribute, \
value, op \
FROM ${groupreply_table} \
WHERE groupname = '%{${group_attribute}}' \
ORDER BY id"
#######################################################################
# Simultaneous Use Checking Queries
#######################################################################
# simul_count_query - query for the number of current connections
# - If this is not defined, no simultaneous use checking
# - will be performed by this module instance
# simul_verify_query - query to return details of current connections
# for verification
# - Leave blank or commented out to disable verification step
# - Note that the returned field order should not be changed.
#
# Note: Sessions that started prior to the most recent reload of their NAS will
# be correctly considered inactive, even if the radacct entry itself is not
# marked as stopped.
#
#######################################################################
simul_count_query = "\
SELECT COUNT(*) \
FROM ${acct_table1} a \
LEFT OUTER JOIN nasreload n USING (nasipaddress) \
WHERE username = '%{SQL-User-Name}' \
AND acctstoptime IS NULL \
AND (a.acctstarttime > n.reloadtime OR n.reloadtime IS NULL)"
simul_verify_query = "\
SELECT \
radacctid, acctsessionid, username, nasipaddress, nasportid, framedipaddress, \
callingstationid, framedprotocol \
FROM ${acct_table1} a \
LEFT OUTER JOIN nasreload n USING (nasipaddress) \
WHERE username = '%{SQL-User-Name}' \
AND acctstoptime IS NULL \
AND (a.acctstarttime > n.reloadtime OR n.reloadtime IS NULL)"
#######################################################################
# Accounting and Post-Auth Queries
#######################################################################
# These queries insert/update accounting and authentication records.
# The query to use is determined by the value of 'reference'.
# This value is used as a configuration path and should resolve to one
# or more 'query's. If reference points to multiple queries, and a query
# fails, the next query is executed.
#
# Behaviour is identical to the old 1.x/2.x module, except we can now
# fail between N queries, and query selection can be based on any
# combination of attributes, or custom 'Acct-Status-Type' values.
#######################################################################
accounting {
reference = "%{tolower:type.%{%{Acct-Status-Type}:-%{Request-Processing-Stage}}.query}"
# Write SQL queries to a logfile. This is potentially useful for bulk inserts
# when used with the rlm_sql_null driver.
# logfile = ${logdir}/accounting.sql
column_list = "\
acctsessionid, acctuniqueid, username, \
realm, nasipaddress, nasportid, \
nasporttype, acctstarttime, acctupdatetime, \
acctstoptime, acctsessiontime, acctauthentic, \
connectinfo_start, connectinfo_stop, acctinputoctets, \
acctoutputoctets, calledstationid, callingstationid, \
acctterminatecause, servicetype, framedprotocol, \
framedipaddress, framedipv6address, framedipv6prefix, \
framedinterfaceid, delegatedipv6prefix ${..class.column_name}"
type {
accounting-on {
#
# "Bulk update" Accounting-On/Off strategy.
#
# Immediately terminate all sessions associated with a
# given NAS.
#
# Note: If a large number of sessions require closing
# then the bulk update may be take a long time to run
# and lock an excessive number of rows. See the
# strategy below for an alternative approach that does
# not touch the radacct session data.
#
query = "\
UPDATE ${....acct_table1} \
SET \
acctstoptime = ${....event_timestamp}, \
acctsessiontime = '${....event_timestamp_epoch}' \
- UNIX_TIMESTAMP(acctstarttime), \
acctterminatecause = '%{%{Acct-Terminate-Cause}:-NAS-Reboot}' \
WHERE acctstoptime IS NULL \
AND nasipaddress = '%{NAS-IP-Address}' \
AND acctstarttime <= ${....event_timestamp}"
#
# "Lightweight" Accounting-On/Off strategy.
#
# Record the reload time of the NAS and let the
# administrator actually close the sessions in radacct
# out-of-band, if desired.
#
# Implementation advice, together with a stored
# procedure for closing sessions and a view showing
# the effective stop time of each session is provided
# in process-radacct.sql.
#
# To enable this strategy, just change the previous
# query to "-query", and this one to "query". The
# previous one will be ignored, and this one will be
# enabled.
#
-query = "\
INSERT INTO nasreload \
SET \
nasipaddress = '%{NAS-IP-Address}', \
reloadtime = ${....event_timestamp} \
ON DUPLICATE KEY UPDATE reloadtime = ${....event_timestamp}"
}
accounting-off {
query = "${..accounting-on.query}"
}
#
# Implement the "sql_session_start" policy.
# See raddb/policy.d/accounting for more details.
#
# You also need to fix the other queries as
# documented below. Look for "sql_session_start".
#
post-auth {
query = "\
INSERT INTO ${....acct_table1} \
(${...column_list}) \
VALUES(\
'%{Acct-Session-Id}', \
'%{Acct-Unique-Session-Id}', \
'%{SQL-User-Name}', \
'%{Realm}', \
'%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}', \
NULLIF('%{%{NAS-Port-ID}:-%{NAS-Port}}', ''), \
'%{NAS-Port-Type}', \
${....event_timestamp}, \
${....event_timestamp}, \
NULL, \
0, \
'', \
'%{Connect-Info}', \
NULL, \
0, \
0, \
'%{Called-Station-Id}', \
'%{Calling-Station-Id}', \
'', \
'%{Service-Type}', \
NULL, \
'', \
'', \
'', \
'', \
'' \
${....class.packet_xlat})"
query = "\
UPDATE ${....acct_table1} SET \
AcctStartTime = ${....event_timestamp}, \
AcctUpdateTime = ${....event_timestamp}, \
ConnectInfo_start = '%{Connect-Info}', \
AcctSessionId = '%{Acct-Session-Id}' \
WHERE UserName = '%{SQL-User-Name}' \
AND NASIPAddress = '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}' \
AND NASPortId = '%{%{NAS-Port-ID}:-%{NAS-Port}}' \
AND NASPortType = '%{NAS-Port-Type}' \
AND AcctStopTime IS NULL"
}
start {
#
# Insert a new record into the sessions table
#
query = "\
INSERT INTO ${....acct_table1} \
(${...column_list}) \
VALUES \
('%{Acct-Session-Id}', \
'%{Acct-Unique-Session-Id}', \
'%{SQL-User-Name}', \
'%{Realm}', \
'%{NAS-IP-Address}', \
'%{%{NAS-Port-ID}:-%{NAS-Port}}', \
'%{NAS-Port-Type}', \
${....event_timestamp}, \
${....event_timestamp}, \
NULL, \
'0', \
'%{Acct-Authentic}', \
'%{Connect-Info}', \
'', \
'0', \
'0', \
'%{Called-Station-Id}', \
'%{Calling-Station-Id}', \
'', \
'%{Service-Type}', \
'%{Framed-Protocol}', \
'%{Framed-IP-Address}', \
'%{Framed-IPv6-Address}', \
'%{Framed-IPv6-Prefix}', \
'%{Framed-Interface-Id}', \
'%{Delegated-IPv6-Prefix}' \
${....class.packet_xlat})"
#
# When using "sql_session_start", you should comment out
# the previous query, and enable this one.
#
# Just change the previous query to "-query",
# and this one to "query". The previous one
# will be ignored, and this one will be
# enabled.
#
-query = "\
UPDATE ${....acct_table1} \
SET \
AcctSessionId = '%{Acct-Session-Id}', \
AcctUniqueId = '%{Acct-Unique-Session-Id}', \
AcctAuthentic = '%{Acct-Authentic}', \
ConnectInfo_start = '%{Connect-Info}', \
ServiceType = '%{Service-Type}', \
FramedProtocol = '%{Framed-Protocol}', \
framedipaddress = '%{Framed-IP-Address}', \
framedipv6address = '%{Framed-IPv6-Address}', \
framedipv6prefix = '%{Framed-IPv6-Prefix}', \
framedinterfaceid = '%{Framed-Interface-Id}', \
delegatedipv6prefix = '%{Delegated-IPv6-Prefix}', \
AcctStartTime = ${....event_timestamp}, \
AcctUpdateTime = ${....event_timestamp} \
WHERE UserName = '%{SQL-User-Name}' \
AND NASIPAddress = '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}' \
AND NASPortId = '%{%{NAS-Port-ID}:-%{NAS-Port}}' \
AND NASPortType = '%{NAS-Port-Type}' \
AND AcctStopTime IS NULL"
#
# Key constraints prevented us from inserting a new session,
# use the alternate query to update an existing session.
#
query = "\
UPDATE ${....acct_table1} SET \
acctstarttime = ${....event_timestamp}, \
acctupdatetime = ${....event_timestamp}, \
connectinfo_start = '%{Connect-Info}' \
WHERE AcctUniqueId = '%{Acct-Unique-Session-Id}'"
}
interim-update {
#
# Update an existing session and calculate the interval
# between the last data we received for the session and this
# update. This can be used to find stale sessions.
#
query = "\
UPDATE ${....acct_table1} \
SET \
acctupdatetime = (@acctupdatetime_old:=acctupdatetime), \
acctupdatetime = ${....event_timestamp}, \
acctinterval = ${....event_timestamp_epoch} - \
UNIX_TIMESTAMP(@acctupdatetime_old), \
framedipaddress = '%{Framed-IP-Address}', \
framedipv6address = '%{Framed-IPv6-Address}', \
framedipv6prefix = '%{Framed-IPv6-Prefix}', \
framedinterfaceid = '%{Framed-Interface-Id}', \
delegatedipv6prefix = '%{Delegated-IPv6-Prefix}', \
acctsessiontime = %{%{Acct-Session-Time}:-NULL}, \
acctinputoctets = '%{%{Acct-Input-Gigawords}:-0}' \
<< 32 | '%{%{Acct-Input-Octets}:-0}', \
acctoutputoctets = '%{%{Acct-Output-Gigawords}:-0}' \
<< 32 | '%{%{Acct-Output-Octets}:-0}' \
WHERE AcctUniqueId = '%{Acct-Unique-Session-Id}'"
#
# The update condition matched no existing sessions. Use
# the values provided in the update to create a new session.
#
query = "\
INSERT INTO ${....acct_table1} \
(${...column_list}) \
VALUES \
('%{Acct-Session-Id}', \
'%{Acct-Unique-Session-Id}', \
'%{SQL-User-Name}', \
'%{Realm}', \
'%{NAS-IP-Address}', \
'%{%{NAS-Port-ID}:-%{NAS-Port}}', \
'%{NAS-Port-Type}', \
FROM_UNIXTIME(${....event_timestamp_epoch} - %{%{Acct-Session-Time}:-0}), \
${....event_timestamp}, \
NULL, \
%{%{Acct-Session-Time}:-NULL}, \
'%{Acct-Authentic}', \
'%{Connect-Info}', \
'', \
'%{%{Acct-Input-Gigawords}:-0}' << 32 | '%{%{Acct-Input-Octets}:-0}', \
'%{%{Acct-Output-Gigawords}:-0}' << 32 | '%{%{Acct-Output-Octets}:-0}', \
'%{Called-Station-Id}', \
'%{Calling-Station-Id}', \
'', \
'%{Service-Type}', \
'%{Framed-Protocol}', \
'%{Framed-IP-Address}', \
'%{Framed-IPv6-Address}', \
'%{Framed-IPv6-Prefix}', \
'%{Framed-Interface-Id}', \
'%{Delegated-IPv6-Prefix}' \
${....class.packet_xlat})"
#
# When using "sql_session_start", you should comment out
# the previous query, and enable this one.
#
# Just change the previous query to "-query",
# and this one to "query". The previous one
# will be ignored, and this one will be
# enabled.
#
-query = "\
UPDATE ${....acct_table1} \
SET \
AcctSessionId = '%{Acct-Session-Id}', \
AcctUniqueId = '%{Acct-Unique-Session-Id}', \
AcctAuthentic = '%{Acct-Authentic}', \
ConnectInfo_start = '%{Connect-Info}', \
ServiceType = '%{Service-Type}', \
FramedProtocol = '%{Framed-Protocol}', \
framedipaddress = '%{Framed-IP-Address}', \
framedipv6address = '%{Framed-IPv6-Address}', \
framedipv6prefix = '%{Framed-IPv6-Prefix}', \
framedinterfaceid = '%{Framed-Interface-Id}', \
delegatedipv6prefix = '%{Delegated-IPv6-Prefix}', \
AcctUpdateTime = ${....event_timestamp}, \
AcctSessionTime = %{%{Acct-Session-Time}:-NULL}, \
AcctInputOctets = '%{%{Acct-Input-Gigawords}:-0}' \
<< 32 | '%{%{Acct-Input-Octets}:-0}', \
AcctOutputOctets = '%{%{Acct-Output-Gigawords}:-0}' \
<< 32 | '%{%{Acct-Output-Octets}:-0}' \
WHERE UserName = '%{SQL-User-Name}' \
AND NASIPAddress = '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}' \
AND NASPortId = '%{%{NAS-Port-ID}:-%{NAS-Port}}' \
AND NASPortType = '%{NAS-Port-Type}' \
AND AcctStopTime IS NULL"
}
stop {
#
# Session has terminated, update the stop time and statistics.
#
query = "\
UPDATE ${....acct_table2} SET \
acctstoptime = ${....event_timestamp}, \
acctsessiontime = %{%{Acct-Session-Time}:-NULL}, \
acctinputoctets = '%{%{Acct-Input-Gigawords}:-0}' \
<< 32 | '%{%{Acct-Input-Octets}:-0}', \
acctoutputoctets = '%{%{Acct-Output-Gigawords}:-0}' \
<< 32 | '%{%{Acct-Output-Octets}:-0}', \
acctterminatecause = '%{Acct-Terminate-Cause}', \
connectinfo_stop = '%{Connect-Info}' \
WHERE AcctUniqueId = '%{Acct-Unique-Session-Id}'"
#
# The update condition matched no existing sessions. Use
# the values provided in the update to create a new session.
#
query = "\
INSERT INTO ${....acct_table2} \
(${...column_list}) \
VALUES \
('%{Acct-Session-Id}', \
'%{Acct-Unique-Session-Id}', \
'%{SQL-User-Name}', \
'%{Realm}', \
'%{NAS-IP-Address}', \
'%{%{NAS-Port-ID}:-%{NAS-Port}}', \
'%{NAS-Port-Type}', \
FROM_UNIXTIME(${....event_timestamp_epoch} - %{%{Acct-Session-Time}:-0}), \
${....event_timestamp}, \
${....event_timestamp}, \
%{%{Acct-Session-Time}:-NULL}, \
'%{Acct-Authentic}', \
'', \
'%{Connect-Info}', \
'%{%{Acct-Input-Gigawords}:-0}' << 32 | '%{%{Acct-Input-Octets}:-0}', \
'%{%{Acct-Output-Gigawords}:-0}' << 32 | '%{%{Acct-Output-Octets}:-0}', \
'%{Called-Station-Id}', \
'%{Calling-Station-Id}', \
'%{Acct-Terminate-Cause}', \
'%{Service-Type}', \
'%{Framed-Protocol}', \
'%{Framed-IP-Address}', \
'%{Framed-IPv6-Address}', \
'%{Framed-IPv6-Prefix}', \
'%{Framed-Interface-Id}', \
'%{Delegated-IPv6-Prefix}' \
${....class.packet_xlat})"
#
# When using "sql_session_start", you should comment out
# the previous query, and enable this one.
#
# Just change the previous query to "-query",
# and this one to "query". The previous one
# will be ignored, and this one will be
# enabled.
#
-query = "\
UPDATE ${....acct_table1} \
SET \
AcctSessionId = '%{Acct-Session-Id}', \
AcctUniqueId = '%{Acct-Unique-Session-Id}', \
AcctAuthentic = '%{Acct-Authentic}', \
ConnectInfo_start = '%{Connect-Info}', \
ServiceType = '%{Service-Type}', \
FramedProtocol = '%{Framed-Protocol}', \
framedipaddress = '%{Framed-IP-Address}', \
framedipv6address = '%{Framed-IPv6-Address}', \
framedipv6prefix = '%{Framed-IPv6-Prefix}', \
framedinterfaceid = '%{Framed-Interface-Id}', \
delegatedipv6prefix = '%{Delegated-IPv6-Prefix}', \
AcctStopTime = ${....event_timestamp}, \
AcctUpdateTime = ${....event_timestamp}, \
AcctSessionTime = %{Acct-Session-Time}, \
AcctInputOctets = '%{%{Acct-Input-Gigawords}:-0}' \
<< 32 | '%{%{Acct-Input-Octets}:-0}', \
AcctOutputOctets = '%{%{Acct-Output-Gigawords}:-0}' \
<< 32 | '%{%{Acct-Output-Octets}:-0}', \
AcctTerminateCause = '%{Acct-Terminate-Cause}', \
ConnectInfo_stop = '%{Connect-Info}' \
WHERE UserName = '%{SQL-User-Name}' \
AND NASIPAddress = '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}' \
AND NASPortId = '%{%{NAS-Port-ID}:-%{NAS-Port}}' \
AND NASPortType = '%{NAS-Port-Type}' \
AND AcctStopTime IS NULL"
}
#
# No Acct-Status-Type == ignore the packet
#
accounting {
query = "SELECT true"
}
}
}
#######################################################################
# Authentication Logging Queries
#######################################################################
# postauth_query - Insert some info after authentication
#######################################################################
post-auth {
# Write SQL queries to a logfile. This is potentially useful for bulk inserts
# when used with the rlm_sql_null driver.
# logfile = ${logdir}/post-auth.sql
query = "\
INSERT INTO ${..postauth_table} \
(username, reply, reason, authdate ${..class.column_name}) \
VALUES ( \
'%{SQL-User-Name}', \
'%{reply:Packet-Type}', \
'%{reply:Reply-Message}', \
'%S.%M' \
${..class.reply_xlat})"
}
+211
View File
@@ -0,0 +1,211 @@
#
# Example of forbidding all attempts to login via
# realms.
#
deny_realms {
if (&User-Name && (&User-Name =~ /@|\\/)) {
reject
}
}
#
# Filter the username
#
# Force some sanity on User-Name. This helps to avoid issues
# issues where the back-end database is "forgiving" about
# what constitutes a user name.
#
filter_username {
if (&User-Name) {
#
# reject mixed case e.g. "UseRNaMe"
#
#if (&User-Name != "%{tolower:%{User-Name}}") {
# reject
#}
#
# reject all whitespace
# e.g. "user@ site.com", or "us er", or " user", or "user "
#
if (&User-Name =~ / /) {
update request {
&Module-Failure-Message += 'Rejected: User-Name contains whitespace'
}
reject
}
#
# reject Multiple @'s
# e.g. "user@site.com@site.com"
#
if (&User-Name =~ /@[^@]*@/ ) {
update request {
&Module-Failure-Message += 'Rejected: Multiple @ in User-Name'
}
reject
}
#
# reject double dots
# e.g. "user@site..com"
#
if (&User-Name =~ /\.\./ ) {
update request {
&Module-Failure-Message += 'Rejected: User-Name contains multiple ..s'
}
reject
}
#
# must have at least 1 string-dot-string after @
# e.g. "user@site.com"
#
# if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/)) {
# update request {
# &Module-Failure-Message += 'Rejected: Realm does not have at least one dot separator'
# }
# reject
# }
#
# Realm ends with a dot
# e.g. "user@site.com."
#
if (&User-Name =~ /\.$/) {
update request {
&Module-Failure-Message += 'Rejected: Realm ends with a dot'
}
reject
}
#
# Realm begins with a dot
# e.g. "user@.site.com"
#
if (&User-Name =~ /@\./) {
update request {
&Module-Failure-Message += 'Rejected: Realm begins with a dot'
}
reject
}
}
}
#
# Filter the User-Password
#
# Some equipment sends passwords with embedded zeros.
# This policy filters them out.
#
filter_password {
if (&User-Password && \
(&User-Password != "%{string:User-Password}")) {
update request {
&Tmp-String-0 := "%{string:User-Password}"
&User-Password := "%{string:Tmp-String-0}"
&Tmp-String-0 !* ""
}
}
}
filter_inner_identity {
#
# No names, reject.
#
if (!&outer.request:User-Name || !&User-Name) {
update request {
Module-Failure-Message = "User-Name is required for tunneled authentication"
}
reject
}
#
# Do detailed checks only if the inner and outer
# NAIs are different.
#
# If the NAIs are the same, it violates user privacy,
# but is allowed.
#
if (&outer.request:User-Name != &User-Name) {
#
# Get the outer realm.
#
if (&outer.request:User-Name =~ /@([^@]+)$/) {
update request {
Outer-Realm-Name = "%{1}"
}
#
# When we have an outer realm name, the user portion
# MUST either be empty, or begin with "anon".
#
# We don't check for the full "anonymous", because
# some vendors don't follow the standards.
#
if (&outer.request:User-Name !~ /^(anon|@)/) {
update request {
Module-Failure-Message = "User-Name is not anonymized"
}
reject
}
}
#
# There's no outer realm. The outer NAI is different from the
# inner NAI. The User-Name MUST be anonymized.
#
# Otherwise, you could log in as outer "bob", and inner "doug",
# and we'd have no idea which one was correct.
#
elsif (&outer.request:User-Name !~ /^anon/) {
update request {
Module-Failure-Message = "User-Name is not anonymized"
}
reject
}
#
# Get the inner realm.
#
if (&User-Name =~ /@([^@]+)$/) {
update request {
Inner-Realm-Name = "%{1}"
}
#
# Note that we do EQUALITY checks for realm names.
# There is no simple way to do case insensitive checks
# on internationalized domain names. There is no reason
# to allow outer "anonymous@EXAMPLE.COM" and inner
# "user@example.com". The user should enter the same
# realm for both identities.
#
# If the inner realm isn't the same as the outer realm,
# the inner realm MUST be a subdomain of the outer realm.
#
if (&Outer-Realm-Name && \
(&Inner-Realm-Name != &Outer-Realm-Name) && \
(&Inner-Realm-Name !~ /\.%{Outer-Realm-Name}$/)) {
update request {
Module-Failure-Message = "Inner realm '%{Inner-Realm-Name}' and outer realm '%{Outer-Realm-Name}' are not from the same domain."
}
reject
}
#
# It's OK to have an inner realm and no outer realm.
#
# That won't work for roaming, but the local RADIUS server
# can still authenticate the user.
#
}
#
# It's OK to have an outer realm and no inner realm.
#
# It will work for roaming, and the local RADIUS server
# can authenticate the user without the realm.
#
}
}
+15
View File
@@ -0,0 +1,15 @@
vlan_test {
if (&User-Name =~ /.+@(.+)/) {
update control {
Tmp-String-0 := "%{1}"
}
update reply {
Tunnel-Type := VLAN
Tunnel-Medium-Type := IEEE-802
Tunnel-Private-Group-Id := "%{sql:SELECT vlan_id FROM tenant_subnets WHERE tenant_id='%{control:Tmp-String-0}'}"
}
}
}
+1143
View File
File diff suppressed because it is too large Load Diff
+19
View File
@@ -163,3 +163,22 @@ CREATE TABLE IF NOT EXISTS nas (
PRIMARY KEY (id), PRIMARY KEY (id),
KEY nasname (nasname) KEY nasname (nasname)
) ENGINE = INNODB; ) ENGINE = INNODB;
#
# Table structure for table 'radippool'
#
CREATE TABLE IF NOT EXISTS radippool (
id int(11) unsigned NOT NULL auto_increment,
pool_name varchar(30) NOT NULL,
framedipaddress varchar(15) NOT NULL default '',
nasipaddress varchar(15) NOT NULL default '',
calledstationid VARCHAR(30) NOT NULL default '',
callingstationid VARCHAR(30) NOT NULL default '',
expiry_time DATETIME NOT NULL default NOW(),
username varchar(64) NOT NULL default '',
pool_key varchar(64) NOT NULL default '',
PRIMARY KEY (id),
KEY radippool_poolname_expire (pool_name, expiry_time),
UNIQUE KEY framedipaddress_unique (framedipaddress),
KEY radippool_nasip_poolkey_ipaddress (nasipaddress, pool_key, framedipaddress)
) ENGINE=InnoDB;
+65 -6
View File
@@ -282,6 +282,7 @@ listen {
# Make *sure* that 'preprocess' comes before any realm if you # Make *sure* that 'preprocess' comes before any realm if you
# need to setup hints for the remote radius server # need to setup hints for the remote radius server
authorize { authorize {
dynamic_clients
# #
# Take a User-Name, and perform some checks on it, for spaces and other # Take a User-Name, and perform some checks on it, for spaces and other
# invalid characters. If the User-Name appears invalid, reject the # invalid characters. If the User-Name appears invalid, reject the
@@ -416,8 +417,35 @@ authorize {
# Look in an SQL database. The schema of the database is meant to mirror the "users" file. # Look in an SQL database. The schema of the database is meant to mirror the "users" file.
# #
# See "Authorization Queries" in mods-available/sql # See "Authorization Queries" in mods-available/sql
-sql sql
### Node pinning + client type (optimized)
update control {
Tmp-String-0 := "%{sql:SELECT node_ip FROM node_assignments WHERE username='%{User-Name}'}"
Tmp-String-1 := "%{sql:SELECT client_type FROM node_assignments WHERE username='%{User-Name}'}"
}
# No assignment → reject
if (&control:Tmp-String-0 == "") {
update reply {
Reply-Message := "No node assignment - access denied"
}
reject
}
# Wrong node → reject (string compare to avoid type mismatch)
if ("%{control:Tmp-String-0}" != "%{NAS-IP-Address}") {
update reply {
Reply-Message := "Wrong node %{NAS-IP-Address} expected %{control:Tmp-String-0}"
}
reject
}
# Engineers → dynamic pool
if (&control:Tmp-String-1 == "engineer") {
update control {
Pool-Name := "engineers-%{sql:SELECT SUBSTRING_INDEX('%{User-Name}','@',-1)}"
}
}
# If you are using /etc/smbpasswd, and are also doing mschap authentication, the un-comment this line, and configure the 'smbpasswd' module. # If you are using /etc/smbpasswd, and are also doing mschap authentication, the un-comment this line, and configure the 'smbpasswd' module.
# smbpasswd # smbpasswd
@@ -603,13 +631,13 @@ accounting {
# #
# Create a 'detail'ed log of the packets. # Create a 'detail'ed log of the packets.
# Note that accounting requests which are proxied are also logged in the detail file. # Note that accounting requests which are proxied are also logged in the detail file.
detail # detail
# daily # daily
# Update the wtmp file # Update the wtmp file
# #
# If you don't use "radlast", you can delete this line. # If you don't use "radlast", you can delete this line.
unix # unix
# For Simultaneous-Use tracking. # For Simultaneous-Use tracking.
# Due to packet losses in the network, the data here may be incorrect. There is little we can do about it. # Due to packet losses in the network, the data here may be incorrect. There is little we can do about it.
@@ -618,11 +646,20 @@ accounting {
# Return an address to the IP Pool when we see a stop record. # Return an address to the IP Pool when we see a stop record.
# Ensure that &control:Pool-Name is set to determine which pool of IPs are used. # Ensure that &control:Pool-Name is set to determine which pool of IPs are used.
# sqlippool # sqlippool
### rebuild Pool-Name
update control {
Pool-Name := "engineers-%{sql:SELECT SUBSTRING_INDEX('%{User-Name}','@',-1)}"
}
### apply only for engineers
if (&control:Pool-Name =~ /^engineers-/) {
sqlippool
}
# Log traffic to an SQL database. # Log traffic to an SQL database.
# See "Accounting queries" in mods-available/sql # See "Accounting queries" in mods-available/sql
-sql sql
# #
# If you receive stop packets with zero session length, # If you receive stop packets with zero session length,
@@ -751,8 +788,30 @@ post-auth {
# #
# Ensure that &control:Pool-Name is set to determine which # Ensure that &control:Pool-Name is set to determine which
# pool of IPs are used. # pool of IPs are used.
# sqlippool # sqlippool
# Engineers → dynamic IP
#
if (&control:Pool-Name =~ /^engineers-/) {
update reply {
Session-Timeout := 3600
}
sqlippool
}
#
# HARD CHECK: must have IP
#
if (!&reply:Framed-IP-Address) {
update reply {
Reply-Message := "No IP assigned - access denied"
}
reject
}
# Query VLAN ID from your DB
vlan_test
# Create the CUI value and add the attribute to Access-Accept. # Create the CUI value and add the attribute to Access-Accept.
# Uncomment the line below if *returning* the CUI. # Uncomment the line below if *returning* the CUI.
+4 -4
View File
@@ -63,7 +63,7 @@ listen {
# Send packets to the default virtual server # Send packets to the default virtual server
virtual_server = default virtual_server = default
clients = radsec # clients = radsec
# Use the haproxy "PROXY protocol". # Use the haproxy "PROXY protocol".
# #
@@ -373,7 +373,7 @@ listen {
# #
# Require a client certificate. # Require a client certificate.
# #
require_client_cert = yes require_client_cert = no
# #
# As of version 2.1.10, client certificates can be # As of version 2.1.10, client certificates can be
@@ -525,8 +525,8 @@ home_server tls {
# #
# openssl dhparam -out certs/dh 1024 # openssl dhparam -out certs/dh 1024
# #
dh_file = ${certdir}/dh # dh_file = ${certdir}/dh
random_file = /dev/urandom # random_file = /dev/urandom
# #
# The default fragment size is 1K. # The default fragment size is 1K.
-115
View File
@@ -1,115 +0,0 @@
apiVersion: v1
entries:
freeradius:
- annotations:
category: AccessManagement
apiVersion: v2
appVersion: 3.2.7
created: "2025-06-16T16:16:37.456715181+02:00"
dependencies:
- name: st-common
repository: https://startechnica.github.io/apps
version: 0.1.12
- condition: mariadb.enabled
name: mariadb
repository: oci://registry-1.docker.io/bitnamicharts
version: 20.x.x
description: FreeRADIUS is a modular, high performance free RADIUS suite developed
and distributed under the GNU General Public License, version 2, and is free
for download and use.
digest: da3ba6c773298d8e6ff0df4cb8eb2d9effb81e92d4991a74908a136c70457920
home: https://github.com/startechnica/apps/tree/main/charts/freeradius
icon: https://freeradius.org/img/wordmark.svg
keywords:
- freeradius
- radius
- mysql
- postgresql
- ldap
kubeVersion: '>=1.24.0-0'
maintainers:
- email: firmansyah@nainggolan.id
name: firmansyahn
url: https://firmansyah.nainggolan.id
name: freeradius
sources:
- https://freeradius.org/
- https://github.com/FreeRADIUS/freeradius-server
type: application
urls:
- freeradius-1.0.3.tgz
version: 1.0.3
mariadb:
- annotations:
category: Database
images: |
- name: mariadb
image: docker.io/bitnami/mariadb:11.4.7-debian-12-r1
- name: mysqld-exporter
image: docker.io/bitnami/mysqld-exporter:0.17.2-debian-12-r11
- name: os-shell
image: docker.io/bitnami/os-shell:12-debian-12-r46
licenses: Apache-2.0
tanzuCategory: service
apiVersion: v2
appVersion: 11.4.7
created: "2025-06-16T16:16:37.459591908+02:00"
dependencies:
- name: common
repository: oci://registry-1.docker.io/bitnamicharts
tags:
- bitnami-common
version: 2.x.x
description: MariaDB is an open source, community-developed SQL database server
that is widely in use around the world due to its enterprise features, flexibility,
and collaboration with leading tech firms.
digest: 1e5034974b28d6ab585efdaf9a345ea4e4d3024417b680b83f656a4c0b1e2a84
home: https://bitnami.com
icon: https://dyltqmyl993wv.cloudfront.net/assets/stacks/mariadb/img/mariadb-stack-220x234.png
keywords:
- mariadb
- mysql
- database
- sql
- prometheus
maintainers:
- name: Broadcom, Inc. All Rights Reserved.
url: https://github.com/bitnami/charts
name: mariadb
sources:
- https://github.com/bitnami/charts/tree/main/bitnami/mariadb
urls:
- charts/mariadb-20.5.7.tgz
version: 20.5.7
st-common:
- annotations:
artifacthub.io/changes: |
- kind: added
description: Add dotenv and envvars names helper
category: Infrastructure
apiVersion: v2
appVersion: 0.1.12
created: "2025-06-16T16:16:37.460145288+02:00"
description: A Library Helm Chart for grouping common logic between Startechnica
charts. This chart is not deployable by itself.
digest: 4c1c7304cf4dbb900b6fd847cf1d38e6f8d1db46dbf080b4d52c884b84a36747
home: https://github.com/startechnica/apps/tree/main/charts/common
icon: https://startechnica.github.io/apps/images/star.png
keywords:
- common
- helper
- template
- function
kubeVersion: '>=1.20.0-0'
maintainers:
- email: firmansyah@nainggolan.id
name: firmansyahn
url: https://firmansyah.nainggolan.id
name: st-common
sources:
- https://startechnica.github.io/apps
type: library
urls:
- charts/st-common-0.1.12.tgz
version: 0.1.12
generated: "2025-06-16T16:16:37.449242718+02:00"
Binary file not shown.
+1
View File
@@ -38,6 +38,7 @@ data:
FREERADIUS_MODS_SQL_TABLE_GROUPREPLY: {{ .Values.modsEnabled.sql.table.groupreply }} FREERADIUS_MODS_SQL_TABLE_GROUPREPLY: {{ .Values.modsEnabled.sql.table.groupreply }}
FREERADIUS_MODS_SQL_TABLE_POSTAUTH: {{ .Values.modsEnabled.sql.table.postauth }} FREERADIUS_MODS_SQL_TABLE_POSTAUTH: {{ .Values.modsEnabled.sql.table.postauth }}
FREERADIUS_MODS_SQL_TABLE_USERGROUP: {{ .Values.modsEnabled.sql.table.usergroup }} FREERADIUS_MODS_SQL_TABLE_USERGROUP: {{ .Values.modsEnabled.sql.table.usergroup }}
FREERADIUS_MODS_SQL_TABLE_RADIPPOOL: {{ .Values.modsEnabled.sql.table.sqlippool }}
FREERADIUS_MODS_SQL_TLS_ENABLE: {{ ternary "yes" "no" .Values.modsEnabled.sql.tls.enabled | quote }} FREERADIUS_MODS_SQL_TLS_ENABLE: {{ ternary "yes" "no" .Values.modsEnabled.sql.tls.enabled | quote }}
FREERADIUS_MODS_SQL_READ_CLIENTS: {{ ternary "yes" "no" .Values.modsEnabled.sql.readClients | quote }} FREERADIUS_MODS_SQL_READ_CLIENTS: {{ ternary "yes" "no" .Values.modsEnabled.sql.readClients | quote }}
+19
View File
@@ -0,0 +1,19 @@
{{- /*
Copyright (c) 2025 Firmansyah Nainggolan. All Rights Reserved.
SPDX-License-Identifier: APACHE-2.0
*/}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ printf "%s-filter" (include "st-common.names.fullname" .) }}
namespace: {{ include "st-common.names.namespace" . | quote }}
labels: {{- include "st-common.labels.standard" . | nindent 4 }}
{{- if .Values.commonLabels }}
{{- include "st-common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }}
{{- end }}
{{- if .Values.commonAnnotations }}
annotations: {{- include "st-common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }}
{{- end }}
data:
{{ (.Files.Glob "files/policy/filter").AsConfig | indent 2 }}
+19
View File
@@ -0,0 +1,19 @@
{{- /*
Copyright (c) 2025 Firmansyah Nainggolan. All Rights Reserved.
SPDX-License-Identifier: APACHE-2.0
*/}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ printf "%s-mods-config" (include "st-common.names.fullname" .) }}
namespace: {{ include "st-common.names.namespace" . | quote }}
labels: {{- include "st-common.labels.standard" . | nindent 4 }}
{{- if .Values.commonLabels }}
{{- include "st-common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }}
{{- end }}
{{- if .Values.commonAnnotations }}
annotations: {{- include "st-common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }}
{{- end }}
data:
{{ (.Files.Glob "files/mods-config/queries.conf").AsConfig | indent 2 }}
+4
View File
@@ -16,6 +16,10 @@ metadata:
annotations: {{- include "st-common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} annotations: {{- include "st-common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }}
{{- end }} {{- end }}
data: data:
{{ (.Files.Glob "files/mods-available/eap").AsConfig | indent 2 }}
{{ (.Files.Glob "files/mods-available/dynamic_clients").AsConfig | indent 2 }}
{{ (.Files.Glob "files/mods-available/sqlippool").AsConfig | indent 2 }}
{{- if .Values.modsEnabled.sql.enabled }} {{- if .Values.modsEnabled.sql.enabled }}
{{ (.Files.Glob "files/mods-available/sql").AsConfig | indent 2 }} {{ (.Files.Glob "files/mods-available/sql").AsConfig | indent 2 }}
{{- end }} {{- end }}
+19
View File
@@ -0,0 +1,19 @@
{{- /*
Copyright (c) 2025 Firmansyah Nainggolan. All Rights Reserved.
SPDX-License-Identifier: APACHE-2.0
*/}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ printf "%s-radius-conf" (include "st-common.names.fullname" .) }}
namespace: {{ include "st-common.names.namespace" . | quote }}
labels: {{- include "st-common.labels.standard" . | nindent 4 }}
{{- if .Values.commonLabels }}
{{- include "st-common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }}
{{- end }}
{{- if .Values.commonAnnotations }}
annotations: {{- include "st-common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }}
{{- end }}
data:
{{ (.Files.Glob "files/radius.conf").AsConfig | indent 2 }}
+19
View File
@@ -0,0 +1,19 @@
{{- /*
Copyright (c) 2025 Firmansyah Nainggolan. All Rights Reserved.
SPDX-License-Identifier: APACHE-2.0
*/}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ printf "%s-vlan" (include "st-common.names.fullname" .) }}
namespace: {{ include "st-common.names.namespace" . | quote }}
labels: {{- include "st-common.labels.standard" . | nindent 4 }}
{{- if .Values.commonLabels }}
{{- include "st-common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }}
{{- end }}
{{- if .Values.commonAnnotations }}
annotations: {{- include "st-common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }}
{{- end }}
data:
{{ (.Files.Glob "files/policy/vlan").AsConfig | indent 2 }}
+33
View File
@@ -274,6 +274,27 @@ spec:
mountPath: /etc/freeradius/mods-enabled/sql mountPath: /etc/freeradius/mods-enabled/sql
subPath: sql subPath: sql
{{- end }} {{- end }}
- name: freeradius-mods
mountPath: /etc/freeradius/mods-enabled/eap
subPath: eap
- name: freeradius-mods
mountPath: /etc/freeradius/mods-enabled/dynamic_clients
subPath: dynamic_clients
- name: freeradius-mods
mountPath: /etc/freeradius/mods-enabled/sqlippool
subPath: sqlippool
- name: freeradius-mods-config
mountPath: /etc/freeradius/mods-config/sql/main/mysql/queries.conf
subPath: queries.conf
- name: freeradius-radius-conf
mountPath: /etc/freeradius/radius.conf
subPath: radius.conf
- name: freeradius-filter
mountPath: /etc/freeradius/policy.d/filter
subPath: filter
- name: freeradius-vlan
mountPath: /etc/freeradius/policy.d/vlan
subPath: vlan
- name: freeradius-sites - name: freeradius-sites
mountPath: /etc/freeradius/sites-enabled/default mountPath: /etc/freeradius/sites-enabled/default
subPath: default subPath: default
@@ -319,6 +340,18 @@ spec:
- name: freeradius-sites - name: freeradius-sites
configMap: configMap:
name: {{ printf "%s-sites" (include "st-common.names.fullname" .) }} name: {{ printf "%s-sites" (include "st-common.names.fullname" .) }}
- name: freeradius-mods-config
configMap:
name: {{ printf "%s-mods-config" (include "st-common.names.fullname" .) }}
- name: freeradius-radius-conf
configMap:
name: {{ printf "%s-radius-conf" (include "st-common.names.fullname" .) }}
- name: freeradius-filter
configMap:
name: {{ printf "%s-filter" (include "st-common.names.fullname" .) }}
- name: freeradius-vlan
configMap:
name: {{ printf "%s-vlan" (include "st-common.names.fullname" .) }}
- name: temp - name: temp
emptyDir: {} emptyDir: {}
- name: shared-certs - name: shared-certs
+3 -2
View File
@@ -70,8 +70,8 @@ diagnosticMode:
## @param image.debug Specify if debug logs should be enabled ## @param image.debug Specify if debug logs should be enabled
## ##
image: image:
registry: docker.io registry: gitea.infrastructure.helmholz.cloud
repository: freeradius/freeradius-server repository: gitea_admin/freeradius-server
tag: "3.2.7" tag: "3.2.7"
## Specify a imagePullPolicy ## Specify a imagePullPolicy
## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent'
@@ -825,6 +825,7 @@ modsEnabled:
groupreply: radgroupreply groupreply: radgroupreply
postauth: radpostauth postauth: radpostauth
usergroup: radusergroup usergroup: radusergroup
sqlippool: radippool
## @param modsEnabled.sql.groupAttribute The group attribute specific to this instance of rlm_sql ## @param modsEnabled.sql.groupAttribute The group attribute specific to this instance of rlm_sql
## ##
groupAttribute: SQL-Group groupAttribute: SQL-Group