diff --git a/.gitignore b/.gitignore index b3f910c5eec040dae269333e36738b18d21a4f0f..eab982a888fbccd30148ae4aaa458303b32c12c9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ group_vars/* releases/* contract/* paquerette_utils.conf.yml + +# IDE +.idea \ No newline at end of file diff --git a/roles/_app_backup_data/README.md b/roles/_app_backup_data/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a151482a0b9ccb6b673525d7d3b91f8310633c83 --- /dev/null +++ b/roles/_app_backup_data/README.md @@ -0,0 +1,2 @@ +# Backup task for Apps Data + diff --git a/roles/_app_backup_data/tasks/install.yml b/roles/_app_backup_data/tasks/install.yml new file mode 100644 index 0000000000000000000000000000000000000000..f1029ce062a066cda53b4c971e272dec844423d6 --- /dev/null +++ b/roles/_app_backup_data/tasks/install.yml @@ -0,0 +1,10 @@ +--- + + - name: "template for backup - no database" + template: + src: app_backup_data.j2 + dest: "{{ backup_item_dir }}/20-{{ app_instance_id }}-data.sh" + mode: 0640 + when: app_backup_data == "yes" + + diff --git a/roles/_app_backup_data/tasks/main.yml b/roles/_app_backup_data/tasks/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..af2267e1ffef66cfe604372bc17eabeec79dc3a0 --- /dev/null +++ b/roles/_app_backup_data/tasks/main.yml @@ -0,0 +1,10 @@ +--- + +- import_tasks: install.yml + when: app_run in ['install', 'reinstall'] + +#- import_tasks: upgrade.yml +# when: app_run == 'upgrade' + +- import_tasks: uninstall.yml + when: app_run == 'uninstall' diff --git a/roles/_app_backup_data/tasks/uninstall.yml b/roles/_app_backup_data/tasks/uninstall.yml new file mode 100644 index 0000000000000000000000000000000000000000..c3c6c084487231ff0b10ca4f3d3b9258f94c944a --- /dev/null +++ b/roles/_app_backup_data/tasks/uninstall.yml @@ -0,0 +1,5 @@ +--- + - name: "remove backup task" + file: + path: "{{ backup_item_dir }}/20-{{ app_instance_id }}-data.sh" + state: absent diff --git a/roles/_app_backup_data/templates/app_backup_data.j2 b/roles/_app_backup_data/templates/app_backup_data.j2 new file mode 100644 index 0000000000000000000000000000000000000000..0880f7e3bf66847920d7be1d2bdc3bf6a2c78ef0 --- /dev/null +++ b/roles/_app_backup_data/templates/app_backup_data.j2 @@ -0,0 +1,5 @@ +# Note: the spaces around the equal sign ('=') are optional. +when = everyday at {{ backup_app_service_conf_time | mandatory }} + +rsync -aAx --del {{ app_data }} {{ backup_prod_dir }}/{{ app_instance_id }}.data +rc=$?; if [[ $rc != 0 ]]; then error "app" ; fi diff --git a/roles/_web_app/defaults/main.yml b/roles/_web_app/defaults/main.yml index 38e10e063a336fe1c0e601bc5b0f22723915da43..4f17c556c263cad82c2e1969b3c8ef8edead19c0 100644 --- a/roles/_web_app/defaults/main.yml +++ b/roles/_web_app/defaults/main.yml @@ -2,7 +2,7 @@ packages_list: [] app_user_chrooted: "yes" - app_group: "{{ app_user }}" + app_group: "www-data" app_git_repo: "" app_src: "" @@ -28,6 +28,8 @@ app_wsgi_entry_point: "{{ app_instance_root }}/{{ app_instance_id }}.wsgi" app_wsgi_script_reloading: "On" app_wsgi_pass_authorization: "On" + + app_backup_data: "no" diff --git a/roles/_web_app/tasks/install.yml b/roles/_web_app/tasks/install.yml index 97949e5924935f6d4a982c8646f5e55bfc7abf6e..f02846372a98657e8ddf0e2322f0b6b0c7ee47e5 100644 --- a/roles/_web_app/tasks/install.yml +++ b/roles/_web_app/tasks/install.yml @@ -24,6 +24,11 @@ - import_role: name: _app_backup + - name: "Backup data" + import_role: + name: _app_backup_data + when: app_backup_data == "yes" + ### reverse proxy configuration - block: diff --git a/roles/garradin_instance/.gitignore b/roles/garradin_instance/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..85e7c1dfcb7fbb33f932c81024018cd8c10519da --- /dev/null +++ b/roles/garradin_instance/.gitignore @@ -0,0 +1 @@ +/.idea/ diff --git a/roles/garradin_instance/README.md b/roles/garradin_instance/README.md new file mode 100644 index 0000000000000000000000000000000000000000..441927e49c713e8ef886dc0df0fe43561c6247de --- /dev/null +++ b/roles/garradin_instance/README.md @@ -0,0 +1,26 @@ +# Instance Garradin + +## Résumé + +Rôle permettant d'installer garradin. https://garradin.eu + +## Variables + +### Configuration du mail +```` +smtp_security: STARTTLS +smtp_host: false +smtp_user: null +smtp_pass: null +smtp_port: 587 +```` + +### Chrootage de l'environnement utilisateur +```` +app_user_chrooted: "yes" +```` + +### Version de l'application +```` +app_version: 1.1.14 +```` \ No newline at end of file diff --git a/roles/garradin_instance/defaults/main.yml b/roles/garradin_instance/defaults/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..0f47072dfff86f59c27371c24f8c10b12478f99a --- /dev/null +++ b/roles/garradin_instance/defaults/main.yml @@ -0,0 +1,21 @@ +--- +app_version: 1.1.14 + +app_user_chrooted: "yes" + +php_composer: "no" +python3: "no" +app_wsgi: "no" + +# +# smtp default parameters +# + +smtp_security: STARTTLS +smtp_host: false +smtp_user: null +smtp_pass: null +smtp_port: 587 + + +app_backup_data: "yes" diff --git a/roles/garradin_instance/handlers/main.yml b/roles/garradin_instance/handlers/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..0bb9a7bd2393dcb08ffdec6ada2f1715d5eacbbe --- /dev/null +++ b/roles/garradin_instance/handlers/main.yml @@ -0,0 +1,7 @@ +--- + +- name: reload nginx web_app - Garradin + service: name=nginx state=reloaded + +- name: reload php-fpm web_app - Garradin + service: name=php{{ php_version }}-fpm state=reloaded \ No newline at end of file diff --git a/roles/garradin_instance/tasks/configure.yml b/roles/garradin_instance/tasks/configure.yml new file mode 100644 index 0000000000000000000000000000000000000000..509d62006ebc60eb15342d5ce028b973726d8fc5 --- /dev/null +++ b/roles/garradin_instance/tasks/configure.yml @@ -0,0 +1,22 @@ +--- + +- name: "Configuring Garradin" + template: + src: "config.local.php.j2" + dest: "{{ app_instance_root }}/config.local.php" + owner: "{{ app_user }}" + group: "{{ app_group }}" + backup: yes + mode: 0660 + tags: + - garradin_local_conf + +- name: "cron mode for background jobs" + cron: + name: "{{ app_instance_root }}/scripts/cron.php >/dev/null 2>&1" + user: "{{ app_user }}" + day: "*/1" + job: "php -f {{ app_instance_root }}/cron.php >/dev/null 2>&1" + tags: + - garradin_cron + diff --git a/roles/garradin_instance/tasks/main.yml b/roles/garradin_instance/tasks/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..6fdeae59f6c4a1c5483008cbec40c26e222ef6c7 --- /dev/null +++ b/roles/garradin_instance/tasks/main.yml @@ -0,0 +1,41 @@ +--- + +- import_role: + name: _web_app + +- name: "template {{ rev_proxy }}_app.j2 {{ app_instance_id }}" + template: + src: "{{ rev_proxy }}_app.j2" + dest: "/etc/{{ rev_proxy }}/sites-available/{{ app_instance_id }}.conf" + when: app_wsgi == "no" + notify: reload {{ rev_proxy }} web_app + tags: + - garradin_rev_proxy + +- name: Check if app folder exists + stat: + path: "{{ app_instance_root }}" + register: app_folder + +- name: Check if data folder exists + stat: + path: "{{ app_data }}" + register: data_folder + tags: "data_setup" + +- name: "dir {{ app_data }}" + file: + path: "{{ app_data }}" + state: directory + mode: 0700 + group: "www-data" + owner: "{{ app_user }}" + when: data_folder.stat.exists == false and app_folder.stat.exists + tags: "data_setup" + +- name: Configuring garradin if app folder exists + import_tasks: configure.yml + when: app_folder.stat.exists + tags: + - garradin_configure + diff --git a/roles/garradin_instance/templates/config.local.php.j2 b/roles/garradin_instance/templates/config.local.php.j2 new file mode 100644 index 0000000000000000000000000000000000000000..792d2568df0f9563d8957cc0f225a206309626fc --- /dev/null +++ b/roles/garradin_instance/templates/config.local.php.j2 @@ -0,0 +1,466 @@ + Avancé (accessible uniquement si ENABLE_TECH_DETAILS est à true) + * + * Défaut : null + */ + +//const FILE_STORAGE_CONFIG = null; + +/** + * Forcer le quota disponible pour les fichiers + * + * Si cette constante est renseignée (en octets) alors il ne sera + * pas possible de stocker plus que cette valeur. + * Tout envoi de fichier sera refusé. + * + * Défaut : null (dans ce cas c'est le stockage qui détermine la taille disponible, donc généralement l'espace dispo sur le disque dur !) + */ + +//const FILE_STORAGE_QUOTA = 10000; // Forcer le quota alloué à 10 Mo, quel que soit le backend de stockage + +/** + * Commande de création de PDF + * + * Commande qui sera exécutée pour créer un fichier PDF à partir d'un HTML. + * Si laissé non spécifié (ou NULL), Garradin essaiera de détecter une solution entre + * PrinceXML, Chromium, wkhtmltopdf ou weasyprint. + * + * %1$s sera remplacé par le chemin du fichier HTML, et %2$s par le chemin du fichier PDF. + * + * Exemple : chromium --headless --print-to-pdf=%2$s %1$s + * + * Défaut : null + */ +const PDF_COMMAND = 'chromium --headless --print-to-pdf=%2$s %1$s'; + +/** + * Clé de licence + * + * Cette clé permet de débloquer certaines fonctionnalités dans des extensions officielles. + * + * Pour l'obtenir il faut se créer un compte sur Garradin.eu + * et faire une contribution financière. + * La clé apparaîtra ensuite en dessous des informations + * de l'association dans la page "Mon abonnement Garradin.eu". + * + * Il faut recopier cette clé dans le fichier config.local.php + * dans la constante CONTRIBUTOR_LICENSE. + * + * Merci de ne pas essayer de contourner cette licence et de contribuer au + * financement de notre travail :-) + */ +//const CONTRIBUTOR_LICENSE = 'XXXXX'; diff --git a/roles/garradin_instance/templates/nginx_app.j2 b/roles/garradin_instance/templates/nginx_app.j2 new file mode 100644 index 0000000000000000000000000000000000000000..683848a8ecbc3eb3c5ac7d47c34c6115bb2e7f91 --- /dev/null +++ b/roles/garradin_instance/templates/nginx_app.j2 @@ -0,0 +1,92 @@ +upstream php-handler{{ app_instance_id }} { + server unix:/var/run/php/php{{ php_version }}-fpm-{{ app_user }}.sock; +} + + +map $http_user_agent $log_ua { + ~Monit 0; + default 1; +} + +server { + listen 80; + listen [::]:80; + server_name {{ app_domain | mandatory }}; + # enforce https + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name {{ app_domain }}; + + ssl_certificate /etc/letsencrypt/live/{{ app_domain }}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/{{ app_domain }}/privkey.pem; + + # Add headers to serve security related headers + # Before enabling Strict-Transport-Security headers please read into this + # topic first. + # add_header Strict-Transport-Security "max-age=15768000; + # includeSubDomains; preload;"; + # + # WARNING: Only add the preload option once you read about + # the consequences in https://hstspreload.org/. This option + # will add the domain to a hardcoded list that is shipped + # in all major browsers and getting removed from this list + # could take several months. + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header X-Robots-Tag all; # https://developers.google.com/search/docs/advanced/robots/robots_meta_tag + add_header X-Download-Options noopen; + add_header X-Permitted-Cross-Domain-Policies none; + add_header Strict-Transport-Security "max-age=15768000"; + + access_log {{ www_log }}/{{ app_instance_id }}/access.log combined if=$log_ua; + error_log {{ www_log }}/{{ app_instance_id }}/error.log; + + include {{ app_instance_www_root }}/nginx/*.conf; + + + # set max upload size + client_max_body_size 512M; + fastcgi_buffers 64 4K; + + # Enable gzip but do not remove ETag headers + gzip on; + gzip_vary on; + gzip_comp_level 4; + gzip_min_length 256; + gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; + gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; + + location / { + + # Path to source + alias {{ app_instance_www_root }}/www/; + + if ($scheme = http) { + rewrite ^ https://$server_name$request_uri? permanent; + } + + index index.php /_route.php; + try_files $uri $uri/ //_route.php?$query_string; + + location ~ \.php$ { + if (!-e $request_filename) { + rewrite ^/?(.*)$ /_route.php?/$1 last; + break; + } + fastcgi_pass unix:/var/run/php/php{{ php_version }}-fpm-{{ app_user }}.sock; + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param REMOTE_USER $remote_user; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param SCRIPT_FILENAME $request_filename; + } + + # Increase size limit + client_max_body_size 2M; + } + +} diff --git a/roles/garradin_instance/vars/main.yml b/roles/garradin_instance/vars/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..68704b7f099af5d0a4a1806210386f1f0d4e190e --- /dev/null +++ b/roles/garradin_instance/vars/main.yml @@ -0,0 +1,13 @@ +--- +app_program: "Garradin" + +app_src_root_name: "garradin-{{ app_version }}" +database_type: "sqlite" +packages_list: [ "php{{ php_version }}-sqlite3", "chromium-browser", "php{{ php_version }}-fpm", "php{{ php_version }}-intl", "php{{ php_version }}-cli", "php{{ php_version }}-imagick" ] + +app_src: "https://fossil.kd2.org/garradin/uv/garradin-{{ app_version }}.tar.bz2" +php_version: "7.4" + +app_data: "{{ app_instance_root }}/../{{ app_instance_id }}.data" + +app_group: www-data