Initial stack: FreeRADIUS + Node API + docker-compose

This commit is contained in:
Codex Bot
2025-09-24 14:12:26 -06:00
commit 6ef48911ef
7 changed files with 189 additions and 0 deletions

38
docker-compose.yml Normal file
View File

@@ -0,0 +1,38 @@
version: "3.9"
services:
node:
build: ./node-api
ports:
- "3000:3000"
environment:
- VLAN_ID=2
- MAX_UP=10000000
- MAX_DOWN=10000000
networks:
- radius_net
freeradius:
image: freeradius/freeradius-server:3.2.2
depends_on:
- node
ports:
- "1812:1812/udp"
- "1813:1813/udp"
environment:
- REST_ENDPOINT=http://node:3000
- RADIUS_CLIENTS_CIDR=${RADIUS_CLIENTS_CIDR:-0.0.0.0/0}
- RADIUS_SHARED_SECRET=${RADIUS_SHARED_SECRET:-testing123}
volumes:
- ./freeradius/mods-available/rest:/etc/freeradius/mods-available/rest:ro
- ./freeradius/mods-available/rest:/etc/freeradius/mods-enabled/rest:ro
- ./freeradius/sites-enabled/default:/etc/freeradius/sites-enabled/default:ro
- ./freeradius/clients.conf:/etc/freeradius/clients.conf:ro
command: ["-X"]
networks:
- radius_net
networks:
radius_net:
driver: bridge

7
freeradius/clients.conf Normal file
View File

@@ -0,0 +1,7 @@
client unifi {
ipaddr = %{env:RADIUS_CLIENTS_CIDR}
secret = %{env:RADIUS_SHARED_SECRET}
require_message_authenticator = no
nastype = other
}

View File

@@ -0,0 +1,22 @@
rest {
# Timeouts
connect_timeout = 4
read_timeout = 8
# Authorize: llama al API Node
authorize {
uri = "%{env:REST_ENDPOINT:-http://node:3000}/authorize"
method = "post"
body = "json"
# send_all = yes -> envía todos los atributos del paquete
# por defecto rlm_rest ya serializa atributos en JSON
}
# Accounting: opcional
accounting {
uri = "%{env:REST_ENDPOINT:-http://node:3000}/accounting"
method = "post"
body = "json"
}
}

View File

@@ -0,0 +1,43 @@
server default {
listen {
type = auth
ipaddr = *
port = 1812
}
listen {
type = acct
ipaddr = *
port = 1813
}
authorize {
# Llama a la API REST para decidir y añadir atributos
rest
# Si la API no estableció Auth-Type, aceptamos por defecto (demo)
if (!&control:Auth-Type) {
update control {
Auth-Type := Accept
}
}
}
authenticate {
# Aceptar todo cuando control:Auth-Type := Accept
Auth-Type Accept {
ok
}
}
accounting {
rest
ok
}
post-auth {
# Aquí podríamos volver a llamar a REST para atributos dinámicos
# rest
}
}

11
node-api/Dockerfile Normal file
View File

@@ -0,0 +1,11 @@
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production || npm i --only=production
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]

56
node-api/index.js Normal file
View File

@@ -0,0 +1,56 @@
import express from 'express';
import morgan from 'morgan';
const app = express();
app.use(express.json());
app.use(morgan('dev'));
const VLAN_ID = process.env.VLAN_ID || '2';
const MAX_UP = process.env.MAX_UP || '10000000'; // bits per second
const MAX_DOWN = process.env.MAX_DOWN || '10000000'; // bits per second
// Helper: standard Accept with VLAN + bandwidth
function buildAcceptPayload(extra = {}) {
return {
control: {
'Auth-Type': 'Accept',
...extra.control,
},
reply: {
'Tunnel-Type': 'VLAN',
'Tunnel-Medium-Type': 'IEEE-802',
'Tunnel-Private-Group-Id': String(VLAN_ID),
'WISPr-Bandwidth-Max-Down': String(MAX_DOWN),
'WISPr-Bandwidth-Max-Up': String(MAX_UP),
...extra.reply,
},
};
}
// Authorize endpoint: FreeRADIUS rlm_rest calls this in authorize {}
app.post('/authorize', (req, res) => {
console.log('--- RADIUS Authorize Request ---');
console.log(JSON.stringify(req.body, null, 2));
// Por ahora aprobamos todas las solicitudes válidas (si traen User-Name)
const attrs = (req.body && (req.body.attributes || req.body.request)) || {};
if (!attrs['User-Name'] && !attrs['User-Name*0']) {
// Responder vacío -> no cambia nada; o devolver 204
return res.status(200).json({});
}
return res.status(200).json(buildAcceptPayload());
});
// Accounting endpoint (opcional)
app.post('/accounting', (req, res) => {
console.log('--- RADIUS Accounting ---');
console.log(JSON.stringify(req.body, null, 2));
return res.status(200).json({});
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Node RADIUS REST API listening on :${port}`);
});

12
node-api/package.json Normal file
View File

@@ -0,0 +1,12 @@
{
"name": "radius-rest-api",
"version": "0.1.0",
"private": true,
"main": "index.js",
"type": "module",
"dependencies": {
"express": "^4.19.2",
"morgan": "^1.10.0"
}
}