Initial stack: FreeRADIUS + Node API + docker-compose
This commit is contained in:
38
docker-compose.yml
Normal file
38
docker-compose.yml
Normal 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
7
freeradius/clients.conf
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
client unifi {
|
||||||
|
ipaddr = %{env:RADIUS_CLIENTS_CIDR}
|
||||||
|
secret = %{env:RADIUS_SHARED_SECRET}
|
||||||
|
require_message_authenticator = no
|
||||||
|
nastype = other
|
||||||
|
}
|
||||||
|
|
||||||
22
freeradius/mods-available/rest
Normal file
22
freeradius/mods-available/rest
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
43
freeradius/sites-enabled/default
Normal file
43
freeradius/sites-enabled/default
Normal 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
11
node-api/Dockerfile
Normal 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
56
node-api/index.js
Normal 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
12
node-api/package.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user