L'utilisation d'un reverse proxy n'est pas en soit une configuration exotique, c'est quelque chose de très pratiqué et qui en général ne pose que très peu voire aucun problème.
Sauf que dans le cas de Loomio, par défaut amené avec l'excellent Let'sEncrypt, cela donne du fil à retordre !
Quel étrange casse-tête… Après des jours à essayer de faire marcher Loomio en reverse proxy, avec des erreurs d'upstream dans les logs nginx du conteneur et ceux du reverse proxy ; un, deux certificats ; un, deux, trois, quatre virtualhosts dans la conf du reverse Nginx, des problèmes de contenu mixed (lorsque le navigateur Web détecte des flux HTTP sur une connexion HTTPS)…
A force de tests, d'échanges (merci à Minus - https://www.debian-fr.org/t/loomio-derriere-un-reverseproxy/70055/6) et un peu aussi d'acharnement, nous sommes finalement arrivé (un peu par hasard !) à faire fonctionner Loomio derrière un reverse proxy Nginx, sans HTTPS côté Loomio, sans warning de contenu mixed sur HTTPS et avec mise à jour dynamique du contenu sans aucun rechargement.
…et toujours des alertes sur l'upstream du NGINX Loomio ! Les erreurs semblent en lien avec le conteneur Worker, mais finalement sans grande gravité.
Au final la configuration détaillée ci-dessous tourne avec un reverse proxy Nginx écoutant en 443 et redirigeant vers le port 80 du Nginx Loomio, c'est merveilleux
<diagram>
!@4 | |||||
AAA | |||||
!@4 | |||||
BBB | |||||
: | |||||
L | ~@2 | CCC | |||
! | |||||
) | ~ | DDD | |||
! | |||||
) | ~ | DDD | |||
! | |||||
) | ~ | DDD | |||
! | |||||
) | ~ | DDD | |||
! | |||||
) | ~ | DDD | |||
! | |||||
` | ~ | DDD |
</diagram>
On enlève tout ce qui se rapport à Let'sEncrypt, la partie HTTPS est à la seule charge du reverse proxy. On indique également de ne mapper que le port 8001 vers le port 80 pour Nginx :
#letsencrypt: # image: jrcs/letsencrypt-nginx-proxy-companion # volumes: # - /var/run/docker.sock:/var/run/docker.sock:ro # - ./certificates:/etc/nginx/certs:rw # volumes_from: # - nginx nginx: image: jwilder/nginx-proxy volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - ./nginx/vhost.d:/etc/nginx/vhost.d - ./nginx/html:/usr/share/nginx/html ports: - 8001:80 links: - loomio - faye loomio: image: loomio/loomio expose: - 3000 env_file: ./env links: - db environment: - DATABASE_URL=postgresql://postgres:password@db/loomio_production volumes: - ./attachments:/loomio/public/system/ worker: image: loomio/loomio env_file: ./env links: - db:db environment: - DATABASE_URL=postgresql://postgres:password@db/loomio_production volumes: - ./attachments:/loomio/public/system/attachments command: "bundle exec rake jobs:work" db: image: postgres volumes: - ./pgdata:/pgdata environment: - POSTGRES_PASSWORD=password - PGDATA=/pgdata mailin: image: loomio/mailin-docker ports: - 25:25 links: - loomio environment: - WEBHOOK_URL=http://loomio.example.com/email_processor/ faye: build: faye/. env_file: ./faye_env
Pour les fichiers env et faye_env :
# this is the hostname of your app used by loomio CANONICAL_HOST=loomio.example.com # this is to tell nginx that you want requests for this hostname to come to the app VIRTUAL_HOST=loomio.example.com # this is to configure letsencrypt to automatically issue and renew your hostname #LETSENCRYPT_HOST=loomio.example.com #LETSENCRYPT_EMAIL=hostmaster@example.com # the number of dots in your hostname TLD_LENGTH=2 # smtp settings SUPPORT_EMAIL=postmaster@example.com SMTP_DOMAIN=loomio.example.com SMTP_SERVER=mail.example.com SMTP_PORT=25 #SMTP_USERNAME=smtpusername #SMTP_PASSWORD=smtppassword FAYE_URL=https://faye.loomio.nomagic.fr/faye REPLY_HOSTNAME=loomio.example.com # helper bot is the account which welcomes people to their groups. HELPER_BOT_EMAIL=no-reply@loomio.example.com RAILS_ENV=production FORCE_SSL=1 DEVISE_SECRET=monDeviseSecret SECRET_COOKIE_TOKEN=monSecretCookieToken PRIVATE_PUB_SECRET_TOKEN=monSecretToken
# this is to tell nginx that you want requests for this hostname to come to the app VIRTUAL_HOST=faye.loomio.example.com # this is to configure letsencrypt to automatically issue and renew your hostname #LETSENCRYPT_HOST=faye.loomio.example.com #LETSENCRYPT_EMAIL=hostmaster@example.com PRIVATE_PUB_SECRET_TOKEN=monPrivatePubScretToken
Démarrer / redémarrer les conteneurs :
$ docker-compose down Stopping loomiodeploy_nginx_1 ... done Stopping loomiodeploy_mailin_1 ... done Stopping loomiodeploy_loomio_1 ... done Stopping loomiodeploy_worker_1 ... done Stopping loomiodeploy_db_1 ... done Stopping loomiodeploy_faye_1 ... done Removing loomiodeploy_nginx_1 ... done Removing loomiodeploy_mailin_1 ... done Removing loomiodeploy_loomio_1 ... done Removing loomiodeploy_worker_1 ... done Removing loomiodeploy_db_1 ... done Removing loomiodeploy_faye_1 ... done $ docker-compose up -d Creating loomiodeploy_faye_1 Creating loomiodeploy_db_1 Creating loomiodeploy_worker_1 Creating loomiodeploy_loomio_1 Creating loomiodeploy_mailin_1 Creating loomiodeploy_nginx_1
Et on contrôle les logs :
docker-compose logs
Côté reverse proxy, on ne déclare que la partie HTTPS. On a créé deux certificats : un pour loomio.example.com et un autre pour faye.loomio.example.com. On a utilisé Let'Encrypt pour générer les certificats.
On déclare 2 hôtes virtuels :
## loomio ############################################## upstream coreos-loomio { keepalive 100; server coreos.example.com:8001; } server { listen 443 ssl; listen [::]:443 ssl; server_name loomio.example.com ; # SSL specifique ssl_certificate /etc/letsencrypt/certs/loomio_ca.crt; ssl_certificate_key /etc/letsencrypt/private/loomio.key; ## HTTPS config ssl_session_timeout 10m; ssl_session_cache builtin:1000 shared:SSL:10m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ## Send header to tell the browser to prefer https to http traffic add_header Strict-Transport-Security max-age=31536000; # Logging access_log /var/log/nginx/loomio_access.log; access_log /var/log/nginx/loomio_upstream.log upstreamlog; error_log /var/log/nginx/loomio_error.log; location / { # mêmes paramètres pour tout le monde include conf.d/proxy.ini; # le reste du chemin est en HTTP dans le LAN # cf conf.d/upstream.ini proxy_pass http://coreos-loomio; ## assure de garder le keepalive actif proxy_set_header Connection ""; proxy_read_timeout 300; proxy_connect_timeout 300; } } ## faye ################################################ upstream coreos-loomio-faye { keepalive 100; server server coreos.example.com:8001; } server { listen 443 ssl; listen [::]:443 ssl; server_name faye.loomio.example.com ; # SSL specifique ssl_certificate /etc/letsencrypt/certs/faye.loomio_ca.crt; ssl_certificate_key /etc/letsencrypt/private/faye.loomio.key; ## HTTPS config ssl_session_timeout 10m; ssl_session_cache builtin:1000 shared:SSL:10m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ## Send header to tell the browser to prefer https to http traffic add_header Strict-Transport-Security max-age=31536000; # Logging access_log /var/log/nginx/loomio-faye_access.log; access_log /var/log/nginx/loomio-faye_upstream.log upstreamlog; error_log /var/log/nginx/loomio-faye_error.log; location / { # mêmes paramètres pour tout le monde include conf.d/proxy.ini; # le reste du chemin est en HTTP dans le LAN proxy_pass http://coreos-loomio-faye; ## assure de garder le keepalive actif proxy_set_header Connection ""; proxy_read_timeout 300; proxy_connect_timeout 300; } }
Dans notre exemple on utilise un fichier commun pour les paramètres du proxy :
# Proxy config proxy_set_header Host $host; 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 $scheme; proxy_redirect off; proxy_http_version 1.1; proxy_next_upstream off;
On test puis redémarre notre reverse-proxy :
# service nginx configtest Testing nginx configuration:. # service nginx restart
On se met en écoute sur les logs des hôtes virtuels :
# tail /var/log/nginx/loomio*error.log -f ==> /var/log/nginx/loomio_error.log <== ==> /var/log/nginx/loomio-faye_error.log <==
Pendant les longues heures passées à chercher ce qui n'allait pas, nous avons glané quelques infos pratiques sur Loomio, on les rassemble ici :
Sur l'hôte hébergeant Loomio :
docker exec -i -t loomiodeploy_db_1 /bin/bash
psql -dloomio_production -Upostgres -W
⇒ mot de passe par défaut = password
select id, email, is_admin from users;
update users set is_admin=true where email='myemail@example.com';
⇒ veiller à ce cet utilisateur soit le seul à posséder cet email (je pense que loomio bloque la création de 2 utilisateurs ayant le même courriel, mais je n'ai pas vérifié).
Vous pouvez maintenant accéder à l'interface d'administration sur https://loomio.example.com/admin, en faisant attention à ne rien casser (!)
Si ça vous gêne d'avoir un mot de passe password, indépendamment du fait que la base n'est pas accessible directement depuis l'extérieur, voilà comment procéder :
Sur l'hôte hébergeant Loomio :
docker exec -i -t loomiodeploy_db_1 /bin/bash
psql -dpostgres -Upostgres -W postgres=# \password postgres (affiché dans la console postgresql) Enter new password: (affiché dans la console postgresql) Enter it again:
Ctrl-d
), tester de vous reconnecter avec le nouveau mot de passe.exit
)$ docker-compose down Stopping loomiodeploy_nginx_1 ... done Stopping loomiodeploy_mailin_1 ... done Stopping loomiodeploy_loomio_1 ... done Stopping loomiodeploy_worker_1 ... done Stopping loomiodeploy_db_1 ... done Stopping loomiodeploy_faye_1 ... done Removing loomiodeploy_nginx_1 ... done Removing loomiodeploy_mailin_1 ... done Removing loomiodeploy_loomio_1 ... done Removing loomiodeploy_worker_1 ... done Removing loomiodeploy_db_1 ... done Removing loomiodeploy_faye_1 ... done $ docker-compose up -d Creating loomiodeploy_faye_1 Creating loomiodeploy_db_1 Creating loomiodeploy_worker_1 Creating loomiodeploy_loomio_1 Creating loomiodeploy_mailin_1 Creating loomiodeploy_nginx_1
docker-compose logs
Pour tester l'application ou procéder à son installation, c'est par là !