On a mergé avec joinjabber.org ! \o/

This commit is contained in:
southerntofu 2021-06-30 20:46:59 +00:00
parent 206d7aed71
commit b1802a48f9
37 changed files with 124 additions and 546 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "roles"]
path = roles
url = https://codeberg.org/southerntofu/ansible-selfhosted

103
README.md
View File

@ -1,3 +1,5 @@
**ATTENTION:** Tout ce qui suit ci-dessous n'est plus d'actualité. La documentation de nos recettes est disponible [ici](https://codeberg.org/southerntofu/ansible-selfhosted).
Bienvenue sur le dépôt de la configuration système de ~fr !
On écrit des recettes [Ansible](https://fr.wikipedia.org/wiki/Ansible_(logiciel)) pour décrire les étapes à suivre pour configurer notre serveur basé sur Debian 10 Buster (stable). Pour l'instant, on gère:
@ -7,3 +9,104 @@ On écrit des recettes [Ansible](https://fr.wikipedia.org/wiki/Ansible_(logiciel
- des [.onion](https://fr.wikipedia.org/wiki/.onion) pour les pages perso
Nous mettons à disposition un [guide utilisateurice](docs/utilisateurice.md) et un [guide administrateurice](docs/administrateurice.md). Nous essayons de rendre notre système compréhensible et reproductible. Si quelque chose n'est pas clair, ou que ça ne fonctionne pas chez toi, c'est un bug alors n'hésite pas à le signaler.
Nos recettes visent à configurer tout un système d'hébergement mutualisé à partir d'un fichier de configuration central: [config.yml](https://tildegit.org/tilde-fr/infra/src/branch/master/config.yml). En suivant des principes [déclaratifs](https://fr.wikipedia.org/wiki/Programmation_d%C3%A9clarative), nous espérons obtenir des recettes robustes et interchangeables.
**ATTENTION:** Tout ce qui suit ci-dessous n'est plus d'actualité. La documentation de nos recettes est disponible [ici](https://codeberg.org/southerntofu/ansible-selfhosted).
# Configuration déclarative
La mise en place d'un système de configuration déclarative nous force à nous concentrer sur les fonctionnalités et services que nous voulons mettre en place (qui sont communes à de nombreux hébergeurs), sans nous soucier précisément de comment faire.
Plus précisément, cette approche nous donne de la marge de manoeuvre pour expérimenter plusieurs approches pour implémenter une fonctionnalité. Par exemple, les pages personnelles de nos utilisateurices peuvent être servies par différents serveurs web.. pourquoi devrait-on toujours réécrire des recettes spécifiquement conçues pour notre système et notre serveur web préféré ?
Nous prenons l'approche inverse : nous décrivons les fonctionnalités que nous voulons obtenir dans la configuration, et laissons le champ libre à différents roles d'implémenter l'interface de configuration correspondante pour les mettre en oeuvre concrètement. Cela nous permettrait par exemple de remplacer un serveur web par un autre de façon entièrement transparente pour les sysadmin.
De plus, cette approche facilite les nouvelles implémentations. En effet, chaque logiciel a ses spécificités et écrire des recettes génériques pour un service peut être complexe. Une fois le champ des besoins étudié et délimité, la standardisation d'une interface de configuration permet aux futures implémentations d'avoir un aperçu clair des concepts et fonctionnalités en jeu.
Bref, notre système est actuellement basé sur des recettes Ansible. Mais le but est de concevoir un système qui permettent à d'autres implémentations d'être compatibles avec nos configurations, qu'elles soient écrites en bash, en Nix, en Guix... Le projet Yunohost considère actuellement de migrer progressivement [vers un modèle déclaratif](https://github.com/YunoHost/issues/issues/1614).
# Services
L'architecture de nos recettes reposent sur la distinction entre **services** et **roles**. Un service est une référence qui définit une interface de configuration. Un role est une implémentation spécifique d'un service.
Par exemple, `webserver` est un service, qui est implémenté par un rôle apache, nginx, ou lighttpd. Pour passer de apache à nginx, on peut simplement faire:
```
$ rm roles/webserver
$ ln -s roles/nginx roles/webserver
```
## Gestionnaires de paquets
Les gestionnaires de paquets sont des rôles chargés de l'installation de paquets additionnels sur le système. Parmi ceux-ci, on retrouve [npm](https://www.npmjs.com/), [cargo](https://doc.rust-lang.org/cargo/) ou encore [apt](https://en.wikipedia.org/wiki/APT_(Debian)).
Interface :
```
packages:
x: [ packageA, packageB ]
y: [ packageC, packageD ]
```
Gestionnaires de paquets implémentés :
- [x] apt (appelé `debian`)
- [x] cargo (appelé `rust`)
- [ ] npm
De plus, un gestionnaire de paquets `custom` permet de définir des recettes personnalisées pour des logiciels qui ne sont pas packagés autrement.
Fonctionnalités implémentées :
- [x] liste de paquets
- [ ] sources tierces
- [ ] paramètres de compilation
Actuellement, tous les gestionnaires de paquets dans roles/ ont un nom commençant par `.`. Dans le futur, ce préfixe sera probablement renommé `pkg-`, de telle façon qu'un gestionnaire de paquets `x` soit situé dans `roles/pkg-x`.
## Webserver
Le serveur web est un service qui permet de servir des pages et applications web.
Interface :
```
webserver:
vhosts:
- hostname: example.org
aliases: [ www.example.org ]
root: /var/www/example.org
- hostname: thepiratebay.example.org
proxy: [ https://thepiratebay.org ]
```
En plus de cette interface de configuration, le service webserver peut être appelé par d'autres services avec des paramètres additionnels. Par exemple, les pages [well-known](https://en.wikipedia.org/wiki/Well-known_URIs) sont configurées directement par d'autres services, comme ceci:
```
webserver:
vhosts:
- hostname: example.org
well-known:
regex: ^/~(.+?)(/.*)?$
alias: /home/$1/public/html/tilde/$2
autoindex: on|off
```
C'est ainsi que sont activées les pages perso sur le domaine principal du serveur. (TODO: ce n'est pas encore le cas!)
# Mutualisation
Le but de ce projet est de mutualiser des recettes d'administration système afin d'encourager les bonnes pratiques et de faciliter la mise en oeuvre de services autohébergés. En cela, notre projet est similaire à [Yunohost](https://yunohost.org/), [AlternC](https://alternc.com/), [ISPConfig](https://www.ispconfig.org/), ou encore [Freedombone](https://freedombone.net/).
Pourtant, nous faisons des choix techniques radicalement différents afin d'explorer ce que la configuration déclarative pourrait apporter aux solutions d'autohébergement.
# Écrire des recettes
Pour l'instant, toutes les recettes sont contenues dans ce dépôt. Pourtant, il est possible d'utiliser des recettes tierces via des sous-modules git. Cela pose des questions de sécurité, qui ont très bien été étudiées par le projet [Guix](https://guix.gnu.org/blog/2020/securing-updates/).
Note: pour le moment, ces recettes ne sont pas spécifiquement articulées pour pouvoir facilement remplacer des bouts. Cela va demander du taf en plus mais ça avance.
# Sécurité
**ATTENTION:** les recettes présentées ici sont dévelopées avec amateurisme et ne présentent aucune garantie de fiabilité ou de sécurité. Ne nous fais pas confiance, mais viens participer au projet pour l'améliorer !

View File

@ -1,15 +1,25 @@
hostname: fr.tild3.org
roles: [ webserver ]
hostname: "fr.tild3.org"
contact: "root@fr.tild3.org"
services:
- ".common"
- "webserver"
- "peering"
- "unix_users"
webserver:
vhosts:
- host: "fr.tild3.org"
template: "zola"
git: "https://tildegit.org/tilde-fr/site"
irc_announce:
chan: "#fr"
# TODO: reimplement peering with new recipes
peers:
- name: tilde.netlib.re
client_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEHsVZvvVX3VPj2sWxrb8LJrn3650aoLAZgbY7+CB+NU"
server_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHUAIuwEhFXTDfOEG+hQ2d/xeUwsgPJQF7oeNYr1ZXnG"
- name: tilde.netlib.re
client_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEHsVZvvVX3VPj2sWxrb8LJrn3650aoLAZgbY7+CB+NU"
server_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHUAIuwEhFXTDfOEG+hQ2d/xeUwsgPJQF7oeNYr1ZXnG"
packages:
debian: [ subversion, mercurial, htop, tmux, vim, emacs, mutt, weechat, elinks, rsync, dnsutils, make, g++, libssl-dev, mosh, gopher, sl, jq ]
rust: [ lsd ]
custom: [ zola, ttbp ]
users:
- name: tofu
sudo: true

View File

@ -1,6 +1,6 @@
#!/bin/bash
CMD="ANSIBLE_RETRY_FILES_ENABLED=0 ansible-playbook -e @config.yml roles/recette.yml"
CMD="ANSIBLE_RETRY_FILES_ENABLED=0 ansible-playbook -e @config.yml roles/main.yml"
DEPS=("ansible-playbook" "grep")
REMOTE=false

1
roles Submodule

@ -0,0 +1 @@
Subproject commit 0c58e456a5cfd97169b8907bebbb34c28db4333d

View File

@ -1,5 +0,0 @@
# Because we are using logrotate for greater flexibility, disable the
# internal certbot logrotation.
max-log-backups = 0
rsa-key-size = 4096
email = southerntofu@thunix.net

View File

@ -1,3 +0,0 @@
HiddenServiceDir /var/lib/tor/{{ item.name }}
HiddenServiceVersion 3
HiddenServicePort 80 127.0.0.1:80

View File

@ -1,4 +0,0 @@
Host *
HostKeyAlgorithms ssh-ed25519
PubkeyAcceptedKeyTypes ssh-ed25519
PasswordAuthentication no

View File

@ -1,4 +0,0 @@
- name: reload tor
service:
name: tor
state: restarted

View File

@ -1,40 +0,0 @@
- name: common-backports
lineinfile:
path: /etc/apt/sources.list.d/backports.list
line: deb http://ftp.debian.org/debian buster-backports main contrib
create: yes
state: present
- name: common-base-pkg
apt:
state: present
name: [ certbot, tor, sudo ]
update_cache: yes
# TODO: configurable contact email from config.yml
- name: common-certbot-setup
copy:
src: ../files/letsencrypt_cli.ini
dest: /etc/letsencrypt/cli.ini
- include: tor.yml
- name: common-users-gen
include_tasks: users/main.yml
when: users is defined
- name: common-peering
include: peering/main.yml
when: peers is defined
- name: common-additional-packages
include_tasks: packages.yml
when: packages is defined
- name: common-roles
include_role:
name: "{{ current_role }}"
loop: "{{ roles }}"
loop_control:
loop_var: current_role
when: roles is defined

View File

@ -1,10 +0,0 @@
# Quand packages est vide, on arrive pas ici
# Les gestionnaires de paquets sont des rôles qui commencent par .
- name: common-package-managers
include_role:
# Chaque gestionnaire de paquets peut estimer que sa liste n'est pas vide
name: ".{{ current_role.key }}"
loop: "{{ packages | dict2items }}"
loop_control:
loop_var: current_role

View File

@ -1,15 +0,0 @@
- name: common-peering-home
file:
path: "/home/peers"
state: directory
- stat:
path: "/home/peers/self"
register: local_peer
- include: setup_local.yml
when: ! local_peer.stat.exists
- name: common-peering-remote
include: setup_peer.yml
loop: "{{ peers }}"

View File

@ -1,34 +0,0 @@
- name: common-peering-local-account
user:
name: "peer"
state: present
skeleton: /etc/skel
shell: /bin/bash
system: no
createhome: yes
home: "/home/peers/self"
- name: common-peering-local-ln
file:
src: /home/peers/self
dest: "/home/peers/{{ hostname }}"
state: link
- file:
path: /home/peers/self/.ssh
owner: peer
group: peer
state: directory
- name: common-peering-local-genkey
become: yes
become_user: peer
command:
creates: /home/peers/self/.ssh/id_ed25519.pub
cmd: ssh-keygen -t ed25519 -f /home/peers/self/.ssh/id_ed25519 -N ""
- name: common-peering-local-confkey
copy:
src: ../files/ssh_config
dest: /home/peers/self/.ssh/config

View File

@ -1,24 +0,0 @@
- name: common-peering-remote-account
user:
name: "{{ item.name }}"
state: present
skeleton: /etc/skel
shell: /bin/bash
system: no
createhome: yes
home: "/home/peers/{{ item.name }}"
- name: common-peering-remote-key
lineinfile:
path: "/home/peers/{{ item.name }}/.ssh/authorized_keys"
line: "{{ item.client_key }}"
create: yes
# TODO: dans authorized_keys pour restreindre le compte à SCP
# no-port-forwarding,no-pty,command="scp source target" ssh-dss ...
# TODO: chroot
- name: common-peering-remote-known
lineinfile:
path: /home/peers/self/.ssh/known_hosts
create: yes
line: "{{ item.name }} {{ item.server_key }}"

View File

@ -1,14 +0,0 @@
- name: common-tor-create
file:
path: /etc/tor/onions
state: directory
owner: debian-tor
group: debian-tor
mode: '0740'
- name: common-tor-config
lineinfile:
path: /etc/tor/torrc
line: "%include /etc/tor/onions"
state: present
notify: reload tor

View File

@ -1,20 +0,0 @@
- include_tasks: setup_user.yml
loop: "{{ users }}"
- stat:
path: "/var/lib/tor/{{ item.name }}/hostname"
loop: "{{ users }}"
register: onion_exists
changed_when: not onion_exists.stat.exists
- name: common-users-tor-reload
service:
name: tor
state: restarted
when: onion_exists.changed
- name: common-users-tor-wait
wait_for:
path: "/var/lib/tor/{{ item.name }}/hostname"
loop: "{{ users }}"
when: onion_exists.changed

View File

@ -1,39 +0,0 @@
- name: common-users-setup-account
user:
name: "{{ item.name }}"
state: present
skeleton: /etc/skel
shell: /bin/bash
system: no
createhome: yes
home: "/home/{{ item.name }}"
register: new_user
- name: common-users-setup-sudo
user:
name: "{{ item.name }}"
group: sudo
when: item.sudo|default(false) == true
- name: common-users-setup-key
authorized_key:
user: "{{ item.name }}"
state: present
key: "{{ item.key }}"
- name: common-users-setup-onion
template:
src: ../../files/onion.conf.j2
dest: "/etc/tor/onions/{{ item.name }}.conf"
- name: common-users-setup-irc
irc:
msg: "{{ irc_announce.msg | default('Bienvenue à ' ~ item.name ~ ' sur le serveur!') }}"
server: "{{ irc_announce.server | default('irc.tilde.chat') }}"
port: "{{ irc_announce.port | default(6697) }}"
channel: "{{ irc_announce.chan }}"
nick: "{{ irc_announce.nick | default('ansibot') }}"
nick_to: "{{ irc_announce.query | default([]) }}"
use_ssl: "{{ irc_announce.tls | default(true) }}"
timeout: "{{ irc_announce.timeout | default(10) }}"
when: new_user.changed and irc_announce is defined

Binary file not shown.

View File

@ -1,14 +0,0 @@
# Pour l'instant, il n'est pas possible d'avoir un paquet qui ne porte pas le nom de son binaire
# parce qu'on vérifie que le binaire est installé
# A terme, ça sera à chaque paquet de vérifier lui-même s'il est installé
# Vérifier quels paquets custom sont installés
- stat:
path: "/usr/local/bin/{{ item }}"
loop: "{{ packages.custom }}"
register: custom_exists
- name: "Installer les paquets custom activés dans la config"
include: "{{ item.item }}/main.yml"
loop: "{{ custom_exists.results | default([]) }}"
when: not item.stat.exists

View File

@ -1,26 +0,0 @@
- stat:
path: /usr/local/bin/ttbp
register: ttbp
- name: custom-ttbp-source
git:
repo: https://tildegit.org/envs/ttbp.git
dest: /tmp/ttbp
when: not ttbp.stat.exists
- name: custom-ttbp-pkg
apt:
name: "python-setuptools"
state: present
- name: custom-ttbp-setup
command:
cmd: "python /tmp/ttbp/setup.py install"
chdir: /tmp/ttbp
when: not ttbp.stat.exists
- name: custom-ttbp-tmp
file:
path: /tmp/ttbp
state: absent
when: not ttbp.stat.exists

View File

@ -1,7 +0,0 @@
# Malheureusement zola compile pas sur debian buster (rustc v1.34 contre 1.36 requis)
# Donc on copie un binaire que j'ai compilé avec amour
- name: custom-zola-setup
copy:
src: ../../files/bin/zola
dest: /usr/local/bin/zola
mode: 0755

View File

@ -1,4 +0,0 @@
- name: debian-pkg
apt:
state: present
name: "{{ packages.debian }}"

View File

@ -1,50 +0,0 @@
- name: rust-setup
apt:
state: present
name:
- rustc
- cargo
- cargo-doc
update_cache: yes
- name: rust-user
user:
name: "rust"
state: present
skeleton: /etc/skel
shell: /bin/bash
system: no
createhome: yes
home: "/home/rust"
- name: rust-cargo-folder
file:
path: /home/rust/.cargo
state: directory
owner: rust
group: rust
- name: rust-bin-ownership
file:
path: /usr/local/bin
state: directory
owner: rust
group: rust
mode: 0755
recurse: yes
- name: rust-bin-symlink
file:
dest: /home/rust/.cargo/bin
src: /usr/local/bin
force: yes
follow: no
state: link
- name: rust-pkg
become:
become_user: rust
command:
cmd: "cargo install {{ item }}"
creates: "/usr/local/bin/{{ item }}"
loop: "{{ packages.rust }}"

View File

@ -1 +0,0 @@
Les rôles qui commencent par . (.debian, .rust, .custom) sont des gestionnaires de paquets. Il suffit de rajouter une clé dans packages dans la configuration du serveur pour créer un nouveau gestionnaire de paquet qui sera appelé ici.

View File

@ -1,7 +0,0 @@
# Les rôles dont le nom est préfixé d'un . ne sont pas faits pour être activés dans la config
- name: Installer le serveur
hosts: all
roles:
- .common

View File

@ -1,37 +0,0 @@
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /var/www/html;
location /.well-known/acme-challenge {
try_files $uri $uri/ =404;
}
location / {
return 302 https://$host$request_uri;
}
}
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
ssl_certificate /etc/letsencrypt/live/{{ hostname }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ hostname }}/privkey.pem;
server_name _;
root /var/www/html;
index index.html;
location ~ ^/~(.+?)(/.*)?$ {
alias /home/$1/public/html/tilde/$2;
autoindex on;
#try_files $2 $2/ = 404;
}
location / {
try_files $uri $uri/ =404;
}
}

View File

@ -1,12 +0,0 @@
server {
listen 80;
listen [::]:80;
server_name {{ web_onion.stdout }};
root /home/{{ item.name }}/public/html/onion;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}

View File

@ -1,16 +0,0 @@
# Taken from https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf
# This file contains important security parameters. If you modify this file
# manually, Certbot will be unable to automatically provide future security
# updates. Instead, Certbot will print and log an error message with a path to
# the up-to-date file that you will need to refer to when manually updating
# this file.
ssl_session_cache shared:le_nginx_SSL:10m;
ssl_session_timeout 1440m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";

View File

@ -1,16 +0,0 @@
{% for user in users %}
server {
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /etc/letsencrypt/live/{{ user.name }}.{{ hostname }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ user.name }}.{{ hostname }}/privkey.pem;
server_name {{ user.name }}.{{ hostname }};
root /home/{{ user.name }}/public/html/domain;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
{% endfor %}

View File

@ -1,2 +0,0 @@
- name: webserver-reload-nginx
service: name=nginx state=restarted

View File

@ -1,10 +0,0 @@
- name: webserver-certbot-main
command:
creates: /etc/letsencrypt/live/{{ hostname }}/fullchain.pem
cmd: certbot certonly --non-interactive --agree-tos --webroot -w /var/www/html -d {{ hostname }} -d www.{{ hostname }}
- name: webserver-certbot-users
command:
creates: "/etc/letsencrypt/live/{{ item.name }}.{{ hostname }}/fullchain.pem"
cmd: "certbot certonly --non-interactive --agree-tos --webroot -w /var/www/html -d {{ item.name }}.{{ hostname }}"
loop: "{{ users }}"

View File

@ -1,7 +0,0 @@
---
# This playbook contains all of the www config
- include: packages.yml
# TODO: Some certbot is needed before we can load the whole nginx config so we need some intermediary step (bootstrapping process)
- include: nginx.yml
- include: certbot.yml

View File

@ -1,31 +0,0 @@
# Need to check because we don't want to overwrite user content
# with symlinks in the name of idempotence :D
- name: webserver-multisite-check
stat:
path: "/home/{{ user.name }}/public/html"
# In case it's a symlink to elsewhere
follow: yes
register: publichtml
- name: webserver-multisite-folder
file:
path: "/home/{{ user.name }}/public/html"
state: directory
owner: "{{ user.name }}"
group: "{{ user.name }}"
mode: 0755
when: not publichtml.stat.exists
- name: webserver-multisite-symlink
file:
path: "/home/{{ user.name }}/public/html/{{ item }}"
src: "/home/{{ user.name }}/public_html"
state: link
owner: "{{ user.name }}"
group: "{{ user.name }}"
mode: 0755
loop:
- onion
- domain
- tilde
when: not publichtml.stat.exists

View File

@ -1,27 +0,0 @@
- name: webserver-default-config
template:
src: ../files/default-site.conf.j2
dest: /etc/nginx/sites-available/default-site.conf
notify: webserver-reload-nginx
- name: webserver-default-symlink
file:
src: /etc/nginx/sites-available/default-site.conf
dest: /etc/nginx/sites-enabled/default-site.conf
state: link
- name: webserver-tls-config
copy:
src: ../files/ssl.conf
dest: /etc/nginx/conf.d/ssl.conf
notify: webserver-reload-nginx
- name: webserver-personal-pages
include: pages_perso.yml
- name: webserver-bucket-size
lineinfile:
path: /etc/nginx/nginx.conf
line: "server_names_hash_bucket_size 128;"
insertafter: "^http {"
notify: webserver-reload-nginx

View File

@ -1,23 +0,0 @@
- name: webserver-onion-hostname
command: "cat /var/lib/tor/{{ item.name }}/hostname"
register: web_onion
changed_when: false
- name: webserver-onion-giveuser
copy:
src: "/var/lib/tor/{{ item.name }}/hostname"
remote_src: true
dest: "/home/{{ item.name }}/onion"
mode: 0744
- name: webserver-onion-config
template:
src: ../files/onion.conf.j2
dest: "/etc/nginx/sites-available/{{ item.name }}.onion.conf"
notify: webserver-reload-nginx
- name: webserver-onion-symlink
file:
src: "/etc/nginx/sites-available/{{ item.name }}.onion.conf"
dest: "/etc/nginx/sites-enabled/{{ item.name }}.onion.conf"
state: link

View File

@ -1,12 +0,0 @@
- name: webserver-pkg
apt:
name:
- nginx
- php-fpm
- php-curl
- php-gd
- php-intl
- php-sqlite3
- php-mbstring
state: present
update_cache: yes

View File

@ -1,25 +0,0 @@
- name: webserver-perso-config
template:
src: ../files/users.conf.j2
dest: /etc/nginx/sites-available/users-site.conf
- name: webserver-perso-symlink
file:
src: /etc/nginx/sites-available/users-site.conf
dest: /etc/nginx/sites-enabled/users-site.conf
state: link
- name: webserver-perso-publichtml
file:
path: /etc/skel/public_html
state: directory
- name: webserver-perso-multisite
include: multisite.yml
loop: "{{ users }}"
loop_control:
loop_var: user
- name: webserver-perso-onions
include: onions_perso.yml
loop: "{{ users }}"