Un blog mas

Bitácora de vuelo

Poste.io es una imagen para implementar un servidor de correo completo, con todas las funciones necesarias, como ser SMTP, IMAP, POP3, webmail, antivirus, antispam, y un panel de administración muy completo desde donde se pueden agregar multiples dominios, cuantas de correo, certificados, y modificar la configuración de cada uno de los componentes.

El único requisito para su instalación es tener Docker corriendo sobre una máquina con 1GB de RAM y una IP pública la cual asignaremos en nuestro DNS a mail.sarubbi.com.ar

pablo@mail:~# sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
pablo@mail:~# sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
pablo@mail:~# sudo apt update
pablo@mail:~# sudo apt install docker.io docker-compose

Es necesario crear un archivo docker-compose.yaml con los siguientes parámetros:

pablo@mail:~# cat /root/docker-compose.yaml 
version: "3.4"
services:
  poste:
    image: analogic/poste.io
    restart: always
    network_mode: "host"
    expose:
      - 25
      - 80
      - 443
      - 110
      - 143
      - 465
      - 587
      - 993
      - 995
    volumes:
      - /mnt/mail:/data
    environment:
      - HTTPS=ON
      - DISABLE_CLAMAV=TRUE

restart: always para que se inicie el contenedor cada vez que la máquina inicie.

netkwork_mode: «host» hace que el contenedor use la IP pública de la máquina y no una IP interna de Docker.

expose: – <port> para publicar todos los puertos que necesitamos.

volumes : – <dir> para almacenar lo archivos de configuración y toda la información de los mails.

Para luego correr la imágen de la siguiente manera:

pablo@mail:~# sudo docker-compose -f /root/docker-compose.yaml up -d

Con docke ps podemos verificar que el contenedor este corriendo

pablo@mail:~# sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                  PORTS               NAMES
b2ab960cdc6c        analogic/poste.io   "/init"             5 seconds ago          Up 5 seconds (healthy)                       root_poste_1

Para terminar la configuración debermos acceder desde unn browser a https://mail.sarubbi.com.ar/

La primera vez fallará el HTTPS porque aún no tenemos un certificado definido. Continuamos aunque diga que no es seguro (porque sabemos que es seguro no?)

Completamos los datos:

Mailserver hostname: mail.sarubbi.com.ar

Administrator email: administrador@sarubbi.com.ar

Password: ******************

Para crear un certificado con letsencrypt accedemos al menú «System settings» -> «TLS Certificate» -> «issue free letsencrypt.org certificate» -> «enabled» -> «Save changes»

Automáticamente se conectará a letsencrypt.org, generará el certificado y lo instalará en nuestro contenedor.

Algo para destacar es revisar el panel de «Server status» -> «Connection diagnostics» desde donde podemos verificar que tengamos todos los servicios funcionando.

Gracias tocayo por la data: https://youtu.be/K4-uD1VHCz0

Se prepara la estructura de directorios de la CA para contener los certificados.
En /etc/ssl/HA/

root@ubuntu-ha:~# mkdir -p {private,servers,clients,csr,crl}

Generación de la CA

Se general un archivo openssl.cnf para configuración de los certificados

        [ ca ]
            default_ca      = CA_default
        [ CA_default ]
            dir             = /etc/ssl/HA 
            certs           = /etc/ssl/HA
            certificate     = /etc/ssl/HA/ca.pem
            private_key     = /etc/ssl/HA/private/ca.key
            crl_dir         = /etc/ssl/HA/crl
            database        = /etc/ssl/HA/index.txt
            serial          = /etc/ssl/HA/serial
            crlnumber       = /etc/ssl/HA/crlnumber
            crl             = /etc/ssl/HA/ca.crl
            default_md      = sha256

        [req]
            default_bits = 2048
            prompt = no
            default_md = sha256
            req_extensions = req_ext
            distinguished_name = dn
        [ dn ]
            C=AR
            ST=CABA
            L=CABA
            O=Mi Empresa SA
            OU=IT
            emailAddress=admin@miDominio.com.ar
            CN = miDominio-CA
            [ req_ext ]
            subjectAltName = @alt_names
            [ alt_names ]
            DNS.1 =miDominio-CA

Se genera la clave privada y certificado de la CA

root@ubuntu-ha:~# openssl req -newkey rsa:4096 -keyform PEM -keyout ./private/ca.key -x509 -days 3650 -outform PEM -out ./ca.pem -config openssl.cnf

Certificado de cliente

root@ubuntu-ha:~# NOMBRE="cliente1"
root@ubuntu-ha:~# MAIL="cliente1@miDominio.com.ar"

Se crea la clave privada

root@ubuntu-ha:~# openssl genrsa -out ./clients/$NOMBRE.key 4096

Se genera la solicitud de firma (CA) de certificados para la conexión de los clientes

root@ubuntu-ha:~# openssl req -new -key ./clients/$NOMBRE.key -out ./csr/$NOMBRE.csr -subj "/C=AR/ST=CABA/L=CABA/O=Mi Empresa SA/OU=IT/emailAddress=$MAIL/CN=$NOMBRE.miDominio.com.ar"

Se firma (CA) el certificado del cliente que caduca en un año

root@ubuntu-ha:~# openssl x509 -req -in ./csr/$NOMBRE.csr -CA ./ca.pem -CAkey ./private/ca.key -CAcreateserial -extensions client -days 365 -outform PEM -out ./clients/$NOMBRE.pem

Generamos una clave simétrica aleatoria para proteger el certificado P12

root@ubuntu-ha:~# PASS=`openssl rand -base64 9`
root@ubuntu-ha:~# echo $PASS > ./clients/$NOMBRE.txt

Convertir certificado en formato válido para navegador (P12)

root@ubuntu-ha:~# openssl pkcs12 -export -inkey ./clients/$NOMBRE.key -in ./clients/$NOMBRE.pem -out ./clients/$NOMBRE.p12 -passout pass:$PASS

Revocar certificados de clientes

root@ubuntu-ha:~# mv ./clients/$NOMBRE.* ./crl/
root@ubuntu-ha:~# openssl ca -revoke ./crl/$NOMBRE.pem -config openssl.cnf

Generar lista de revocados

root@ubuntu-ha:~# openssl ca -gencrl -out ca.crl -crldays 1095 -config openssl.cnf

Certificado de Servidor con Let’s Encrypt

Instalar certbot

root@ubuntu-ha:~# aptitude install certbot

Se generan los certificados verificados con el registro _acme-challenge del DNS

root@ubuntu-ha:~# certbot certonly --server https://acme-v02.api.letsencrypt.org/directory --manual --preferred-challenges dns -d "haproxy.midominio.com.ar"

Se crea un certificado haproxy.pem on la chain y la clave privada

root@ubuntu-ha:~# cat /etc/letsencrypt/live/haproxy.midominio.com.ar/fullchain.pem /etc/letsencrypt/live/haproxy.midominio.com.ar/privkey.pem > /etc/ssl/HA/server/haproxy.pem

Configuración del HAProxy

Se edita el frontend del HAProxy en el archivo /etc/haproxy/haproxy.cfg con los parámetros crt (certificado de servidor), ca-file (certificado de cliente) y crl-file (lista de revocados) como requeridos.

bind *:443 ssl crt /etc/ssl/HA/server/haproxy.pem verify required ca-file /etc/ssl/HA/ca.pem crl-file /etc/ssl/HA/ca.crl 

Preparación del entorno

Para estas pruebas se generaron tres contenedores Ubuntu Server 18.04 con LXC sobre Kubuntu Desktop 18.04

+------------+---------+----------------------+------+------------+-----------+
|   NOMBRE   | ESTADO  |         IPV4         | IPV6 |    TIPO    | SNAPSHOTS |
+------------+---------+----------------------+------+------------+-----------+
| ubuntu-ha  | RUNNING | 10.56.142.111 (eth0) |      | PERSISTENT | 0         |
+------------+---------+----------------------+------+------------+-----------+
| ubuntu-wp1 | RUNNING | 10.56.142.244 (eth0) |      | PERSISTENT | 0         |
+------------+---------+----------------------+------+------------+-----------+
| ubuntu-wp2 | RUNNING | 10.56.142.34 (eth0)  |      | PERSISTENT | 0         |
+------------+---------+----------------------+------+------------+-----------+

HAProxy

En ubuntu-ha se instaló haproxy y modificó el archivo de configuración para que redirija el tráfico a ubuntu-wp1 o ubuntu-wp2, según la URL destino (wp1.local o wp2.local).

root@ubuntu-ha:~# apt-get install haproxy

y en /etc/haproxy/haproxy.cfg

global
    daemon
    maxconn 256
    user    haproxy
    group   haproxy
    log     127.0.0.1       local0
    log     127.0.0.1       local1  notice

defaults
	mode	http
	log     global
	timeout connect 5000ms
	timeout client  50000ms
	timeout server  50000ms
	stats enable
	stats uri /haproxy?stats

frontend localnodes
    bind *:8080
    mode http
    use_backend wp1 if { hdr(host) -i wp1.local }
    use_backend wp2 if { hdr(host) -i wp2.local }    

backend wp1
    server wp1 10.56.142.244:80

backend wp2
    server wp2 10.56.142.34:80

Los parámetros stats habilitan una página de estadisticas sobre el uso de HAProxy

Sitio wp1.local (Apache)

En ubuntu-wp1 se instaló y configuró Apache2, PHP7.2 y MySql Server con una copia de este blog para generar contenido.

root@ubuntu-wp1:~# apt-get install apache2 php libapache2-mod-php php-mysql mysql-server

Sitio wp2.local (Nginx)

En ubuntu-wp2 se instaló y configuró Nginx, PHP7.2 y Mysql Server con otra copia de este blog para generar contenido.

root@ubuntu-wp2:~# apt-get install nginx php php-fpm php-mysql mysql-server

En /etc/nginx/sites-avaible/default se modificaron las siguientes lineas:

index index.php;

	location ~ \.php$ {
		include snippets/fastcgi-php.conf;
	
		# With php-fpm (or other unix sockets):
		fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
		# With php-cgi (or other tcp sockets):
		#fastcgi_pass 127.0.0.1:9000;
	}

	location ~ /\.ht {
		deny all;
	}

Para diferenciar los sitios se modificó el encabezado agregando el nombre del Host.

Acceso a http://wp1.local desde la notebook

Acceso a http://wp2.local desde la notebook

Test de Stress

Se realizó desde un host externo con Apache Benchmark con 1000 accesos y 100 concurrencias a cada uno de los sitios publicados.

pablo@laptop:~$ ab -k -n1000 -c100 -H 'Accept-Encoding: gzip,deflate' http://wp1.local/
This is ApacheBench, Version 2.3 <$Revision: 1807734 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking wp1.local (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        Apache/2.4.29
Server Hostname:        wp1.local
Server Port:            80

Document Path:          /
Document Length:        9488 bytes

Concurrency Level:      100
Time taken for tests:   34.707 seconds
Complete requests:      1000
Failed requests:        920
   (Connect: 0, Receive: 0, Length: 920, Exceptions: 0)
Non-2xx responses:      913
Keep-Alive requests:    1000
Total transferred:      3575219 bytes
HTML transferred:       3264024 bytes
Requests per second:    28.81 [#/sec] (mean)
Time per request:       3470.670 [ms] (mean)
Time per request:       34.707 [ms] (mean, across all concurrent requests)
Transfer rate:          100.60 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    4  10.9      0      40
Processing:   173  985 3175.4    282   34662
Waiting:      173  985 3175.4    281   34662
Total:        173  989 3181.0    282   34702

Percentage of the requests served within a certain time (ms)
  50%    282
  66%    329
  75%    370
  80%    390
  90%   2371
  95%   3145
  98%   4579
  99%  12671
 100%  34702 (longest request)
pablo@laptop:~$ ab -k -n1000 -c100 -H 'Accept-Encoding: gzip,deflate' http://wp2.local/
This is ApacheBench, Version 2.3 <$Revision: 1807734 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking wp2.local (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        nginx/1.14.0
Server Hostname:        wp2.local
Server Port:            80

Document Path:          /
Document Length:        47713 bytes

Concurrency Level:      100
Time taken for tests:   22.371 seconds
Complete requests:      1000
Failed requests:        0
Keep-Alive requests:    0
Total transferred:      48204000 bytes
HTML transferred:       47713000 bytes
Requests per second:    44.70 [#/sec] (mean)
Time per request:       2237.120 [ms] (mean)
Time per request:       22.371 [ms] (mean, across all concurrent requests)
Transfer rate:          2104.23 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       16   27  12.7     26     291
Processing:   136 2100 330.7   2174    2508
Waiting:       88 2047 331.6   2123    2431
Total:        165 2127 330.2   2201    2541

Percentage of the requests served within a certain time (ms)
  50%   2201
  66%   2224
  75%   2237
  80%   2244
  90%   2262
  95%   2287
  98%   2370
  99%   2429
 100%   2541 (longest request)

Estadísticas de HAProxy

Preparación del entorno

Para estas pruebas se generaron tres contenedores Ubuntu Server 18.04 con LXC sobre Kubuntu Desktop 18.04

+------------+---------+----------------------+------+------------+-----------+
|   NOMBRE   | ESTADO  |         IPV4         | IPV6 |    TIPO    | SNAPSHOTS |
+------------+---------+----------------------+------+------------+-----------+
| ubuntu-ha  | RUNNING | 10.56.142.111 (eth0) |      | PERSISTENT | 0         |
+------------+---------+----------------------+------+------------+-----------+
| ubuntu-wp1 | RUNNING | 10.56.142.244 (eth0) |      | PERSISTENT | 0         |
+------------+---------+----------------------+------+------------+-----------+
| ubuntu-wp2 | RUNNING | 10.56.142.34 (eth0)  |      | PERSISTENT | 0         |
+------------+---------+----------------------+------+------------+-----------+

HAProxy

En ubuntu-ha se instaló haproxy y modificó el archivo de configuración para que balancee el tráfico http://pablo.sarubbi.com.ar entre los servidores web implementados en ubuntu-wp1 y ubuntu-wp2.

root@ubuntu-ha:~# apt-get install haproxy

y en /etc/haproxy/haproxy.cfg

global
    daemon
    maxconn 256
    user    haproxy
    group   haproxy
    log     127.0.0.1       local0
    log     127.0.0.1       local1  notice

defaults
	mode	http
	log     global
	timeout connect 5000ms
	timeout client  50000ms
	timeout server  50000ms
	stats enable
	stats uri /haproxy?stats

frontend localnodes
    bind *:8080
    mode http
    default_backend nodes

backend nodes
    mode http
    balance roundrobin
    option forwardfor
    http-request set-header X-Forwarded-Port %[dst_port]
    http-request add-header X-Forwarded-Proto https if { ssl_fc }
    option httpchk HEAD / HTTP/1.1\r\nHost:localhost
    server wp1 10.56.142.244:80 check
    server wp2 10.56.142.34:80  check

Los parámetros stats habilitan una página de estadisticas sobre el uso de HAProxy

Sitio pablosarubbi.com.ar en ubuntu-wp1 (Apache)

En ubuntu-wp1 se instaló y configuró Apache2, PHP7.2 y MySql Server con una copia de este blog para generar contenido.

root@ubuntu-wp1:~# apt-get install apache2 php libapache2-mod-php php-mysql mysql-server

Sitio pablosarubbi.com.ar en ubuntu-wp2 (Nginx)

En ubuntu-wp2 se instaló y configuró Nginx, PHP7.2 y Mysql Server con otra copia de este blog para generar contenido.

root@ubuntu-wp2:~# apt-get install nginx php php-fpm php-mysql mysql-server

En /etc/nginx/sites-avaible/default se modificaron las siguientes lineas:

index index.php;

	location ~ \.php$ {
		include snippets/fastcgi-php.conf;
	
		# With php-fpm (or other unix sockets):
		fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
		# With php-cgi (or other tcp sockets):
		#fastcgi_pass 127.0.0.1:9000;
	}

	location ~ /\.ht {
		deny all;
	}

Para diferenciar los sitios se modificó el encabezado agregando el nombre del Host.

Pruebas de balanceo

Primer acceso a http://pablo.sarubbi.com.ar reenvía al sitio en ubuntu-wp1

El segundo acceso a http://pablo.sarubbi.com.ar reenvía al sitio en ubuntu-wp2

Test de Stress

Se realizó desde un host externo con Apache Benchmark con 1000 accesos con 100 concurrencias.

pablo@laptop:~$ ab -k -n1000 -c100 -H 'Accept-Encoding: gzip,deflate' http://pablo.sarubbi.com.ar/
This is ApacheBench, Version 2.3 <$Revision: 1807734 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking pablo.sarubbi.com.ar (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        nginx/1.14.0
Server Hostname:        pablo.sarubbi.com.ar
Server Port:            80

Document Path:          /
Document Length:        47713 bytes

Concurrency Level:      100
Time taken for tests:   37.221 seconds
Complete requests:      1000
Failed requests:        300
   (Connect: 0, Receive: 0, Length: 300, Exceptions: 0)
Non-2xx responses:      235
Keep-Alive requests:    300
Total transferred:      35093415 bytes
HTML transferred:       34643711 bytes
Requests per second:    26.87 [#/sec] (mean)
Time per request:       3722.117 [ms] (mean)
Time per request:       37.221 [ms] (mean, across all concurrent requests)
Transfer rate:          920.74 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   18  12.6     23      46
Processing:    37 1883 2420.1   1592   36583
Waiting:       37 1842 2424.7   1538   36583
Total:         41 1902 2421.3   1612   36602

Percentage of the requests served within a certain time (ms)
  50%   1612
  66%   2129
  75%   2236
  80%   2364
  90%   2853
  95%   3824
  98%   9170
  99%   9721
 100%  36602 (longest request)

Estadísticas de HAProxy

Conclusiones

Se evidencia que HAProxy ejecuta correctamente el balanceo de carga, aunque en este entorno de pruebas se pudo evidenciar que con la configuración por default, y contenedores con 512MB de RAM, el Nginx mantiene un comportamiento mucho mas estable que el Apache.

Módulos:
apt: para instalar zabbix_agent
replace: para reemplazar la configuración por default en el archivo /etc/zabbix/zabbix_agent
– service: para restartear el zabbix-agent.

Generar un archivo zabbix_agent.yml con el siguiente código:

---
- hosts: all
  remote_user: admin
  become: yes
  tasks:
    - name: 'Install zabbix-agent package'
      apt:
        name: zabbix-agent
        update_cache: yes
    - name: Stop service zabbix-agent
      service:
        name: zabbix-agent
        state: stopped
    - name: 'Replace Server parameter'
      replace:
        path: /etc/zabbix/zabbix_agentd.conf
        regexp: '^Server=(.*)'
        replace: 'Server=zabbix.bancointerfinanzas.com.ar'
    - name: 'Replace ServerActive parameter'
      replace:
        path: /etc/zabbix/zabbix_agentd.conf          
        regexp: '^ServerActive=(.*)'
        replace: 'ServerActive=zabbix.bancointerfinanzas.com.ar'
    - name: 'Replace Hostname parameter'
      replace:
        path: /etc/zabbix/zabbix_agentd.conf          
        regexp: '^Hostname=(.*)'
        replace: 'Hostname={{ ansible_hostname }}'
    - name: Start service zabbix-agent
      service:
        name: zabbix-agent
        state: started

El script se ejecuta de la siguiente manera:

pablo@ansible:~$ ansible-playbook zabbix-agent.yml -K
SUDO password: 

PLAY [192.168.0.100] ***************************************************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************************************************************
ok: [192.168.0.100]

TASK [Install zabbix-agent package] **********************************************************************************************************************************************
changed: [192.168.0.100]

TASK [Stop service zabbix-agent] *************************************************************************************************************************************************
changed: [192.168.0.100]

TASK [Replace Server parameter] **************************************************************************************************************************************************
changed: [192.168.0.100]

TASK [Replace ServerActive parameter] ********************************************************************************************************************************************
changed: [192.168.0.100]

TASK [Replace Hostname parameter] ************************************************************************************************************************************************
changed: [192.168.0.100]

TASK [Start service zabbix-agent] ************************************************************************************************************************************************
changed: [192.168.0.100]

PLAY RECAP ***********************************************************************************************************************************************************************
192.168.0.100                : ok=7    changed=6    unreachable=0    failed=0   
Stop SOPA