From 6918bfc1cea5f65690910a4f6a546e25b42a9709 Mon Sep 17 00:00:00 2001 From: Daniel Berteaud Date: Fri, 4 Mar 2022 18:00:06 +0100 Subject: [PATCH] Update to 2022-03-04 18:00 --- roles/common/tasks/system.yml | 9 +++ roles/gitea/defaults/main.yml | 4 +- roles/includes/create_selfsigned_cert.yml | 6 +- .../templates/lemonldap-ng.ini.j2 | 2 +- .../templates/llng-uwsgi.service.j2 | 2 +- roles/letsencrypt/templates/domains.txt.j2 | 7 ++ roles/pgadmin4/defaults/main.yml | 2 +- roles/sftpgo/defaults/main.yml | 39 +++++++--- .../files/hooks/anonymous-ftp-password-hook | 16 +++++ roles/sftpgo/meta/main.yml | 3 +- roles/sftpgo/tasks/conf.yml | 71 +++++++++++++++++++ roles/sftpgo/tasks/directories.yml | 3 + roles/sftpgo/tasks/install.yml | 9 +++ roles/sftpgo/tasks/iptables.yml | 13 ++-- roles/sftpgo/templates/dehydrated_hook.j2 | 20 ++++++ roles/sftpgo/templates/pre-backup.j2 | 4 ++ roles/sftpgo/vars/RedHat.yml | 2 +- 17 files changed, 190 insertions(+), 22 deletions(-) create mode 100644 roles/sftpgo/files/hooks/anonymous-ftp-password-hook create mode 100644 roles/sftpgo/templates/dehydrated_hook.j2 diff --git a/roles/common/tasks/system.yml b/roles/common/tasks/system.yml index 6114845..f22dd17 100644 --- a/roles/common/tasks/system.yml +++ b/roles/common/tasks/system.yml @@ -111,11 +111,20 @@ lineinfile: path=/etc/screenrc regexp='^shell\s.*' line='shell -/bin/sh' when: ansible_os_family == 'Debian' +- name: Install rsyslog + package: name=rsyslog + when: not system_disable_syslog + +- name: Check if rsyslog is installed + stat: path=/lib/systemd/system/rsyslog.service + register: system_rsyslog_unit + - name: Handle syslog daemon service: name: rsyslog state: "{{ (system_disable_syslog | default(False)) | ternary('stopped','started') }}" enabled: "{{ (system_disable_syslog | default(False)) | ternary(False,True) }}" + when: system_rsyslog_unit.stat.exists - name: Remove old bash aliases script file: path=/etc/profile.d/bash_aliases.sh state=absent diff --git a/roles/gitea/defaults/main.yml b/roles/gitea/defaults/main.yml index f5f835f..f086be3 100644 --- a/roles/gitea/defaults/main.yml +++ b/roles/gitea/defaults/main.yml @@ -1,11 +1,11 @@ --- # Version to install -gitea_version: 1.16.2 +gitea_version: 1.16.3 # URL to the binary gitea_bin_url: https://dl.gitea.io/gitea/{{ gitea_version }}/gitea-{{ gitea_version }}-linux-amd64 # sha256 of the binary -gitea_bin_sha256: ec9b01d119cfe47df44d580c1d321132ce054ff139b05b0a35da91268ca2bcbe +gitea_bin_sha256: ae6af3a29aa2e7420fb7dc7f57e417b079d1d587387bb76f7193b7bf9716df26 # Handle updates. If set to false, ansible will only install # Gitea and then won't touch an existing installation gitea_manage_upgrade: True diff --git a/roles/includes/create_selfsigned_cert.yml b/roles/includes/create_selfsigned_cert.yml index 7f0c830..8d49562 100644 --- a/roles/includes/create_selfsigned_cert.yml +++ b/roles/includes/create_selfsigned_cert.yml @@ -2,6 +2,10 @@ - name: Ensure openssl is installed package: name=openssl + when: openssl_installed is not defined + +- name: Mark openssl as installed + set_fact: openssl_installed=True - name: Create cert dir file: path={{ cert_path | dirname }} state=directory @@ -17,4 +21,4 @@ creates: "{{ cert_path }}" - name: Restrict permissions of the private key - file: path={{ cert_key_path }} owner={{ cert_user | default(omit) }} group={{ cert_user | default(omit) }} mode=600 + file: path={{ cert_key_path }} owner={{ cert_user | default(omit) }} group={{ cert_group | default(omit) }} mode={{ cert_mode | default('600') }} diff --git a/roles/lemonldap_ng/templates/lemonldap-ng.ini.j2 b/roles/lemonldap_ng/templates/lemonldap-ng.ini.j2 index f1f481f..4e9347b 100644 --- a/roles/lemonldap_ng/templates/lemonldap-ng.ini.j2 +++ b/roles/lemonldap_ng/templates/lemonldap-ng.ini.j2 @@ -15,7 +15,7 @@ globalStorageOptions = { localSessionStorage = Cache::FileCache localSessionStorageOptions = { \ 'namespace' => 'sessions', \ - 'default_expires_in' => '300', \ + 'default_expires_in' => '{{ llng_session_cache }}', \ 'directory_umask' => '007', \ 'cache_root' => '/var/cache/lemonldap-ng', \ 'cache_depth' => 3 \ diff --git a/roles/lemonldap_ng/templates/llng-uwsgi.service.j2 b/roles/lemonldap_ng/templates/llng-uwsgi.service.j2 index 5d5151f..40a4ea1 100644 --- a/roles/lemonldap_ng/templates/llng-uwsgi.service.j2 +++ b/roles/lemonldap_ng/templates/llng-uwsgi.service.j2 @@ -29,7 +29,7 @@ ExecStart=/usr/sbin/uwsgi \ --limit-post 0 \ --safe-pidfile /run/llng-uwsgi/llng-uwsgi.pid \ --die-on-term -ExecReload=kill -HUP $MAINPID +ExecReload=/usr/bin/kill -HUP $MAINPID PrivateTmp=yes PrivateDevices=yes ProtectSystem=full diff --git a/roles/letsencrypt/templates/domains.txt.j2 b/roles/letsencrypt/templates/domains.txt.j2 index 0380317..a30e09d 100644 --- a/roles/letsencrypt/templates/domains.txt.j2 +++ b/roles/letsencrypt/templates/domains.txt.j2 @@ -42,3 +42,10 @@ {% if fpbx_letsencrypt_cert is defined and fpbx_letsencrypt_cert is string and fpbx_letsencrypt_cert not in letsencrypt_certs | default([]) | map(attribute='common_name') %} {{ fpbx_letsencrypt_cert }} {% endif %} +{% if sftpgo_extra_conf is defined %} +{% for service in ['ftpd','webdavd','httpd','telemetry'] %} +{% if sftpgo_extra_conf[service] is defined and sftpgo_extra_conf[service].certificate_file is defined and sftpgo_extra_conf[service].certificate_file is search('^letsencrypt:') and sftpgo_extra_conf[service].certificate_file | regex_replace('^letsencrypt:','') not in letsencrypt_certs | default([]) | map(attribute='common_name') %} +{{ sftpgo_extra_conf[service].certificate_file | regex_replace('^letsencrypt:','') }} +{% endif %} +{% endfor %} +{% endif %} diff --git a/roles/pgadmin4/defaults/main.yml b/roles/pgadmin4/defaults/main.yml index 084df8d..bae53bf 100644 --- a/roles/pgadmin4/defaults/main.yml +++ b/roles/pgadmin4/defaults/main.yml @@ -10,7 +10,7 @@ pga_src_ip: [] # Root dir where the app will be installed pga_root_dir: /opt/pgadmin4_{{ pga_id }} # Version to deploy -pga_version: '6.4' +pga_version: '6.5' # URL of the wheel pga_pip_url: https://ftp.postgresql.org/pub/pgadmin/pgadmin4/v{{ pga_version }}/pip/pgadmin4-{{ pga_version }}-py3-none-any.whl diff --git a/roles/sftpgo/defaults/main.yml b/roles/sftpgo/defaults/main.yml index 0d4709a..030d0f5 100644 --- a/roles/sftpgo/defaults/main.yml +++ b/roles/sftpgo/defaults/main.yml @@ -17,6 +17,10 @@ sftpgo_root_dir: /opt/sftpgo sftpgo_user: sftpgo # Database settings +# Engine can be sqlite or mysql +sftpgo_db_engine: mysql + +# Settings for mysql engine sftpgo_db_server: "{{ mysql_server | default('localhost') }}" sftpgo_db_port: 3306 sftpgo_db_name: sftpgo @@ -27,10 +31,11 @@ sftpgo_db_user: sftpgo # You can restrict access per port. This can be a list of IP (or network in CIDR notation) # Access will be controlled by iptables sftpgo_src_ip: [] -sftpgo_sftp_src_ip: "{{ sftpgo_src_ip }}" -sftpgo_ftp_src_ip: "{{ sftpgo_src_ip }}" -sftpgo_webdav_src_ip: "{{ sftpgo_src_ip }}" -sftpgo_http_src_ip: "{{ sftpgo_src_ip }}" +sftpgo_sftpd_src_ip: "{{ sftpgo_src_ip }}" +sftpgo_ftpd_src_ip: "{{ sftpgo_src_ip }}" +sftpgo_webdavd_src_ip: "{{ sftpgo_src_ip }}" +sftpgo_httpd_src_ip: "{{ sftpgo_src_ip }}" +sftpgo_telemetry_src_ip: [] # Base configuration of the service sftpgo_base_conf: @@ -52,7 +57,7 @@ sftpgo_base_conf: sftpd: bindings: port: 2022 - max_auth_tries: 4 + max_auth_tries: 4 ftpd: bindings: port: 2021 @@ -61,14 +66,18 @@ sftpgo_base_conf: end: 50200 force_passive_ip: '' disable_active_mode: True + # If you want to use Let's Encrypt cert (obtain with dehydrated), you can set + # certificate_file: letsencrypt:mycert.example.org + # No need to configure certificate_key_file, the role will handle it webdavd: bindings: port: 2080 + address: 0.0.0.0 prefix: /dav proxy_allowed: [] data_provider: - driver: mysql - name: "{{ sftpgo_db_name }}" + driver: "{{ sftpgo_db_engine }}" + name: "{{ (sftpgo_db_engine == 'mysql') | ternary(sftpgo_db_name, sftpgo_root_dir ~ '/data/sftpgo.sqlite') }}" host: "{{ sftpgo_db_server }}" port: "{{ sftpgo_db_port }}" username: "{{ sftpgo_db_user }}" @@ -77,10 +86,16 @@ sftpgo_base_conf: delayed_quota_update: 60 pool_size: 5 users_base_dir: "{{ sftpgo_root_dir }}/data/home/" + # If you want to allow anonymous FTP, you can create a user named anonymous (set a password to whatever you want, it won't be checked) + # and enable the following settings + # This hook will accept any password for the anonymous user + # check_password_hook: "{{ sftpgo_root_dir }}/bin/anonymous-ftp-password-hook" + # This will restrict the hook to the FTP protocol + # check_password_scope: 2 httpd: bindings: port: 8080 - address: '' + address: 0.0.0.0 proxy_allowed: [] enable_web_admin: True enable_web_client: True @@ -89,7 +104,13 @@ sftpgo_base_conf: backups_path: "{{ sftpgo_root_dir }}/backup" max_upload_file_size: 1048576000 telemetry: - bind_port: 0 + bind_port: 8081 + bind_address: 0.0.0.0 + smtp: + host: localhost + port: 25 + from: FTP Service + templates_path: "{{ sftpgo_root_dir }}/app/templates" # You can override and/or add custom settings here. Same format as sftpgo_base_conf # The extra conf will be merged on top of the base conf diff --git a/roles/sftpgo/files/hooks/anonymous-ftp-password-hook b/roles/sftpgo/files/hooks/anonymous-ftp-password-hook new file mode 100644 index 0000000..5ca9907 --- /dev/null +++ b/roles/sftpgo/files/hooks/anonymous-ftp-password-hook @@ -0,0 +1,16 @@ +#!/bin/bash -e + +if [[ "${SFTPGO_AUTHD_USERNAME:=}" != "anonymous" ]]; then + cat <<_EOF +{ + "status": 2, + "to_verify": "${SFTPGO_AUTHD_PASSWORD:=}" +} +_EOF +else + cat <<_EOF +{ + "status": 1 +} +_EOF +fi diff --git a/roles/sftpgo/meta/main.yml b/roles/sftpgo/meta/main.yml index d8c4089..691435d 100644 --- a/roles/sftpgo/meta/main.yml +++ b/roles/sftpgo/meta/main.yml @@ -1,5 +1,6 @@ --- dependencies: + - role: mkdir - role: mysql_server - when: sftpgo_db_server in ['localhost','127.0.0.1'] + when: sftpgo_db_server in ['localhost','127.0.0.1'] and sftpgo_db_engine == 'mysql' diff --git a/roles/sftpgo/tasks/conf.yml b/roles/sftpgo/tasks/conf.yml index 743d514..73b2592 100644 --- a/roles/sftpgo/tasks/conf.yml +++ b/roles/sftpgo/tasks/conf.yml @@ -1,10 +1,81 @@ --- +# When you configure Let's Encrypt certificate, sftpgo can't directly read the cert and key from /var/lib/dehydrated +# so a deploy_cert hook will copy them under {{ sftpgo_root_dir }}/etc/ssl +# But we still need to know the Let's Encrypt cert to use so the deploy hook will know which one to copy +# We do so by configuring certificate_file: letsencrypt:foo.example.org in SFTPGo configuration +- name: Handle Let's Encrypt cert + set_fact: + sftpgo_conf: "{{ sftpgo_conf | combine({ item: {'certificate_file': sftpgo_root_dir ~ '/etc/ssl/' ~ item ~ '.crt', 'certificate_key_file': sftpgo_root_dir ~ '/etc/ssl/' ~ item ~ '.key'}}, recursive=True) }}" + loop: + - ftpd + - webdavd + - httpd + - telemetry + when: + - sftpgo_conf[item].certificate_file is defined + - sftpgo_conf[item].certificate_file is search('^letsencrypt:') + tags: sftpgo + - name: Deploy sftpgo config template: src=sftpgo.yml.j2 dest={{ sftpgo_root_dir }}/etc/sftpgo.yml group={{ sftpgo_user }} mode=640 notify: restart sftpgo tags: sftpgo +- name: Generate self-signed certificate for ftpd + import_tasks: ../includes/create_selfsigned_cert.yml + vars: + cert_path: "{{ sftpgo_root_dir }}/etc/ssl/ftpd.crt" + cert_key_path: "{{ sftpgo_root_dir }}/etc/ssl/ftpd.key" + cert_group: "{{ sftpgo_user }}" + cert_mode: 640 + tags: sftpgo + +- name: Generate self-signed certificate for webdavd + import_tasks: ../includes/create_selfsigned_cert.yml + vars: + cert_path: "{{ sftpgo_root_dir }}/etc/ssl/webdavd.crt" + cert_key_path: "{{ sftpgo_root_dir }}/etc/ssl/webdavd.key" + cert_group: "{{ sftpgo_user }}" + cert_mode: 640 + tags: sftpgo + +- name: Generate self-signed certificate for httpd + import_tasks: ../includes/create_selfsigned_cert.yml + vars: + cert_path: "{{ sftpgo_root_dir }}/etc/ssl/httpd.crt" + cert_key_path: "{{ sftpgo_root_dir }}/etc/ssl/httpd.key" + cert_group: "{{ sftpgo_user }}" + cert_mode: 640 + tags: sftpgo + +- name: Generate self-signed certificate for telemetry + import_tasks: ../includes/create_selfsigned_cert.yml + vars: + cert_path: "{{ sftpgo_root_dir }}/etc/ssl/telemetry.crt" + cert_key_path: "{{ sftpgo_root_dir }}/etc/ssl/telemetry.key" + cert_group: "{{ sftpgo_user }}" + cert_mode: 640 + tags: sftpgo + +- name: Set permissions on certificates + file: path={{ sftpgo_root_dir }}/etc/ssl/{{ item }}.crt owner=root group={{ sftpgo_user }} mode=644 + loop: + - ftpd + - webdavd + - httpd + - telemetry + tags: sftpgo + +- name: Set permissions on private keys + file: path={{ sftpgo_root_dir }}/etc/ssl/{{ item }}.key owner=root group={{ sftpgo_user }} mode=640 + loop: + - ftpd + - webdavd + - httpd + - telemetry + tags: sftpgo + - name: Init or upgrade the database command: "{{ sftpgo_root_dir }}/app/sftpgo --config-file {{ sftpgo_root_dir }}/etc/sftpgo.yml initprovider" become_user: "{{ sftpgo_user }}" diff --git a/roles/sftpgo/tasks/directories.yml b/roles/sftpgo/tasks/directories.yml index 7e299e0..b0818ba 100644 --- a/roles/sftpgo/tasks/directories.yml +++ b/roles/sftpgo/tasks/directories.yml @@ -16,6 +16,9 @@ - dir: etc owner: "{{ sftpgo_user }}" mode: 700 + - dir: etc/ssl + owner: "{{ sftpgo_user }}" + mode: 700 - dir: bin - dir: data owner: "{{ sftpgo_user }}" diff --git a/roles/sftpgo/tasks/install.yml b/roles/sftpgo/tasks/install.yml index 1ff5acf..0a3da0e 100644 --- a/roles/sftpgo/tasks/install.yml +++ b/roles/sftpgo/tasks/install.yml @@ -62,6 +62,7 @@ - db_server: "{{ sftpgo_db_server }}" - db_port: "{{ sftpgo_db_port }}" - db_pass: "{{ sftpgo_db_pass }}" + when: sftpgo_db_engine == 'mysql' tags: sftpgo - name: Install backups hooks @@ -70,3 +71,11 @@ - pre - post tags: sftpgo + +- name: Install dehydrated hook + template: src=dehydrated_hook.j2 dest=/etc/dehydrated/hooks_deploy_cert.d/sftpgo mode=755 + tags: sftpgo + +- name: Install SFTPGo hooks + copy: src=hooks/ dest={{ sftpgo_root_dir }}/bin/ mode=755 + tags: sftpgo diff --git a/roles/sftpgo/tasks/iptables.yml b/roles/sftpgo/tasks/iptables.yml index bdf8065..89214aa 100644 --- a/roles/sftpgo/tasks/iptables.yml +++ b/roles/sftpgo/tasks/iptables.yml @@ -3,19 +3,22 @@ - name: Handle sftpgo ports in the firewall iptables_raw: name: "{{ item.name }}" - state: "{{ (item.src_ip | length > 0) | ternary('present','absent') }}" + state: "{{ (item.src_ip | length > 0 and (item.port is not string or item.port != '0')) | ternary('present','absent') }}" rules: "-A INPUT -m state --state NEW -p tcp {{ item.port is string | ternary('--dport ' ~ item.port, '-m multiport --dports ' ~ item.port | join(',')) }} -s {{ item.src_ip | join(',') }} -j ACCEPT" with_items: - port: "{{ sftpgo_conf.sftpd.bindings.port }}" name: sftpgo_sftp_port - src_ip: "{{ sftpgo_sftp_src_ip }}" + src_ip: "{{ sftpgo_sftpd_src_ip }}" - port: "{{ [sftpgo_conf.ftpd.bindings.port,sftpgo_conf.ftpd.passive_port_range.start ~ ':' ~ sftpgo_conf.ftpd.passive_port_range.end] }}" name: sftpgo_ftp_port - src_ip: "{{ sftpgo_ftp_src_ip }}" + src_ip: "{{ sftpgo_ftpd_src_ip }}" - port: "{{ sftpgo_conf.webdavd.bindings.port }}" name: sftpgo_webdav_port - src_ip: "{{ sftpgo_webdav_src_ip }}" + src_ip: "{{ sftpgo_webdavd_src_ip }}" - port: "{{ sftpgo_conf.httpd.bindings.port }}" name: sftpgo_http_port - src_ip: "{{ sftpgo_http_src_ip }}" + src_ip: "{{ sftpgo_httpd_src_ip }}" + - port: "{{ sftpgo_conf.telemetry.bind_port }}" + name: sftpgo_metrics_port + src_ip: "{{ sftpgo_telemetry_src_ip }}" tags: firewall,sftpgo diff --git a/roles/sftpgo/templates/dehydrated_hook.j2 b/roles/sftpgo/templates/dehydrated_hook.j2 new file mode 100644 index 0000000..9ecf4e6 --- /dev/null +++ b/roles/sftpgo/templates/dehydrated_hook.j2 @@ -0,0 +1,20 @@ +#!/bin/bash -e + +SFTPGO_RELOAD=0 +{% for service in ['ftpd','webdavd','httpd','telemetry'] %} +{% if sftpgo_conf[service].certificate_file is defined and sftpgo_conf[service].certificate_file is search('^letsencrypt:') %} +{% set certificate_name = sftpgo_conf[service].certificate_file | regex_replace('^letsencrypt:', '') %} +if [ $1 == "{{ certificate_name }}" ]; then + SFTPGO_RELOAD=1 + cp /var/lib/dehydrated/certificates/certs/{{ certificate_name }}/fullchain.pem {{ sftpgo_root_dir }}/etc/ssl/{{ service }}.crt + cp /var/lib/dehydrated/certificates/certs/{{ certificate_name }}/privkey.pem {{ sftpgo_root_dir }}/etc/ssl/{{ service }}.key + chown root:{{ sftpgo_user }} {{ sftpgo_root_dir }}/etc/ssl/{{ service }}.{crt,key} + chmod 644 {{ sftpgo_root_dir }}/etc/ssl/{{ service }}.crt + chmod 640 {{ sftpgo_root_dir }}/etc/ssl/{{ service }}.key +fi +{% endif %} +{% endfor %} + +if [ "$SFTPGO_RELOAD" == "1" ]; then + systemctl reload sftpgo.service +fi diff --git a/roles/sftpgo/templates/pre-backup.j2 b/roles/sftpgo/templates/pre-backup.j2 index 4a7bc87..58032e5 100644 --- a/roles/sftpgo/templates/pre-backup.j2 +++ b/roles/sftpgo/templates/pre-backup.j2 @@ -2,6 +2,9 @@ set -eo pipefail +{% if sftpgo_db_engine == 'sqlite' %} +sqlite3 {{ sftpgo_root_dir }}/data/sftpgo.sqlite .dump | zstd -c > {{ sftpgo_root_dir }}/backup/sftpgo.sql.zst +{% elif sftpgo_db_engine == 'mysql' %} /usr/bin/mysqldump \ {% if sftpgo_db_server not in ['localhost', '127.0.0.1'] %} --user={{ sftpgo_db_user | quote }} \ @@ -12,5 +15,6 @@ set -eo pipefail --quick --single-transaction \ --add-drop-table {{ sftpgo_db_name }} | \ zstd -c > {{ sftpgo_root_dir }}/backup/{{ sftpgo_db_name }}.sql.zst +{% endif %} cp -a {{ sftpgo_root_dir }}/etc/id_* {{ sftpgo_root_dir }}/backup/ diff --git a/roles/sftpgo/vars/RedHat.yml b/roles/sftpgo/vars/RedHat.yml index 9d4a72a..4027e90 100644 --- a/roles/sftpgo/vars/RedHat.yml +++ b/roles/sftpgo/vars/RedHat.yml @@ -3,4 +3,4 @@ sftpgo_packages: - tar - zstd - - mariadb + - "{{ (sftpgo_db_engine == 'mysql') | ternary('mariadb', 'sqlite') }}"