|
|
|
# Migración de servidor OpenVPN desde Debian Stretch a Buster |
|
|
\ No newline at end of file |
|
|
|
## Introducción
|
|
|
|
|
|
|
|
El actual servicio OpenVPN provisto en la plataforma del interior, fue aprovisionado completamente con Ansible. Desde puesta en producción, [hasta la gestión de usuarios (conceder/revocar accesos)](Conceder-revocar-credenciales-de-acceso-a-servidor-OpenVPN), es uno de nuestros servicios completamente automatizados.
|
|
|
|
|
|
|
|
Una vez liberada Debian Buster y actualizados nuestros servidores físicos, [nos disponemos a hacer las actualizaciones](actualizacion-lxc-en-pve6-upgrade-a-Buster.) pertinentes a *playbooks* y *roles* implicados para poder actualizar el sistema operativo del hosts sin perder la utilidad de las herramientas Ansible y respetando el paradigma [IaaC](la-importancia-de-la-rama-estable).
|
|
|
|
|
|
|
|
En el caso concreto del servidor OpenVPN, para el aprovisionamiento de nuestra primer instancia (sobre Stretch) partimos de [un role comunitario](https://github.com/Stouts/Stouts.openvpn) sobre el que implementamos soporte para IPv6 y otras cuantas mejoras puntuales. Para cuando estas mejoras fueron enviadas al role comunitario, su mantenedor ya había abandonado el proyecto. Finalmente se utilizó [una versión propia](https://github.com/UdelaRInterior/Stouts.openvpn/tree/v1.0.0-udelarinterior) para la primer puesta en producción.
|
|
|
|
|
|
|
|
Retomando el desarrollo del role para soportar Buster, [no encontramos con que el proyecto comunitario había sido retomado](https://github.com/UdelaRInterior/Stouts.openvpn/pull/2#pullrequestreview-356020211) por otra persona y avanzado bastante, particularmente en la gestión de claves criptográficas de los usuarios con una nueva versión de Easy-RSA.
|
|
|
|
|
|
|
|
Estos cambios implicaban una gran mejoría (particularmente de seguridad), pero también la pérdia de compatibilidad con nuestro desarrollo y la estructura de llaves del servidor en producción. Pese a ello, a largo plazo siempre es preferible acompasarse al desarrollo comunitario que ser mantenedor solitario de un *fork*.
|
|
|
|
|
|
|
|
Como resultado, readaptamos todas nuestra mejoras a la nueva estructura del role y las enviamos en PR ([#151](https://github.com/Stouts/Stouts.openvpn/pull/151), [#158](https://github.com/Stouts/Stouts.openvpn/pull/158)). Al momento de escribir este texto, dichos cambios se encuentran en proceso de revisión. A la espera de que sean fusionados, comenzamos la planifición de migración de nuestro servidor en producción
|
|
|
|
|
|
|
|
|
|
|
|
## Plan
|
|
|
|
|
|
|
|
El servidor OpenVPN en producción se encuentra en un host independiente (contenedor LXC) creado también a partir de nuestros *playbooks* Ansible, por lo cual todo su contenido está representado y es generable a través de código (a excepción de las claves criptográficas, ver párrafo siguiente). Esto nos permitirá realizar la migración rápidamente y de la forma mas limpia: destruir el antiguo contenedor Strectch (previo bacukup Proxmox por prevención) y reemplazarlo por uno nuevo a partir de una imagen de Buster.
|
|
|
|
|
|
|
|
Lo único que es necesario respaldar del contenedor a destruir para recuperar en el nuevo, son las claves criptográficas del servidor y de los clientes. Esto con el objetivo de que la migración sea invisible para los usuarios. Bien podrían generarse llaves nuevas para todos los usuarios y distribuirlas a sus propietarios para que reconfiguren sus clientes.
|
|
|
|
|
|
|
|
Adicionalmente al respaldo, tal como se comentaba en la introducción, será necesario reestructurar el árbol de directorios para hacerlp compatible con la nueva estructura. Vamos a ello.
|
|
|
|
|
|
|
|
|
|
|
|
### Respaldo y reestructuración de claves
|
|
|
|
|
|
|
|
Todos los archivos que competen a la instancia de OpenVPN se encuentran en el directorio `/etc/openvpn`. Previo a migrar, la esctructura es la siguiente:
|
|
|
|
```
|
|
|
|
/etc/openvpn
|
|
|
|
├── build-client.sh
|
|
|
|
├── build-server.sh
|
|
|
|
├── client
|
|
|
|
├── ipp.txt
|
|
|
|
├── keys
|
|
|
|
│ ├── 00.pem
|
|
|
|
│ ├── 01.pem
|
|
|
|
│ ├── 02.pem
|
|
|
|
│ ├── 03.pem
|
|
|
|
│ ├── 04.pem
|
|
|
|
│ ├── ca.crt
|
|
|
|
│ ├── ca.key
|
|
|
|
│ ├── dh2048.pem
|
|
|
|
│ ├── index.txt
|
|
|
|
│ ├── index.txt.attr
|
|
|
|
│ ├── index.txt.attr.old
|
|
|
|
│ ├── index.txt.old
|
|
|
|
│ ├── serial
|
|
|
|
│ ├── serial.old
|
|
|
|
│ ├── server.crt
|
|
|
|
│ ├── server.csr
|
|
|
|
│ ├── server.key
|
|
|
|
│ ├── ta.key
|
|
|
|
│ ├── usuario1.crt
|
|
|
|
│ ├── usuario1.csr
|
|
|
|
│ ├── usuario1.key
|
|
|
|
│ ├── usuario1.ovpn
|
|
|
|
│ ├── usuario1.zip
|
|
|
|
│ ├── usuario2.crt
|
|
|
|
│ ├── usuario2.csr
|
|
|
|
│ ├── usuario2.key
|
|
|
|
│ ├── usuario2.ovpn
|
|
|
|
│ ├── usuario2.zip
|
|
|
|
│ ├── usuario3.crt
|
|
|
|
│ ├── usuario3.csr
|
|
|
|
│ ├── usuario3.key
|
|
|
|
│ ├── usuario3.ovpn
|
|
|
|
│ ├── usuario3.zip
|
|
|
|
│ ├── usuario4.crt
|
|
|
|
│ ├── usuario4.csr
|
|
|
|
│ ├── usuario4.key
|
|
|
|
│ ├── usuario4.ovpn
|
|
|
|
│ └── usuario4.zip
|
|
|
|
├── openvpn-status.log
|
|
|
|
├── revoke-client.sh
|
|
|
|
├── server
|
|
|
|
├── server.conf
|
|
|
|
├── update-resolv-conf
|
|
|
|
└── vars
|
|
|
|
```
|
|
|
|
|
|
|
|
Ejecutando la nueva versión del role (que emplea Easy-RSA 3.X en lugar de la 2.X) sobre un host de *staging* para crear los mismos usuarios que existen en el de producción, determinamos la nueva estructura a utilizar:
|
|
|
|
```
|
|
|
|
/etc/openvpn
|
|
|
|
├── ccd
|
|
|
|
├── client
|
|
|
|
├── ipp.txt
|
|
|
|
├── keys
|
|
|
|
│ ├── ca.crt
|
|
|
|
│ ├── certs_by_serial
|
|
|
|
│ │ ├── 145692A7680221D6F22D27CC1F487CC1.pem
|
|
|
|
│ │ ├── 234311A456BD9189BC7A0C1A90F91A79.pem
|
|
|
|
│ │ ├── 658676756BD9189BC324436654345643.pem
|
|
|
|
│ │ ├── 3D07FECD16A6B1C889DD54EF4D86344C.pem
|
|
|
|
│ │ └── 398CB17B8E3822DEA04CF8B2B933735D.pem
|
|
|
|
│ ├── dh.pem
|
|
|
|
│ ├── index.txt
|
|
|
|
│ ├── index.txt.attr
|
|
|
|
│ ├── index.txt.attr.old
|
|
|
|
│ ├── index.txt.old
|
|
|
|
│ ├── issued
|
|
|
|
│ │ ├── server.crt
|
|
|
|
│ │ ├── usuario1.crt
|
|
|
|
│ │ ├── usuario2.crt
|
|
|
|
│ │ ├── usuario3.crt
|
|
|
|
│ │ └── usuario4.crt
|
|
|
|
│ ├── private
|
|
|
|
│ │ ├── ca.key
|
|
|
|
│ │ ├── server.key
|
|
|
|
│ │ ├── usuario1.key
|
|
|
|
│ │ ├── usuario2.key
|
|
|
|
│ │ ├── usuario3.key
|
|
|
|
│ │ └── usuario4.key
|
|
|
|
│ ├── renewed
|
|
|
|
│ │ ├── certs_by_serial
|
|
|
|
│ │ ├── private_by_serial
|
|
|
|
│ │ └── reqs_by_serial
|
|
|
|
│ ├── reqs
|
|
|
|
│ │ ├── server.req
|
|
|
|
│ │ ├── usuario1.req
|
|
|
|
│ │ ├── usuario2.req
|
|
|
|
│ │ ├── usuario3.req
|
|
|
|
│ │ └── usuario4.req
|
|
|
|
│ ├── revoked
|
|
|
|
│ │ ├── certs_by_serial
|
|
|
|
│ │ ├── private_by_serial
|
|
|
|
│ │ └── reqs_by_serial
|
|
|
|
│ ├── serial
|
|
|
|
│ └── serial.old
|
|
|
|
├── keys.zip
|
|
|
|
├── openvpn-status.log
|
|
|
|
├── ovpns
|
|
|
|
│ ├── ta.key
|
|
|
|
│ ├── usuario1.ovpn
|
|
|
|
│ ├── usuario1.zip
|
|
|
|
│ ├── usuario2.ovpn
|
|
|
|
│ ├── usuario2.zip
|
|
|
|
│ ├── usuario3.ovpn
|
|
|
|
│ ├── usuario3.zip
|
|
|
|
│ ├── usuario4.ovpn
|
|
|
|
│ └── usuario4.zip
|
|
|
|
├── scripts
|
|
|
|
├── server
|
|
|
|
├── server.conf
|
|
|
|
└── update-resolv-conf
|
|
|
|
```
|
|
|
|
|
|
|
|
> Es importante destacar que en ambos casos los archivos `.zip` son generados automáticamente por el role para cada usuario a partir de sus `.crt`, `.key`, `.ovpn` más el `ca.crt` y `ta.key` del servidor.
|
|
|
|
|
|
|
|
A simple vista se observa que el mayor cambio de la nueva estructura, es el aumento de segmentación por tipo de archivo uitilizando nuevos directorios. Para adaptar esto bastará simplemente con crear los nuevos directorios y mover los archivos a sus nuevas ubicaciones.
|
|
|
|
|
|
|
|
Sin embargo, hay un cambio fundamental que tal vez no es tan perceptible: ahora los certificados `.pem` no se nombran con una secuencia hexadecimal de 2 dígitos sino de 32. Además, esos nombres son referenciados en el archivo `index.txt`, por lo que también será necesario actualizarlo
|
|
|
|
|
|
|
|
```
|
|
|
|
V 821643218736Z 01 unknown /C=UY/ST=MVD/L=Mvdeo/O=UdelaR/OU=MyOrg/CN=server/name=EasyRSA/emailAddress=user@mail.com
|
|
|
|
V 821643218748Z 02 unknown /C=UY/ST=MVD/L=Mvdeo/O=UdelaR/OU=MyOrg/CN=usuario1/name=EasyRSA/emailAddress=user@mail.com
|
|
|
|
V 821643218748Z 03 unknown /C=UY/ST=MVD/L=Mvdeo/O=UdelaR/OU=MyOrg/CN=usuario2/name=EasyRSA/emailAddress=user@mail.com
|
|
|
|
V 821643218748Z 04 unknown /C=UY/ST=MVD/L=Mvdeo/O=UdelaR/OU=MyOrg/CN=usuario3/name=EasyRSA/emailAddress=user@mail.com
|
|
|
|
V 821643218749Z 05 unknown /C=UY/ST=MVD/L=Mvdeo/O=UdelaR/OU=MyOrg/CN=usuario4/name=EasyRSA/emailAddress=user@mail.com
|
|
|
|
```
|
|
|
|
|
|
|
|
Será necesario por tanto, renombrar estos archivos con la cadena de 32 dígitos respectiva que se generó en el host de *staging* para luego actualizar su refeencia en el `index.txt`
|
|
|
|
|
|
|
|
|
|
|
|
## Manos a la obra
|
|
|
|
|
|
|
|
Comenzamos por recuperar el directorio `/etc/openvpn` del servidor en producción a un espacio de trabajo (por ej. `/root/recuperado`) en el host OpenVPN de *staging* que creamos previamente. Para la transferencia podemos utilizar herramientas como `rsync` o `scp`.
|
|
|
|
|
|
|
|
En el espacio de trabajo, creamos la estructura de carpetas mínima que nos compete para esta migración
|
|
|
|
|
|
|
|
```
|
|
|
|
root@staging-host:~# mkdir reestructura
|
|
|
|
root@staging-host:~# mkdir reestructura/ovpns
|
|
|
|
root@staging-host:~# mkdir reestructura/keys
|
|
|
|
root@staging-host:~# mkdir reestructura/keys/certs_by_serial
|
|
|
|
root@staging-host:~# mkdir reestructura/keys/issued
|
|
|
|
root@staging-host:~# mkdir reestructura/keys/private
|
|
|
|
root@staging-host:~# mkdir reestructura/keys/reqs
|
|
|
|
```
|
|
|
|
|
|
|
|
Comenzamos a copiar todo los archivos recuperados en su directorio correspondiente:
|
|
|
|
```
|
|
|
|
root@staging-host:~# cp recuperado/keys/ca.key reestructura/keys/
|
|
|
|
root@staging-host:~# cp recuperado/keys/ta.key reestructura/keys/
|
|
|
|
|
|
|
|
root@staging-host:~# cp recuperado/keys/*.pem reestructura/keys/certs_by_serial/
|
|
|
|
root@staging-host:~# cp recuperado/keys/*.crt reestructura/keys/issued/
|
|
|
|
root@staging-host:~# cp recuperado/keys/*.key reestructura/keys/private/
|
|
|
|
root@staging-host:~# cp recuperado/keys/*.csr reestructura/keys/reqs/
|
|
|
|
|
|
|
|
# Notar que aquí también aprovechamos a renombrar
|
|
|
|
root@staging-host:~# cp recuperado/keys/dh2048.pem reestructura/keys/dh.pem
|
|
|
|
|
|
|
|
# Los archivos restantes para completar la nueva estructura, los
|
|
|
|
# traemos desde los archivos generados para el servidor de staging
|
|
|
|
root@staging-host:~# cp /etc/openvpn/keys/index.* reestructura/keys/
|
|
|
|
root@staging-host:~# cp /etc/openvpn/keys/serial* reestructura/keys/
|
|
|
|
root@staging-host:~# cp -R /etc/openvpn/keys/revoked reestructura/keys/
|
|
|
|
```
|
|
|
|
|
|
|
|
Una vez copiados todos los archivos necesarios, procedemos a renombrar los certificados `.pem` ubicados en `reestructura/keys/certs_by_serial/` con el nombre que le fue asignado en el archivo `reestructura/keys/index.txt`, que luce así:
|
|
|
|
```
|
|
|
|
V 216432187157Z ED9B8EFEB89AF0EEE2BEBAFC451A5699 unknown /C=UY/ST=MVD/L=Mvdeo/O=UdelaR/OU=MyOrg/CN=server/emailAddress=user@mail.com
|
|
|
|
V 216432187158Z 99B505AA3B46E1CE94F8FB505C7B82E8 unknown /C=UY/ST=MVD/L=Mvdeo/O=UdelaR/OU=MyOrg/CN=usuario1/emailAddress=user@mail.com
|
|
|
|
V 216432187158Z A95D66EB290A629D4D7C9E999C926816 unknown /C=UY/ST=MVD/L=Mvdeo/O=UdelaR/OU=MyOrg/CN=usuario2/emailAddress=user@mail.com
|
|
|
|
V 216432187159Z 6382D6FE78B556E5EC98225373348F4E unknown /C=UY/ST=MVD/L=Mvdeo/O=UdelaR/OU=MyOrg/CN=usuario3/emailAddress=user@mail.com
|
|
|
|
V 216432187200Z F98CB17B8E3822DE804CF8B2B933735D unknown /C=UY/ST=MVD/L=Mvdeo/O=UdelaR/OU=MyOrg/CN=usuario4/emailAddress=user@mail.com
|
|
|
|
```
|
|
|
|
|
|
|
|
Por cada fila, en `CN=...` obtenemos el identificador de usuario, y en la tercer columna el nombre que debe tener su archivo `.pem`
|
|
|
|
|
|
|
|
Para saber a que usuario corresponde cada `reestructura/keys/certs_by_serial/XX.pem` a renombrar, tenemos dos alternativas:
|
|
|
|
* mirar la correspondencia en el `index.txt` del servidor en producción
|
|
|
|
* utilizar el comando compuesto `openssl x509 -in <<< ARCHIVO .PEM >>> --subject | head -1 | awk '{ print $18 }'` para cada archivo
|
|
|
|
|
|
|
|
|
|
|
|
Por último solo nos queda renombrar la extensión de los archivos almacenados en `reestructura/keys/reqs` de `.csr` a `.req`. Esto podemos hacerlo fácil y rápido con el utilitario `rename` de la siguiente manera:
|
|
|
|
|
|
|
|
```
|
|
|
|
root@staging-host:~/reestructura/keys/reqs# rename 's/\.csr/\.req/' *.csr
|
|
|
|
```
|
|
|
|
|
|
|
|
Concluída la reestructuración, el resultado final luce así:
|
|
|
|
```
|
|
|
|
~/reestructura
|
|
|
|
├── keys
|
|
|
|
│ ├── ca.crt
|
|
|
|
│ ├── certs_by_serial
|
|
|
|
│ │ ├── ED9B8EFEB89AF0EEE2BEBAFC451A5699.pem
|
|
|
|
│ │ ├── 99B505AA3B46E1CE94F8FB505C7B82E8.pem
|
|
|
|
│ │ ├── A95D66EB290A629D4D7C9E999C926816.pem
|
|
|
|
│ │ ├── 6382D6FE78B556E5EC98225373348F4E.pem
|
|
|
|
│ │ └── F98CB17B8E3822DE804CF8B2B933735D.pem
|
|
|
|
│ ├── dh.pem
|
|
|
|
│ ├── index.txt
|
|
|
|
│ ├── index.txt.attr
|
|
|
|
│ ├── index.txt.attr.old
|
|
|
|
│ ├── index.txt.old
|
|
|
|
│ ├── issued
|
|
|
|
│ │ ├── server.crt
|
|
|
|
│ │ ├── usuario1.crt
|
|
|
|
│ │ ├── usuario2.crt
|
|
|
|
│ │ ├── usuario3.crt
|
|
|
|
│ │ └── usuario4.crt
|
|
|
|
│ ├── private
|
|
|
|
│ │ ├── ca.key
|
|
|
|
│ │ ├── server.key
|
|
|
|
│ │ ├── usuario1.key
|
|
|
|
│ │ ├── usuario2.key
|
|
|
|
│ │ ├── usuario3.key
|
|
|
|
│ │ └── usuario4.key
|
|
|
|
│ ├── renewed
|
|
|
|
│ │ ├── certs_by_serial
|
|
|
|
│ │ ├── private_by_serial
|
|
|
|
│ │ └── reqs_by_serial
|
|
|
|
│ ├── reqs
|
|
|
|
│ │ ├── server.req
|
|
|
|
│ │ ├── usuario1.req
|
|
|
|
│ │ ├── usuario2.req
|
|
|
|
│ │ ├── usuario3.req
|
|
|
|
│ │ └── usuario4.req
|
|
|
|
│ ├── revoked
|
|
|
|
│ │ ├── certs_by_serial
|
|
|
|
│ │ ├── private_by_serial
|
|
|
|
│ │ └── reqs_by_serial
|
|
|
|
│ ├── serial
|
|
|
|
│ └── serial.old
|
|
|
|
└── ovpns
|
|
|
|
└── ta.key
|
|
|
|
```
|
|
|
|
|
|
|
|
> Podemos probar que todo resultó correctamente, sobreescribiendo el contendido de `/etc/openvpn` del servidor de *staging* con el de `~/reestructura` y comprobar que un cliente puede conectarse satisfactoriamente a este servidor utilizando sus llaves de producción
|
|
|
|
|
|
|
|
Con todo listo, solo queda recrear el nuevo servidor sobre un nuevo template de Debian Buster. De momento utilizaremos la [v2.0.0-udelarinterior.rc0](https://github.com/UdelaRInterior/Stouts.openvpn/tree/v2.0.0-udelarinterior.rc0) de nuestro role (mientras la proposiciones no sean fusionadas en el *upstream*) y los *playbook* desde la rama correspondiente de ***config*** para atender la tarea de migración de OpenVPN.
|
|
|
|
|
|
|
|
Los pasos finales serán:
|
|
|
|
* Realizar un ***backup Proxmox*** antes de comenzar la operación para tener un punto de retorno en caso de incidentes imprevistos
|
|
|
|
* Ejecutar nuestro *playbook* mayor, delimitado solo al host de interés, y salteando el *tag* `openvpn`
|
|
|
|
```
|
|
|
|
ansible-playbook --limit < fqdn del host en prod > --skip-tags "openvpn" site.yml
|
|
|
|
```
|
|
|
|
Una vez finalizado, tendremos un nuevo servidor Debian Buster aprovisionado con todos los usuarios y configuraciones típicas que se utilizan en la plataforma
|
|
|
|
* En el flamante contender, acceder por SSH y crear el directorio `/etc/openvpn`:
|
|
|
|
* Dentro de él, recuperar con `rsync` el contenido de `~/reestructura` desde el host utilizado en *staging*. Resultado:
|
|
|
|
```
|
|
|
|
/etc/openvpn
|
|
|
|
├── keys/
|
|
|
|
└── ovpns/
|
|
|
|
```
|
|
|
|
* Asegurar que `root` es propietario de toda esta arborescencia de archivos y directorios:
|
|
|
|
```
|
|
|
|
root@prod-host:~# chown root: -R /etc/openvpn
|
|
|
|
```
|
|
|
|
* Finalente, ejecutar el playbook mayor salteando todo menos el *tag* `openvpn`:
|
|
|
|
```
|
|
|
|
ansible-playbook --limit < fqdn del host en prod > --tags "openvpn" site.yml
|
|
|
|
```
|
|
|
|
De este modo concluirá la instalación y aprovisionamiento del nuevo OpenVPN server, sobre el juego de usuarios y llaves preexistente
|
|
|
|
|
|
|
|
|