Implementación de una plataforma segura de software NAS


En un artículo anterior , se describió el diseño de la plataforma de software NAS.
Es hora de implementarlo.


Cheque


Asegúrese de verificar el estado de la piscina antes de comenzar:


zpool status -v 

El grupo y todos los discos deben estar EN LÍNEA.


Además, supongo que en la etapa anterior todo se hizo de acuerdo con las instrucciones , y funciona, o usted mismo comprende bien lo que está haciendo.


Servicios


En primer lugar, vale la pena ocuparse de una gestión conveniente si no lo hizo desde el principio.
Será requerido:


  • Servidor SSH: apt-get install openssh-server . Si no sabe cómo configurar SSH, hacer NAS en Linux es demasiado temprano Puede leer las características de su uso en este artículo y luego usar uno de los manuales .
  • tmux o screen : apt-get install tmux . Para guardar la sesión al iniciar sesión a través de SSH y usar varias ventanas.

Después de instalar SSH, debe agregar un usuario para no iniciar sesión a través de SSH como root (la entrada está deshabilitada de forma predeterminada y no necesita habilitarla):


 zfs create rpool/home/user adduser user cp -a /etc/skel/.[!.]* /home/user chown -R user:user /home/user 

Para la administración remota, este es un mínimo suficiente.


Sin embargo, mientras necesita mantener el teclado y el monitor conectados, como También deberá reiniciar al actualizar el kernel y para asegurarse de que todo funciona inmediatamente después de la carga.


Una alternativa es usar Virtual KVM, que proporciona IME . Hay una consola allí, aunque en mi caso está implementada como un applet de Java, lo cual no es muy conveniente.


Personalización


Preparación de caché


Por lo que recuerdas, en la configuración descrita por mí hay un SSD separado bajo L2ARC, que aún no se usa, pero se toma "para el crecimiento".


Opcional, pero es recomendable llenar este SSD con datos aleatorios (en el caso de Samsung EVO, aún se rellenará con ceros después de blkdiscard, pero no en todos los SSD como este):


 dd if=/dev/urandom of=/dev/disk/by-id/ata-Samsung_SSD_850_EVO bs=4M && blkdiscard /dev/disk/by-id/ata-Samsung_SSD_850_EVO 

Deshabilitar la compresión de registro


En ZFS, la compresión ya se usa, porque la compresión de registros a través de gzip será claramente superflua.
Apagar:


 for file in /etc/logrotate.d/* ; do if grep -Eq "(^|[^#y])compress" "$file" ; then sed -i -r "s/(^|[^#y])(compress)/\1#\2/" "$file" fi done 

Actualización del sistema


Aquí todo es simple:


 apt-get dist-upgrade --yes reboot 

Crear una instantánea para un nuevo estado


Después de reiniciar, para corregir el nuevo estado de trabajo, debe volver a escribir la primera instantánea:


 zfs destroy rpool/ROOT/debian@install zfs snapshot rpool/ROOT/debian@install 

Organización del sistema de archivos


Particionamiento para SLOG


Lo primero que debe hacer para lograr el rendimiento normal de ZFS es colocar SLOG en el SSD.
Permítame recordarle que SLOG en la configuración utilizada está duplicado en dos SSD: para ello, los dispositivos en LUKS-XTS se crearán en la parte superior de la cuarta sección de cada SSD:


 dd if=/dev/urandom of=/etc/keys/slog.key bs=1 count=4096 cryptsetup --verbose --cipher "aes-xts-plain64:sha512" --key-size 512 --key-file /etc/keys/slog.key luksFormat /dev/disk/by-id/ata-Samsung_SSD_850_PRO-part4 cryptsetup --verbose --cipher "aes-xts-plain64:sha512" --key-size 512 --key-file /etc/keys/slog.key luksFormat /dev/disk/by-id/ata-Micron_1100-part4 echo "slog0_crypt1 /dev/disk/by-id/ata-Samsung_SSD_850_PRO-part4 /etc/keys/slog.key luks,discard" >> /etc/crypttab echo "slog0_crypt2 /dev/disk/by-id/ata-Micron_1100-part4 /etc/keys/slog.key luks,discard" >> /etc/crypttab 

Particionamiento para L2ARC y Swap


Primero necesitas crear particiones bajo swap y l2arc:


 sgdisk -n1:0:48G -t1:8200 -c1:part_swap -n2::196G -t2:8200 -c2:part_l2arc /dev/disk/by-id/ata-Samsung_SSD_850_EVO 

La partición de intercambio y L2ARC se cifrarán en una clave aleatoria, como después de un reinicio, no son necesarios y siempre es posible recrearlos.
Por lo tanto, en crypttab, se escribe una línea para cifrar / descifrar particiones en modo plano:


 echo swap_crypt /dev/disk/by-id/ata-Samsung_SSD_850_EVO-part1 /dev/urandom swap,cipher=aes-xts-plain64:sha512,size=512 >> /etc/crypttab echo l2arc_crypt /dev/disk/by-id/ata-Samsung_SSD_850_EVO-part2 /dev/urandom cipher=aes-xts-plain64:sha512,size=512 >> /etc/crypttab 

Luego debe reiniciar los demonios y habilitar el intercambio:


 echo 'vm.swappiness = 10' >> /etc/sysctl.conf sysctl vm.swappiness=10 systemctl daemon-reload systemctl start systemd-cryptsetup@swap_crypt.service echo /dev/mapper/swap_crypt none swap sw,discard 0 0 >> /etc/fstab swapon -av 

Porque swapiness no se usa activamente en el SSD; el parámetro swapiness , que es 60 por defecto, debe establecerse en 10.


L2ARC aún no se utiliza en esta etapa, pero la sección para él ya está lista:


 $ ls /dev/mapper/ control l2arc_crypt root_crypt1 root_crypt2 slog0_crypt1 slog0_crypt2 swap_crypt tank0_crypt0 tank0_crypt1 tank0_crypt2 tank0_crypt3 

Tanque de piscinasN


Se tank0 la creación del grupo tank0 , tank1 se crea por analogía.


Para no crear las mismas particiones manualmente y evitar errores, escribí un script para crear particiones cifradas para grupos:


create_crypt_pool.sh
 #!/bin/bash KEY_SIZE=512 POOL_NAME="$1" KEY_FILE="/etc/keys/${POOL_NAME}.key" LUKS_PARAMS="--verbose --cipher aes-xts-plain64:sha${KEY_SIZE} --key-size $KEY_SIZE" [ -z "$1" ] && { echo "Error: pool name empty!" ; exit 1; } shift [ -z "$*" ] && { echo "Error: devices list empty!" ; exit 1; } echo "Devices: $*" read -p "Is it ok? " a [ "$a" != "y" ] && { echo "Bye"; exit 1; } dd if=/dev/urandom of=$KEY_FILE bs=1 count=4096 phrase="?" read -s -p "Password: " phrase echo read -s -p "Repeat password: " phrase1 echo [ "$phrase" != "$phrase1" ] && { echo "Error: passwords is not equal!" ; exit 1; } echo "### $POOL_NAME" >> /etc/crypttab index=0 for i in $*; do echo "$phrase"|cryptsetup $LUKS_PARAMS luksFormat "$i" || exit 1 echo "$phrase"|cryptsetup luksAddKey "$i" $KEY_FILE || exit 1 dev_name="${POOL_NAME}_crypt${index}" echo "${dev_name} $i $KEY_FILE luks" >> /etc/crypttab cryptsetup luksOpen --key-file $KEY_FILE "$i" "$dev_name" || exit 1 index=$((index + 1)) done echo "###" >> /etc/crypttab phrase="=====================================================" phrase1="=================================================" unset phrase unset phrase1 

Ahora, con este script, debe crear un grupo para almacenar datos:


 ./create_crypt_pool.sh zpool create -o ashift=12 -O atime=off -O compression=lz4 -O normalization=formD tank0 raidz1 /dev/disk/by-id/dm-name-tank0_crypt* 

Para notas sobre el parámetro ashift=12 , vea mis artículos anteriores y comentarios sobre ellos.


Después de crear el grupo, apagué su registro en el SSD:


 zpool add tank0 log mirror /dev/disk/by-id/dm-name-slog0_crypt1 /dev/disk/by-id/dm-name-slog0_crypt2 

En el futuro, con OMV instalado y configurado, será posible crear grupos a través de la GUI:


La creación de ZFS cayó en la GUI WEB OMV


Habilitar la importación de grupos y los volúmenes de montaje automático en el arranque


Para garantizar la habilitación de grupos de montaje automático, ejecute los siguientes comandos:


 rm /etc/zfs/zpool.cache systemctl enable zfs-import-scan.service systemctl enable zfs-mount.service systemctl enable zfs-import-cache.service 

En esta etapa, se completa la configuración del subsistema de disco.


Sistema operativo


El primer paso es instalar y configurar OMV para finalmente obtener algún tipo de base para el NAS.


Instalar OMV


OMV se instalará como un paquete deb. Para hacer esto, es posible utilizar las instrucciones oficiales .


El script add_repo.sh agrega el repositorio OMV Arrakis a /etc/apt/ sources.list.d para que el sistema por lotes vea el repositorio.


add_repo.sh
 cat <<EOF >> /etc/apt/sources.list.d/openmediavault.list deb http://packages.openmediavault.org/public arrakis main # deb http://downloads.sourceforge.net/project/openmediavault/packages arrakis main ## Uncomment the following line to add software from the proposed repository. # deb http://packages.openmediavault.org/public arrakis-proposed main # deb http://downloads.sourceforge.net/project/openmediavault/packages arrakis-proposed main ## This software is not part of OpenMediaVault, but is offered by third-party ## developers as a service to OpenMediaVault users. deb http://packages.openmediavault.org/public arrakis partner # deb http://downloads.sourceforge.net/project/openmediavault/packages arrakis partner EOF 

Tenga en cuenta que, en comparación con el original, se incluye el repositorio de socios.


Para instalar e inicializar, debe ejecutar los siguientes comandos.


Comandos para instalar OMV.
 ./add_repo.sh export LANG=C export DEBIAN_FRONTEND=noninteractive export APT_LISTCHANGES_FRONTEND=none apt-get update apt-get --allow-unauthenticated install openmediavault-keyring apt-get update apt-get --yes --auto-remove --show-upgraded \ --allow-downgrades --allow-change-held-packages \ --no-install-recommends \ --option Dpkg::Options::="--force-confdef" \ --option DPkg::Options::="--force-confold" \ install postfix openmediavault # Initialize the system and database. omv-initsystem 

OMV instalado. Utiliza su propio núcleo, y después de la instalación puede ser necesario reiniciar.


Después de reiniciar, la interfaz OpenMediaVault estará disponible en el puerto 80 (vaya al navegador en el NAS por dirección IP):



El nombre de usuario / contraseña predeterminado es admin/openmediavault .


Configurar OMV


Además, la mayor parte de la configuración pasará por la GUI WEB.


Establecer una conexión segura


Ahora debe cambiar la contraseña del administrador WEB y generar un certificado para el NAS para poder trabajar en HTTPS en el futuro.


La contraseña se cambia en la pestaña "Sistema-> Configuración general-> Contraseña de administrador web" .
Para generar un certificado en la pestaña "Sistema-> Certificados-> SSL", seleccione "Agregar-> Crear" .


El certificado creado será visible en la misma pestaña:


Certificado


Después de crear el certificado, en la pestaña "Sistema-> Configuración general", active la casilla de verificación "Activar SSL / TLS" .


Se requerirá un certificado antes de completar la configuración. La versión final utilizará un certificado firmado para contactar a OMV.


Ahora debe iniciar sesión en OMV, en el puerto 443, o simplemente asignar el prefijo https:// a IP en el navegador.


Si logró iniciar sesión, en la pestaña "Sistema-> Configuración general" debe habilitar la casilla de verificación "Forzar SSL / TLS".


Cambie los puertos 80 y 443 a 10080 y 10443 .
E intente iniciar sesión en la siguiente dirección: https://IP_NAS:10443 .
Cambiar los puertos es importante porque los puertos 80 y 443 usarán el contenedor acoplable con nginx-reverse-proxy.


Ajustes primarios


La configuración mínima que debe hacerse primero:


  • En la pestaña "Sistema-> Fecha y hora" verifique el valor de zona horaria y configure el servidor NTP.
  • En la pestaña "Sistema-> Monitoreo" habilite la recopilación de estadísticas de rendimiento.
  • En la pestaña "Sistema-> Administración de energía" probablemente valga la pena desactivar "Monitoreo" para que OMV no intente controlar los ventiladores.

Red


Si la segunda interfaz de red NAS aún no se ha conectado, conéctela al enrutador.


Entonces:


  • En la pestaña "Sistema-> Red", establezca el nombre de host en "nas" (o lo que desee).
  • Configure la vinculación para las interfaces como se muestra en la siguiente figura: "Sistema-> Red-> Interfaces-> Agregar-> Enlace" .
  • Agregue las reglas de firewall necesarias en la pestaña "Sistema-> Red-> Firewall" . Primero, el acceso a los puertos 10443, 10080, 443, 80, 22 para SSH y el permiso para recibir / enviar ICMP son suficientes.

Configuración de unión


Como resultado, deberían aparecer las interfaces en la vinculación, que el enrutador verá como una interfaz y le asignará una dirección IP:


Interfaces de unión


Si lo desea, es posible configurar adicionalmente SSH desde la GUI WEB:


Configuración SSH


Repositorios y módulos


En la pestaña "Sistema-> Administración de actualizaciones-> Configuración" active "Actualizaciones compatibles con la comunidad" .


Primero, agregue repositorios adicionales OMV .
Esto se puede hacer simplemente instalando el complemento o paquete, como se indica en el foro .


En la página "Sistema-> Complementos" necesita encontrar el complemento "openmediavault-omvextrasorg" e instalarlo.


Como resultado, el ícono "OMV-Extras" aparecerá en el menú del sistema (se puede ver en las capturas de pantalla).


Vaya allí y habilite los siguientes repositorios:


  • OMV-Extras.org. Un repositorio estable que contiene muchos complementos.
  • Pruebas OMV-Extras.org. Algunos complementos de este repositorio no están en el repositorio estable.
  • Docker CE. En realidad, Docker.

En la pestaña "Sistema-> OMV Extras-> Kernel" puede seleccionar el kernel que necesita, incluido el kernel de Proxmox (no lo instalé yo mismo, porque todavía no lo necesito, por lo tanto no lo recomiendo):



Instale los complementos necesarios ( negrita absolutamente necesaria, en cursiva - opcional, que no instalé):


Lista de complementos.
  • openmediavault-apttool. GUI mínimo para trabajar con un sistema por lotes. Agrega "Servicios-> Apttool" .
  • openmediavault-anacron. Agrega la capacidad de trabajar desde la GUI con un programador asíncrono. Agrega "Sistema-> Anacron" .
  • Openmediavault-backup. Proporciona sistema de respaldo en almacenamiento. Agrega una página "Sistema-> Copia de seguridad" .
  • openmediavault-diskstats. Necesario para recopilar estadísticas sobre el rendimiento del disco.
  • openmediavault-dnsmasq . Le permite elevar el servidor DNS y DHCP en el NAS. Como hago esto en un enrutador, no lo necesito.
  • openmediavault-docker-gui . Interfaz de gestión de contenedores Docker. Agrega "Servicios-> Docker" .
  • openmediavault-ldap . Soporte para autenticación a través de LDAP. Agrega "Gestión de derechos -> Servicio de directorio" .
  • openmediavault-letsencrypt . Soporte para Let's Encrypt desde la GUI. No es necesario, ya que utiliza la incrustación en el contenedor nginx-reverse-proxy.
  • Openmediavault-luksencryption . Soporte de cifrado LUKS. Es necesario que los discos encriptados sean visibles en la interfaz OMV. Agrega "Almacenamiento-> Cifrado" .
  • openmediavault-nut . Soporte de UPS. Agrega "Servicios-> UPS" .
  • openmediavault-omvextrasorg . OMV Extras ya debería estar instalado.
  • openmediavault-resetperms. Le permite restablecer permisos y restablecer listas de control de acceso en directorios compartidos. Agrega "Control de acceso -> Directorios generales -> Restablecer permisos" .
  • Openmediavault-route. Complemento útil para la gestión de enrutamiento. Agrega "Sistema-> Red-> Ruta estática" .
  • openmediavault-symlinks. Proporciona la capacidad de crear enlaces simbólicos. Agrega la página "Servicios-> Enlaces simbólicos" .
  • openmediavault-unionfilesystems. Soporte de UnionFS. Puede ser útil en el futuro, aunque la ventana acoplable utiliza ZFS como back-end. Agrega "Almacenamiento-> Union Filesystems" .
  • openmediavault-virtualbox . Se puede usar para incorporar capacidades de administración de máquinas virtuales en la GUI.
  • openmediavault-zfs . El complemento agrega soporte ZFS a OpenMediaVault. Después de la instalación, aparecerá la página "Almacenamiento-> ZFS" .

Discos


Todos los discos que están en el sistema deben ser OMV visibles. Asegúrese de esto mirando la pestaña "Almacenamiento-> Discos" . Si no todos los discos están visibles, ejecute un escaneo.


Discos en el sistema


Allí, en todos los discos duros, se debe habilitar el almacenamiento en caché de escritura (haciendo clic en un disco de la lista y haciendo clic en el botón "Editar").


Asegúrese de que todas las secciones cifradas estén visibles en la pestaña "Almacenamiento-> Cifrado" :


Particiones cifradas


Ahora es el momento de configurar SMART, indicado como un medio para aumentar la confiabilidad:


  • Vaya a la pestaña "Almacenamiento-> SMART-> Configuración" . Encienda SMART.
  • En el mismo lugar, seleccione los valores de los niveles de temperatura de los discos (crítico, generalmente 60 C, y el régimen de temperatura óptimo del disco 15-45 C).
  • Vaya a la pestaña "Almacenamiento-> SMART-> Dispositivos" . Encienda el monitoreo para cada unidad.
  • Vaya a la pestaña "Almacenamiento-> SMART-> Pruebas programadas" . Agregue una autocomprobación breve una vez al día para cada disco y una autocomprobación larga una vez al mes. Además, para que los períodos de autoevaluación no se superpongan.

En esto, la configuración del disco puede considerarse completa.


Sistemas de archivos y directorios compartidos


Debe crear sistemas de archivos para directorios predefinidos.
Esto se puede hacer desde la consola o desde la interfaz web OMV ( Almacenamiento-> ZFS-> Seleccionar tanque de grupo0-> Botón Agregar -> Sistema de archivos ).


Comandos para crear FS.
 zfs create -o utf8only=on -o normalization=formD -p tank0/user_data/books zfs create -o utf8only=on -o normalization=formD -p tank0/user_data/music zfs create -o utf8only=on -o normalization=formD -p tank0/user_data/pictures zfs create -o utf8only=on -o normalization=formD -p tank0/user_data/downloads zfs create -o compression=off -o utf8only=on -o normalization=formD -p tank0/user_data/videos 

El resultado debería ser la siguiente estructura de directorios:



Después de eso, agregue los FS creados como directorios compartidos en la página "Administración de derechos de acceso- > Directorios generales-> Agregar" .
Tenga en cuenta que el parámetro "Dispositivo" es igual a la ruta al sistema de archivos creado en ZFS, y el parámetro "Ruta" para todos los directorios es "/".



Copia de seguridad


La copia de seguridad se realiza mediante dos herramientas:



Si usa el complemento, lo más probable es que obtenga un error:


 lsblk: /dev/block/0:22: not a block device 

Para solucionarlo, como lo señalaron los desarrolladores de OMV en esta "configuración muy no estándar", sería posible rechazar el complemento y usar las herramientas de ZFS en forma de zfs send/receive de zfs send/receive .
O bien, especifique explícitamente el parámetro "Dispositivo raíz" en forma de dispositivo físico desde el que se realiza la descarga.
Es más conveniente para mí usar el complemento y hacer una copia de seguridad del sistema operativo desde la interfaz, en lugar de enmarcar algo propio con zfs send, por lo que prefiero la segunda opción.


Configuración de respaldo


Para que la copia de seguridad funcione, primero cree el sistema de archivos tank0/apps/backup través de ZFS, luego en el menú "Sistema-> Copia de seguridad" haga clic en "+" en el campo de parámetro "Carpeta pública" y agregue el dispositivo creado como el objetivo y el campo "Ruta" establecido en "/".


También hay problemas con zfs-auto-snapshot. Si no está configurado, tomará fotografías cada hora, todos los días, todas las semanas, todos los meses durante un año.
El resultado es lo que está en la captura de pantalla:


Una gran cantidad de spam de zfs-auto-snapshot


Si ya te has encontrado con esto, ejecuta el siguiente código para eliminar las instantáneas automáticas:


 zfs list -t snapshot -o name -S creation | grep "@zfs-auto-snap" | tail -n +1500 | xargs -n 1 zfs destroy -vr 

Luego configure zfs-auto-snapshot para que se ejecute en cron.
Para comenzar, simplemente elimine /etc/cron.hourly/zfs-auto-snapshot si no necesita tomar fotografías cada hora.


Notificaciones por correo electrónico


La notificación por correo electrónico se ha indicado como uno de los medios para lograr confiabilidad.
Por lo tanto, ahora necesita configurar notificaciones por correo electrónico.
Para hacer esto, registre una casilla en uno de los servidores públicos (bueno, o configure el servidor SMTP usted mismo, si realmente tiene razones para hacerlo).


Luego debe ir a la página "Sistema-> Notificación" e ingresar:


  • Dirección del servidor SMTP.
  • Puerto del servidor SMTP.
  • Nombre de usuario
  • Dirección del remitente (generalmente el primer componente de la dirección coincide con el nombre).
  • Contraseña de usuario
  • En el campo "Destinatario", su dirección habitual a la que el NAS enviará notificaciones.

Es muy recomendable habilitar SSL / TLS.


En la captura de pantalla se muestra un ejemplo de configuración para Yandex:


Notificaciones por correo electrónico


Configuración de red fuera del NAS


Dirección IP


Utilizo una dirección IP estática blanca, que cuesta más de 100 rublos por mes. Si no desea pagar y su dirección es dinámica, pero no para NAT, es posible ajustar registros DNS externos a través de la API del servicio seleccionado.
Sin embargo, debe tenerse en cuenta que una dirección que no es NAT de repente puede convertirse en una dirección NAT: por regla general, los proveedores no ofrecen ninguna garantía.


Enrutador


Como enrutador, tengo un Mikrotik RouterBoard , similar al de la imagen a continuación.


Router Mikrotik


Se requieren tres cosas en el enrutador:


  • Configure las direcciones estáticas para el NAS. En mi caso, las direcciones se emiten a través de DHCP, y necesito asegurarme de que los adaptadores con una dirección MAC específica siempre obtengan la misma dirección IP. En RouterOS, esto se hace en la pestaña "IP-> Servidor DHCP" con el botón "Hacer estática" .
  • Configure el servidor DNS de manera que para el nombre "nas", así como los nombres que terminen en ".nas" y ".NAS.cloudns.cc" (donde "NAS" es la zona en ClouDNS o un servicio similar) le dé al sistema IP. Dónde hacer esto en RouterOS se muestra en la siguiente captura de pantalla. En mi caso, esto se implementa haciendo coincidir el nombre con una expresión regular: " ^.*\.nas$|^nas$|^.*\.NAS.cloudns.cc$ "
  • Configurar el reenvío de puertos. En RouterOS, esto se hace en la pestaña "IP-> Firewall" , no me detendré más en esto.

Configurar DNS en RouterOS


ClouDNS


CLouDNS es simple. Crea una cuenta, confirma. Los registros NS ya estarán registrados con usted. A continuación, se requiere una configuración mínima.


Primero, debe crear las zonas necesarias (la zona con el nombre NAS resaltado en rojo en la captura de pantalla es lo que debe crear con un nombre diferente, por supuesto).


Crear una zona en ClouDNS


En segundo lugar, en esta zona debe registrar los siguientes registros A :


  • nas , www , omv , control y un nombre vacío . Para acceder a la interfaz OMV.
  • ldap . Interfaz PhpLdapAdmin
  • ssp . Interfaz para cambiar contraseñas de usuario.
  • prueba Servidor de prueba

Los nombres de dominio restantes se agregarán a medida que se agreguen los servicios.
Haga clic en la zona, luego "Agregar nuevo registro" , seleccione el tipo A, ingrese el nombre de la zona y la dirección IP del enrutador detrás del cual se encuentra el NAS.


Se agregaron registros


En segundo lugar, debe acceder a la API. En ClouDNS se paga, por lo que primero debe pagarlo. En otros servicios, es gratis. Si sabe cuál es mejor, y esto es compatible con Lexicon , escriba en los comentarios.


Después de obtener acceso a la API, debe agregar un nuevo usuario de API allí.


Agregar usuario de la API de ClouDNS


"IP address" IP : , API. , , API, auth-id auth-password . Lexicon, .



ClouDNS .



Docker


openmediavault-docker-gui, docker-ce .


, docker-compose , :


 apt-get install docker-compose 

:


 zfs create -p /tank0/docker/services 

, /var/lib/docker . ( , SSD), , , .


..,
. .



, .
, GUI , : , .. , .


/var/lib :


 service docker stop zfs create -o com.sun:auto-snapshot=false -p /tank0/docker/lib rm -rf /var/lib/docker ln -s /tank0/docker/lib /var/lib/docker service docker start 

:


 $ ls -l /var/lib/docker lrwxrwxrwx 1 root root 17 Apr 7 12:35 /var/lib/docker -> /tank0/docker/lib 

:


 docker network create docker0 

Docker .


nginx-reverse-proxy


Docker , .


, .


: nginx-proxy letsencrypt-dns .


, OMV 10080 10443, 80 443.


/tank0/docker/services/nginx-proxy/docker-compose.yml
 version: '2' networks: docker0: external: name: docker0 services: nginx-proxy: networks: - docker0 restart: always image: jwilder/nginx-proxy ports: - "80:80" - "443:443" volumes: - ./certs:/etc/nginx/certs:ro - ./vhost.d:/etc/nginx/vhost.d - ./html:/usr/share/nginx/html - /var/run/docker.sock:/tmp/docker.sock:ro - ./local-config:/etc/nginx/conf.d - ./nginx.tmpl:/app/nginx.tmpl labels: - "com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=true" letsencrypt-dns: image: adferrand/letsencrypt-dns volumes: - ./certs/letsencrypt:/etc/letsencrypt environment: - "LETSENCRYPT_USER_MAIL=MAIL@MAIL.COM" - "LEXICON_PROVIDER=cloudns" - "LEXICON_OPTIONS=--delegated NAS.cloudns.cc" - "LEXICON_PROVIDER_OPTIONS=--auth-id=CLOUDNS_ID --auth-password=CLOUDNS_PASSWORD" 

:


  • nginx-reverse-proxy — c .
  • letsencrypt-dns — ACME Let's Encrypt.

nginx-reverse-proxy jwilder/nginx-proxy .


docker0 — , , docker-compose.
nginx-proxy — , . docker0. , 80 443 ports (, , docker0, ).
restart: always , .


:


  • certs /etc/nginx/certs — , , Let's Encrypt. ACME .
  • ./vhost.d:/etc/nginx/vhost.d — . .
  • ./html:/usr/share/nginx/html — . .
  • /var/run/docker.sock , /tmp/docker.sock — Docker . docker-gen .
  • ./local-config , /etc/nginx/conf.d — nginx. , .
  • ./nginx.tmpl , /app/nginx.tmpl — nginx, docker-gen .

letsencrypt-dns adferrand/letsencrypt-dns . ACME Lexicon, DNS .


certs/letsencrypt /etc/letsencrypt .


, :


  • LETSENCRYPT_USER_MAIL=MAIL@MAIL.COM — Let's Encrypt. , .
  • LEXICON_PROVIDER=cloudns — Lexicon. — cloudns .
  • LEXICON_PROVIDER_OPTIONS=--auth-id=CLOUDNS_ID --auth-password=CLOUDNS_PASSWORD --delegated=NAS.cloudns.cc — CLOUDNS_ID ClouDNS . CLOUDNS_PASSWORD — , API. NAS.cloudns.cc, NAS — DNS . cloudns , (cloudns.cc), ClouDNS API .

: .
, , , , Let's encrypt:


 $ ls ./certs/letsencrypt/ accounts archive csr domains.conf keys live renewal renewal-hooks 

, , .


/tank0/docker/services/nginx-proxy/nginx.tmpl
 {{ $CurrentContainer := where $ "ID" .Docker.CurrentContainerID | first }} {{ define "upstream" }} {{ if .Address }} {{/* If we got the containers from swarm and this container's port is published to host, use host IP:PORT */}} {{ if and .Container.Node.ID .Address.HostPort }} # {{ .Container.Node.Name }}/{{ .Container.Name }} server {{ .Container.Node.Address.IP }}:{{ .Address.HostPort }}; {{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}} {{ else if .Network }} # {{ .Container.Name }} server {{ .Network.IP }}:{{ .Address.Port }}; {{ end }} {{ else if .Network }} # {{ .Container.Name }} {{ if .Network.IP }} server {{ .Network.IP }} down; {{ else }} server 127.0.0.1 down; {{ end }} {{ end }} {{ end }} # If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the # scheme used to connect to this server map $http_x_forwarded_proto $proxy_x_forwarded_proto { default $http_x_forwarded_proto; '' $scheme; } # If we receive X-Forwarded-Port, pass it through; otherwise, pass along the # server port the client connected to map $http_x_forwarded_port $proxy_x_forwarded_port { default $http_x_forwarded_port; '' $server_port; } # If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any # Connection header that may have been passed to this server map $http_upgrade $proxy_connection { default upgrade; '' close; } # Apply fix for very long server names server_names_hash_bucket_size 128; # Default dhparam {{ if (exists "/etc/nginx/dhparam/dhparam.pem") }} ssl_dhparam /etc/nginx/dhparam/dhparam.pem; {{ end }} # Set appropriate X-Forwarded-Ssl header map $scheme $proxy_x_forwarded_ssl { default off; https on; } gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; log_format vhost '$host $remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent"'; access_log off; {{ if $.Env.RESOLVERS }} resolver {{ $.Env.RESOLVERS }}; {{ end }} {{ if (exists "/etc/nginx/proxy.conf") }} include /etc/nginx/proxy.conf; {{ else }} # HTTP 1.1 support proxy_http_version 1.1; proxy_buffering off; proxy_set_header Host $http_host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $proxy_connection; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl; proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port; # Mitigate httpoxy attack (see README for details) proxy_set_header Proxy ""; {{ end }} {{ $enable_ipv6 := eq (or ($.Env.ENABLE_IPV6) "") "true" }} server { server_name _; # This is just an invalid value which will never trigger on a real hostname. listen 80; {{ if $enable_ipv6 }} listen [::]:80; {{ end }} access_log /var/log/nginx/access.log vhost; return 503; } {{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} server { server_name _; # This is just an invalid value which will never trigger on a real hostname. listen 443 ssl http2; {{ if $enable_ipv6 }} listen [::]:443 ssl http2; {{ end }} access_log /var/log/nginx/access.log vhost; return 503; ssl_session_tickets off; ssl_certificate /etc/nginx/certs/default.crt; ssl_certificate_key /etc/nginx/certs/default.key; } {{ end }} {{ range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }} {{ $host := trim $host }} {{ $is_regexp := hasPrefix "~" $host }} {{ $upstream_name := when $is_regexp (sha1 $host) $host }} # {{ $host }} upstream {{ $upstream_name }} { {{ range $container := $containers }} {{ $addrLen := len $container.Addresses }} {{ range $knownNetwork := $CurrentContainer.Networks }} {{ range $containerNetwork := $container.Networks }} {{ if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }} ## Can be connected with "{{ $containerNetwork.Name }}" network {{/* If only 1 port exposed, use that */}} {{ if eq $addrLen 1 }} {{ $address := index $container.Addresses 0 }} {{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }} {{/* If more than one port exposed, use the one matching VIRTUAL_PORT env var, falling back to standard web port 80 */}} {{ else }} {{ $port := coalesce $container.Env.VIRTUAL_PORT "80" }} {{ $address := where $container.Addresses "Port" $port | first }} {{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }} {{ end }} {{ else }} # Cannot connect to network of this container server 127.0.0.1 down; {{ end }} {{ end }} {{ end }} {{ end }} } {{ $default_host := or ($.Env.DEFAULT_HOST) "" }} {{ $default_server := index (dict $host "" $default_host "default_server") $host }} {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}} {{ $proto := trim (or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http") }} {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} {{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }} {{/* Get the HTTPS_METHOD defined by containers w/ the same vhost, falling back to "redirect" */}} {{ $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) "redirect" }} {{/* Get the SSL_POLICY defined by containers w/ the same vhost, falling back to "Mozilla-Intermediate" */}} {{ $ssl_policy := or (first (groupByKeys $containers "Env.SSL_POLICY")) "Mozilla-Intermediate" }} {{/* Get the HSTS defined by containers w/ the same vhost, falling back to "max-age=31536000" */}} {{ $hsts := or (first (groupByKeys $containers "Env.HSTS")) "max-age=31536000" }} {{/* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}} {{ $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }} {{/* Get the first cert name defined by containers w/ the same vhost */}} {{ $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }} {{/* Get the best matching cert by name for the vhost. */}} {{ $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}} {{/* vhostCert is actually a filename so remove any suffixes since they are added later */}} {{ $vhostCert := trimSuffix ".crt" $vhostCert }} {{ $vhostCert := trimSuffix ".key" $vhostCert }} {{/* Use the cert specified on the container or fallback to the best vhost match */}} {{ $cert := (coalesce $certName $vhostCert) }} {{ $is_https := (and (ne $https_method "nohttps") (ne $cert "") (or (and (exists (printf "/etc/nginx/certs/letsencrypt/live/%s/fullchain.pem" $cert)) (exists (printf "/etc/nginx/certs/letsencrypt/live/%s/privkey.pem" $cert))) (and (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert)))) ) }} {{ if $is_https }} {{ if eq $https_method "redirect" }} server { server_name {{ $host }}; listen 80 {{ $default_server }}; {{ if $enable_ipv6 }} listen [::]:80 {{ $default_server }}; {{ end }} access_log /var/log/nginx/access.log vhost; return 301 https://$host$request_uri; } {{ end }} server { server_name {{ $host }}; listen 443 ssl http2 {{ $default_server }}; {{ if $enable_ipv6 }} listen [::]:443 ssl http2 {{ $default_server }}; {{ end }} access_log /var/log/nginx/access.log vhost; {{ if eq $network_tag "internal" }} # Only allow traffic from internal clients include /etc/nginx/network_internal.conf; {{ end }} {{ if eq $ssl_policy "Mozilla-Modern" }} ssl_protocols TLSv1.2; ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; {{ else if eq $ssl_policy "Mozilla-Intermediate" }} ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:!DSS'; {{ else if eq $ssl_policy "Mozilla-Old" }} ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP'; {{ else if eq $ssl_policy "AWS-TLS-1-2-2017-01" }} ssl_protocols TLSv1.2; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES128-SHA256:AES256-GCM-SHA384:AES256-SHA256'; {{ else if eq $ssl_policy "AWS-TLS-1-1-2017-01" }} ssl_protocols TLSv1.1 TLSv1.2; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA'; {{ else if eq $ssl_policy "AWS-2016-08" }} ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA'; {{ else if eq $ssl_policy "AWS-2015-05" }} ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DES-CBC3-SHA'; {{ else if eq $ssl_policy "AWS-2015-03" }} ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA:DES-CBC3-SHA'; {{ else if eq $ssl_policy "AWS-2015-02" }} ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA'; {{ end }} ssl_prefer_server_ciphers on; ssl_session_timeout 5m; ssl_session_cache shared:SSL:50m; ssl_session_tickets off; {{ if (and (exists (printf "/etc/nginx/certs/letsencrypt/live/%s/fullchain.pem" $cert)) (exists (printf "/etc/nginx/certs/letsencrypt/live/%s/privkey.pem" $cert))) }} ssl_certificate /etc/nginx/certs/letsencrypt/live/{{ (printf "%s/fullchain.pem" $cert) }}; ssl_certificate_key /etc/nginx/certs/letsencrypt/live/{{ (printf "%s/privkey.pem" $cert) }}; {{ else if (and (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }} ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $cert) }}; ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $cert) }}; {{ end }} {{ if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }} ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }}; {{ end }} {{ if (exists (printf "/etc/nginx/certs/%s.chain.pem" $cert)) }} ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate {{ printf "/etc/nginx/certs/%s.chain.pem" $cert }}; {{ end }} {{ if (and (ne $https_method "noredirect") (ne $hsts "off")) }} add_header Strict-Transport-Security "{{ trim $hsts }}" always; {{ end }} {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} include {{ printf "/etc/nginx/vhost.d/%s" $host }}; {{ else if (exists "/etc/nginx/vhost.d/default") }} include /etc/nginx/vhost.d/default; {{ end }} location / { {{ if eq $proto "uwsgi" }} include uwsgi_params; uwsgi_pass {{ trim $proto }}://{{ trim $upstream_name }}; {{ else if eq $proto "fastcgi" }} root {{ trim $vhost_root }}; include fastcgi.conf; fastcgi_pass {{ trim $upstream_name }}; {{ else }} proxy_pass {{ trim $proto }}://{{ trim $upstream_name }}; {{ end }} {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} auth_basic "Restricted {{ $host }}"; auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; {{ end }} {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; {{ else if (exists "/etc/nginx/vhost.d/default_location") }} include /etc/nginx/vhost.d/default_location; {{ end }} } } {{ end }} {{ if or (not $is_https) (eq $https_method "noredirect") }} server { server_name {{ $host }}; listen 80 {{ $default_server }}; {{ if $enable_ipv6 }} listen [::]:80 {{ $default_server }}; {{ end }} access_log /var/log/nginx/access.log vhost; {{ if eq $network_tag "internal" }} # Only allow traffic from internal clients include /etc/nginx/network_internal.conf; {{ end }} {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} include {{ printf "/etc/nginx/vhost.d/%s" $host }}; {{ else if (exists "/etc/nginx/vhost.d/default") }} include /etc/nginx/vhost.d/default; {{ end }} location / { {{ if eq $proto "uwsgi" }} include uwsgi_params; uwsgi_pass {{ trim $proto }}://{{ trim $upstream_name }}; {{ else if eq $proto "fastcgi" }} root {{ trim $vhost_root }}; include fastcgi.conf; fastcgi_pass {{ trim $upstream_name }}; {{ else }} proxy_pass {{ trim $proto }}://{{ trim $upstream_name }}; {{ end }} {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} auth_basic "Restricted {{ $host }}"; auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; {{ end }} {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; {{ else if (exists "/etc/nginx/vhost.d/default_location") }} include /etc/nginx/vhost.d/default_location; {{ end }} } } {{ if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} server { server_name {{ $host }}; listen 443 ssl http2 {{ $default_server }}; {{ if $enable_ipv6 }} listen [::]:443 ssl http2 {{ $default_server }}; {{ end }} access_log /var/log/nginx/access.log vhost; return 500; ssl_certificate /etc/nginx/certs/default.crt; ssl_certificate_key /etc/nginx/certs/default.key; } {{ end }} {{ end }} {{ end }} 

, nginx /etc/nginx/certs/%s.crt /etc/nginx/certs/%s.pem , %s — ( — , ).


/etc/nginx/certs/letsencrypt/live/%s/{fullchain.pem, privkey.pem} , :


 {{ $is_https := (and (ne $https_method "nohttps") (ne $cert "") (or (and (exists (printf "/etc/nginx/certs/letsencrypt/live/%s/fullchain.pem" $cert)) (exists (printf "/etc/nginx/certs/letsencrypt/live/%s/privkey.pem" $cert)) ) (and (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert)) ) ) ) }} 

, domains.conf .


/tank0/docker/services/nginx-proxy/certs/letsencrypt/domains.conf
 *.NAS.cloudns.cc NAS.cloudns.cc 

. , , , client_max_body_size 20, .


/tank0/docker/services/nginx-proxy/local-config/max_upload_size.conf
 client_max_body_size 20G; 

, :


 docker-compose up 

( ), Ctrl+C :


 docker-compose up -d 


— nginx, . , , .
, NAS.


.


docker-compose :


/tank0/docker/services/test_nginx/docker-compose.yml
 version: '2' networks: docker0: external: name: docker0 services: nginx-local: restart: always image: nginx:alpine expose: - 80 - 443 environment: - "VIRTUAL_HOST=test.NAS.cloudns.cc" - "VIRTUAL_PROTO=http" - "VIRTUAL_PORT=80" - CERT_NAME=NAS.cloudns.cc networks: - docker0 

:


  • docker0 — . .
  • expose — , . , 80 HTTP 443 HTTPS.
  • VIRTUAL_HOST=test.NAS.cloudns.cc — , nginx-reverse-proxy .
  • VIRTUAL_PROTO=http — nginx-reverse-proxy . , HTTP.
  • VIRTUAL_PORT=80 — nginx-reverse-proxy.
  • CERT_NAME=NAS.cloudns.cc — . , , . NAS — DNS .
  • networks — , nginx-reverse-proxy docker0 .

, . docker-compose up , test.NAS.cloudns.cc .


:


 $ docker-compose up Creating testnginx_nginx-local_1 Attaching to testnginx_nginx-local_1 nginx-local_1 | 172.22.0.5 - - [29/Jul/2018:15:32:02 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537 (KHTML, like Gecko) Chrome/67.0 Safari/537" "192.168.2.3" nginx-local_1 | 2018/07/29 15:32:02 [error] 8#8: *2 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.22.0.5, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "test.NAS.cloudns.cc", referrer: "https://test.NAS.cloudns.cc/" nginx-local_1 | 172.22.0.5 - - [29/Jul/2018:15:32:02 +0000] "GET /favicon.ico HTTP/1.1" 404 572 "https://test.NAS.cloudns.cc/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537 (KHTML, like Gecko) Chrome/67.0 Safari/537" "192.168.2.3" 

:


Lanzado Nginx


, , , , : .


, Ctrl+C, docker-compose down .


local-rpoxy


, nginx-default , nas, omv 10080 10443 .


.


/tank0/docker/services/nginx-local/docker-compose.yml
 version: '2' networks: docker0: external: name: docker0 services: nginx-local: restart: always image: nginx:alpine expose: - 80 - 443 environment: - "VIRTUAL_HOST=NAS.cloudns.cc,nas,nas.*,www.*,omv.*,nas-controller.nas" - "VIRTUAL_PROTO=http" - "VIRTUAL_PORT=80" - CERT_NAME=NAS.cloudns.cc volumes: - ./local-config:/etc/nginx/conf.d networks: - docker0 

docker-compose , .
, , , NAS.cloudns.cc . , NAS DNS , .


/tank0/docker/services/nginx-local/local-config/default.conf
 # If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the # scheme used to connect to this server map $http_x_forwarded_proto $proxy_x_forwarded_proto { default $http_x_forwarded_proto; '' $scheme; } # If we receive X-Forwarded-Port, pass it through; otherwise, pass along the # server port the client connected to map $http_x_forwarded_port $proxy_x_forwarded_port { default $http_x_forwarded_port; '' $server_port; } # Set appropriate X-Forwarded-Ssl header map $scheme $proxy_x_forwarded_ssl { default off; https on; } access_log on; error_log on; # HTTP 1.1 support proxy_http_version 1.1; proxy_buffering off; proxy_set_header Host $http_host; proxy_set_header Upgrade $http_upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl; proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port; # Mitigate httpoxy attack (see README for details) proxy_set_header Proxy ""; server { server_name _; # This is just an invalid value which will never trigger on a real hostname. listen 80; return 503; } server { server_name www.* nas.* omv.* ""; listen 80; location / { proxy_pass https://172.21.0.1:10443/; } } # nas-controller server { server_name nas-controller.nas; listen 80 ; location / { proxy_pass https://nas-controller/; } } 

  • 172.21.0.1 — . 443, OMV HTTPS. .
  • https://nas-controller/ — -, IPMI, nas, nas-controller.nas, nas-controller. .

LDAP


LDAP-


LDAP- — .
Docker . , , .


LDIF- .


/tank0/docker/services/ldap/docker-compose.yml
 version: "2" networks: ldap: docker0: external: name: docker0 services: open-ldap: image: "osixia/openldap:1.2.0" hostname: "open-ldap" restart: always environment: - "LDAP_ORGANISATION=NAS" - "LDAP_DOMAIN=nas.nas" - "LDAP_ADMIN_PASSWORD=ADMIN_PASSWORD" - "LDAP_CONFIG_PASSWORD=CONFIG_PASSWORD" - "LDAP_TLS=true" - "LDAP_TLS_ENFORCE=false" - "LDAP_TLS_CRT_FILENAME=ldap_server.crt" - "LDAP_TLS_KEY_FILENAME=ldap_server.key" - "LDAP_TLS_CA_CRT_FILENAME=ldap_server.crt" volumes: - ./certs:/container/service/slapd/assets/certs - ./ldap_data/var/lib:/var/lib/ldap - ./ldap_data/etc/ldap/slapd.d:/etc/ldap/slapd.d networks: - ldap ports: - 172.21.0.1:389:389 - 172.21.0.1::636:636 phpldapadmin: image: "osixia/phpldapadmin:0.7.1" hostname: "nas.nas" restart: always networks: - ldap - docker0 expose: - 443 links: - open-ldap:open-ldap-server volumes: - ./certs:/container/service/phpldapadmin/assets/apache2/certs environment: - VIRTUAL_HOST=ldap.* - VIRTUAL_PORT=443 - VIRTUAL_PROTO=https - CERT_NAME=NAS.cloudns.cc - "PHPLDAPADMIN_LDAP_HOSTS=open-ldap-server" #- "PHPLDAPADMIN_HTTPS=false" - "PHPLDAPADMIN_HTTPS_CRT_FILENAME=certs/ldap_server.crt" - "PHPLDAPADMIN_HTTPS_KEY_FILENAME=private/ldap_server.key" - "PHPLDAPADMIN_HTTPS_CA_CRT_FILENAME=certs/ldap_server.crt" - "PHPLDAPADMIN_LDAP_CLIENT_TLS_REQCERT=allow" ldap-ssp: image: openfrontier/ldap-ssp:https volumes: #- ./ssp/mods-enabled/ssl.conf:/etc/apache2/mods-enabled/ssl.conf - /etc/ssl/certs/ssl-cert-snakeoil.pem:/etc/ssl/certs/ssl-cert-snakeoil.pem - /etc/ssl/private/ssl-cert-snakeoil.key:/etc/ssl/private/ssl-cert-snakeoil.key restart: always networks: - ldap - docker0 expose: - 80 links: - open-ldap:open-ldap-server environment: - VIRTUAL_HOST=ssp.* - VIRTUAL_PORT=80 - VIRTUAL_PROTO=http - CERT_NAME=NAS.cloudns.cc - "LDAP_URL=ldap://open-ldap-server:389" - "LDAP_BINDDN=cn=admin,dc=nas,dc=nas" - "LDAP_BINDPW=ADMIN_PASSWORD" - "LDAP_BASE=ou=users,dc=nas,dc=nas" - "MAIL_FROM=admin@nas.nas" - "PWD_MIN_LENGTH=8" - "PWD_MIN_LOWER=3" - "PWD_MIN_DIGIT=2" - "SMTP_HOST=" - "SMTP_USER=" - "SMTP_PASS=" 

:



LDAP- , :


  • LDAP_ORGANISATION=NAS — . .
  • LDAP_DOMAIN=nas.nas — . . , .
  • LDAP_ADMIN_PASSWORD=ADMIN_PASSWORD — .
  • LDAP_CONFIG_PASSWORD=CONFIG_PASSWORD — .

-, " ", .


:


  • /container/service/slapd/assets/certs certs — . .
  • ./ldap_data/ — , . LDAP .

ldap , 389 ( LDAP) 636 (LDAP SSL, ) .


PhpLdapAdmin : LDAP ldap 443 docker0 , , nginx-reverse-proxy.


:


  • VIRTUAL_HOST=ldap.* — , nginx-reverse-proxy .
  • VIRTUAL_PORT=443 — nginx-reverse-proxy.
  • VIRTUAL_PROTO=https — nginx-reverse-proxy.
  • CERT_NAME=NAS.cloudns.cc — , .

SSL .


SSP HTTP .
, , .


— LDAP.


  • LDAP_URL=ldap://open-ldap-server:389 — LDAP (. links ).
  • LDAP_BINDDN=cn=admin,dc=nas,dc=nas — .
  • LDAP_BINDPW=ADMIN_PASSWORD — , , open-ldap.
  • LDAP_BASE=ou=users,dc=nas,dc=nas — , .

LDAP LDAP :


 apt-get install ldap-utils ldapadd -x -H ldap://172.21.0.1 -D "cn=admin,dc=nas,dc=nas" -W -f ldifs/inititialize_ldap.ldif ldapadd -x -H ldap://172.21.0.1 -D "cn=admin,dc=nas,dc=nas" -W -f ldifs/base.ldif ldapadd -x -H ldap://172.21.0.1 -D "cn=admin,cn=config" -W -f ldifs/gitlab_attr.ldif 

gitlab_attr.ldif , Gitlab ( ) .
.


LDAP
 $ ldapsearch -x -H ldap://172.21.0.1 -b dc=nas,dc=nas -D "cn=admin,dc=nas,dc=nas" -W Enter LDAP Password: # extended LDIF # # LDAPv3 # base <dc=nas,dc=nas> with scope subtree # filter: (objectclass=*) # requesting: ALL # # nas.nas dn: dc=nas,dc=nas objectClass: top objectClass: dcObject objectClass: organization o: NAS dc: nas # admin, nas.nas dn: cn=admin,dc=nas,dc=nas objectClass: simpleSecurityObject objectClass: organizationalRole cn: admin description: LDAP administrator ... # ldap_users, groups, nas.nas dn: cn=ldap_users,ou=groups,dc=nas,dc=nas cn: ldap_users gidNumber: 500 objectClass: posixGroup objectClass: top # search result search: 2 result: 0 Success # numResponses: 12 # numEntries: 11 

LDAP . WEB-.


OMV LDAP


LDAP , OMV : , , , , — .


LDAP .


:


Configure OMV para trabajar con LDAP



, , NAS USB.
.
NUT GUI OMV.
"->" , , , , "eaton".


" " :


 driver = usbhid-ups port = auto desc = "Eaton 9130 700 VA" vendorid = 0463 pollinterval = 10 

  • driver = usbhid-ups — USB, USB HID.
  • vendorid — , lsusb .
  • pollinterval — c.

.


lsusb , :


 # lsusb Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub --> Bus 001 Device 003: ID 0463:ffff MGE UPS Systems UPS Bus 001 Device 004: ID 046b:ff10 American Megatrends, Inc. Virtual Keyboard and Mouse Bus 001 Device 002: ID 046b:ff01 American Megatrends, Inc. Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub 

" " " ".
, :


Configuración de UPS


. , .
.


Conclusión


. , , , , .
— .


-, OMV .


WEB-, , :



Docker WEB-:



, OMV .


:


Programa de uso de la red


:


Gráfico de uso de memoria


CPU:


Gráfico de uso de la CPU




.
!


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


All Articles