Development

This commit is contained in:
Matthew Grotke 2026-06-07 23:49:42 -04:00
parent 64f83d683e
commit 1a473296b7
2 changed files with 96 additions and 0 deletions

View file

@ -7,11 +7,15 @@ module config.
"""
import re
import sqlite3
import subprocess
from pathlib import Path
from cryptography.fernet import Fernet
import mod_shared as shared
RADIUS_SECRET_FILE = shared.SCRIPT_DIR / ".radius-secret"
CREDENTIALS_KEY_FILE = shared.SCRIPT_DIR / ".credentials-key"
CREDENTIALS_DB_FILE = shared.SCRIPT_DIR / ".client-credentials"
RADIUS_CLIENTS_CONF = Path("/etc/freeradius/3.0/clients.conf")
RADIUS_USERS_FILE = Path("/etc/freeradius/3.0/users")
RADIUS_CONF_FILE = Path("/etc/freeradius/3.0/radiusd.conf")
@ -21,6 +25,44 @@ RADIUS_LOG_FILE = Path("/var/log/freeradius/radius.log")
RADIUS_HUNTGROUP_NAME = "routlin-aps"
# digest_type values (mirrors clientcredentials/action.py)
DIGEST_CYPHERTEXT_FERNET = 0
USER_TYPE_SUPPLICANT = 1
# ===================================================================
# Credential helpers
# ===================================================================
def _load_fernet():
if not CREDENTIALS_KEY_FILE.exists():
print(f"WARNING: {CREDENTIALS_KEY_FILE} not found - cannot decrypt supplicant passwords")
return None
try:
return Fernet(CREDENTIALS_KEY_FILE.read_text().strip().encode())
except Exception as exc:
print(f"WARNING: Could not load credentials key: {exc}")
return None
def _load_supplicant_credentials():
if not CREDENTIALS_DB_FILE.exists():
return []
try:
conn = sqlite3.connect(str(CREDENTIALS_DB_FILE))
conn.row_factory = sqlite3.Row
rows = conn.execute(
"SELECT username, password, digest_type, vlan FROM credentials"
" WHERE user_type=? AND enabled=1",
(USER_TYPE_SUPPLICANT,)
).fetchall()
conn.close()
return [dict(r) for r in rows]
except Exception as exc:
print(f"WARNING: Could not read credentials DB: {exc}")
return []
# ===================================================================
# Data helpers
# ===================================================================
@ -153,6 +195,45 @@ def build_radius_users(data):
"",
]
if auth_mode in ('eap_password', 'eap_certificate'):
creds = _load_supplicant_credentials()
fernet = _load_fernet() if auth_mode == 'eap_password' else None
for cred in creds:
vlan = vlan_by_name.get(cred['vlan'])
if not vlan:
continue
vlan_id = vlan.get('vlan_id')
username = cred['username']
if auth_mode == 'eap_password':
if fernet is None:
print(f"WARNING: Skipping '{username}' - credentials key unavailable")
continue
if cred['digest_type'] != DIGEST_CYPHERTEXT_FERNET:
print(f"WARNING: Skipping '{username}' - unexpected digest_type {cred['digest_type']}")
continue
try:
plaintext = fernet.decrypt(cred['password'].encode()).decode()
except Exception:
print(f"WARNING: Skipping '{username}' - decryption failed")
continue
lines += [
f"# {username} -> VLAN {vlan_id} ({vlan['name']})",
f"{username} Cleartext-Password := \"{plaintext}\"",
f" Tunnel-Type = VLAN,",
f" Tunnel-Medium-Type = IEEE-802,",
f" Tunnel-Private-Group-Id = \"{vlan_id}\"",
"",
]
else: # eap_certificate - cert verified by TLS stack, entry provides VLAN reply attrs
lines += [
f"# {username} -> VLAN {vlan_id} ({vlan['name']})",
f"{username} Auth-Type := EAP",
f" Tunnel-Type = VLAN,",
f" Tunnel-Medium-Type = IEEE-802,",
f" Tunnel-Private-Group-Id = \"{vlan_id}\"",
"",
]
default_id = default_vlan.get('vlan_id')
ap_ips = fr_opts.get('ap_ips', [])
if apply_to == 'wireless':