Esta es una continuación de mi pregunta anterior.en el envío de rutas estáticas desde la implementación del servidor DHCP de Freeradiusen combinación con el servidor VPN Strongswan.
Al depurar Freeradius usando tcpdump y Wireshark, descubrí que puedo enviar rutas estáticas sin clases desde el servidor DHCP de Freeradius agregando opciones DHCP-Classless-Static-Route
y DHCP-Site-specific-25
(también conocidas como ruta estática de Microsoft) a mis secciones DHCP-Discover
y DHCP-Request
al archivo de configuración del servidor dhcp.
Sin embargo: parece que el cliente VPN de Microsoft no acepta las rutas estáticas si configuro la puerta de enlace predeterminada 0.0.0.0
como lo sugiereDocumentación de Strongswan.
Al menos no puedo encontrar las rutas anunciadas en mi cliente Windows cuando uso route print -4
.
Además, no puedo agregar las rutas manualmente en el cliente de Windows cuando lo uso 0.0.0.0
como puerta de enlace estándar a través de una interfaz VPN.
Sin embargo:
Digamos que quiero acceder a la subred 192.168.200.0/24
a través de VPN y mi servidor VPN asigna la dirección 192.168.201.2/24
a mi cliente de Windows. Entonces es posible crear una ruta estática en el lado del cliente de Windows declarando que se puede acceder a la subred 192.168.200.0/24 a través de 192.168.201.2 usando el comando de Windows:
route add 192.168.200.0 mask 255.255.255.0 192.168.201.2
Sé que parece un poco extraño, pero puedo hacer ping a cualquier host de la 192.168.200.0
subred, así que mientras funcione, estaré contento. :-)
Pero: estaría más feliz si pudiera hacer lo mismo anunciando las rutas desde mi servidor VPN en lugar de hacerlo manualmente en todos los clientes VPN. :-)
Eso significa que tengo que hacer un poco de programación dinámica en la configuración de DHCP en Freeradius. En mi caso, significa que tengo que hacer una referencia a un módulo perl en DHCP-Discover y DHCP-request que toma la dirección IP vpn del cliente asignada, la convierte en octetos y la combina con las rutas estáticas que también se proporcionan como octetos.
Un ejemplo:
La subred 192.168.200.0/24
se codificará como 0x18c0a8c8
se codifica primero la máscara de subred.
El cliente 192.168.201.2/24
se codificará como 0xc0a8c902
si simplemente estuviera convirtiendo cada número en la dirección IP a hexadecimal.
La codificación final de la ruta será: 0x18c0a8c8c0a8c902
ya que es solo una concatinación de las dos cadenas.
Luego tengo que usar update reply
con el siguiente código:
update reply {
&DHCP-Classless-Static-Route = 0x18c0a8c8c0a8c902
&DHCP-Site-specific-25 = 0x18c0a8c8c0a8c902
}
Si hay más rutas, todas las rutas se concatenarán en una cadena larga.
La parte complicada:
Supongamos que tiene la configuración predeterminada del servidor DHCP de Freeradius como se encuentra en freeradius/3.0/sites-available/dhcp
el archivo.
La estructura general del archivo para DHCP-Discover y DHCP-Request es la siguiente:
dhcp DHCP-Request {
update reply {
&DHCP-Message-Type = DHCP-Ack
}
update reply {
# General DHCP options, such as default GW, DNS, IP-address lease time etc.
}
update control {
&Pool-Name := "vpn_pool"
}
dhcp_sqlippool
ok
}
Entonces, hasta donde tengo entendido, necesito llamar a mi módulo Perl después de dhcp_sqlippool
haberlo llamado y antes de regresar ok
, porque dhcp_sqlippool
es el módulo que asigna la dirección IP al cliente VPN.
Eso significa que mi versión sería algo como:
dhcp DHCP-Request {
update reply {
&DHCP-Message-Type = DHCP-Ack
}
update reply {
# General DHCP options, such as default GW, DNS, IP-address lease time etc.
}
update control {
&Pool-Name := "vpn_pool"
}
dhcp_sqlippool
perl
# If perl module returned no error
if(ok) {
update reply {
# Perl-Route contains a hex encoded string with all routes.
&DHCP-Classless-Static-Route = Perl-Route
&DHCP-Site-specific-25 = Perl-Route
}
}
# Not sure if this one is needed?
update reply {
&DHCP-End-Of-Options = 255
}
ok
}
Para que funcione, tengo que habilitar Perl en la freeradius/3.0/mods-enabled
carpeta y modificar el nombre del archivo freeradius/3.0/mods-enabled/perl
para que apunte a mi módulo Perl. Como por ejemplo:
filename = ${modconfdir}/${.:instance}/dhcp/Options.pm
Pero, ¿cómo hago referencia a la llamada a Perl de la manera correcta?
Pensé que tenía que habilitar la línea func_post_auth = post_auth
y freeradius/3.0/mods-enabled/perl
crear una sub post_auth
sección en mi módulo Perl para manejar llamadas desde Freeradius, pero por lo que puedo ver en mi registro, aparece el siguiente error en Freeradius:
(8) perl: perl_embed:: module = /etc/freeradius/3.0/mods-config/perl/dhcp/Options.pm ,
func = post_auth exit status= Undefined subroutine &main::post_auth called.
...
(8) [perl] = fail
(8) } # dhcp DHCP-Discover = fail
Entonces, ¿qué es lo que no veo?
Respuesta1
Me golpeé la cabeza contra la pared varias veces, pero al menos logré que el módulo Perl funcionara, aunque no estoy completamente donde quiero estar, ya que el enrutamiento estático a través de DHCP no pasa del servidor DHCP de Freeradius al cliente VPN a través de Strongswan. , pero la depuración de paquetes UDP desde el servidor DHCP de Freeradius implica que el problema está en otra parte.
De todos modos esto es lo que hice:
- Habilite el módulo Perl
freeradius/3.0/mods-enabled
y configure al menos las siguientes líneas:
perl {
# Perl code location: ("freeradius/3.0/mods-config/dhcp/Options.pm")
filename = ${modconfdir}/${.:instance}/dhcp/Options.pm
# DHCP module is called during freeradius post_auth
func_post_auth = post_auth
}
- Modificar
freeradius/3.0/sites-enabled/dhcp
Los lugares relevantes sonDHCP-Discover
yDHCP-Request
:
dhcp DHCP-Discover {
update reply {
DHCP-Message-Type = DHCP-Offer
}
# The contents here are invented. Change them!
update reply {
&DHCP-Domain-Name-Server = 192.168.200.1
&DHCP-Subnet-Mask = 255.255.255.0
&DHCP-IP-Address-Lease-Time = 86400
&DHCP-DHCP-Server-Identifier = 192.168.200.4
}
# Or, allocate IPs from the DHCP pool in SQL. You may need to
# set the pool name here if you haven't set it elsewhere.
update control {
&Pool-Name := "vpn_pool"
}
dhcp_sqlippool
# Call static route generation.
perl
ok
}
dhcp DHCP-Request {
# Response packet type. See DHCP-Discover section above.
update reply {
&DHCP-Message-Type = DHCP-Ack
}
# The contents here are invented. Change them!
update reply {
&DHCP-Domain-Name-Server = 192.168.200.1
&DHCP-Subnet-Mask = 255.255.255.0
&DHCP-IP-Address-Lease-Time = 86400
&DHCP-DHCP-Server-Identifier = 192.168.200.4
}
# Or, allocate IPs from the DHCP pool in SQL. You may need to
# set the pool name here if you haven't set it elsewhere.
update control {
&Pool-Name := "vpn_pool"
}
dhcp_sqlippool
# Call static route generation.
perl
ok
}
- Cree el código perl ubicado en
freeradius/3.0/mods-config/perl/dhcp/Options.pm
:
use strict;
use warnings;
use Data::Dumper;
use Net::IP;
# Bring the global hashes into the package scope
our (%RAD_REQUEST, %RAD_REPLY, %RAD_CHECK);
#
# This the remapping of return values
#
use constant {
RLM_MODULE_REJECT => 0, # immediately reject the request
RLM_MODULE_OK => 2, # the module is OK, continue
RLM_MODULE_HANDLED => 3, # the module handled the request, so stop
RLM_MODULE_INVALID => 4, # the module considers the request invalid
RLM_MODULE_USERLOCK => 5, # reject the request (user is locked out)
RLM_MODULE_NOTFOUND => 6, # user not found
RLM_MODULE_NOOP => 7, # module succeeded without doing anything
RLM_MODULE_UPDATED => 8, # OK (pairs modified)
RLM_MODULE_NUMCODES => 9 # How many return codes there are
};
# Same as src/include/radiusd.h
use constant L_DBG=> 1;
use constant L_AUTH=> 2;
use constant L_INFO=> 3;
use constant L_ERR=> 4;
use constant L_PROXY=> 5;
use constant L_ACCT=> 6;
# Function to handle post_auth
sub post_auth {
# Get VPN Client IP from Freeradius DHCP server.
my $client_ip = new Net::IP ( $RAD_REQUEST{'DHCP-Requested-IP-Address'} ) or die (Net::IP::Error());
# An example of 2 routing rules sent ('192.168.20.0/24' and '192.168.200.0/24')
my @routes = (new Net::IP('192.168.20/24'), new Net::IP('192.168.200/24'));
# Measure how many elements there is in the routes array.
my $size = @routes;
# Convert client ip into hex code.
my $client_octets = get_ip_octets ($client_ip->ip(),$client_ip->prefixlen());
# Freeradius want the encoded string start with '0x'
# followed by the encoded octets as hex.
my $octet_str = "0x";
for(my $i = 0; $i < $size; $i++)
{
# Convert subnet into octets, skipping ending zeroes.
my $route_octets = get_ip_octets ($routes[$i]->ip(),$routes[$i]->prefixlen());
# Convert network prefix into octets
my $hex_prefix = sprintf("%02x", $routes[$i]->prefixlen());
# Route is encoded by network octets followed by subnet octets
$route_octets = $hex_prefix . $route_octets;
# The entire route string is the route octets followed by gateway octets ('the client vpn ip').
my $route_str = $route_octets . $client_octets;
$octet_str = $octet_str . $route_str;
}
# Classless static routing (dhcp option 121)
$RAD_REPLY{'DHCP-Classless-Static-Route'} = $octet_str;
# Microsoft classless static routing (dhcp option 249)
$RAD_REPLY{'DHCP-Site-specific-25'} = $octet_str;
return RLM_MODULE_OK;
}
sub get_ip_octets {
# First parameter: Source ip address
my $sip = $_[0];
# Second parameter: Bitlength of network (aka CIDR notation).
my $cidr = $_[1];
my @decimals = split('\.', $sip);
my $index = int($cidr / 8) ;
my $result = '';
for(my $i = 0; $i < $index; $i++)
{
# Convert each number in ip address to hex and format with leading
# zero in case converted number is less than 16.
$result = $result . sprintf("%02x", $decimals[$i]);
}
return $result;
}
El código Perl se puede modificar desde aquí, por lo que la opción 121oLa opción 249 se envía dependiendo del sistema operativo del cliente.
También dejo la posibilidad de hacer el código más genérico, de modo que las rutas estáticas se puedan definir directamente en el archivo de configuración de Freeradius para el lector.