OpenVPN con autenticación y autorización avanzadas

El artículo analiza la configuración de OpenVPN con características adicionales:

  • Certificados de token para autenticación primaria (Rutoken como ejemplo)
  • Servidor de fondo LDAP para autenticación secundaria (usando ActiveDirectory como ejemplo)
  • filtrado de recursos internos disponibles para los usuarios (a través de iptables)

También describe cómo configurar clientes para Linux, Windows y MacOS.

Configuración del servidor


Instalar OpenVPN


Tome el script Nyr / openvpn-install , ejecute desde la raíz.

git clone https://github.com/Nyr/openvpn-install.git cd openvpn-install 

El proceso de inicio hará algunas preguntas.

  • protocolo udp
  • puerto 1194
  • Servidores DNS - Local
  • ip externa: dirección de puerta de enlace en Internet a través de la cual estará disponible el servidor vpn

También hay una versión de seguridad mejorada del script original: github.com/Angristan/OpenVPN-install . Tiene más configuraciones de cifrado con explicaciones de por qué.

Gestión de usuarios


Agregando
Si no se usan tokens, el usuario se agrega a través del mismo script. El script esencialmente genera una configuración ovpn personalizada e inserta un certificado firmado por el certificado raíz allí.

Si se utilizan tokens (consulte la sección sobre tokens a continuación), el certificado se redacta manualmente en función de la solicitud del certificado que se genera en el token. La configuración del usuario debe realizarse manualmente desde la plantilla existente (desde la misma desde la que se genera el script de configuración). La plantilla está aquí /etc/openvpn/client-common.txt . No está incluido en el paquete openvpn y es generado por el script durante el proceso de configuración.

Eliminar
La eliminación de usuarios se realiza a través del mismo script de instalación. El certificado se agrega a la CRL , la nueva CRL se envía al servidor vpn. El servidor considera que todos los certificados que están en CRL no son válidos y se niega a aceptar.

Cómo revocar un certificado manualmente:

 cd /etc/openvpn/easyrsa #   ./easyrsa revoke $CLIENT #   crl ./easyrsa gen-crl #   crl rm -rf /etc/openvpn/crl.pem #    cp /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn/crl.pem # openvpn    crl,       nobody chown nobody:nobody /etc/openvpn/crl.pem 

Filtrado de hosts disponibles para clientes


Los clientes deben estar limitados por los hosts a los que pueden acceder en la red cuando se conectan a openvpn.

Manualmente

La idea es capturar paquetes incluso en la interfaz tun0 , en la que provienen de los clientes y filtrarlos antes de que entren en NAT. Después de NAT, no habrá razón para filtrarlos: todos tendrán una dirección IP de servidor openvpn en la red interna. Antes de ingresar a NAT, los paquetes para cada usuario tienen su propia dirección IP única (para la correspondencia de direcciones IP y usuarios, consulte el archivo /etc/openvpn/ipp.txt ).

Los paquetes que pasan a través del sistema (no provienen directamente de él y no son entrantes, es decir, de hecho son enrutados por el sistema) son procesados ​​por la tabla FORWARD. Las tablas en iptables se procesan de arriba a abajo, si ninguna de las reglas de la tabla llevó a una decisión sobre el destino del paquete, entonces se activa la regla predeterminada.

Preparando la tabla ADELANTE:

 #   iptables -F FORWARD #     FORWARD -    iptables -P FORWARD DROP #     iptables -I FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT 

Reglas de ejemplo para un cliente específico. Dado que la regla predeterminada para la tabla es DROP, solo queda permitir esos pares de host + puerto donde pueda. Permita el acceso al puerto en el host + haga ping al host en sí:

 iptables -I FORWARD -s 10.8.0.3 -i tun0 -d 10.0.2.3 -p tcp --dport 443 -j ACCEPT iptables -I FORWARD -s 10.8.0.3 -i tun0 -d 10.0.2.3 -p icmp --icmp-type echo-request -j ACCEPT 

En el ejemplo anterior, el host 10.8.0.3 tiene acceso permitido al puerto 443 del host 10.0.2.3.

Cómo cerrar el acceso:

 #         iptables -L FORWARD --line-numbers #     iptables -D FORWARD { } 

Luego debe encontrar todas las reglas para un cliente en particular y eliminarlas.

Durante la depuración, es conveniente observar qué reglas funcionan. Cada regla tiene un contador de paquetes procesados.

 #  ,      watch iptables -nvL FORWARD #     iptables -Z FORWARD 

Automáticamente

Un servidor openvpn tiene la capacidad de ejecutar scripts para ciertas acciones. En particular, al conectar y desconectar clientes. Los scripts se pueden escribir en cualquier cosa si solo son ejecutables. Dentro del script, las variables de entorno pasan todo tipo de parámetros a la conexión actual. Nos interesan las variables:

  • common_name (nombre del propietario del certificado; lo que conduce al campo de nombre común al crear el certificado)
  • ifconfig_pool_remote_ip (dirección IP del cliente en tun0)
  • script_type (qué evento ocurrió: conectar o desconectar).

Necesita privilegios de root para administrar iptables. Openvpn después de conectarse restablece los derechos a nadie y ejecuta scripts desde él. Es malo no permitir que nadie haga algo por debajo de sudo, y es mejor no usar un asterisco en las reglas, pero de alguna manera debes permitir que el usuario controle iptables.

 # /etc/sudoers.d/50_openvpn # #    nobody ALL = NOPASSWD: /sbin/iptables -A FORWARD* #     nobody ALL = NOPASSWD: /sbin/iptables -L FORWARD* #    nobody ALL = NOPASSWD: /sbin/iptables -D FORWARD* 

En la configuración del servidor, debe agregar permiso para ejecutar archivos de terceros y habilitar dos enlaces responsables de conectar y desconectar al usuario.

 script-security 2 client-connect /etc/openvpn/bin/hosts.rb client-disconnect /etc/openvpn/bin/hosts.rb 

El script en sí, que lee las configuraciones y aplica las reglas para iptables. El guión funciona con los mismos principios que se describen en la sección anterior.

/openvpn/bin/hosts.rb
 #!/usr/bin/ruby # -*- coding: utf-8 -*- require 'pp' def log(string) puts 'hosts.rb: ' + string end def parse_config_file(name) config_path = "hosts/#{name}" unless File.exist?(config_path) puts "There is no specific configuration for #{name}." p name exit 0 end config_source = IO.read(config_path).split("\n") config = config_source.inject([]) do |result,line| ip, port, protocol = line.split(/\s+/) result << { ip: ip, port: port, protocol: protocol || 'tcp' } end end def get_config(name) user_config = parse_config_file(name) if user_config everybody_config = parse_config_file('everybody') end everybody_config + user_config end def apply_rule(rule) command = "sudo iptables #{rule}" log(command) system(command) end def remove_rule(number) command = "sudo iptables -D FORWARD #{number}" log(command) system(command) end def allow_target(source_ip, options) #         . apply_rule("-A FORWARD -s #{source_ip} -i tun0 -d #{options[:ip]} -p #{options[:protocol]} --dport #{options[:port]} -j ACCEPT") #       apply_rule("-A FORWARD -s #{source_ip} -i tun0 -d #{options[:ip]} -p icmp --icmp-type echo-request -j ACCEPT") end def clear_targets(source_ip) #      FORWARD,  source_ip. rules_exist = true while rules_exist table = `sudo iptables -L FORWARD --line-number`.split("\n") the_line = table.find do |line| fields = line.split(/\s+/) ip = fields[4] ip == source_ip end if the_line number = the_line.split(/\s+/)[0] remove_rule(number) else rules_exist = false end end end ################################################################################ script_type = ENV['script_type'] log(script_type) name = ENV['common_name'] source_ip = ENV['ifconfig_pool_remote_ip'] case script_type when 'client-connect' config = get_config(name) config.each{|target| allow_target(source_ip, target)} when 'client-disconnect' clear_targets(source_ip) else puts "Unknown script type #{script_type}." end 

Las reglas se almacenan en archivos correspondientes al nombre común de los certificados en la carpeta /etc/openvpn/hosts . Especifican exactamente qué direcciones IP están disponibles para un cliente en particular. Separador: un número arbitrario de espacios. La dirección IP, el puerto y el protocolo (tcp o udp) se escriben a través del separador.

 10.0.0.24 53 udp 10.0.0.25 53 udp 10.0.2.3 443 tcp 

Como resultado, la siguiente estructura debería /etc/openvpn en la carpeta /etc/openvpn

├── bin
│ └── hosts.rb
├── anfitriones
│ ├── usuario1
│ ├── usuario2
│ └── todos
├── server.conf
└── ...

User1 y user2 son archivos en el formato anterior. Describen a qué hosts tiene acceso el usuario con el nombre común correspondiente.

Existe otro archivo adicional para everybody , que contiene reglas que se aplican a todos los clientes, siempre que haya un archivo de configuración separado para estos clientes. Es decir, si el usuario tiene una lista de hosts a los que puede ir, everybody aplicará esta lista y los hosts que figuran en everybody . Si no, entonces no everybody aplicables. Por ejemplo, es conveniente colocar un servidor DNS en este archivo.

Registro

El script de instalación incluye solo el registro de las conexiones actuales (parámetro de status) . Para que aparezca un registro regular, debe agregar una línea a la configuración del servidor ( /etc/openvpn/server.conf ):
 log-append /var/log/openvpn.log 


LDAP

Existe un complemento openvpn-auth-ldap que le permite autenticar a un usuario nuevamente a través de LDAP.

Entregar paquete:

 sudo yum install openvpn-auth-ldap 

Añadir a server.conf:

 plugin /usr/lib64/openvpn/plugin/lib/openvpn-auth-ldap.so "/etc/openvpn/ldap.conf" 

Cree una configuración para ldap en /etc/openvpn/ldap.conf :
 <LDAP> URL ldaps://{LDAP_DOMAIN_HERE} Timeout 15 TLSEnable no FollowReferrals yes BindDN "BIND_DN_HERE" Password "BIND_PASSWORD_HERE" </LDAP> <Authorization> BaseDN "{BASE_DN_HERE}" SearchFilter "(&(sAMAccountName=%u)(objectClass=organizationalPerson)(objectCategory=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))" RequireGroup false </Authorization> 

Agregue la línea a la configuración personalizada de ovpn:

 auth-user-pass 

Por lo tanto, primero se le pedirá al usuario el nombre de usuario y la contraseña del dominio, luego el PIN del token. Si uno de estos pasos falla, la conexión no se establecerá.

La descripción de las opciones para ldap.conf está en el repositorio de complementos . Es compatible con la autenticación por membresía de grupo, pero no lo he probado.

Velocidad


El mayor aumento de velocidad da la inclusión del modo udp. Esto se recomienda en todos los manuales. El punto es que no tiene sentido iniciar una conexión de cliente tcp en un canal tcp. Un tcp en el cliente es suficiente para hacer la entrega correcta de paquetes. Si se pierden paquetes en el canal udp, la conexión tcp del cliente controlará el ajuste de entrega.

La velocidad aumentará al menos porque no hay necesidad de esperar la confirmación de la entrega de cada paquete en el canal. Hay un segundo problema con tcp: un paquete tcp de cliente probablemente no cabe en un paquete del canal vpn. MTU es igual, pero se deben agregar encabezados al paquete del cliente. Como resultado, debe enviar dos paquetes dentro del canal vpn por paquete de usuario.

TCP tiene sentido usar cuando es imposible de otra manera. Por ejemplo, cuando vpn funciona a través del canal ssh.

Ejemplo de una configuración completa del servidor


ejemplo-servidor.conf
 port 1194 proto tcp dev tun sndbuf 0 rcvbuf 0 ca ca.crt cert server.crt key server.key dh dh.pem tls-auth ta.key 0 topology subnet server 10.8.0.0 255.255.255.0 ifconfig-pool-persist ipp.txt push "redirect-gateway def1 bypass-dhcp" push "dhcp-option DNS 10.0.0.25" push "dhcp-option DNS 10.0.0.24" keepalive 10 120 cipher AES-256-CBC comp-lzo user nobody group nobody persist-key persist-tun status openvpn-status.log verb 3 crl-verify crl.pem log-append /var/log/openvpn.log script-security 2 client-connect /etc/openvpn/bin/hosts.rb client-disconnect /etc/openvpn/bin/hosts.rb 


Configuración de token


Biblioteca PKCS # 11


Para trabajar con tokens, necesitas una biblioteca especial. La biblioteca es necesaria tanto para crear pares de claves como para la conexión real. Descargar para todas las plataformas por el enlace .

Dondequiera que se encuentre librtpkcs11ecp.so más adelante, esta es la biblioteca que debe descargarse y colocarse en un lugar conveniente.

Crear un certificado en un token


Genere un par de claves en el token. El parámetro id aquí es el número de serie de la ranura en el token donde encaja el par de claves.

 pkcs11-tool --module /usr/lib64/librtpkcs11ecp.so --keypairgen --key-type rsa:2048 -l --id 01 

Realice una solicitud de certificado para la clave pública. En el proceso de creación de una solicitud de certificado, se establece la duración del certificado y el nombre común, que se utiliza para filtrar las direcciones IP disponibles dentro de la red. El nombre común debe coincidir con el inicio de sesión en ActiveDirectory para que no haya confusión.

 openssl openssl> engine -t dynamic -pre SO_PATH:/usr/lib64/openssl/engines/pkcs11.so -pre ID:pkcs11 -pre LIST_ADD:1 -pre LOAD -pre MODULE_PATH:/usr/lib64/librtpkcs11ecp.so openssl> req -engine pkcs11 -new -key slot_0-id_01 -keyform engine -out /home/john/good.req 

La solicitud recibida se debe mover a la /etc/openvpn/easy-rsa/pki/reqs/ . La extensión del archivo debe ser req .
Convertir una solicitud en un certificado:

 cd /etc/openvpn/easy-rsa/ ./easyrsa sign-req client good 

Después de eso, aparecerá un certificado con el mismo nombre pero con la extensión crt en la carpeta /etc/openvpn/easy-rsa/pki/issued/ .

Antes de grabar, el certificado debe convertirse a DER:

 openssl x509 -in /home/user/user-cert.pem -out /home/user/user-cert.crt -outform DER 

Escribir un certificado para un token:

 pkcs11-tool --module /usr/lib/librtpkcs11ecp.so -l -y cert -w /home/user/user-cert.crt --id 45 --label TEST 

Está escrito sobre la base del artículo "Uso de Rutoken EDS con OpenSSL (RSA)" .

Uso de token para autenticación


Encuentre la identificación del certificado que se presentará al servidor:

 $ openvpn --show-pkcs11-ids /usr/lib64/librtpkcs11ecp.so The following objects are available for use. Each object shown below may be used as parameter to --pkcs11-id option please remember to use single quote mark. Certificate DN: /CN=User1 Serial: 490B82C4000000000075 Serialized id: aaaa/bbb/41545F5349474E415455524581D2A1A1B23C4AA4CB17FAF7A4600 

Estamos interesados ​​en la identificación serializada aquí.

Opciones que deben ingresarse en la configuración de ovpn para que los tokens recojan:

 pkcs11-providers /usr/lib64/librtpkcs11ecp.so pkcs11-id 'aaaa/bbb/41545F5349474E415455524581D2A1A1B23C4AA4CB17FAF7A4600' 

La opción pkcs11-id debe estar entre comillas simples.

Este manual tiene sentido en todas las plataformas. Debe especificar la ruta a la biblioteca y la identificación del certificado en el token. La biblioteca puede llamarse de manera un poco diferente, ser .dll , no .so , pero el significado es el mismo.

En este caso, debe eliminar las secciones cert y key del archivo ovpn, ya que el certificado y la clave privada se tomarán del token.

La configuración completa del cliente (para Windows) se ve así:

cliente.ovpn
client
dev tun
proto tcp
sndbuf 0
rcvbuf 0
remote 78.47.37.247 22222
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-CBC
comp-lzo
setenv opt block-outside-dns
key-direction 1
verb 3

pkcs11-providers "c://Windows//System32//rtPKCS11ECP.dll"
pkcs11-id 'Aktiv\x20Co\x2E/Rutoken\x20ECP/342b871d/Rutoken/01'

-----BEGIN CERTIFICATE-----
{CERT_HERE}
-----END CERTIFICATE-----


<tls-auth>
#
# 2048 bit OpenVPN static key
#
-----BEGIN OpenVPN Static key V1-----
{KEY_HERE}
-----END OpenVPN Static key V1-----
</tls-auth>


Escrito en base a "Cómo agregar autenticación de doble factor a una configuración de OpenVPN usando tarjetas inteligentes del lado del cliente" .

Configuración del cliente


Linux


Openvpn tiene un error que impide que el usuario ingrese el código PIN del token si el paquete está construido con soporte systemd. Como systemd ha estado en todas partes últimamente, todos los paquetes que ya están disponibles en los repositorios se compilan con su soporte. Los clientes en Linux necesitan recolectar el paquete por su cuenta. Aquí hay un ejemplo de configuración que funcionó para mí en Arch Linux:

 ./configure \ --prefix=/usr \ --sbindir=/usr/bin \ --enable-iproute2 \ --enable-pkcs11 \ --enable-plugins \ --enable-x509-alt-username 

Puede verificar que openvpn se compila con o sin systemd utilizando el siguiente comando:

 openvpn --version | grep --color enable_systemd 

Mas os


En Mac OS, solo hay un cliente gratuito: Tunnelblink .

No sabe cómo ingresar un código PIN desde un token desde la interfaz gráfica de usuario. El error se describe, por ejemplo, aquí: https://groups.google.com/forum/#!topic/tunnelblick-discuss/f_Rp_2nV-x8 Se omite al iniciar openvpn desde la consola. Esto no es sorprendente, dado que el cliente oficial para Windows tampoco lo sabe.

También en Mac OS (a diferencia de Windows) se requieren scripts adicionales para configurar la red. Si simplemente ejecuta openvpn desde la consola, entonces DNS no funcionará (quizás algo más, solo aparecerá DNS).

TunnelBlick tiene estos scripts de configuración de red, solo es necesario invocarlos al establecer y desconectar la conexión. Lo que necesita agregar a la configuración de ovpn:

 script-security 2 up "/Applications/Tunnelblick.app/Contents/Resources/client.up.tunnelblick.sh -9 -d -f -m -w -ptADGNWradsgnw" down "/Applications/Tunnelblick.app/Contents/Resources/client.down.tunnelblick.sh -9 -d -f -m -w -ptADGNWradsgnw" 

Un script de ejemplo para iniciar una conexión openvpn, que se puede colocar en el escritorio y pinchar con el mouse:

 #!/bin/bash tunnelblick=/Applications/Tunnelblick.app/Contents/Resources/openvpn/openvpn-2.4.2-openssl-1.0.2k sudo $tunnelblick/openvpn --config $tunnelblick/user.ovpn 

Ventanas


Debajo de Windows, todo parece funcionar. El cliente oficial no sabe cómo ingresar el código PIN desde el token, logra abrir vpn manualmente desde la consola.

Lo más importante es hacer todo desde debajo del administrador. Ejecute el instalador del cliente como administrador. Inicie un terminal en el que openvpn se inicie también con derechos de administrador; de lo contrario, no podrá controlar la interfaz de red.

En Windows, la ruta a la biblioteca para trabajar con tokens se debe registrar mediante barras diagonales dobles. Esto se aplica tanto a la configuración de ovpn como a la opción --show-pkcs11-ids en la línea de comandos.

 pkcs11-providers "c://Windows//System32//rtPKCS11ECP.dll" pkcs11-id 'Aktiv\x20Co\x2E/Rutoken\x20ECP/342b871d/Rutoken/01' 

Source: https://habr.com/ru/post/es433250/


All Articles