diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2d30104f..d9e6c849 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,46 @@ The **patch** part changes incrementally at each release.
### Security
+## [10.5.0] 2021-04-01
+
+### Added
+
+* apache: new variables for logrotate + server-status
+* filebeat: package can be upgraded to latest (default: False)
+* haproxy: possible admin access with login/pass
+* lxc-php: Add PHP 7.4 support
+* metricbeat: package can be upgraded to latest (default: False)
+* metricbeat: new variables to configure SSL mode
+* nagios-nrpe: new script check_phpfpm_multi
+* nginx: add access to server status on default VHost
+* postfix: add smtpd_relay_restrictions in configuration
+
+### Changed
+
+* apache: rotate logs daily instead of weekly
+* apache: deny requests to ^/evolinux_fpm_status-.*
+* certbot: use a fixed 1.9.0 version of the certbot-auto script (renamed "letsencrypt-auto")
+* certbot: use the legacy script on Debian 8 and 9
+* elasticsearch: log rotation is more readable/maintainable
+* evoacme: upstream release 21.01
+* evolinux-users: Add sudo rights for nagios for multi-php lxc
+* listupgrade: update script from upstream
+* minifirewall: change some defaults
+* nagios-nrpe: update check_phpfpm_status.pl & install perl dependencies
+* redis: use /run instead or /var/run
+* redis: escape password in Munin configuration
+
+### Fixed
+
+* bind9: added log files to apparmor definition so bind can run
+* filebeat: fix Ansible syntax error
+* nagios-nrpe: libfcgi-client-perl is not available before Debian 10
+* redis: socket/pid directories have the correct permissions
+
+### Removed
+
+* nginx: no more "minimal" mode, but the package remains customizable.
+
## [10.4.0] 2020-12-24
### Added
diff --git a/README.md b/README.md
index e2094277..03588208 100644
--- a/README.md
+++ b/README.md
@@ -50,6 +50,8 @@ Before starting anything of importance, we suggest contacting us to discuss what
Our conventions are available in the "ansible-public":https://gitea.evolix.org/evolix/ansible-public repository, in the CONVENTIONS.md file.
+All modifications should be documented in the CHANGELOG file, to help review releases. We encourage atomic commits, on a single role, and with the CHANGELOG in the same commit.
+
## Workflow
The ideal and most typical workflow is to create a branch, based on the "unstable" branch. The branch should have a descriptive name (a ticket/issue number is great). The branch can be treated as a pull-request or merge-request. It should be propery tested and reviewed before merging into "unstable".
diff --git a/apache/defaults/main.yml b/apache/defaults/main.yml
index 15ff1a53..e49dbc20 100644
--- a/apache/defaults/main.yml
+++ b/apache/defaults/main.yml
@@ -11,6 +11,7 @@ apache_evolinux_default_enabled: True
apache_evolinux_default_ssl_cert: /etc/ssl/certs/ssl-cert-snakeoil.pem
apache_evolinux_default_ssl_key: /etc/ssl/private/ssl-cert-snakeoil.key
+apache_serverstatus_host: 127.0.0.1
apache_serverstatus_suffix: ""
apache_serverstatus_suffix_file: "/etc/evolinux/apache_serverstatus_suffix"
@@ -20,4 +21,5 @@ apache_munin_include: True
general_alert_email: "root@localhost"
log2mail_alert_email: Null
-apache_serverstatus_host: 127.0.0.1
+apache_logrotate_frequency: daily
+apache_logrotate_rotate: 365
diff --git a/apache/files/evolinux-defaults.conf b/apache/files/evolinux-defaults.conf
index 348717ea..e5eadda8 100644
--- a/apache/files/evolinux-defaults.conf
+++ b/apache/files/evolinux-defaults.conf
@@ -9,16 +9,19 @@ StartServers 50
MinSpareServers 20
MaxSpareServers 30
MaxRequestsPerChild 0
+
AllowOverride None
Require all granted
# "Require not env XXX" is not supported :(
Deny from env=GoAway
+
-SSLProtocol all -SSLv2 -SSLv3
-SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5:!RC4
+ SSLProtocol all -SSLv2 -SSLv3
+ SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5:!RC4
+
Require all denied
@@ -31,6 +34,10 @@ SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5:!RC4
-LimitUIDRange 0 6000
-LimitGIDRange 0 6000
+ LimitUIDRange 0 6000
+ LimitGIDRange 0 6000
+
+
+ Require all denied
+
diff --git a/apache/tasks/main.yml b/apache/tasks/main.yml
index b7611cac..3854c539 100644
--- a/apache/tasks/main.yml
+++ b/apache/tasks/main.yml
@@ -161,19 +161,19 @@
tags:
- apache
-- name: "logrotate: rotate weekly"
+- name: "logrotate: {{ apache_logrotate_frequency }}"
replace:
dest: /etc/logrotate.d/apache2
regexp: "(daily|weekly|monthly)"
- replace: "weekly"
+ replace: "{{ apache_logrotate_frequency }}"
tags:
- apache
-- name: "logrotate: keep 52 files"
+- name: "logrotate: rotate {{ apache_logrotate_rotate }}"
replace:
dest: /etc/logrotate.d/apache2
regexp: '^(\s+rotate) \d+$'
- replace: '\1 52'
+ replace: '\1 {{ apache_logrotate_rotate }}'
tags:
- apache
diff --git a/bind/handlers/main.yml b/bind/handlers/main.yml
index 1eee71f6..8bb61a21 100644
--- a/bind/handlers/main.yml
+++ b/bind/handlers/main.yml
@@ -2,6 +2,11 @@
- name: reload systemd
command: systemctl daemon-reload
+- name: restart apparmor
+ service:
+ name: apparmor
+ state: restarted
+
- name: restart bind
service:
name: bind9
diff --git a/bind/tasks/main.yml b/bind/tasks/main.yml
index 3ae02f24..c5b9110c 100644
--- a/bind/tasks/main.yml
+++ b/bind/tasks/main.yml
@@ -8,6 +8,16 @@
bind_chroot_path: /var/chroot-bind
when: bind_chroot_set
+- name: configure apparmor
+ template:
+ src: apparmor.usr.sbin.named.j2
+ dest: /etc/apparmor.d/usr.sbin.named
+ owner: root
+ group: root
+ mode: '0644'
+ force: yes
+ notify: restart apparmor
+
- name: package are installed
apt:
name:
diff --git a/bind/templates/apparmor.usr.sbin.named.j2 b/bind/templates/apparmor.usr.sbin.named.j2
new file mode 100644
index 00000000..9a554437
--- /dev/null
+++ b/bind/templates/apparmor.usr.sbin.named.j2
@@ -0,0 +1,95 @@
+# vim:syntax=apparmor
+# Last Modified: Tue Mar 9 14:17:50 EST 2021
+#include
+
+/usr/sbin/named flags=(attach_disconnected) {
+ #include
+ #include
+
+ capability net_bind_service,
+ capability setgid,
+ capability setuid,
+ capability sys_chroot,
+ capability sys_resource,
+
+ # /etc/bind should be read-only for bind
+ # /var/lib/bind is for dynamically updated zone (and journal) files.
+ # /var/cache/bind is for slave/stub data, since we're not the origin of it.
+ # See /usr/share/doc/bind9/README.Debian.gz
+ /etc/bind/** r,
+ /var/lib/bind/** rw,
+ /var/lib/bind/ rw,
+ /var/cache/bind/** lrw,
+ /var/cache/bind/ rw,
+
+ # Database file used by allow-new-zones
+ /var/cache/bind/_default.nzd-lock rwk,
+
+ # gssapi
+ /etc/krb5.keytab kr,
+ /etc/bind/krb5.keytab kr,
+
+ # ssl
+ /etc/ssl/openssl.cnf r,
+
+ # root hints from dns-data-root
+ /usr/share/dns/root.* r,
+
+ # GeoIP data files for GeoIP ACLs
+ /usr/share/GeoIP/** r,
+
+ # dnscvsutil package
+ /var/lib/dnscvsutil/compiled/** rw,
+
+ # Allow changing worker thread names
+ owner @{PROC}/@{pid}/task/@{tid}/comm rw,
+
+ @{PROC}/net/if_inet6 r,
+ @{PROC}/*/net/if_inet6 r,
+ @{PROC}/sys/net/ipv4/ip_local_port_range r,
+ /usr/sbin/named mr,
+ /{,var/}run/named/named.pid w,
+ /{,var/}run/named/session.key w,
+ # support for resolvconf
+ /{,var/}run/named/named.options r,
+
+ # some people like to put logs in /var/log/named/ instead of having
+ # syslog do the heavy lifting.
+ {{ bind_log_file }} rw,
+ {{ bind_query_file }} rw,
+
+ # gssapi
+ /var/lib/sss/pubconf/krb5.include.d/** r,
+ /var/lib/sss/pubconf/krb5.include.d/ r,
+ /var/lib/sss/mc/initgroups r,
+ /etc/gss/mech.d/ r,
+
+ # ldap
+ /etc/ldap/ldap.conf r,
+ /{,var/}run/slapd-*.socket rw,
+
+ # dynamic updates
+ /var/tmp/DNS_* rw,
+
+ # dyndb backends
+ /usr/lib/bind/*.so rm,
+
+ # Samba DLZ
+ /{usr/,}lib/@{multiarch}/samba/bind9/*.so rm,
+ /{usr/,}lib/@{multiarch}/samba/gensec/*.so rm,
+ /{usr/,}lib/@{multiarch}/samba/ldb/*.so rm,
+ /{usr/,}lib/@{multiarch}/ldb/modules/ldb/*.so rm,
+ /var/lib/samba/bind-dns/dns.keytab rk,
+ /var/lib/samba/bind-dns/named.conf r,
+ /var/lib/samba/bind-dns/dns/** rwk,
+ /var/lib/samba/private/dns.keytab rk,
+ /var/lib/samba/private/named.conf r,
+ /var/lib/samba/private/dns/** rwk,
+ /etc/samba/smb.conf r,
+ /dev/urandom rwmk,
+ owner /var/tmp/krb5_* rwk,
+
+ # Site-specific additions and overrides. See local/README for details.
+ #include
+}
+
diff --git a/certbot/defaults/main.yml b/certbot/defaults/main.yml
index 876be14e..99f02e15 100644
--- a/certbot/defaults/main.yml
+++ b/certbot/defaults/main.yml
@@ -1,3 +1,4 @@
---
certbot_work_dir: /var/lib/letsencrypt
+certbot_custom_crontab: True
diff --git a/certbot/files/letsencrypt-auto b/certbot/files/letsencrypt-auto
new file mode 100644
index 00000000..2a0cda9b
--- /dev/null
+++ b/certbot/files/letsencrypt-auto
@@ -0,0 +1,1996 @@
+#!/bin/sh
+#
+# Download and run the latest release version of the Certbot client.
+#
+# NOTE: THIS SCRIPT IS AUTO-GENERATED AND SELF-UPDATING
+#
+# IF YOU WANT TO EDIT IT LOCALLY, *ALWAYS* RUN YOUR COPY WITH THE
+# "--no-self-upgrade" FLAG
+#
+# IF YOU WANT TO SEND PULL REQUESTS, THE REAL SOURCE FOR THIS FILE IS
+# letsencrypt-auto-source/letsencrypt-auto.template AND
+# letsencrypt-auto-source/pieces/bootstrappers/*
+
+set -e # Work even if somebody does "sh thisscript.sh".
+
+# Note: you can set XDG_DATA_HOME or VENV_PATH before running this script,
+# if you want to change where the virtual environment will be installed
+
+# HOME might not be defined when being run through something like systemd
+if [ -z "$HOME" ]; then
+ HOME=~root
+fi
+if [ -z "$XDG_DATA_HOME" ]; then
+ XDG_DATA_HOME=~/.local/share
+fi
+if [ -z "$VENV_PATH" ]; then
+ # We export these values so they are preserved properly if this script is
+ # rerun with sudo/su where $HOME/$XDG_DATA_HOME may have a different value.
+ export OLD_VENV_PATH="$XDG_DATA_HOME/letsencrypt"
+ export VENV_PATH="/opt/eff.org/certbot/venv"
+fi
+VENV_BIN="$VENV_PATH/bin"
+BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt"
+LE_AUTO_VERSION="1.9.0"
+BASENAME=$(basename $0)
+USAGE="Usage: $BASENAME [OPTIONS]
+A self-updating wrapper script for the Certbot ACME client. When run, updates
+to both this script and certbot will be downloaded and installed. After
+ensuring you have the latest versions installed, certbot will be invoked with
+all arguments you have provided.
+
+Help for certbot itself cannot be provided until it is installed.
+
+ --debug attempt experimental installation
+ -h, --help print this help
+ -n, --non-interactive, --noninteractive run without asking for user input
+ --no-bootstrap do not install OS dependencies
+ --no-permissions-check do not warn about file system permissions
+ --no-self-upgrade do not download updates
+ --os-packages-only install OS dependencies and exit
+ --install-only install certbot, upgrade if needed, and exit
+ -v, --verbose provide more output
+ -q, --quiet provide only update/error output;
+ implies --non-interactive
+
+All arguments are accepted and forwarded to the Certbot client when run."
+export CERTBOT_AUTO="$0"
+
+for arg in "$@" ; do
+ case "$arg" in
+ --debug)
+ DEBUG=1;;
+ --os-packages-only)
+ OS_PACKAGES_ONLY=1;;
+ --install-only)
+ INSTALL_ONLY=1;;
+ --no-self-upgrade)
+ # Do not upgrade this script (also prevents client upgrades, because each
+ # copy of the script pins a hash of the python client)
+ NO_SELF_UPGRADE=1;;
+ --no-permissions-check)
+ NO_PERMISSIONS_CHECK=1;;
+ --no-bootstrap)
+ NO_BOOTSTRAP=1;;
+ --help)
+ HELP=1;;
+ --noninteractive|--non-interactive)
+ NONINTERACTIVE=1;;
+ --quiet)
+ QUIET=1;;
+ renew)
+ ASSUME_YES=1;;
+ --verbose)
+ VERBOSE=1;;
+ -[!-]*)
+ OPTIND=1
+ while getopts ":hnvq" short_arg $arg; do
+ case "$short_arg" in
+ h)
+ HELP=1;;
+ n)
+ NONINTERACTIVE=1;;
+ q)
+ QUIET=1;;
+ v)
+ VERBOSE=1;;
+ esac
+ done;;
+ esac
+done
+
+if [ $BASENAME = "letsencrypt-auto" ]; then
+ # letsencrypt-auto does not respect --help or --yes for backwards compatibility
+ NONINTERACTIVE=1
+ HELP=0
+fi
+
+# Set ASSUME_YES to 1 if QUIET or NONINTERACTIVE
+if [ "$QUIET" = 1 -o "$NONINTERACTIVE" = 1 ]; then
+ ASSUME_YES=1
+fi
+
+say() {
+ if [ "$QUIET" != 1 ]; then
+ echo "$@"
+ fi
+}
+
+error() {
+ echo "$@"
+}
+
+# Support for busybox and others where there is no "command",
+# but "which" instead
+if command -v command > /dev/null 2>&1 ; then
+ export EXISTS="command -v"
+elif which which > /dev/null 2>&1 ; then
+ export EXISTS="which"
+else
+ error "Cannot find command nor which... please install one!"
+ exit 1
+fi
+
+# Certbot itself needs root access for almost all modes of operation.
+# certbot-auto needs root access to bootstrap OS dependencies and install
+# Certbot at a protected path so it can be safely run as root. To accomplish
+# this, this script will attempt to run itself as root if it doesn't have the
+# necessary privileges by using `sudo` or falling back to `su` if it is not
+# available. The mechanism used to obtain root access can be set explicitly by
+# setting the environment variable LE_AUTO_SUDO to 'sudo', 'su', 'su_sudo',
+# 'SuSudo', or '' as used below.
+
+# Because the parameters in `su -c` has to be a string,
+# we need to properly escape it.
+SuSudo() {
+ args=""
+ # This `while` loop iterates over all parameters given to this function.
+ # For each parameter, all `'` will be replace by `'"'"'`, and the escaped string
+ # will be wrapped in a pair of `'`, then appended to `$args` string
+ # For example, `echo "It's only 1\$\!"` will be escaped to:
+ # 'echo' 'It'"'"'s only 1$!'
+ # │ │└┼┘│
+ # │ │ │ └── `'s only 1$!'` the literal string
+ # │ │ └── `\"'\"` is a single quote (as a string)
+ # │ └── `'It'`, to be concatenated with the strings following it
+ # └── `echo` wrapped in a pair of `'`, it's totally fine for the shell command itself
+ while [ $# -ne 0 ]; do
+ args="$args'$(printf "%s" "$1" | sed -e "s/'/'\"'\"'/g")' "
+ shift
+ done
+ su root -c "$args"
+}
+
+# Sets the environment variable SUDO to be the name of the program or function
+# to call to get root access. If this script already has root privleges, SUDO
+# is set to an empty string. The value in SUDO should be run with the command
+# to called with root privileges as arguments.
+SetRootAuthMechanism() {
+ SUDO=""
+ if [ -n "${LE_AUTO_SUDO+x}" ]; then
+ case "$LE_AUTO_SUDO" in
+ SuSudo|su_sudo|su)
+ SUDO=SuSudo
+ ;;
+ sudo)
+ SUDO="sudo -E"
+ ;;
+ '')
+ # If we're not running with root, don't check that this script can only
+ # be modified by system users and groups.
+ NO_PERMISSIONS_CHECK=1
+ ;;
+ *)
+ error "Error: unknown root authorization mechanism '$LE_AUTO_SUDO'."
+ exit 1
+ esac
+ say "Using preset root authorization mechanism '$LE_AUTO_SUDO'."
+ else
+ if test "`id -u`" -ne "0" ; then
+ if $EXISTS sudo 1>/dev/null 2>&1; then
+ SUDO="sudo -E"
+ else
+ say \"sudo\" is not available, will use \"su\" for installation steps...
+ SUDO=SuSudo
+ fi
+ fi
+ fi
+}
+
+if [ "$1" = "--cb-auto-has-root" ]; then
+ shift 1
+else
+ SetRootAuthMechanism
+ if [ -n "$SUDO" ]; then
+ say "Requesting to rerun $0 with root privileges..."
+ $SUDO "$0" --cb-auto-has-root "$@"
+ exit 0
+ fi
+fi
+
+# Runs this script again with the given arguments. --cb-auto-has-root is added
+# to the command line arguments to ensure we don't try to acquire root a
+# second time. After the script is rerun, we exit the current script.
+RerunWithArgs() {
+ "$0" --cb-auto-has-root "$@"
+ exit 0
+}
+
+BootstrapMessage() {
+ # Arguments: Platform name
+ say "Bootstrapping dependencies for $1... (you can skip this with --no-bootstrap)"
+}
+
+ExperimentalBootstrap() {
+ # Arguments: Platform name, bootstrap function name
+ if [ "$DEBUG" = 1 ]; then
+ if [ "$2" != "" ]; then
+ BootstrapMessage $1
+ $2
+ fi
+ else
+ error "FATAL: $1 support is very experimental at present..."
+ error "if you would like to work on improving it, please ensure you have backups"
+ error "and then run this script again with the --debug flag!"
+ error "Alternatively, you can install OS dependencies yourself and run this script"
+ error "again with --no-bootstrap."
+ exit 1
+ fi
+}
+
+DeprecationBootstrap() {
+ # Arguments: Platform name, bootstrap function name
+ if [ "$DEBUG" = 1 ]; then
+ if [ "$2" != "" ]; then
+ BootstrapMessage $1
+ $2
+ fi
+ else
+ error "WARNING: certbot-auto support for this $1 is DEPRECATED!"
+ error "Please visit certbot.eff.org to learn how to download a version of"
+ error "Certbot that is packaged for your system. While an existing version"
+ error "of certbot-auto may work currently, we have stopped supporting updating"
+ error "system packages for your system. Please switch to a packaged version"
+ error "as soon as possible."
+ exit 1
+ fi
+}
+
+MIN_PYTHON_2_VERSION="2.7"
+MIN_PYVER2=$(echo "$MIN_PYTHON_2_VERSION" | sed 's/\.//')
+MIN_PYTHON_3_VERSION="3.6"
+MIN_PYVER3=$(echo "$MIN_PYTHON_3_VERSION" | sed 's/\.//')
+# Sets LE_PYTHON to Python version string and PYVER to the first two
+# digits of the python version.
+# MIN_PYVER and MIN_PYTHON_VERSION are also set by this function, and their
+# values depend on if we try to use Python 3 or Python 2.
+DeterminePythonVersion() {
+ # Arguments: "NOCRASH" if we shouldn't crash if we don't find a good python
+ #
+ # If no Python is found, PYVER is set to 0.
+ if [ "$USE_PYTHON_3" = 1 ]; then
+ MIN_PYVER=$MIN_PYVER3
+ MIN_PYTHON_VERSION=$MIN_PYTHON_3_VERSION
+ for LE_PYTHON in "$LE_PYTHON" python3; do
+ # Break (while keeping the LE_PYTHON value) if found.
+ $EXISTS "$LE_PYTHON" > /dev/null && break
+ done
+ else
+ MIN_PYVER=$MIN_PYVER2
+ MIN_PYTHON_VERSION=$MIN_PYTHON_2_VERSION
+ for LE_PYTHON in "$LE_PYTHON" python2.7 python27 python2 python; do
+ # Break (while keeping the LE_PYTHON value) if found.
+ $EXISTS "$LE_PYTHON" > /dev/null && break
+ done
+ fi
+ if [ "$?" != "0" ]; then
+ if [ "$1" != "NOCRASH" ]; then
+ error "Cannot find any Pythons; please install one!"
+ exit 1
+ else
+ PYVER=0
+ return 0
+ fi
+ fi
+
+ PYVER=$("$LE_PYTHON" -V 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//')
+ if [ "$PYVER" -lt "$MIN_PYVER" ]; then
+ if [ "$1" != "NOCRASH" ]; then
+ error "You have an ancient version of Python entombed in your operating system..."
+ error "This isn't going to work; you'll need at least version $MIN_PYTHON_VERSION."
+ exit 1
+ fi
+ fi
+}
+
+# If new packages are installed by BootstrapDebCommon below, this version
+# number must be increased.
+BOOTSTRAP_DEB_COMMON_VERSION=1
+
+BootstrapDebCommon() {
+ # Current version tested with:
+ #
+ # - Ubuntu
+ # - 14.04 (x64)
+ # - 15.04 (x64)
+ # - Debian
+ # - 7.9 "wheezy" (x64)
+ # - sid (2015-10-21) (x64)
+
+ # Past versions tested with:
+ #
+ # - Debian 8.0 "jessie" (x64)
+ # - Raspbian 7.8 (armhf)
+
+ # Believed not to work:
+ #
+ # - Debian 6.0.10 "squeeze" (x64)
+
+ if [ "$QUIET" = 1 ]; then
+ QUIET_FLAG='-qq'
+ fi
+
+ apt-get $QUIET_FLAG update || error apt-get update hit problems but continuing anyway...
+
+ # virtualenv binary can be found in different packages depending on
+ # distro version (#346)
+
+ virtualenv=
+ # virtual env is known to apt and is installable
+ if apt-cache show virtualenv > /dev/null 2>&1 ; then
+ if ! LC_ALL=C apt-cache --quiet=0 show virtualenv 2>&1 | grep -q 'No packages found'; then
+ virtualenv="virtualenv"
+ fi
+ fi
+
+ if apt-cache show python-virtualenv > /dev/null 2>&1; then
+ virtualenv="$virtualenv python-virtualenv"
+ fi
+
+ augeas_pkg="libaugeas0 augeas-lenses"
+
+ if [ "$ASSUME_YES" = 1 ]; then
+ YES_FLAG="-y"
+ fi
+
+ apt-get install $QUIET_FLAG $YES_FLAG --no-install-recommends \
+ python \
+ python-dev \
+ $virtualenv \
+ gcc \
+ $augeas_pkg \
+ libssl-dev \
+ openssl \
+ libffi-dev \
+ ca-certificates \
+
+
+ if ! $EXISTS virtualenv > /dev/null ; then
+ error Failed to install a working \"virtualenv\" command, exiting
+ exit 1
+ fi
+}
+
+# If new packages are installed by BootstrapRpmCommonBase below, version
+# numbers in rpm_common.sh and rpm_python3.sh must be increased.
+
+# Sets TOOL to the name of the package manager
+# Sets appropriate values for YES_FLAG and QUIET_FLAG based on $ASSUME_YES and $QUIET_FLAG.
+# Note: this function is called both while selecting the bootstrap scripts and
+# during the actual bootstrap. Some things like prompting to user can be done in the latter
+# case, but not in the former one.
+InitializeRPMCommonBase() {
+ if type dnf 2>/dev/null
+ then
+ TOOL=dnf
+ elif type yum 2>/dev/null
+ then
+ TOOL=yum
+
+ else
+ error "Neither yum nor dnf found. Aborting bootstrap!"
+ exit 1
+ fi
+
+ if [ "$ASSUME_YES" = 1 ]; then
+ YES_FLAG="-y"
+ fi
+ if [ "$QUIET" = 1 ]; then
+ QUIET_FLAG='--quiet'
+ fi
+}
+
+BootstrapRpmCommonBase() {
+ # Arguments: whitespace-delimited python packages to install
+
+ InitializeRPMCommonBase # This call is superfluous in practice
+
+ pkgs="
+ gcc
+ augeas-libs
+ openssl
+ openssl-devel
+ libffi-devel
+ redhat-rpm-config
+ ca-certificates
+ "
+
+ # Add the python packages
+ pkgs="$pkgs
+ $1
+ "
+
+ if $TOOL list installed "httpd" >/dev/null 2>&1; then
+ pkgs="$pkgs
+ mod_ssl
+ "
+ fi
+
+ if ! $TOOL install $YES_FLAG $QUIET_FLAG $pkgs; then
+ error "Could not install OS dependencies. Aborting bootstrap!"
+ exit 1
+ fi
+}
+
+# If new packages are installed by BootstrapRpmCommon below, this version
+# number must be increased.
+BOOTSTRAP_RPM_COMMON_VERSION=1
+
+BootstrapRpmCommon() {
+ # Tested with:
+ # - Fedora 20, 21, 22, 23 (x64)
+ # - Centos 7 (x64: on DigitalOcean droplet)
+ # - CentOS 7 Minimal install in a Hyper-V VM
+ # - CentOS 6
+
+ InitializeRPMCommonBase
+
+ # Most RPM distros use the "python" or "python-" naming convention. Let's try that first.
+ if $TOOL list python >/dev/null 2>&1; then
+ python_pkgs="$python
+ python-devel
+ python-virtualenv
+ python-tools
+ python-pip
+ "
+ # Fedora 26 starts to use the prefix python2 for python2 based packages.
+ # this elseif is theoretically for any Fedora over version 26:
+ elif $TOOL list python2 >/dev/null 2>&1; then
+ python_pkgs="$python2
+ python2-libs
+ python2-setuptools
+ python2-devel
+ python2-virtualenv
+ python2-tools
+ python2-pip
+ "
+ # Some distros and older versions of current distros use a "python27"
+ # instead of the "python" or "python-" naming convention.
+ else
+ python_pkgs="$python27
+ python27-devel
+ python27-virtualenv
+ python27-tools
+ python27-pip
+ "
+ fi
+
+ BootstrapRpmCommonBase "$python_pkgs"
+}
+
+# If new packages are installed by BootstrapRpmPython3 below, this version
+# number must be increased.
+BOOTSTRAP_RPM_PYTHON3_LEGACY_VERSION=1
+
+# Checks if rh-python36 can be installed.
+Python36SclIsAvailable() {
+ InitializeRPMCommonBase >/dev/null 2>&1;
+
+ if "${TOOL}" list rh-python36 >/dev/null 2>&1; then
+ return 0
+ fi
+ if "${TOOL}" list centos-release-scl >/dev/null 2>&1; then
+ return 0
+ fi
+ return 1
+}
+
+# Try to enable rh-python36 from SCL if it is necessary and possible.
+EnablePython36SCL() {
+ if "$EXISTS" python3.6 > /dev/null 2> /dev/null; then
+ return 0
+ fi
+ if [ ! -f /opt/rh/rh-python36/enable ]; then
+ return 0
+ fi
+ set +e
+ if ! . /opt/rh/rh-python36/enable; then
+ error 'Unable to enable rh-python36!'
+ exit 1
+ fi
+ set -e
+}
+
+# This bootstrap concerns old RedHat-based distributions that do not ship by default
+# with Python 2.7, but only Python 2.6. We bootstrap them by enabling SCL and installing
+# Python 3.6. Some of these distributions are: CentOS/RHEL/OL/SL 6.
+BootstrapRpmPython3Legacy() {
+ # Tested with:
+ # - CentOS 6
+
+ InitializeRPMCommonBase
+
+ if ! "${TOOL}" list rh-python36 >/dev/null 2>&1; then
+ echo "To use Certbot on this operating system, packages from the SCL repository need to be installed."
+ if ! "${TOOL}" list centos-release-scl >/dev/null 2>&1; then
+ error "Enable the SCL repository and try running Certbot again."
+ exit 1
+ fi
+ if [ "${ASSUME_YES}" = 1 ]; then
+ /bin/echo -n "Enabling the SCL repository in 3 seconds... (Press Ctrl-C to cancel)"
+ sleep 1s
+ /bin/echo -ne "\e[0K\rEnabling the SCL repository in 2 seconds... (Press Ctrl-C to cancel)"
+ sleep 1s
+ /bin/echo -e "\e[0K\rEnabling the SCL repository in 1 second... (Press Ctrl-C to cancel)"
+ sleep 1s
+ fi
+ if ! "${TOOL}" install "${YES_FLAG}" "${QUIET_FLAG}" centos-release-scl; then
+ error "Could not enable SCL. Aborting bootstrap!"
+ exit 1
+ fi
+ fi
+
+ # CentOS 6 must use rh-python36 from SCL
+ if "${TOOL}" list rh-python36 >/dev/null 2>&1; then
+ python_pkgs="rh-python36-python
+ rh-python36-python-virtualenv
+ rh-python36-python-devel
+ "
+ else
+ error "No supported Python package available to install. Aborting bootstrap!"
+ exit 1
+ fi
+
+ BootstrapRpmCommonBase "${python_pkgs}"
+
+ # Enable SCL rh-python36 after bootstrapping.
+ EnablePython36SCL
+}
+
+# If new packages are installed by BootstrapRpmPython3 below, this version
+# number must be increased.
+BOOTSTRAP_RPM_PYTHON3_VERSION=1
+
+BootstrapRpmPython3() {
+ # Tested with:
+ # - Fedora 29
+
+ InitializeRPMCommonBase
+
+ # Fedora 29 must use python3-virtualenv
+ if $TOOL list python3-virtualenv >/dev/null 2>&1; then
+ python_pkgs="python3
+ python3-virtualenv
+ python3-devel
+ "
+ else
+ error "No supported Python package available to install. Aborting bootstrap!"
+ exit 1
+ fi
+
+ BootstrapRpmCommonBase "$python_pkgs"
+}
+
+# If new packages are installed by BootstrapSuseCommon below, this version
+# number must be increased.
+BOOTSTRAP_SUSE_COMMON_VERSION=1
+
+BootstrapSuseCommon() {
+ # SLE12 don't have python-virtualenv
+
+ if [ "$ASSUME_YES" = 1 ]; then
+ zypper_flags="-nq"
+ install_flags="-l"
+ fi
+
+ if [ "$QUIET" = 1 ]; then
+ QUIET_FLAG='-qq'
+ fi
+
+ if zypper search -x python-virtualenv >/dev/null 2>&1; then
+ OPENSUSE_VIRTUALENV_PACKAGES="python-virtualenv"
+ else
+ # Since Leap 15.0 (and associated Tumbleweed version), python-virtualenv
+ # is a source package, and python2-virtualenv must be used instead.
+ # Also currently python2-setuptools is not a dependency of python2-virtualenv,
+ # while it should be. Installing it explicitly until upstream fix.
+ OPENSUSE_VIRTUALENV_PACKAGES="python2-virtualenv python2-setuptools"
+ fi
+
+ zypper $QUIET_FLAG $zypper_flags in $install_flags \
+ python \
+ python-devel \
+ $OPENSUSE_VIRTUALENV_PACKAGES \
+ gcc \
+ augeas-lenses \
+ libopenssl-devel \
+ libffi-devel \
+ ca-certificates
+}
+
+# If new packages are installed by BootstrapArchCommon below, this version
+# number must be increased.
+BOOTSTRAP_ARCH_COMMON_VERSION=1
+
+BootstrapArchCommon() {
+ # Tested with:
+ # - ArchLinux (x86_64)
+ #
+ # "python-virtualenv" is Python3, but "python2-virtualenv" provides
+ # only "virtualenv2" binary, not "virtualenv".
+
+ deps="
+ python2
+ python-virtualenv
+ gcc
+ augeas
+ openssl
+ libffi
+ ca-certificates
+ pkg-config
+ "
+
+ # pacman -T exits with 127 if there are missing dependencies
+ missing=$(pacman -T $deps) || true
+
+ if [ "$ASSUME_YES" = 1 ]; then
+ noconfirm="--noconfirm"
+ fi
+
+ if [ "$missing" ]; then
+ if [ "$QUIET" = 1 ]; then
+ pacman -S --needed $missing $noconfirm > /dev/null
+ else
+ pacman -S --needed $missing $noconfirm
+ fi
+ fi
+}
+
+# If new packages are installed by BootstrapGentooCommon below, this version
+# number must be increased.
+BOOTSTRAP_GENTOO_COMMON_VERSION=1
+
+BootstrapGentooCommon() {
+ PACKAGES="
+ dev-lang/python:2.7
+ dev-python/virtualenv
+ app-admin/augeas
+ dev-libs/openssl
+ dev-libs/libffi
+ app-misc/ca-certificates
+ virtual/pkgconfig"
+
+ ASK_OPTION="--ask"
+ if [ "$ASSUME_YES" = 1 ]; then
+ ASK_OPTION=""
+ fi
+
+ case "$PACKAGE_MANAGER" in
+ (paludis)
+ cave resolve --preserve-world --keep-targets if-possible $PACKAGES -x
+ ;;
+ (pkgcore)
+ pmerge --noreplace --oneshot $ASK_OPTION $PACKAGES
+ ;;
+ (portage|*)
+ emerge --noreplace --oneshot $ASK_OPTION $PACKAGES
+ ;;
+ esac
+}
+
+# If new packages are installed by BootstrapFreeBsd below, this version number
+# must be increased.
+BOOTSTRAP_FREEBSD_VERSION=1
+
+BootstrapFreeBsd() {
+ if [ "$QUIET" = 1 ]; then
+ QUIET_FLAG="--quiet"
+ fi
+
+ pkg install -Ay $QUIET_FLAG \
+ python \
+ py27-virtualenv \
+ augeas \
+ libffi
+}
+
+# If new packages are installed by BootstrapMac below, this version number must
+# be increased.
+BOOTSTRAP_MAC_VERSION=1
+
+BootstrapMac() {
+ if hash brew 2>/dev/null; then
+ say "Using Homebrew to install dependencies..."
+ pkgman=brew
+ pkgcmd="brew install"
+ elif hash port 2>/dev/null; then
+ say "Using MacPorts to install dependencies..."
+ pkgman=port
+ pkgcmd="port install"
+ else
+ say "No Homebrew/MacPorts; installing Homebrew..."
+ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
+ pkgman=brew
+ pkgcmd="brew install"
+ fi
+
+ $pkgcmd augeas
+ if [ "$(which python)" = "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python" \
+ -o "$(which python)" = "/usr/bin/python" ]; then
+ # We want to avoid using the system Python because it requires root to use pip.
+ # python.org, MacPorts or HomeBrew Python installations should all be OK.
+ say "Installing python..."
+ $pkgcmd python
+ fi
+
+ # Workaround for _dlopen not finding augeas on macOS
+ if [ "$pkgman" = "port" ] && ! [ -e "/usr/local/lib/libaugeas.dylib" ] && [ -e "/opt/local/lib/libaugeas.dylib" ]; then
+ say "Applying augeas workaround"
+ mkdir -p /usr/local/lib/
+ ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib/
+ fi
+
+ if ! hash pip 2>/dev/null; then
+ say "pip not installed"
+ say "Installing pip..."
+ curl --silent --show-error --retry 5 https://bootstrap.pypa.io/get-pip.py | python
+ fi
+
+ if ! hash virtualenv 2>/dev/null; then
+ say "virtualenv not installed."
+ say "Installing with pip..."
+ pip install virtualenv
+ fi
+}
+
+# If new packages are installed by BootstrapSmartOS below, this version number
+# must be increased.
+BOOTSTRAP_SMARTOS_VERSION=1
+
+BootstrapSmartOS() {
+ pkgin update
+ pkgin -y install 'gcc49' 'py27-augeas' 'py27-virtualenv'
+}
+
+# If new packages are installed by BootstrapMageiaCommon below, this version
+# number must be increased.
+BOOTSTRAP_MAGEIA_COMMON_VERSION=1
+
+BootstrapMageiaCommon() {
+ if [ "$QUIET" = 1 ]; then
+ QUIET_FLAG='--quiet'
+ fi
+
+ if ! urpmi --force $QUIET_FLAG \
+ python \
+ libpython-devel \
+ python-virtualenv
+ then
+ error "Could not install Python dependencies. Aborting bootstrap!"
+ exit 1
+ fi
+
+ if ! urpmi --force $QUIET_FLAG \
+ git \
+ gcc \
+ python-augeas \
+ libopenssl-devel \
+ libffi-devel \
+ rootcerts
+ then
+ error "Could not install additional dependencies. Aborting bootstrap!"
+ exit 1
+ fi
+}
+
+
+# Set Bootstrap to the function that installs OS dependencies on this system
+# and BOOTSTRAP_VERSION to the unique identifier for the current version of
+# that function. If Bootstrap is set to a function that doesn't install any
+# packages BOOTSTRAP_VERSION is not set.
+if [ -f /etc/debian_version ]; then
+ Bootstrap() {
+ BootstrapMessage "Debian-based OSes"
+ BootstrapDebCommon
+ }
+ BOOTSTRAP_VERSION="BootstrapDebCommon $BOOTSTRAP_DEB_COMMON_VERSION"
+elif [ -f /etc/mageia-release ]; then
+ # Mageia has both /etc/mageia-release and /etc/redhat-release
+ DEPRECATED_OS=1
+elif [ -f /etc/redhat-release ]; then
+ # Run DeterminePythonVersion to decide on the basis of available Python versions
+ # whether to use 2.x or 3.x on RedHat-like systems.
+ # Then, revert LE_PYTHON to its previous state.
+ prev_le_python="$LE_PYTHON"
+ unset LE_PYTHON
+ DeterminePythonVersion "NOCRASH"
+
+ RPM_DIST_NAME=`(. /etc/os-release 2> /dev/null && echo $ID) || echo "unknown"`
+
+ if [ "$PYVER" -eq 26 -a $(uname -m) != 'x86_64' ]; then
+ # 32 bits CentOS 6 and affiliates are not supported anymore by certbot-auto.
+ DEPRECATED_OS=1
+ fi
+
+ # Set RPM_DIST_VERSION to VERSION_ID from /etc/os-release after splitting on
+ # '.' characters (e.g. "8.0" becomes "8"). If the command exits with an
+ # error, RPM_DIST_VERSION is set to "unknown".
+ RPM_DIST_VERSION=$( (. /etc/os-release 2> /dev/null && echo "$VERSION_ID") | cut -d '.' -f1 || echo "unknown")
+
+ # If RPM_DIST_VERSION is an empty string or it contains any nonnumeric
+ # characters, the value is unexpected so we set RPM_DIST_VERSION to 0.
+ if [ -z "$RPM_DIST_VERSION" ] || [ -n "$(echo "$RPM_DIST_VERSION" | tr -d '[0-9]')" ]; then
+ RPM_DIST_VERSION=0
+ fi
+
+ # Handle legacy RPM distributions
+ if [ "$PYVER" -eq 26 ]; then
+ # Check if an automated bootstrap can be achieved on this system.
+ if ! Python36SclIsAvailable; then
+ INTERACTIVE_BOOTSTRAP=1
+ fi
+
+ Bootstrap() {
+ BootstrapMessage "Legacy RedHat-based OSes that will use Python3"
+ BootstrapRpmPython3Legacy
+ }
+ USE_PYTHON_3=1
+ BOOTSTRAP_VERSION="BootstrapRpmPython3Legacy $BOOTSTRAP_RPM_PYTHON3_LEGACY_VERSION"
+
+ # Try now to enable SCL rh-python36 for systems already bootstrapped
+ # NB: EnablePython36SCL has been defined along with BootstrapRpmPython3Legacy in certbot-auto
+ EnablePython36SCL
+ else
+ # Starting to Fedora 29, python2 is on a deprecation path. Let's move to python3 then.
+ # RHEL 8 also uses python3 by default.
+ if [ "$RPM_DIST_NAME" = "fedora" -a "$RPM_DIST_VERSION" -ge 29 ]; then
+ RPM_USE_PYTHON_3=1
+ elif [ "$RPM_DIST_NAME" = "rhel" -a "$RPM_DIST_VERSION" -ge 8 ]; then
+ RPM_USE_PYTHON_3=1
+ elif [ "$RPM_DIST_NAME" = "centos" -a "$RPM_DIST_VERSION" -ge 8 ]; then
+ RPM_USE_PYTHON_3=1
+ else
+ RPM_USE_PYTHON_3=0
+ fi
+
+ if [ "$RPM_USE_PYTHON_3" = 1 ]; then
+ Bootstrap() {
+ BootstrapMessage "RedHat-based OSes that will use Python3"
+ BootstrapRpmPython3
+ }
+ USE_PYTHON_3=1
+ BOOTSTRAP_VERSION="BootstrapRpmPython3 $BOOTSTRAP_RPM_PYTHON3_VERSION"
+ else
+ Bootstrap() {
+ BootstrapMessage "RedHat-based OSes"
+ BootstrapRpmCommon
+ }
+ BOOTSTRAP_VERSION="BootstrapRpmCommon $BOOTSTRAP_RPM_COMMON_VERSION"
+ fi
+ fi
+
+ LE_PYTHON="$prev_le_python"
+elif [ -f /etc/os-release ] && `grep -q openSUSE /etc/os-release` ; then
+ DEPRECATED_OS=1
+elif [ -f /etc/arch-release ]; then
+ DEPRECATED_OS=1
+elif [ -f /etc/manjaro-release ]; then
+ DEPRECATED_OS=1
+elif [ -f /etc/gentoo-release ]; then
+ DEPRECATED_OS=1
+elif uname | grep -iq FreeBSD ; then
+ DEPRECATED_OS=1
+elif uname | grep -iq Darwin ; then
+ DEPRECATED_OS=1
+elif [ -f /etc/issue ] && grep -iq "Amazon Linux" /etc/issue ; then
+ Bootstrap() {
+ ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon
+ }
+ BOOTSTRAP_VERSION="BootstrapRpmCommon $BOOTSTRAP_RPM_COMMON_VERSION"
+elif [ -f /etc/product ] && grep -q "Joyent Instance" /etc/product ; then
+ DEPRECATED_OS=1
+else
+ DEPRECATED_OS=1
+fi
+
+# We handle this case after determining the normal bootstrap version to allow
+# variables like USE_PYTHON_3 to be properly set. As described above, if the
+# Bootstrap function doesn't install any packages, BOOTSTRAP_VERSION should not
+# be set so we unset it here.
+if [ "$NO_BOOTSTRAP" = 1 ]; then
+ Bootstrap() {
+ :
+ }
+ unset BOOTSTRAP_VERSION
+fi
+
+if [ "$DEPRECATED_OS" = 1 ]; then
+ Bootstrap() {
+ error "Skipping bootstrap because certbot-auto is deprecated on this system."
+ }
+ unset BOOTSTRAP_VERSION
+fi
+
+# Sets PREV_BOOTSTRAP_VERSION to the identifier for the bootstrap script used
+# to install OS dependencies on this system. PREV_BOOTSTRAP_VERSION isn't set
+# if it is unknown how OS dependencies were installed on this system.
+SetPrevBootstrapVersion() {
+ if [ -f $BOOTSTRAP_VERSION_PATH ]; then
+ PREV_BOOTSTRAP_VERSION=$(cat "$BOOTSTRAP_VERSION_PATH")
+ # The list below only contains bootstrap version strings that existed before
+ # we started writing them to disk.
+ #
+ # DO NOT MODIFY THIS LIST UNLESS YOU KNOW WHAT YOU'RE DOING!
+ elif grep -Fqx "$BOOTSTRAP_VERSION" << "UNLIKELY_EOF"
+BootstrapDebCommon 1
+BootstrapMageiaCommon 1
+BootstrapRpmCommon 1
+BootstrapSuseCommon 1
+BootstrapArchCommon 1
+BootstrapGentooCommon 1
+BootstrapFreeBsd 1
+BootstrapMac 1
+BootstrapSmartOS 1
+UNLIKELY_EOF
+ then
+ # If there's no bootstrap version saved to disk, but the currently selected
+ # bootstrap script is from before we started saving the version number,
+ # return the currently selected version to prevent us from rebootstrapping
+ # unnecessarily.
+ PREV_BOOTSTRAP_VERSION="$BOOTSTRAP_VERSION"
+ fi
+}
+
+TempDir() {
+ mktemp -d 2>/dev/null || mktemp -d -t 'le' # Linux || macOS
+}
+
+# Returns 0 if a letsencrypt installation exists at $OLD_VENV_PATH, otherwise,
+# returns a non-zero number.
+OldVenvExists() {
+ [ -n "$OLD_VENV_PATH" -a -f "$OLD_VENV_PATH/bin/letsencrypt" ]
+}
+
+# Given python path, version 1 and version 2, check if version 1 is outdated compared to version 2.
+# An unofficial version provided as version 1 (eg. 0.28.0.dev0) will be treated
+# specifically by printing "UNOFFICIAL". Otherwise, print "OUTDATED" if version 1
+# is outdated, and "UP_TO_DATE" if not.
+# This function relies only on installed python environment (2.x or 3.x) by certbot-auto.
+CompareVersions() {
+ "$1" - "$2" "$3" << "UNLIKELY_EOF"
+import sys
+from distutils.version import StrictVersion
+
+try:
+ current = StrictVersion(sys.argv[1])
+except ValueError:
+ sys.stdout.write('UNOFFICIAL')
+ sys.exit()
+
+try:
+ remote = StrictVersion(sys.argv[2])
+except ValueError:
+ sys.stdout.write('UP_TO_DATE')
+ sys.exit()
+
+if current < remote:
+ sys.stdout.write('OUTDATED')
+else:
+ sys.stdout.write('UP_TO_DATE')
+UNLIKELY_EOF
+}
+
+# Create a new virtual environment for Certbot. It will overwrite any existing one.
+# Parameters: LE_PYTHON, VENV_PATH, PYVER, VERBOSE
+CreateVenv() {
+ "$1" - "$2" "$3" "$4" << "UNLIKELY_EOF"
+#!/usr/bin/env python
+import os
+import shutil
+import subprocess
+import sys
+
+
+def create_venv(venv_path, pyver, verbose):
+ if os.path.exists(venv_path):
+ shutil.rmtree(venv_path)
+
+ stdout = sys.stdout if verbose == '1' else open(os.devnull, 'w')
+
+ if int(pyver) <= 27:
+ # Use virtualenv binary
+ environ = os.environ.copy()
+ environ['VIRTUALENV_NO_DOWNLOAD'] = '1'
+ command = ['virtualenv', '--no-site-packages', '--python', sys.executable, venv_path]
+ subprocess.check_call(command, stdout=stdout, env=environ)
+ else:
+ # Use embedded venv module in Python 3
+ command = [sys.executable, '-m', 'venv', venv_path]
+ subprocess.check_call(command, stdout=stdout)
+
+
+if __name__ == '__main__':
+ create_venv(*sys.argv[1:])
+
+UNLIKELY_EOF
+}
+
+# Check that the given PATH_TO_CHECK has secured permissions.
+# Parameters: LE_PYTHON, PATH_TO_CHECK
+CheckPathPermissions() {
+ "$1" - "$2" << "UNLIKELY_EOF"
+"""Verifies certbot-auto cannot be modified by unprivileged users.
+
+This script takes the path to certbot-auto as its only command line
+argument. It then checks that the file can only be modified by uid/gid
+< 1000 and if other users can modify the file, it prints a warning with
+a suggestion on how to solve the problem.
+
+Permissions on symlinks in the absolute path of certbot-auto are ignored
+and only the canonical path to certbot-auto is checked. There could be
+permissions problems due to the symlinks that are unreported by this
+script, however, issues like this were not caused by our documentation
+and are ignored for the sake of simplicity.
+
+All warnings are printed to stdout rather than stderr so all stderr
+output from this script can be suppressed to avoid printing messages if
+this script fails for some reason.
+
+"""
+from __future__ import print_function
+
+import os
+import stat
+import sys
+
+
+FORUM_POST_URL = 'https://community.letsencrypt.org/t/certbot-auto-deployment-best-practices/91979/'
+
+
+def has_safe_permissions(path):
+ """Returns True if the given path has secure permissions.
+
+ The permissions are considered safe if the file is only writable by
+ uid/gid < 1000.
+
+ The reason we allow more IDs than 0 is because on some systems such
+ as Debian, system users/groups other than uid/gid 0 are used for the
+ path we recommend in our instructions which is /usr/local/bin. 1000
+ was chosen because on Debian 0-999 is reserved for system IDs[1] and
+ on RHEL either 0-499 or 0-999 is reserved depending on the
+ version[2][3]. Due to these differences across different OSes, this
+ detection isn't perfect so we only determine permissions are
+ insecure when we can be reasonably confident there is a problem
+ regardless of the underlying OS.
+
+ [1] https://www.debian.org/doc/debian-policy/ch-opersys.html#uid-and-gid-classes
+ [2] https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/ch-managing_users_and_groups
+ [3] https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/system_administrators_guide/ch-managing_users_and_groups
+
+ :param str path: filesystem path to check
+ :returns: True if the path has secure permissions, otherwise, False
+ :rtype: bool
+
+ """
+ # os.stat follows symlinks before obtaining information about a file.
+ stat_result = os.stat(path)
+ if stat_result.st_mode & stat.S_IWOTH:
+ return False
+ if stat_result.st_mode & stat.S_IWGRP and stat_result.st_gid >= 1000:
+ return False
+ if stat_result.st_mode & stat.S_IWUSR and stat_result.st_uid >= 1000:
+ return False
+ return True
+
+
+def main(certbot_auto_path):
+ current_path = os.path.realpath(certbot_auto_path)
+ last_path = None
+ permissions_ok = True
+ # This loop makes use of the fact that os.path.dirname('/') == '/'.
+ while current_path != last_path and permissions_ok:
+ permissions_ok = has_safe_permissions(current_path)
+ last_path = current_path
+ current_path = os.path.dirname(current_path)
+
+ if not permissions_ok:
+ print('{0} has insecure permissions!'.format(certbot_auto_path))
+ print('To learn how to fix them, visit {0}'.format(FORUM_POST_URL))
+
+
+if __name__ == '__main__':
+ main(sys.argv[1])
+
+UNLIKELY_EOF
+}
+
+if [ "$1" = "--le-auto-phase2" ]; then
+ # Phase 2: Create venv, install LE, and run.
+
+ shift 1 # the --le-auto-phase2 arg
+
+ if [ "$DEPRECATED_OS" = 1 ]; then
+ # Phase 2 damage control mode for deprecated OSes.
+ # In this situation, we bypass any bootstrap or certbot venv setup.
+ error "Your system is not supported by certbot-auto anymore."
+
+ if [ ! -d "$VENV_PATH" ] && OldVenvExists; then
+ VENV_BIN="$OLD_VENV_PATH/bin"
+ fi
+
+ if [ -f "$VENV_BIN/letsencrypt" -a "$INSTALL_ONLY" != 1 ]; then
+ error "Certbot will no longer receive updates."
+ error "Please visit https://certbot.eff.org/ to check for other alternatives."
+ "$VENV_BIN/letsencrypt" "$@"
+ exit 0
+ else
+ error "Certbot cannot be installed."
+ error "Please visit https://certbot.eff.org/ to check for other alternatives."
+ exit 1
+ fi
+ fi
+
+ SetPrevBootstrapVersion
+
+ if [ -z "$PHASE_1_VERSION" -a "$USE_PYTHON_3" = 1 ]; then
+ unset LE_PYTHON
+ fi
+
+ INSTALLED_VERSION="none"
+ if [ -d "$VENV_PATH" ] || OldVenvExists; then
+ # If the selected Bootstrap function isn't a noop and it differs from the
+ # previously used version
+ if [ -n "$BOOTSTRAP_VERSION" -a "$BOOTSTRAP_VERSION" != "$PREV_BOOTSTRAP_VERSION" ]; then
+ # Check if we can rebootstrap without manual user intervention: this requires that
+ # certbot-auto is in non-interactive mode AND selected bootstrap does not claim to
+ # require a manual user intervention.
+ if [ "$NONINTERACTIVE" = 1 -a "$INTERACTIVE_BOOTSTRAP" != 1 ]; then
+ CAN_REBOOTSTRAP=1
+ fi
+ # Check if rebootstrap can be done non-interactively and current shell is non-interactive
+ # (true if stdin and stdout are not attached to a terminal).
+ if [ \( "$CAN_REBOOTSTRAP" = 1 \) -o \( \( -t 0 \) -a \( -t 1 \) \) ]; then
+ if [ -d "$VENV_PATH" ]; then
+ rm -rf "$VENV_PATH"
+ fi
+ # In the case the old venv was just a symlink to the new one,
+ # OldVenvExists is now false because we deleted the venv at VENV_PATH.
+ if OldVenvExists; then
+ rm -rf "$OLD_VENV_PATH"
+ ln -s "$VENV_PATH" "$OLD_VENV_PATH"
+ fi
+ RerunWithArgs "$@"
+ # Otherwise bootstrap needs to be done manually by the user.
+ else
+ # If it is because bootstrapping is interactive, --non-interactive will be of no use.
+ if [ "$INTERACTIVE_BOOTSTRAP" = 1 ]; then
+ error "Skipping upgrade because new OS dependencies may need to be installed."
+ error "This requires manual user intervention: please run this script again manually."
+ # If this is because of the environment (eg. non interactive shell without
+ # --non-interactive flag set), help the user in that direction.
+ else
+ error "Skipping upgrade because new OS dependencies may need to be installed."
+ error
+ error "To upgrade to a newer version, please run this script again manually so you can"
+ error "approve changes or with --non-interactive on the command line to automatically"
+ error "install any required packages."
+ fi
+ # Set INSTALLED_VERSION to be the same so we don't update the venv
+ INSTALLED_VERSION="$LE_AUTO_VERSION"
+ # Continue to use OLD_VENV_PATH if the new venv doesn't exist
+ if [ ! -d "$VENV_PATH" ]; then
+ VENV_BIN="$OLD_VENV_PATH/bin"
+ fi
+ fi
+ elif [ -f "$VENV_BIN/letsencrypt" ]; then
+ # --version output ran through grep due to python-cryptography DeprecationWarnings
+ # grep for both certbot and letsencrypt until certbot and shim packages have been released
+ INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep "^certbot\|^letsencrypt" | cut -d " " -f 2)
+ if [ -z "$INSTALLED_VERSION" ]; then
+ error "Error: couldn't get currently installed version for $VENV_BIN/letsencrypt: " 1>&2
+ "$VENV_BIN/letsencrypt" --version
+ exit 1
+ fi
+ fi
+ fi
+
+ if [ "$LE_AUTO_VERSION" != "$INSTALLED_VERSION" ]; then
+ say "Creating virtual environment..."
+ DeterminePythonVersion
+ CreateVenv "$LE_PYTHON" "$VENV_PATH" "$PYVER" "$VERBOSE"
+
+ if [ -n "$BOOTSTRAP_VERSION" ]; then
+ echo "$BOOTSTRAP_VERSION" > "$BOOTSTRAP_VERSION_PATH"
+ elif [ -n "$PREV_BOOTSTRAP_VERSION" ]; then
+ echo "$PREV_BOOTSTRAP_VERSION" > "$BOOTSTRAP_VERSION_PATH"
+ fi
+
+ say "Installing Python packages..."
+ TEMP_DIR=$(TempDir)
+ trap 'rm -rf "$TEMP_DIR"' EXIT
+ # There is no $ interpolation due to quotes on starting heredoc delimiter.
+ # -------------------------------------------------------------------------
+ cat << "UNLIKELY_EOF" > "$TEMP_DIR/letsencrypt-auto-requirements.txt"
+# This is the flattened list of packages certbot-auto installs.
+# To generate this, do (with docker and package hashin installed):
+# ```
+# letsencrypt-auto-source/rebuild_dependencies.py \
+# letsencrypt-auto-source/pieces/dependency-requirements.txt
+# ```
+# If you want to update a single dependency, run commands similar to these:
+# ```
+# pip install hashin
+# hashin -r dependency-requirements.txt cryptography==1.5.2
+# ```
+ConfigArgParse==1.2.3 \
+ --hash=sha256:edd17be986d5c1ba2e307150b8e5f5107aba125f3574dddd02c85d5cdcfd37dc
+certifi==2020.4.5.1 \
+ --hash=sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304 \
+ --hash=sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519
+cffi==1.14.0 \
+ --hash=sha256:001bf3242a1bb04d985d63e138230802c6c8d4db3668fb545fb5005ddf5bb5ff \
+ --hash=sha256:00789914be39dffba161cfc5be31b55775de5ba2235fe49aa28c148236c4e06b \
+ --hash=sha256:028a579fc9aed3af38f4892bdcc7390508adabc30c6af4a6e4f611b0c680e6ac \
+ --hash=sha256:14491a910663bf9f13ddf2bc8f60562d6bc5315c1f09c704937ef17293fb85b0 \
+ --hash=sha256:1cae98a7054b5c9391eb3249b86e0e99ab1e02bb0cc0575da191aedadbdf4384 \
+ --hash=sha256:2089ed025da3919d2e75a4d963d008330c96751127dd6f73c8dc0c65041b4c26 \
+ --hash=sha256:2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6 \
+ --hash=sha256:337d448e5a725bba2d8293c48d9353fc68d0e9e4088d62a9571def317797522b \
+ --hash=sha256:399aed636c7d3749bbed55bc907c3288cb43c65c4389964ad5ff849b6370603e \
+ --hash=sha256:3b911c2dbd4f423b4c4fcca138cadde747abdb20d196c4a48708b8a2d32b16dd \
+ --hash=sha256:3d311bcc4a41408cf5854f06ef2c5cab88f9fded37a3b95936c9879c1640d4c2 \
+ --hash=sha256:62ae9af2d069ea2698bf536dcfe1e4eed9090211dbaafeeedf5cb6c41b352f66 \
+ --hash=sha256:66e41db66b47d0d8672d8ed2708ba91b2f2524ece3dee48b5dfb36be8c2f21dc \
+ --hash=sha256:675686925a9fb403edba0114db74e741d8181683dcf216be697d208857e04ca8 \
+ --hash=sha256:7e63cbcf2429a8dbfe48dcc2322d5f2220b77b2e17b7ba023d6166d84655da55 \
+ --hash=sha256:8a6c688fefb4e1cd56feb6c511984a6c4f7ec7d2a1ff31a10254f3c817054ae4 \
+ --hash=sha256:8c0ffc886aea5df6a1762d0019e9cb05f825d0eec1f520c51be9d198701daee5 \
+ --hash=sha256:95cd16d3dee553f882540c1ffe331d085c9e629499ceadfbda4d4fde635f4b7d \
+ --hash=sha256:99f748a7e71ff382613b4e1acc0ac83bf7ad167fb3802e35e90d9763daba4d78 \
+ --hash=sha256:b8c78301cefcf5fd914aad35d3c04c2b21ce8629b5e4f4e45ae6812e461910fa \
+ --hash=sha256:c420917b188a5582a56d8b93bdd8e0f6eca08c84ff623a4c16e809152cd35793 \
+ --hash=sha256:c43866529f2f06fe0edc6246eb4faa34f03fe88b64a0a9a942561c8e22f4b71f \
+ --hash=sha256:cab50b8c2250b46fe738c77dbd25ce017d5e6fb35d3407606e7a4180656a5a6a \
+ --hash=sha256:cef128cb4d5e0b3493f058f10ce32365972c554572ff821e175dbc6f8ff6924f \
+ --hash=sha256:cf16e3cf6c0a5fdd9bc10c21687e19d29ad1fe863372b5543deaec1039581a30 \
+ --hash=sha256:e56c744aa6ff427a607763346e4170629caf7e48ead6921745986db3692f987f \
+ --hash=sha256:e577934fc5f8779c554639376beeaa5657d54349096ef24abe8c74c5d9c117c3 \
+ --hash=sha256:f2b0fa0c01d8a0c7483afd9f31d7ecf2d71760ca24499c8697aeb5ca37dc090c
+chardet==3.0.4 \
+ --hash=sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae \
+ --hash=sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691
+configobj==5.0.6 \
+ --hash=sha256:a2f5650770e1c87fb335af19a9b7eb73fc05ccf22144eb68db7d00cd2bcb0902
+cryptography==2.8 \
+ --hash=sha256:02079a6addc7b5140ba0825f542c0869ff4df9a69c360e339ecead5baefa843c \
+ --hash=sha256:1df22371fbf2004c6f64e927668734070a8953362cd8370ddd336774d6743595 \
+ --hash=sha256:369d2346db5934345787451504853ad9d342d7f721ae82d098083e1f49a582ad \
+ --hash=sha256:3cda1f0ed8747339bbdf71b9f38ca74c7b592f24f65cdb3ab3765e4b02871651 \
+ --hash=sha256:44ff04138935882fef7c686878e1c8fd80a723161ad6a98da31e14b7553170c2 \
+ --hash=sha256:4b1030728872c59687badcca1e225a9103440e467c17d6d1730ab3d2d64bfeff \
+ --hash=sha256:58363dbd966afb4f89b3b11dfb8ff200058fbc3b947507675c19ceb46104b48d \
+ --hash=sha256:6ec280fb24d27e3d97aa731e16207d58bd8ae94ef6eab97249a2afe4ba643d42 \
+ --hash=sha256:7270a6c29199adc1297776937a05b59720e8a782531f1f122f2eb8467f9aab4d \
+ --hash=sha256:73fd30c57fa2d0a1d7a49c561c40c2f79c7d6c374cc7750e9ac7c99176f6428e \
+ --hash=sha256:7f09806ed4fbea8f51585231ba742b58cbcfbfe823ea197d8c89a5e433c7e912 \
+ --hash=sha256:90df0cc93e1f8d2fba8365fb59a858f51a11a394d64dbf3ef844f783844cc793 \
+ --hash=sha256:971221ed40f058f5662a604bd1ae6e4521d84e6cad0b7b170564cc34169c8f13 \
+ --hash=sha256:a518c153a2b5ed6b8cc03f7ae79d5ffad7315ad4569b2d5333a13c38d64bd8d7 \
+ --hash=sha256:b0de590a8b0979649ebeef8bb9f54394d3a41f66c5584fff4220901739b6b2f0 \
+ --hash=sha256:b43f53f29816ba1db8525f006fa6f49292e9b029554b3eb56a189a70f2a40879 \
+ --hash=sha256:d31402aad60ed889c7e57934a03477b572a03af7794fa8fb1780f21ea8f6551f \
+ --hash=sha256:de96157ec73458a7f14e3d26f17f8128c959084931e8997b9e655a39c8fde9f9 \
+ --hash=sha256:df6b4dca2e11865e6cfbfb708e800efb18370f5a46fd601d3755bc7f85b3a8a2 \
+ --hash=sha256:ecadccc7ba52193963c0475ac9f6fa28ac01e01349a2ca48509667ef41ffd2cf \
+ --hash=sha256:fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8
+distro==1.5.0 \
+ --hash=sha256:0e58756ae38fbd8fc3020d54badb8eae17c5b9dcbed388b17bb55b8a5928df92 \
+ --hash=sha256:df74eed763e18d10d0da624258524ae80486432cd17392d9c3d96f5e83cd2799
+enum34==1.1.10; python_version < '3.4' \
+ --hash=sha256:a98a201d6de3f2ab3db284e70a33b0f896fbf35f8086594e8c9e74b909058d53 \
+ --hash=sha256:c3858660960c984d6ab0ebad691265180da2b43f07e061c0f8dca9ef3cffd328 \
+ --hash=sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248
+funcsigs==1.0.2 \
+ --hash=sha256:330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca \
+ --hash=sha256:a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50
+idna==2.9 \
+ --hash=sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb \
+ --hash=sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa
+ipaddress==1.0.23 \
+ --hash=sha256:6e0f4a39e66cb5bb9a137b00276a2eff74f93b71dcbdad6f10ff7df9d3557fcc \
+ --hash=sha256:b7f8e0369580bb4a24d5ba1d7cc29660a4a6987763faf1d8a8046830e020e7e2
+josepy==1.3.0 \
+ --hash=sha256:c341ffa403399b18e9eae9012f804843045764d1390f9cb4648980a7569b1619 \
+ --hash=sha256:e54882c64be12a2a76533f73d33cba9e331950fda9e2731e843490b774e7a01c
+mock==1.3.0 \
+ --hash=sha256:1e247dbecc6ce057299eb7ee019ad68314bb93152e81d9a6110d35f4d5eca0f6 \
+ --hash=sha256:3f573a18be94de886d1191f27c168427ef693e8dcfcecf95b170577b2eb69cbb
+parsedatetime==2.5 \
+ --hash=sha256:3b835fc54e472c17ef447be37458b400e3fefdf14bb1ffdedb5d2c853acf4ba1 \
+ --hash=sha256:d2e9ddb1e463de871d32088a3f3cea3dc8282b1b2800e081bd0ef86900451667
+pbr==5.4.5 \
+ --hash=sha256:07f558fece33b05caf857474a366dfcc00562bca13dd8b47b2b3e22d9f9bf55c \
+ --hash=sha256:579170e23f8e0c2f24b0de612f71f648eccb79fb1322c814ae6b3c07b5ba23e8
+pyOpenSSL==19.1.0 \
+ --hash=sha256:621880965a720b8ece2f1b2f54ea2071966ab00e2970ad2ce11d596102063504 \
+ --hash=sha256:9a24494b2602aaf402be5c9e30a0b82d4a5c67528fe8fb475e3f3bc00dd69507
+pyRFC3339==1.1 \
+ --hash=sha256:67196cb83b470709c580bb4738b83165e67c6cc60e1f2e4f286cfcb402a926f4 \
+ --hash=sha256:81b8cbe1519cdb79bed04910dd6fa4e181faf8c88dff1e1b987b5f7ab23a5b1a
+pycparser==2.20 \
+ --hash=sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0 \
+ --hash=sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705
+pyparsing==2.4.7 \
+ --hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \
+ --hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b
+python-augeas==0.5.0 \
+ --hash=sha256:67d59d66cdba8d624e0389b87b2a83a176f21f16a87553b50f5703b23f29bac2
+pytz==2020.1 \
+ --hash=sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed \
+ --hash=sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048
+requests==2.23.0 \
+ --hash=sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee \
+ --hash=sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6
+requests-toolbelt==0.9.1 \
+ --hash=sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f \
+ --hash=sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0
+six==1.15.0 \
+ --hash=sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259 \
+ --hash=sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced
+urllib3==1.25.9 \
+ --hash=sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527 \
+ --hash=sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115
+zope.component==4.6.1 \
+ --hash=sha256:bfbe55d4a93e70a78b10edc3aad4de31bb8860919b7cbd8d66f717f7d7b279ac \
+ --hash=sha256:d9c7c27673d787faff8a83797ce34d6ebcae26a370e25bddb465ac2182766aca
+zope.deferredimport==4.3.1 \
+ --hash=sha256:57b2345e7b5eef47efcd4f634ff16c93e4265de3dcf325afc7315ade48d909e1 \
+ --hash=sha256:9a0c211df44aa95f1c4e6d2626f90b400f56989180d3ef96032d708da3d23e0a
+zope.deprecation==4.4.0 \
+ --hash=sha256:0d453338f04bacf91bbfba545d8bcdf529aa829e67b705eac8c1a7fdce66e2df \
+ --hash=sha256:f1480b74995958b24ce37b0ef04d3663d2683e5d6debc96726eff18acf4ea113
+zope.event==4.4 \
+ --hash=sha256:69c27debad9bdacd9ce9b735dad382142281ac770c4a432b533d6d65c4614bcf \
+ --hash=sha256:d8e97d165fd5a0997b45f5303ae11ea3338becfe68c401dd88ffd2113fe5cae7
+zope.hookable==5.0.1 \
+ --hash=sha256:0194b9b9e7f614abba60c90b231908861036578297515d3d6508eb10190f266d \
+ --hash=sha256:0c2977473918bdefc6fa8dfb311f154e7f13c6133957fe649704deca79b92093 \
+ --hash=sha256:17b8bdb3b77e03a152ca0d5ca185a7ae0156f5e5a2dbddf538676633a1f7380f \
+ --hash=sha256:29d07681a78042cdd15b268ae9decffed9ace68a53eebeb61d65ae931d158841 \
+ --hash=sha256:36fb1b35d1150267cb0543a1ddd950c0bc2c75ed0e6e92e3aaa6ac2e29416cb7 \
+ --hash=sha256:3aed60c2bb5e812bbf9295c70f25b17ac37c233f30447a96c67913ba5073642f \
+ --hash=sha256:3cac1565cc768911e72ca9ec4ddf5c5109e1fef0104f19f06649cf1874943b60 \
+ --hash=sha256:3d4bc0cc4a37c3cd3081063142eeb2125511db3c13f6dc932d899c512690378e \
+ --hash=sha256:3f73096f27b8c28be53ffb6604f7b570fbbb82f273c6febe5f58119009b59898 \
+ --hash=sha256:522d1153d93f2d48aa0bd9fb778d8d4500be2e4dcf86c3150768f0e3adbbc4ef \
+ --hash=sha256:523d2928fb7377bbdbc9af9c0b14ad73e6eaf226349f105733bdae27efd15b5a \
+ --hash=sha256:5848309d4fc5c02150a45e8f8d2227e5bfda386a508bbd3160fed7c633c5a2fa \
+ --hash=sha256:6781f86e6d54a110980a76e761eb54590630fd2af2a17d7edf02a079d2646c1d \
+ --hash=sha256:6fd27921ebf3aaa945fa25d790f1f2046204f24dba4946f82f5f0a442577c3e9 \
+ --hash=sha256:70d581862863f6bf9e175e85c9d70c2d7155f53fb04dcdb2f73cf288ca559a53 \
+ --hash=sha256:81867c23b0dc66c8366f351d00923f2bc5902820a24c2534dfd7bf01a5879963 \
+ --hash=sha256:81db29edadcbb740cd2716c95a297893a546ed89db1bfe9110168732d7f0afdd \
+ --hash=sha256:86bd12624068cea60860a0759af5e2c3adc89c12aef6f71cf12f577e28deefe3 \
+ --hash=sha256:9c184d8f9f7a76e1ced99855ccf390ffdd0ec3765e5cbf7b9cada600accc0a1e \
+ --hash=sha256:acc789e8c29c13555e43fe4bf9fcd15a65512c9645e97bbaa5602e3201252b02 \
+ --hash=sha256:afaa740206b7660d4cc3b8f120426c85761f51379af7a5b05451f624ad12b0af \
+ --hash=sha256:b5f5fa323f878bb16eae68ea1ba7f6c0419d4695d0248bed4b18f51d7ce5ab85 \
+ --hash=sha256:bd89e0e2c67bf4ac3aca2a19702b1a37269fb1923827f68324ac2e7afd6e3406 \
+ --hash=sha256:c212de743283ec0735db24ec6ad913758df3af1b7217550ff270038062afd6ae \
+ --hash=sha256:ca553f524293a0bdea05e7f44c3e685e4b7b022cb37d87bc4a3efa0f86587a8d \
+ --hash=sha256:cab67065a3db92f636128d3157cc5424a145f82d96fb47159c539132833a6d36 \
+ --hash=sha256:d3b3b3eedfdbf6b02898216e85aa6baf50207f4378a2a6803d6d47650cd37031 \
+ --hash=sha256:d9f4a5a72f40256b686d31c5c0b1fde503172307beb12c1568296e76118e402c \
+ --hash=sha256:df5067d87aaa111ed5d050e1ee853ba284969497f91806efd42425f5348f1c06 \
+ --hash=sha256:e2587644812c6138f05b8a41594a8337c6790e3baf9a01915e52438c13fc6bef \
+ --hash=sha256:e27fd877662db94f897f3fd532ef211ca4901eb1a70ba456f15c0866a985464a \
+ --hash=sha256:e427ebbdd223c72e06ba94c004bb04e996c84dec8a0fa84e837556ae145c439e \
+ --hash=sha256:e583ad4309c203ef75a09d43434cf9c2b4fa247997ecb0dcad769982c39411c7 \
+ --hash=sha256:e760b2bc8ece9200804f0c2b64d10147ecaf18455a2a90827fbec4c9d84f3ad5 \
+ --hash=sha256:ea9a9cc8bcc70e18023f30fa2f53d11ae069572a162791224e60cd65df55fb69 \
+ --hash=sha256:ecb3f17dce4803c1099bd21742cd126b59817a4e76a6544d31d2cca6e30dbffd \
+ --hash=sha256:ed794e3b3de42486d30444fb60b5561e724ee8a2d1b17b0c2e0f81e3ddaf7a87 \
+ --hash=sha256:ee885d347279e38226d0a437b6a932f207f691c502ee565aba27a7022f1285df \
+ --hash=sha256:fd5e7bc5f24f7e3d490698f7b854659a9851da2187414617cd5ed360af7efd63 \
+ --hash=sha256:fe45f6870f7588ac7b2763ff1ce98cce59369717afe70cc353ec5218bc854bcc
+zope.interface==5.1.0 \
+ --hash=sha256:0103cba5ed09f27d2e3de7e48bb320338592e2fabc5ce1432cf33808eb2dfd8b \
+ --hash=sha256:14415d6979356629f1c386c8c4249b4d0082f2ea7f75871ebad2e29584bd16c5 \
+ --hash=sha256:1ae4693ccee94c6e0c88a4568fb3b34af8871c60f5ba30cf9f94977ed0e53ddd \
+ --hash=sha256:1b87ed2dc05cb835138f6a6e3595593fea3564d712cb2eb2de963a41fd35758c \
+ --hash=sha256:269b27f60bcf45438e8683269f8ecd1235fa13e5411de93dae3b9ee4fe7f7bc7 \
+ --hash=sha256:27d287e61639d692563d9dab76bafe071fbeb26818dd6a32a0022f3f7ca884b5 \
+ --hash=sha256:39106649c3082972106f930766ae23d1464a73b7d30b3698c986f74bf1256a34 \
+ --hash=sha256:40e4c42bd27ed3c11b2c983fecfb03356fae1209de10686d03c02c8696a1d90e \
+ --hash=sha256:461d4339b3b8f3335d7e2c90ce335eb275488c587b61aca4b305196dde2ff086 \
+ --hash=sha256:4f98f70328bc788c86a6a1a8a14b0ea979f81ae6015dd6c72978f1feff70ecda \
+ --hash=sha256:558a20a0845d1a5dc6ff87cd0f63d7dac982d7c3be05d2ffb6322a87c17fa286 \
+ --hash=sha256:562dccd37acec149458c1791da459f130c6cf8902c94c93b8d47c6337b9fb826 \
+ --hash=sha256:5e86c66a6dea8ab6152e83b0facc856dc4d435fe0f872f01d66ce0a2131b7f1d \
+ --hash=sha256:60a207efcd8c11d6bbeb7862e33418fba4e4ad79846d88d160d7231fcb42a5ee \
+ --hash=sha256:645a7092b77fdbc3f68d3cc98f9d3e71510e419f54019d6e282328c0dd140dcd \
+ --hash=sha256:6874367586c020705a44eecdad5d6b587c64b892e34305bb6ed87c9bbe22a5e9 \
+ --hash=sha256:74bf0a4f9091131de09286f9a605db449840e313753949fe07c8d0fe7659ad1e \
+ --hash=sha256:7b726194f938791a6691c7592c8b9e805fc6d1b9632a833b9c0640828cd49cbc \
+ --hash=sha256:8149ded7f90154fdc1a40e0c8975df58041a6f693b8f7edcd9348484e9dc17fe \
+ --hash=sha256:8cccf7057c7d19064a9e27660f5aec4e5c4001ffcf653a47531bde19b5aa2a8a \
+ --hash=sha256:911714b08b63d155f9c948da2b5534b223a1a4fc50bb67139ab68b277c938578 \
+ --hash=sha256:a5f8f85986197d1dd6444763c4a15c991bfed86d835a1f6f7d476f7198d5f56a \
+ --hash=sha256:a744132d0abaa854d1aad50ba9bc64e79c6f835b3e92521db4235a1991176813 \
+ --hash=sha256:af2c14efc0bb0e91af63d00080ccc067866fb8cbbaca2b0438ab4105f5e0f08d \
+ --hash=sha256:b054eb0a8aa712c8e9030065a59b5e6a5cf0746ecdb5f087cca5ec7685690c19 \
+ --hash=sha256:b0becb75418f8a130e9d465e718316cd17c7a8acce6fe8fe07adc72762bee425 \
+ --hash=sha256:b1d2ed1cbda2ae107283befd9284e650d840f8f7568cb9060b5466d25dc48975 \
+ --hash=sha256:ba4261c8ad00b49d48bbb3b5af388bb7576edfc0ca50a49c11dcb77caa1d897e \
+ --hash=sha256:d1fe9d7d09bb07228650903d6a9dc48ea649e3b8c69b1d263419cc722b3938e8 \
+ --hash=sha256:d7804f6a71fc2dda888ef2de266727ec2f3915373d5a785ed4ddc603bbc91e08 \
+ --hash=sha256:da2844fba024dd58eaa712561da47dcd1e7ad544a257482392472eae1c86d5e5 \
+ --hash=sha256:dcefc97d1daf8d55199420e9162ab584ed0893a109f45e438b9794ced44c9fd0 \
+ --hash=sha256:dd98c436a1fc56f48c70882cc243df89ad036210d871c7427dc164b31500dc11 \
+ --hash=sha256:e74671e43ed4569fbd7989e5eecc7d06dc134b571872ab1d5a88f4a123814e9f \
+ --hash=sha256:eb9b92f456ff3ec746cd4935b73c1117538d6124b8617bc0fe6fda0b3816e345 \
+ --hash=sha256:ebb4e637a1fb861c34e48a00d03cffa9234f42bef923aec44e5625ffb9a8e8f9 \
+ --hash=sha256:ef739fe89e7f43fb6494a43b1878a36273e5924869ba1d866f752c5812ae8d58 \
+ --hash=sha256:f40db0e02a8157d2b90857c24d89b6310f9b6c3642369852cdc3b5ac49b92afc \
+ --hash=sha256:f68bf937f113b88c866d090fea0bc52a098695173fc613b055a17ff0cf9683b6 \
+ --hash=sha256:fb55c182a3f7b84c1a2d6de5fa7b1a05d4660d866b91dbf8d74549c57a1499e8
+zope.proxy==4.3.5 \
+ --hash=sha256:00573dfa755d0703ab84bb23cb6ecf97bb683c34b340d4df76651f97b0bab068 \
+ --hash=sha256:092049280f2848d2ba1b57b71fe04881762a220a97b65288bcb0968bb199ec30 \
+ --hash=sha256:0cbd27b4d3718b5ec74fc65ffa53c78d34c65c6fd9411b8352d2a4f855220cf1 \
+ --hash=sha256:17fc7e16d0c81f833a138818a30f366696653d521febc8e892858041c4d88785 \
+ --hash=sha256:19577dfeb70e8a67249ba92c8ad20589a1a2d86a8d693647fa8385408a4c17b0 \
+ --hash=sha256:207aa914576b1181597a1516e1b90599dc690c095343ae281b0772e44945e6a4 \
+ --hash=sha256:219a7db5ed53e523eb4a4769f13105118b6d5b04ed169a283c9775af221e231f \
+ --hash=sha256:2b50ea79849e46b5f4f2b0247a3687505d32d161eeb16a75f6f7e6cd81936e43 \
+ --hash=sha256:5903d38362b6c716e66bbe470f190579c530a5baf03dbc8500e5c2357aa569a5 \
+ --hash=sha256:5c24903675e271bd688c6e9e7df5775ac6b168feb87dbe0e4bcc90805f21b28f \
+ --hash=sha256:5ef6bc5ed98139e084f4e91100f2b098a0cd3493d4e76f9d6b3f7b95d7ad0f06 \
+ --hash=sha256:61b55ae3c23a126a788b33ffb18f37d6668e79a05e756588d9e4d4be7246ab1c \
+ --hash=sha256:63ddb992931a5e616c87d3d89f5a58db086e617548005c7f9059fac68c03a5cc \
+ --hash=sha256:6943da9c09870490dcfd50c4909c0cc19f434fa6948f61282dc9cb07bcf08160 \
+ --hash=sha256:6ad40f85c1207803d581d5d75e9ea25327cd524925699a83dfc03bf8e4ba72b7 \
+ --hash=sha256:6b44433a79bdd7af0e3337bd7bbcf53dd1f9b0fa66bf21bcb756060ce32a96c1 \
+ --hash=sha256:6bbaa245015d933a4172395baad7874373f162955d73612f0b66b6c2c33b6366 \
+ --hash=sha256:7007227f4ea85b40a2f5e5a244479f6a6dfcf906db9b55e812a814a8f0e2c28d \
+ --hash=sha256:74884a0aec1f1609190ec8b34b5d58fb3b5353cf22b96161e13e0e835f13518f \
+ --hash=sha256:7d25fe5571ddb16369054f54cdd883f23de9941476d97f2b92eb6d7d83afe22d \
+ --hash=sha256:7e162bdc5e3baad26b2262240be7d2bab36991d85a6a556e48b9dfb402370261 \
+ --hash=sha256:814d62678dc3a30f4aa081982d830b7c342cf230ffc9d030b020cb154eeebf9e \
+ --hash=sha256:8878a34c5313ee52e20aa50b03138af8d472bae465710fb954d133a9bfd3c38d \
+ --hash=sha256:a66a0d94e5b081d5d695e66d6667e91e74d79e273eee95c1747717ba9cb70792 \
+ --hash=sha256:a69f5cbf4addcfdf03dda564a671040127a6b7c34cf9fe4973582e68441b63fa \
+ --hash=sha256:b00f9f0c334d07709d3f73a7cb8ae63c6ca1a90c790a63b5e7effa666ef96021 \
+ --hash=sha256:b6ed71e4a7b4690447b626f499d978aa13197a0e592950e5d7020308f6054698 \
+ --hash=sha256:bdf5041e5851526e885af579d2f455348dba68d74f14a32781933569a327fddf \
+ --hash=sha256:be034360dd34e62608419f86e799c97d389c10a0e677a25f236a971b2f40dac9 \
+ --hash=sha256:cc8f590a5eed30b314ae6b0232d925519ade433f663de79cc3783e4b10d662ba \
+ --hash=sha256:cd7a318a15fe6cc4584bf3c4426f092ed08c0fd012cf2a9173114234fe193e11 \
+ --hash=sha256:cf19b5f63a59c20306e034e691402b02055c8f4e38bf6792c23cad489162a642 \
+ --hash=sha256:cfc781ce442ec407c841e9aa51d0e1024f72b6ec34caa8fdb6ef9576d549acf2 \
+ --hash=sha256:dea9f6f8633571e18bc20cad83603072e697103a567f4b0738d52dd0211b4527 \
+ --hash=sha256:e4a86a1d5eb2cce83c5972b3930c7c1eac81ab3508464345e2b8e54f119d5505 \
+ --hash=sha256:e7106374d4a74ed9ff00c46cc00f0a9f06a0775f8868e423f85d4464d2333679 \
+ --hash=sha256:e98a8a585b5668aa9e34d10f7785abf9545fe72663b4bfc16c99a115185ae6a5 \
+ --hash=sha256:f64840e68483316eb58d82c376ad3585ca995e69e33b230436de0cdddf7363f9 \
+ --hash=sha256:f8f4b0a9e6683e43889852130595c8854d8ae237f2324a053cdd884de936aa9b \
+ --hash=sha256:fc45a53219ed30a7f670a6d8c98527af0020e6fd4ee4c0a8fb59f147f06d816c
+
+# Contains the requirements for the letsencrypt package.
+#
+# Since the letsencrypt package depends on certbot and using pip with hashes
+# requires that all installed packages have hashes listed, this allows
+# dependency-requirements.txt to be used without requiring a hash for a
+# (potentially unreleased) Certbot package.
+
+letsencrypt==0.7.0 \
+ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \
+ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9
+
+certbot==1.9.0 \
+ --hash=sha256:d5a804d32e471050921f7b39ed9859e2e9de02824176ed78f57266222036b53a \
+ --hash=sha256:2ff9bf7d9af381c7efee22dec2dd6938d9d8fddcc9e11682b86e734164a30b57
+acme==1.9.0 \
+ --hash=sha256:d8061b396a22b21782c9b23ff9a945b23e50fca2573909a42f845e11d5658ac5 \
+ --hash=sha256:38a1630c98e144136c62eec4d2c545a1bdb1a3cd4eca82214be6b83a1f5a161f
+certbot-apache==1.9.0 \
+ --hash=sha256:09528a820d57e54984d490100644cd8a6603db97bf5776f86e95795ecfacf23d \
+ --hash=sha256:f47fb3f4a9bd927f4812121a0beefe56b163475a28f4db34c64dc838688d9e9e
+certbot-nginx==1.9.0 \
+ --hash=sha256:bb2e3f7fe17f071f350a3efa48571b8ef40a8e4b6db9c6da72539206a20b70be \
+ --hash=sha256:ab26a4f49d53b0e8bf0f903e58e2a840cda233fe1cbbc54c36ff17f973e57d65
+
+UNLIKELY_EOF
+ # -------------------------------------------------------------------------
+ cat << "UNLIKELY_EOF" > "$TEMP_DIR/pipstrap.py"
+#!/usr/bin/env python
+"""A small script that can act as a trust root for installing pip >=8
+Embed this in your project, and your VCS checkout is all you have to trust. In
+a post-peep era, this lets you claw your way to a hash-checking version of pip,
+with which you can install the rest of your dependencies safely. All it assumes
+is Python 2.6 or better and *some* version of pip already installed. If
+anything goes wrong, it will exit with a non-zero status code.
+"""
+# This is here so embedded copies are MIT-compliant:
+# Copyright (c) 2016 Erik Rose
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+from __future__ import print_function
+from distutils.version import StrictVersion
+from hashlib import sha256
+from os import environ
+from os.path import join
+from shutil import rmtree
+try:
+ from subprocess import check_output
+except ImportError:
+ from subprocess import CalledProcessError, PIPE, Popen
+
+ def check_output(*popenargs, **kwargs):
+ if 'stdout' in kwargs:
+ raise ValueError('stdout argument not allowed, it will be '
+ 'overridden.')
+ process = Popen(stdout=PIPE, *popenargs, **kwargs)
+ output, unused_err = process.communicate()
+ retcode = process.poll()
+ if retcode:
+ cmd = kwargs.get("args")
+ if cmd is None:
+ cmd = popenargs[0]
+ raise CalledProcessError(retcode, cmd)
+ return output
+import sys
+from tempfile import mkdtemp
+try:
+ from urllib2 import build_opener, HTTPHandler, HTTPSHandler
+except ImportError:
+ from urllib.request import build_opener, HTTPHandler, HTTPSHandler
+try:
+ from urlparse import urlparse
+except ImportError:
+ from urllib.parse import urlparse # 3.4
+
+
+__version__ = 1, 5, 1
+PIP_VERSION = '9.0.1'
+DEFAULT_INDEX_BASE = 'https://pypi.python.org'
+
+
+# wheel has a conditional dependency on argparse:
+maybe_argparse = (
+ [('18/dd/e617cfc3f6210ae183374cd9f6a26b20514bbb5a792af97949c5aacddf0f/'
+ 'argparse-1.4.0.tar.gz',
+ '62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4')]
+ if sys.version_info < (2, 7, 0) else [])
+
+
+# Be careful when updating the pinned versions here, in particular for pip.
+# Indeed starting from 10.0, pip will build dependencies in isolation if the
+# related projects are compliant with PEP 517. This is not something we want
+# as of now, so the isolation build will need to be disabled wherever
+# pipstrap is used (see https://github.com/certbot/certbot/issues/8256).
+PACKAGES = maybe_argparse + [
+ # Pip has no dependencies, as it vendors everything:
+ ('11/b6/abcb525026a4be042b486df43905d6893fb04f05aac21c32c638e939e447/'
+ 'pip-{0}.tar.gz'.format(PIP_VERSION),
+ '09f243e1a7b461f654c26a725fa373211bb7ff17a9300058b205c61658ca940d'),
+ # This version of setuptools has only optional dependencies:
+ ('37/1b/b25507861991beeade31473868463dad0e58b1978c209de27384ae541b0b/'
+ 'setuptools-40.6.3.zip',
+ '3b474dad69c49f0d2d86696b68105f3a6f195f7ab655af12ef9a9c326d2b08f8'),
+ ('c9/1d/bd19e691fd4cfe908c76c429fe6e4436c9e83583c4414b54f6c85471954a/'
+ 'wheel-0.29.0.tar.gz',
+ '1ebb8ad7e26b448e9caa4773d2357849bf80ff9e313964bcaf79cbf0201a1648')
+]
+
+
+class HashError(Exception):
+ def __str__(self):
+ url, path, actual, expected = self.args
+ return ('{url} did not match the expected hash {expected}. Instead, '
+ 'it was {actual}. The file (left at {path}) may have been '
+ 'tampered with.'.format(**locals()))
+
+
+def hashed_download(url, temp, digest):
+ """Download ``url`` to ``temp``, make sure it has the SHA-256 ``digest``,
+ and return its path."""
+ # Based on pip 1.4.1's URLOpener but with cert verification removed. Python
+ # >=2.7.9 verifies HTTPS certs itself, and, in any case, the cert
+ # authenticity has only privacy (not arbitrary code execution)
+ # implications, since we're checking hashes.
+ def opener(using_https=True):
+ opener = build_opener(HTTPSHandler())
+ if using_https:
+ # Strip out HTTPHandler to prevent MITM spoof:
+ for handler in opener.handlers:
+ if isinstance(handler, HTTPHandler):
+ opener.handlers.remove(handler)
+ return opener
+
+ def read_chunks(response, chunk_size):
+ while True:
+ chunk = response.read(chunk_size)
+ if not chunk:
+ break
+ yield chunk
+
+ parsed_url = urlparse(url)
+ response = opener(using_https=parsed_url.scheme == 'https').open(url)
+ path = join(temp, parsed_url.path.split('/')[-1])
+ actual_hash = sha256()
+ with open(path, 'wb') as file:
+ for chunk in read_chunks(response, 4096):
+ file.write(chunk)
+ actual_hash.update(chunk)
+
+ actual_digest = actual_hash.hexdigest()
+ if actual_digest != digest:
+ raise HashError(url, path, actual_digest, digest)
+ return path
+
+
+def get_index_base():
+ """Return the URL to the dir containing the "packages" folder.
+ Try to wring something out of PIP_INDEX_URL, if set. Hack "/simple" off the
+ end if it's there; that is likely to give us the right dir.
+ """
+ env_var = environ.get('PIP_INDEX_URL', '').rstrip('/')
+ if env_var:
+ SIMPLE = '/simple'
+ if env_var.endswith(SIMPLE):
+ return env_var[:-len(SIMPLE)]
+ else:
+ return env_var
+ else:
+ return DEFAULT_INDEX_BASE
+
+
+def main():
+ python = sys.executable or 'python'
+ pip_version = StrictVersion(check_output([python, '-m', 'pip', '--version'])
+ .decode('utf-8').split()[1])
+ has_pip_cache = pip_version >= StrictVersion('6.0')
+ index_base = get_index_base()
+ temp = mkdtemp(prefix='pipstrap-')
+ try:
+ downloads = [hashed_download(index_base + '/packages/' + path,
+ temp,
+ digest)
+ for path, digest in PACKAGES]
+ # Calling pip as a module is the preferred way to avoid problems about pip self-upgrade.
+ command = [python, '-m', 'pip', 'install', '--no-index', '--no-deps', '-U']
+ # Disable cache since it is not used and it otherwise sometimes throws permission warnings:
+ command.extend(['--no-cache-dir'] if has_pip_cache else [])
+ command.extend(downloads)
+ check_output(command)
+ except HashError as exc:
+ print(exc)
+ except Exception:
+ rmtree(temp)
+ raise
+ else:
+ rmtree(temp)
+ return 0
+ return 1
+
+
+if __name__ == '__main__':
+ sys.exit(main())
+
+UNLIKELY_EOF
+ # -------------------------------------------------------------------------
+ # Set PATH so pipstrap upgrades the right (v)env:
+ PATH="$VENV_BIN:$PATH" "$VENV_BIN/python" "$TEMP_DIR/pipstrap.py"
+ set +e
+ if [ "$VERBOSE" = 1 ]; then
+ "$VENV_BIN/pip" install --disable-pip-version-check --no-cache-dir --require-hashes -r "$TEMP_DIR/letsencrypt-auto-requirements.txt"
+ else
+ PIP_OUT=`"$VENV_BIN/pip" install --disable-pip-version-check --no-cache-dir --require-hashes -r "$TEMP_DIR/letsencrypt-auto-requirements.txt" 2>&1`
+ fi
+ PIP_STATUS=$?
+ set -e
+ if [ "$PIP_STATUS" != 0 ]; then
+ # Report error. (Otherwise, be quiet.)
+ error "Had a problem while installing Python packages."
+ if [ "$VERBOSE" != 1 ]; then
+ error
+ error "pip prints the following errors: "
+ error "====================================================="
+ error "$PIP_OUT"
+ error "====================================================="
+ error
+ error "Certbot has problem setting up the virtual environment."
+
+ if `echo $PIP_OUT | grep -q Killed` || `echo $PIP_OUT | grep -q "allocate memory"` ; then
+ error
+ error "Based on your pip output, the problem can likely be fixed by "
+ error "increasing the available memory."
+ else
+ error
+ error "We were not be able to guess the right solution from your pip "
+ error "output."
+ fi
+
+ error
+ error "Consult https://certbot.eff.org/docs/install.html#problems-with-python-virtual-environment"
+ error "for possible solutions."
+ error "You may also find some support resources at https://certbot.eff.org/support/ ."
+ fi
+ rm -rf "$VENV_PATH"
+ exit 1
+ fi
+
+ if [ -d "$OLD_VENV_PATH" -a ! -L "$OLD_VENV_PATH" ]; then
+ rm -rf "$OLD_VENV_PATH"
+ ln -s "$VENV_PATH" "$OLD_VENV_PATH"
+ fi
+
+ say "Installation succeeded."
+ fi
+
+ # If you're modifying any of the code after this point in this current `if` block, you
+ # may need to update the "$DEPRECATED_OS" = 1 case at the beginning of phase 2 as well.
+
+ if [ "$INSTALL_ONLY" = 1 ]; then
+ say "Certbot is installed."
+ exit 0
+ fi
+
+ "$VENV_BIN/letsencrypt" "$@"
+
+else
+ # Phase 1: Upgrade certbot-auto if necessary, then self-invoke.
+ #
+ # Each phase checks the version of only the thing it is responsible for
+ # upgrading. Phase 1 checks the version of the latest release of
+ # certbot-auto (which is always the same as that of the certbot
+ # package). Phase 2 checks the version of the locally installed certbot.
+ export PHASE_1_VERSION="$LE_AUTO_VERSION"
+
+ if [ ! -f "$VENV_BIN/letsencrypt" ]; then
+ if ! OldVenvExists; then
+ if [ "$HELP" = 1 ]; then
+ echo "$USAGE"
+ exit 0
+ fi
+ # If it looks like we've never bootstrapped before, bootstrap:
+ Bootstrap
+ fi
+ fi
+ if [ "$OS_PACKAGES_ONLY" = 1 ]; then
+ say "OS packages installed."
+ exit 0
+ fi
+
+ DeterminePythonVersion "NOCRASH"
+ # Don't warn about file permissions if the user disabled the check or we
+ # can't find an up-to-date Python.
+ if [ "$PYVER" -ge "$MIN_PYVER" -a "$NO_PERMISSIONS_CHECK" != 1 ]; then
+ # If the script fails for some reason, don't break certbot-auto.
+ set +e
+ # Suppress unexpected error output.
+ CHECK_PERM_OUT=$(CheckPathPermissions "$LE_PYTHON" "$0" 2>/dev/null)
+ CHECK_PERM_STATUS="$?"
+ set -e
+ # Only print output if the script ran successfully and it actually produced
+ # output. The latter check resolves
+ # https://github.com/certbot/certbot/issues/7012.
+ if [ "$CHECK_PERM_STATUS" = 0 -a -n "$CHECK_PERM_OUT" ]; then
+ error "$CHECK_PERM_OUT"
+ fi
+ fi
+
+ if [ "$NO_SELF_UPGRADE" != 1 ]; then
+ TEMP_DIR=$(TempDir)
+ trap 'rm -rf "$TEMP_DIR"' EXIT
+ # ---------------------------------------------------------------------------
+ cat << "UNLIKELY_EOF" > "$TEMP_DIR/fetch.py"
+"""Do downloading and JSON parsing without additional dependencies. ::
+
+ # Print latest released version of LE to stdout:
+ python fetch.py --latest-version
+
+ # Download letsencrypt-auto script from git tag v1.2.3 into the folder I'm
+ # in, and make sure its signature verifies:
+ python fetch.py --le-auto-script v1.2.3
+
+On failure, return non-zero.
+
+"""
+
+from __future__ import print_function, unicode_literals
+
+from distutils.version import LooseVersion
+from json import loads
+from os import devnull, environ
+from os.path import dirname, join
+import re
+import ssl
+from subprocess import check_call, CalledProcessError
+from sys import argv, exit
+try:
+ from urllib2 import build_opener, HTTPHandler, HTTPSHandler
+ from urllib2 import HTTPError, URLError
+except ImportError:
+ from urllib.request import build_opener, HTTPHandler, HTTPSHandler
+ from urllib.error import HTTPError, URLError
+
+PUBLIC_KEY = environ.get('LE_AUTO_PUBLIC_KEY', """-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6MR8W/galdxnpGqBsYbq
+OzQb2eyW15YFjDDEMI0ZOzt8f504obNs920lDnpPD2/KqgsfjOgw2K7xWDJIj/18
+xUvWPk3LDkrnokNiRkA3KOx3W6fHycKL+zID7zy+xZYBuh2fLyQtWV1VGQ45iNRp
+9+Zo7rH86cdfgkdnWTlNSHyTLW9NbXvyv/E12bppPcEvgCTAQXgnDVJ0/sqmeiij
+n9tTFh03aM+R2V/21h8aTraAS24qiPCz6gkmYGC8yr6mglcnNoYbsLNYZ69zF1XH
+cXPduCPdPdfLlzVlKK1/U7hkA28eG3BIAMh6uJYBRJTpiGgaGdPd7YekUB8S6cy+
+CQIDAQAB
+-----END PUBLIC KEY-----
+""")
+
+class ExpectedError(Exception):
+ """A novice-readable exception that also carries the original exception for
+ debugging"""
+
+
+class HttpsGetter(object):
+ def __init__(self):
+ """Build an HTTPS opener."""
+ # Based on pip 1.4.1's URLOpener
+ # This verifies certs on only Python >=2.7.9, and when NO_CERT_VERIFY isn't set.
+ if environ.get('NO_CERT_VERIFY') == '1' and hasattr(ssl, 'SSLContext'):
+ self._opener = build_opener(HTTPSHandler(context=cert_none_context()))
+ else:
+ self._opener = build_opener(HTTPSHandler())
+ # Strip out HTTPHandler to prevent MITM spoof:
+ for handler in self._opener.handlers:
+ if isinstance(handler, HTTPHandler):
+ self._opener.handlers.remove(handler)
+
+ def get(self, url):
+ """Return the document contents pointed to by an HTTPS URL.
+
+ If something goes wrong (404, timeout, etc.), raise ExpectedError.
+
+ """
+ try:
+ # socket module docs say default timeout is None: that is, no
+ # timeout
+ return self._opener.open(url, timeout=30).read()
+ except (HTTPError, IOError) as exc:
+ raise ExpectedError("Couldn't download %s." % url, exc)
+
+
+def write(contents, dir, filename):
+ """Write something to a file in a certain directory."""
+ with open(join(dir, filename), 'wb') as file:
+ file.write(contents)
+
+
+def latest_stable_version(get):
+ """Return the latest stable release of letsencrypt."""
+ metadata = loads(get(
+ environ.get('LE_AUTO_JSON_URL',
+ 'https://pypi.python.org/pypi/certbot/json')).decode('UTF-8'))
+ # metadata['info']['version'] actually returns the latest of any kind of
+ # release release, contrary to https://wiki.python.org/moin/PyPIJSON.
+ # The regex is a sufficient regex for picking out prereleases for most
+ # packages, LE included.
+ return str(max(LooseVersion(r) for r
+ in metadata['releases'].keys()
+ if re.match('^[0-9.]+$', r)))
+
+
+def verified_new_le_auto(get, tag, temp_dir):
+ """Return the path to a verified, up-to-date letsencrypt-auto script.
+
+ If the download's signature does not verify or something else goes wrong
+ with the verification process, raise ExpectedError.
+
+ """
+ le_auto_dir = environ.get(
+ 'LE_AUTO_DIR_TEMPLATE',
+ 'https://raw.githubusercontent.com/certbot/certbot/%s/'
+ 'letsencrypt-auto-source/') % tag
+ write(get(le_auto_dir + 'letsencrypt-auto'), temp_dir, 'letsencrypt-auto')
+ write(get(le_auto_dir + 'letsencrypt-auto.sig'), temp_dir, 'letsencrypt-auto.sig')
+ write(PUBLIC_KEY.encode('UTF-8'), temp_dir, 'public_key.pem')
+ try:
+ with open(devnull, 'w') as dev_null:
+ check_call(['openssl', 'dgst', '-sha256', '-verify',
+ join(temp_dir, 'public_key.pem'),
+ '-signature',
+ join(temp_dir, 'letsencrypt-auto.sig'),
+ join(temp_dir, 'letsencrypt-auto')],
+ stdout=dev_null,
+ stderr=dev_null)
+ except CalledProcessError as exc:
+ raise ExpectedError("Couldn't verify signature of downloaded "
+ "certbot-auto.", exc)
+
+
+def cert_none_context():
+ """Create a SSLContext object to not check hostname."""
+ # PROTOCOL_TLS isn't available before 2.7.13 but this code is for 2.7.9+, so use this.
+ context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+ context.verify_mode = ssl.CERT_NONE
+ return context
+
+
+def main():
+ get = HttpsGetter().get
+ flag = argv[1]
+ try:
+ if flag == '--latest-version':
+ print(latest_stable_version(get))
+ elif flag == '--le-auto-script':
+ tag = argv[2]
+ verified_new_le_auto(get, tag, dirname(argv[0]))
+ except ExpectedError as exc:
+ print(exc.args[0], exc.args[1])
+ return 1
+ else:
+ return 0
+
+
+if __name__ == '__main__':
+ exit(main())
+
+UNLIKELY_EOF
+ # ---------------------------------------------------------------------------
+ if [ "$PYVER" -lt "$MIN_PYVER" ]; then
+ error "WARNING: couldn't find Python $MIN_PYTHON_VERSION+ to check for updates."
+ elif ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then
+ error "WARNING: unable to check for updates."
+ fi
+
+ # If for any reason REMOTE_VERSION is not set, let's assume certbot-auto is up-to-date,
+ # and do not go into the self-upgrading process.
+ if [ -n "$REMOTE_VERSION" ]; then
+ LE_VERSION_STATE=`CompareVersions "$LE_PYTHON" "$LE_AUTO_VERSION" "$REMOTE_VERSION"`
+
+ if [ "$LE_VERSION_STATE" = "UNOFFICIAL" ]; then
+ say "Unofficial certbot-auto version detected, self-upgrade is disabled: $LE_AUTO_VERSION"
+ elif [ "$LE_VERSION_STATE" = "OUTDATED" ]; then
+ say "Upgrading certbot-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
+
+ # Now we drop into Python so we don't have to install even more
+ # dependencies (curl, etc.), for better flow control, and for the option of
+ # future Windows compatibility.
+ "$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION"
+
+ # Install new copy of certbot-auto.
+ # TODO: Deal with quotes in pathnames.
+ say "Replacing certbot-auto..."
+ # Clone permissions with cp. chmod and chown don't have a --reference
+ # option on macOS or BSD, and stat -c on Linux is stat -f on macOS and BSD:
+ cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone"
+ cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone"
+ # Using mv rather than cp leaves the old file descriptor pointing to the
+ # original copy so the shell can continue to read it unmolested. mv across
+ # filesystems is non-atomic, doing `rm dest, cp src dest, rm src`, but the
+ # cp is unlikely to fail if the rm doesn't.
+ mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0"
+ fi # A newer version is available.
+ fi
+ fi # Self-upgrading is allowed.
+
+ RerunWithArgs --le-auto-phase2 "$@"
+fi
diff --git a/certbot/handlers/main.yml b/certbot/handlers/main.yml
index ef3272ee..4363ed3d 100644
--- a/certbot/handlers/main.yml
+++ b/certbot/handlers/main.yml
@@ -19,5 +19,5 @@
systemd:
daemon_reload: yes
-- name: install certbot-auto
- command: /usr/local/bin/certbot --noninteractive --install-only
+- name: install letsencrypt-auto
+ command: /usr/local/bin/letsencrypt-auto --noninteractive --install-only --no-self-upgrade
diff --git a/certbot/tasks/install-legacy.yml b/certbot/tasks/install-legacy.yml
new file mode 100644
index 00000000..fe0cb8e2
--- /dev/null
+++ b/certbot/tasks/install-legacy.yml
@@ -0,0 +1,59 @@
+---
+
+- name: certbot package is removed
+ apt:
+ name: certbot
+ state: absent
+
+- include_role:
+ name: evolix/remount-usr
+
+- name: Let's Encrypt script is present
+ copy:
+ src: letsencrypt-auto
+ dest: /usr/local/bin/letsencrypt-auto
+ mode: '0755'
+ owner: root
+ group: root
+ force: yes
+ notify: install letsencrypt-auto
+
+- name: Check certbot script
+ stat:
+ path: /usr/local/bin/certbot
+ register: certbot_path
+
+- name: Rename certbot script if present
+ command: "mv /usr/local/bin/certbot /usr/local/bin/certbot.bak"
+ when: certbot_path.stat.exists
+
+- name: Let's Encrypt script is symlinked as certbot
+ file:
+ src: "/usr/local/bin/letsencrypt-auto"
+ dest: "/usr/local/bin/certbot"
+ state: link
+
+- name: systemd artefacts are absent
+ file:
+ dest: "{{ item }}"
+ state: absent
+ loop:
+ - /etc/systemd/system/certbot.service
+ - /etc/systemd/system/certbot.service.d
+ - /etc/systemd/system/certbot.timer
+ notify: systemd daemon-reload
+
+- name: custom crontab is present
+ copy:
+ src: cron_jessie
+ dest: /etc/cron.d/certbot
+ force: yes
+ when: certbot_custom_crontab
+
+- name: disable self-upgrade
+ ini_file:
+ dest: "/etc/letsencrypt/cli.ini"
+ section: null
+ option: "no-self-upgrade"
+ value: 0
+ state: present
diff --git a/certbot/tasks/install-sources.yml b/certbot/tasks/install-sources.yml
deleted file mode 100644
index 1bc7c864..00000000
--- a/certbot/tasks/install-sources.yml
+++ /dev/null
@@ -1,35 +0,0 @@
----
-
-- name: certbot package is removed
- apt:
- name: certbot
- state: absent
-
-- include_role:
- name: evolix/remount-usr
-
-- name: Certbot script is downloaded
- get_url:
- url: https://dl.eff.org/certbot-auto
- dest: /usr/local/bin/certbot
- mode: '0755'
- owner: root
- group: root
- force: no
- notify: install certbot-auto
-
-- name: systemd artefacts are absent
- file:
- dest: "{{ item }}"
- state: absent
- loop:
- - /etc/systemd/system/certbot.service
- - /etc/systemd/system/certbot.service.d
- - /etc/systemd/system/certbot.timer
- notify: systemd daemon-reload
-
-- name: custom crontab is present
- copy:
- src: cron_jessie
- dest: /etc/cron.d/certbot
- force: yes
diff --git a/certbot/tasks/main.yml b/certbot/tasks/main.yml
index ed8e8b85..54c1f803 100644
--- a/certbot/tasks/main.yml
+++ b/certbot/tasks/main.yml
@@ -7,17 +7,17 @@
- ansible_distribution_major_version is version('8', '>=')
msg: only compatible with Debian 9+
-- name: Install from sources on Debian 8
- include: install-sources.yml
+- name: Install legacy script on Debian 8 and 9
+ include: install-legacy.yml
when:
- ansible_distribution == "Debian"
- - ansible_distribution_major_version is version('8', '=')
+ - ansible_distribution_major_version is version('10', '<')
-- name: Install package on Debian 9+
+- name: Install package on Debian 10+
include: install-package.yml
when:
- ansible_distribution == "Debian"
- - ansible_distribution_major_version is version('9', '>=')
+ - ansible_distribution_major_version is version('10', '>=')
- include: acme-challenge.yml
diff --git a/certbot/templates/acme-challenge/apache.conf.j2 b/certbot/templates/acme-challenge/apache.conf.j2
index f013957d..ebc99483 100644
--- a/certbot/templates/acme-challenge/apache.conf.j2
+++ b/certbot/templates/acme-challenge/apache.conf.j2
@@ -7,6 +7,5 @@
Alias /.well-known/acme-challenge /var/lib/letsencrypt/.well-known/acme-challenge
Options -Indexes
- Allow from all
Require all granted
diff --git a/elasticsearch/templates/rotate_elasticsearch_logs.j2 b/elasticsearch/templates/rotate_elasticsearch_logs.j2
index 95969f89..849a9ca1 100644
--- a/elasticsearch/templates/rotate_elasticsearch_logs.j2
+++ b/elasticsearch/templates/rotate_elasticsearch_logs.j2
@@ -5,5 +5,10 @@ LOG_DIR=/var/log/elasticsearch
USER=elasticsearch
MAX_AGE={{ elasticsearch_log_rotate_days | mandatory }}
-find ${LOG_DIR} -type f -user ${USER} \( -name "*.log.????-??-??" -o -name "*-????-??-??.log" \) -exec gzip --best {} \;
-find ${LOG_DIR} -type f -user ${USER} \( -name "*.log.????-??-??.gz" -o -name "*-????-??-??.log.gz" \) -ctime +${MAX_AGE} -delete
+# Compress logs
+find ${LOG_DIR} -type f -user ${USER} -name "*.log.????-??-??" -exec gzip --best {} \;
+find ${LOG_DIR} -type f -user ${USER} -name "*-????-??-??.log" -exec gzip --best {} \;
+find ${LOG_DIR} -type f -user ${USER} -name "*.log.??" -not -name "*.gz" -exec gzip --best {} \;
+
+# Delete old logs
+find ${LOG_DIR} -type f -user ${USER} -name "*gz" -ctime +${MAX_AGE} -delete
\ No newline at end of file
diff --git a/evoacme/files/evoacme.sh b/evoacme/files/evoacme.sh
index 2ea2d273..724b7448 100755
--- a/evoacme/files/evoacme.sh
+++ b/evoacme/files/evoacme.sh
@@ -14,7 +14,7 @@ show_version() {
cat <,
+Copyright 2009-2021 Evolix ,
Victor Laborie ,
Jérémy Lecour ,
Benoit Série
@@ -208,6 +208,7 @@ main() {
[ "${TEST}" = "1" ] && CERTBOT_MODE="${CERTBOT_MODE} --test-cert"
[ "${QUIET}" = "1" ] && CERTBOT_MODE="${CERTBOT_MODE} --quiet"
[ "${DRY_RUN}" = "1" ] && CERTBOT_MODE="${CERTBOT_MODE} --dry-run"
+ [ "${CERTBOT_SELF_UPGRADE}" = "0" ] && CERTBOT_MODE="${CERTBOT_MODE} --no-self-upgrade"
local CERTBOT_REGISTRATION="--agree-tos"
if [ -n "${SSL_EMAIL}" ]; then
@@ -309,7 +310,7 @@ readonly QUIET=${QUIET:-"0"}
readonly TEST=${TEST:-"0"}
readonly DRY_RUN=${DRY_RUN:-"0"}
-readonly VERSION="20.12"
+readonly VERSION="21.01"
# Read configuration file, if it exists
[ -r /etc/default/evoacme ] && . /etc/default/evoacme
@@ -323,5 +324,6 @@ readonly LOG_DIR=${LOG_DIR:-"/var/log/evoacme"}
readonly HOOKS_DIR=${HOOKS_DIR:-"${CRT_DIR}/renewal-hooks/deploy"}
readonly SSL_MINDAY=${SSL_MINDAY:-"30"}
readonly SSL_EMAIL=${SSL_EMAIL:-""}
+readonly CERTBOT_SELF_UPGRADE=${CERTBOT_SELF_UPGRADE:-"0"}
main ${ARGS}
diff --git a/evoacme/files/make-csr.sh b/evoacme/files/make-csr.sh
index f82ad65b..edec8787 100755
--- a/evoacme/files/make-csr.sh
+++ b/evoacme/files/make-csr.sh
@@ -13,7 +13,7 @@ show_version() {
cat <,
+Copyright 2009-2021 Evolix ,
Victor Laborie ,
Jérémy Lecour ,
Benoit Série
@@ -265,7 +265,7 @@ readonly ARGS=$@
readonly VERBOSE=${VERBOSE:-"0"}
readonly QUIET=${QUIET:-"0"}
-readonly VERSION="20.12"
+readonly VERSION="21.01"
# Read configuration file, if it exists
[ -r /etc/default/evoacme ] && . /etc/default/evoacme
diff --git a/evoacme/files/vhost-domains.sh b/evoacme/files/vhost-domains.sh
index 5a60c23c..3c1c01d5 100755
--- a/evoacme/files/vhost-domains.sh
+++ b/evoacme/files/vhost-domains.sh
@@ -13,7 +13,7 @@ show_version() {
cat <,
+Copyright 2009-2021 Evolix ,
Victor Laborie ,
Jérémy Lecour ,
Benoit Série
@@ -170,7 +170,7 @@ readonly ARGS=$@
readonly VERBOSE=${VERBOSE:-"0"}
readonly QUIET=${QUIET:-"0"}
-readonly VERSION="20.12"
+readonly VERSION="21.01"
readonly SRV_IP=${SRV_IP:-""}
diff --git a/evoacme/tasks/certbot.yml b/evoacme/tasks/certbot.yml
index 0577abbe..26327569 100644
--- a/evoacme/tasks/certbot.yml
+++ b/evoacme/tasks/certbot.yml
@@ -1,4 +1,8 @@
---
+- name: Do no install certbot crontab
+ set_fact:
+ certbot_custom_crontab: False
+
- include_role:
name: evolix/certbot
diff --git a/evolinux-users/templates/sudoers_stretch.j2 b/evolinux-users/templates/sudoers_stretch.j2
index 025aa0c2..539f871e 100644
--- a/evolinux-users/templates/sudoers_stretch.j2
+++ b/evolinux-users/templates/sudoers_stretch.j2
@@ -6,6 +6,10 @@ nagios ALL = NOPASSWD: /usr/lib/nagios/plugins/check_procs
nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_minifirewall
nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_haproxy_stats
nagios ALL = NOPASSWD: /usr/sbin/bkctld check
+nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_phpfpm_multi /var/lib/lxc/php56/rootfs/etc/php5/fpm/pool.d/
+nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_phpfpm_multi /var/lib/lxc/php70/rootfs/etc/php/7.0/fpm/pool.d/
+nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_phpfpm_multi /var/lib/lxc/php73/rootfs/etc/php/7.3/fpm/pool.d/
+nagios ALL = NOPASSWD: /usr/local/lib/nagios/plugins/check_phpfpm_multi /var/lib/lxc/php74/rootfs/etc/php/7.4/fpm/pool.d/
nagios ALL = (clamav) NOPASSWD: /usr/bin/clamscan /tmp/safe.txt
%{{ evolinux_sudo_group }} ALL=(ALL:ALL) ALL
diff --git a/filebeat/defaults/main.yml b/filebeat/defaults/main.yml
index 322aba46..598a08ed 100644
--- a/filebeat/defaults/main.yml
+++ b/filebeat/defaults/main.yml
@@ -21,3 +21,4 @@ filebeat_logstash_auth_password: ""
filebeat_use_config_template: False
filebeat_update_config: True
filebeat_force_config: True
+filebeat_upgrade_package: False
diff --git a/filebeat/tasks/main.yml b/filebeat/tasks/main.yml
index 34feb5c7..a19848e8 100644
--- a/filebeat/tasks/main.yml
+++ b/filebeat/tasks/main.yml
@@ -30,15 +30,17 @@
- name: Filebeat is installed
apt:
name: filebeat
- state: present
+ state: "{% if filebeat_upgrade_package %}latest{% else %}present{% endif %}"
+ notify: restart filebeat
tags:
- - filebeat
- - packages
+ - filebeat
+ - packages
- name: Filebeat service is enabled
systemd:
name: filebeat
enabled: yes
+ notify: restart filebeat
- name: is logstash-plugin available?
stat:
diff --git a/haproxy/defaults/main.yml b/haproxy/defaults/main.yml
index 42f5483b..c69f48d3 100644
--- a/haproxy/defaults/main.yml
+++ b/haproxy/defaults/main.yml
@@ -18,6 +18,10 @@ haproxy_chroot: /var/lib/haproxy
haproxy_stats_access_ips: []
haproxy_stats_admin_ips: []
+haproxy_stats_users: []
+## use crypt(8) password encryption
+# haproxy_stats_users:
+# - { login: "", password: "" }
haproxy_maintenance_ips: []
haproxy_deny_ips: []
diff --git a/haproxy/templates/haproxy.default.cfg.j2 b/haproxy/templates/haproxy.default.cfg.j2
index 5e4c4b9f..1e6f8be1 100644
--- a/haproxy/templates/haproxy.default.cfg.j2
+++ b/haproxy/templates/haproxy.default.cfg.j2
@@ -35,18 +35,34 @@ defaults
errorfile 504 /etc/haproxy/errors/504.http
{% if haproxy_stats_enable %}
+{% if haproxy_stats_users %}
+userlist stats_users
+{% for user in haproxy_stats_users | default([]) %}
+ user {{ user.login }} password {{ user.password }}
+{% endfor %}
+
+{% endif %}
listen stats
mode http
bind {{ haproxy_stats_bind_directive }}
+ acl stats_access_ips src -f /etc/haproxy/stats_access_ips
+ acl stats_admin_ips src -f /etc/haproxy/stats_admin_ips
+
stats enable
stats refresh 10s
stats uri {{ haproxy_stats_path }}
stats show-legends
stats show-node
- stats admin if { src -f /etc/haproxy/stats_admin_ips }
+ stats admin if stats_admin_ips
+
+{% if haproxy_stats_users %}
+ acl stats_users http_auth(stats_users)
+ stats http-request auth realm "HAProxy admin" if !stats_access_ips !stats_users
+{% else %}
+ stats http-request deny if !stats_access_ips
+{% endif %}
- http-request deny if !{ src -f /etc/haproxy/stats_access_ips }
http-request set-log-level silent
{% endif %}
diff --git a/keepalived/README.md b/keepalived/README.md
new file mode 100644
index 00000000..1768d403
--- /dev/null
+++ b/keepalived/README.md
@@ -0,0 +1,16 @@
+# keepalived
+
+Install Keepalived
+
+## Tasks
+
+Everything is in the `tasks/main.yml` file.
+
+## Available variables
+
+* `keepalived_interface` : Interface used by vrrpd instance (default is the interface reported by ansible_default_ipv4.interface)
+* `keepalived_role` : This can be either master or backup (default: `master`)
+* `keepalived_router_id` : Number between 0 and 255 used to differentiate multiple instances of vrrpd (default: `42`)
+* `keepalived_priority` : Used for electing MASTER, highest priority wins (default : `100` when keepalived_role is set to `master` otherwise `50`)
+* `keepalived_ip` : Address added or deleted on change to MASTER/BACKUP. This is mandatory (default: none)
+* `keepalived_password` : Password for accessing vrrpd. Should be the same on all machines. This is mandatory (default: none)
diff --git a/keepalived/defaults/main.yml b/keepalived/defaults/main.yml
new file mode 100644
index 00000000..bc1294d4
--- /dev/null
+++ b/keepalived/defaults/main.yml
@@ -0,0 +1,6 @@
+keepalived_interface: "{{ ansible_default_ipv4.interface }}"
+keepalived_role: "master"
+keepalived_router_id: "42"
+keepalived_priority: "{% if keepalived_role == 'master' %}100{% else %}50{% endif %}"
+keepalived_ip: ""
+keepalived_password: ""
diff --git a/keepalived/files/check_keepalived b/keepalived/files/check_keepalived
new file mode 100644
index 00000000..e518e99e
--- /dev/null
+++ b/keepalived/files/check_keepalived
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+###############################################################
+# Check Keepalived State #
+# #
+# Author: Zhivko Todorov #
+# Date: 01-Dec-2015 #
+# Version: 0.0.1 #
+# License: GPL #
+###############################################################
+
+
+# set to 'true' if the host is supposed to be in MASTER state
+# or set to 'false' if the host is supposed to be in BACKUP state
+# nrpe cannot receive external variables UNLESS is forced in config
+MASTER='true'
+
+# checking if there are alive keepalived processes so we can trust the content of the notify 'state' file
+KEEPALIVENUM=`ps uax|grep '/usr/sbin/keepalived'|grep -v grep|wc -l|tr -d "\n"`
+
+if [ $KEEPALIVENUM -gt 0 ]; then
+
+ KEEPALIVESTATE=`cat /var/run/keepalive.state`
+
+ if [ "$MASTER" == "true" ]; then
+
+ if [[ $KEEPALIVESTATE == *"MASTER"* ]];then
+ echo $KEEPALIVESTATE
+ exit 0
+ fi
+
+ if [[ $KEEPALIVESTATE == *"BACKUP"* ]];then
+ echo $KEEPALIVESTATE
+ exit 2
+ fi
+
+ else
+
+ if [[ $KEEPALIVESTATE == *"BACKUP"* ]];then
+ echo $KEEPALIVESTATE
+ exit 0
+ fi
+
+ if [[ $KEEPALIVESTATE == *"MASTER"* ]];then
+ echo $KEEPALIVESTATE
+ exit 2
+ fi
+
+ fi
+fi
+
+echo "Keepalived is in UNKNOWN state"
+exit 3
diff --git a/keepalived/files/notify.sh b/keepalived/files/notify.sh
new file mode 100644
index 00000000..b99c0489
--- /dev/null
+++ b/keepalived/files/notify.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+echo $1 $2 is in $3 state > /var/run/keepalive.state
diff --git a/keepalived/handlers/main.yml b/keepalived/handlers/main.yml
new file mode 100644
index 00000000..252fe515
--- /dev/null
+++ b/keepalived/handlers/main.yml
@@ -0,0 +1,10 @@
+---
+- name: restart keepalived
+ systemd:
+ name: keepalived
+ state: restarted
+
+- name: restart nagios-nrpe-server
+ service:
+ name: nagios-nrpe-server
+ state: restarted
diff --git a/keepalived/tasks/main.yml b/keepalived/tasks/main.yml
new file mode 100644
index 00000000..dee97bca
--- /dev/null
+++ b/keepalived/tasks/main.yml
@@ -0,0 +1,59 @@
+- name: install Keepalived service
+ apt:
+ pkg: keepalived
+ state: present
+ tags:
+ - keepalived
+
+- name: Add notify.sh script for NRPE check
+ file:
+ src: notify.sh
+ dest: /etc/keepalived/notify.sh
+ mode: "0755"
+ owner: root
+ group: root
+ force: yes
+ notify: restart keepalived
+ tags:
+ - keepalived
+ - nrpe
+
+- name: check_keepalived is installed
+ file:
+ src: check_keepalived
+ dest: /usr/local/lib/nagios/plugins/check_keepalived
+ mode: "0755"
+ owner: root
+ group: root
+ force: yes
+ tags:
+ - keepalived
+ - nrpe
+
+- name: Use check_keepalived for NRPE
+ lineinfile:
+ dest: /etc/nagios/nrpe.d/evolix.cfg
+ regexp: 'command\[check_keepalived\]'
+ replace: 'command[check_keepalived]=/usr/local/lib/nagios/plugins/check_keepalived'
+ notify: restart nagios-nrpe-server
+ tags:
+ - keepalived
+ - nrpe
+
+- name: generate Keepalived configuration
+ template:
+ src: keepalived.conf.j2
+ dest: /etc/keepalived/keepalived.conf
+ mode: 0644
+ notify: restart keepalived
+ tags:
+ - keepalived
+
+- name: enable and restart Keepalived service
+ systemd:
+ name: keepalived
+ daemon_reload: yes
+ state: started
+ enabled: yes
+ tags:
+ - keepalived
diff --git a/keepalived/templates/keepalived.conf.j2 b/keepalived/templates/keepalived.conf.j2
new file mode 100644
index 00000000..e89463dc
--- /dev/null
+++ b/keepalived/templates/keepalived.conf.j2
@@ -0,0 +1,30 @@
+# {{ ansible_managed }}
+
+vrrp_script chk_sshd {
+ script "/usr/bin/pkill -0 sshd"
+ interval 5
+ weight -4
+ fall 2
+ rise 1
+}
+
+vrrp_instance vrrp {
+ interface {{ keepalived_interface | mandatory }}
+ virtual_router_id {{ keepalived_router_id | mandatory }}
+ state {{ keepalived_role | upper }}
+ priority {{ keepalived_priority }}
+
+ virtual_ipaddress {
+ {{ keepalived_ip | mandatory }}
+ }
+
+ authentication {
+ auth_type PASS
+ auth_pass {{ keepalived_password | mandatory }}
+ }
+
+ track_script {
+ chk_sshd
+ }
+ notify /etc/keepalived/notify.sh
+}
diff --git a/listupgrade/files/listupgrade.sh b/listupgrade/files/listupgrade.sh
index ad04c8ee..d2f4996b 100644
--- a/listupgrade/files/listupgrade.sh
+++ b/listupgrade/files/listupgrade.sh
@@ -238,3 +238,27 @@ echo "$downloadstatus" | grep -q 'Download complete and in download only mode'
if [ $? -ne 0 ]; then
echo "$downloadstatus"
fi;
+
+
+# Also, we try to update each container apt sources
+if which lxc-ls > /dev/null; then
+ for container in $(lxc-ls); do
+
+ aptUpdateOutput=$(lxc-attach -n $container -- apt update 2>&1 | (egrep -ve '^(Listing|WARNING|$)' -e upgraded -e 'up to date' || true ))
+
+ if (echo "$aptUpdateOutput" | egrep "^Err(:[0-9]+)? http"); then
+ echo "FATAL CONTAINER - Not able to fetch all sources (probably a pesky (mini)firewall). Please, fix me"
+ exit 150
+ fi
+
+ # Now we try to fetch all the packages for the next update session
+ downloadstatus=$(lxc-attach -n $container -- apt dist-upgrade --assume-yes --download-only -q2 2>&1)
+ echo "$downloadstatus" | grep -q 'Download complete and in download only mode'
+
+ if [ $? -ne 0 ]; then
+ echo "$downloadstatus"
+ fi;
+
+ done
+fi
+
diff --git a/lxc-php/defaults/main.yml b/lxc-php/defaults/main.yml
index 69ef9380..1cceab35 100644
--- a/lxc-php/defaults/main.yml
+++ b/lxc-php/defaults/main.yml
@@ -8,6 +8,7 @@ php_conf_allow_url_fopen: "Off"
php_conf_disable_functions: "exec,shell-exec,system,passthru,popen"
# Allows accessing a local mysql database using localhost
+lxc_php_create_mysql_link: False
php_conf_mysql_socket_dir: /mysqld
php_conf_mysql_default_socket: "{{ php_conf_mysql_socket_dir }}/mysqld.sock"
@@ -17,3 +18,4 @@ lxc_php_container_releases:
php56: "jessie"
php70: "stretch"
php73: "buster"
+ php74: "buster"
diff --git a/lxc-php/handlers/main.yml b/lxc-php/handlers/main.yml
index b8322e94..95882838 100644
--- a/lxc-php/handlers/main.yml
+++ b/lxc-php/handlers/main.yml
@@ -1,4 +1,9 @@
---
+- name: Reload php74-fpm
+ lxc_container:
+ name: "{{ lxc_php_version }}"
+ container_command: "systemctl reload php7.4-fpm"
+
- name: Reload php73-fpm
lxc_container:
name: "{{ lxc_php_version }}"
diff --git a/lxc-php/tasks/main.yml b/lxc-php/tasks/main.yml
index d3fcad4a..25c0a978 100644
--- a/lxc-php/tasks/main.yml
+++ b/lxc-php/tasks/main.yml
@@ -18,4 +18,7 @@
- include: "php73.yml"
when: lxc_php_version == "php73"
+- include: "php74.yml"
+ when: lxc_php_version == "php74"
+
- include: "misc.yml"
diff --git a/lxc-php/tasks/misc.yml b/lxc-php/tasks/misc.yml
index 3b6164d0..297ee469 100644
--- a/lxc-php/tasks/misc.yml
+++ b/lxc-php/tasks/misc.yml
@@ -28,6 +28,6 @@
name: "{{ lxc_php_version }}"
container_config:
- "lxc.mount.entry = /run/mysqld {{ php_conf_mysql_socket_dir | replace('/', '', 1) }} none bind,create=dir 0 0"
- when: php_conf_mysql_socket_dir is string
+ when: lxc_php_create_mysql_link and php_conf_mysql_socket_dir is string
notify: "Restart container"
diff --git a/lxc-php/tasks/php74.yml b/lxc-php/tasks/php74.yml
new file mode 100644
index 00000000..c32820f1
--- /dev/null
+++ b/lxc-php/tasks/php74.yml
@@ -0,0 +1,55 @@
+---
+
+- name: "{{ lxc_php_version }} - Install dependency packages"
+ lxc_container:
+ name: "{{ lxc_php_version }}"
+ container_command: "DEBIAN_FRONTEND=noninteractive apt install -y wget apt-transport-https gnupg"
+
+- name: "{{ lxc_php_version }} - Add sury repo"
+ lineinfile:
+ dest: "/var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/apt/sources.list.d/sury.list"
+ line: "{{ item }}"
+ state: present
+ create: yes
+ mode: "0644"
+ with_items:
+ - "deb https://packages.sury.org/php/ buster main"
+ - "deb http://pub.evolix.net/ buster-php74/"
+
+- name: Grab pub.evolix.net GPG Key
+ get_url:
+ url: https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x0C016D3BD1195D30105837CC44975278B8612B5D
+ dest: /var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/apt/trusted.gpg.d/reg.asc
+ mode: 0644
+ checksum: sha256:a2e0f56ba433aa0740aad6eeeb43bb67df9ab943d76324382b39948a4c7ce840
+
+- name: Grab packages.sury.org GPG Key
+ get_url:
+ url: https://packages.sury.org/php/apt.gpg
+ dest: /var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/apt/trusted.gpg.d/sury.gpg
+ mode: 0644
+ checksum: sha256:b3ea944563435e54bb64f181ee8bc26200985d09164cdc4c1702fc3ef051f19d
+
+- name: "{{ lxc_php_version }} - Update APT cache"
+ lxc_container:
+ name: "{{ lxc_php_version }}"
+ container_command: "DEBIAN_FRONTEND=noninteractive apt update"
+
+- name: "{{ lxc_php_version }} - Install PHP packages"
+ lxc_container:
+ name: "{{ lxc_php_version }}"
+ container_command: "DEBIAN_FRONTEND=noninteractive apt install -y php-fpm php-cli php-gd php-intl php-imap php-ldap php-mysql php-pgsql php-sqlite3 php-gettext php-curl php-zip php-mbstring php-zip composer libphp-phpmailer"
+
+- name: "{{ lxc_php_version }} - Copy evolinux PHP configuration"
+ template:
+ src: z-evolinux-defaults.ini.j2
+ dest: "{{ line_item }}"
+ mode: "0644"
+ notify: "Reload {{ lxc_php_version }}-fpm"
+ with_items:
+ - "/var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/php/7.4/fpm/conf.d/z-evolinux-defaults.ini"
+ - "/var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/php/7.4/cli/conf.d/z-evolinux-defaults.ini"
+ loop_control:
+ loop_var: line_item
+
+- include: "mail_opensmtpd.yml"
diff --git a/metricbeat/defaults/main.yml b/metricbeat/defaults/main.yml
index 51113271..9529bef3 100644
--- a/metricbeat/defaults/main.yml
+++ b/metricbeat/defaults/main.yml
@@ -7,12 +7,17 @@ metricbeat_elasticsearch_protocol: ""
metricbeat_elasticsearch_auth_api_key: ""
metricbeat_elasticsearch_auth_username: ""
metricbeat_elasticsearch_auth_password: ""
+metricbeat_elasticsearch_ssl: False
+metricbeat_elasticsearch_ssl_certificate_authorities: []
+metricbeat_elasticsearch_ssl_certificate: ""
+metricbeat_elasticsearch_ssl_verification_mode: ""
metricbeat_processors_cloud_metadata: False
metricbeat_use_config_template: False
metricbeat_update_config: True
metricbeat_force_config: True
+metribeat_upgrade_package: False
# Example :
# metricbeat_tags:
diff --git a/metricbeat/tasks/main.yml b/metricbeat/tasks/main.yml
index 1ae3a2cd..d148f1cd 100644
--- a/metricbeat/tasks/main.yml
+++ b/metricbeat/tasks/main.yml
@@ -30,15 +30,17 @@
- name: Metricbeat is installed
apt:
name: metricbeat
- state: present
+ state: "{% if metribeat_upgrade_package %}latest{% else %}present{% endif %}"
+ notify: restart metricbeat
tags:
- - metricbeat
- - packages
+ - metricbeat
+ - packages
- name: Metricbeat service is enabled
systemd:
name: metricbeat
enabled: yes
+ notify: restart metricbeat
# When we don't use a config template (default)
- block:
diff --git a/metricbeat/templates/metricbeat.default.yml.j2 b/metricbeat/templates/metricbeat.default.yml.j2
index bd88cdc6..56c348fc 100644
--- a/metricbeat/templates/metricbeat.default.yml.j2
+++ b/metricbeat/templates/metricbeat.default.yml.j2
@@ -112,6 +112,18 @@ output.elasticsearch:
{% if metricbeat_elasticsearch_auth_password %}
password: "{{ metricbeat_elasticsearch_auth_password }}"
{% endif %}
+{% if metricbeat_elasticsearch_ssl %}
+ ssl.enabled: true
+{% if metricbeat_elasticsearch_ssl_certificate_authorities != [] %}
+ ssl.certificate_authorities: ["{{ metricbeat_elasticsearch_ssl_certificate_authorities | join('", "') }}"]
+{% endif %}
+{% if metricbeat_elasticsearch_ssl_certificate %}
+ ssl.certificate: "{{ metricbeat_elasticsearch_ssl_certificate }}"
+{% endif %}
+{% if metricbeat_elasticsearch_ssl_verification_mode %}
+ ssl.verification_mode: "{{ metricbeat_elasticsearch_ssl_verification_mode }}"
+{% endif %}
+{% endif %}
# ------------------------------ Logstash Output -------------------------------
#output.logstash:
diff --git a/minifirewall/files/minifirewall.conf b/minifirewall/files/minifirewall.conf
index 1c637483..47be78bf 100644
--- a/minifirewall/files/minifirewall.conf
+++ b/minifirewall/files/minifirewall.conf
@@ -18,7 +18,7 @@ DOCKER='off'
INTLAN='192.168.0.2/32'
# Trusted IPv4 addresses for private and semi-public services
-TRUSTEDIPS='62.212.121.90 88.179.18.233 31.170.8.4 31.170.9.129'
+TRUSTEDIPS='31.170.9.129 62.212.121.90 31.170.8.4 82.65.34.85 54.37.106.210 51.210.84.146'
# Privilegied IPv4 addresses for semi-public services
# (no need to add again TRUSTEDIPS)
@@ -30,15 +30,15 @@ PRIVILEGIEDIPS=''
# Protected services
# (add also in Public services if needed)
-SERVICESTCP1p='22'
+SERVICESTCP1p='22222'
SERVICESUDP1p=''
# Public services (IPv4/IPv6)
-SERVICESTCP1='25 53 443 993 995 22222'
-SERVICESUDP1='53'
+SERVICESTCP1='22222'
+SERVICESUDP1=''
# Semi-public services (IPv4)
-SERVICESTCP2='20 21 22 80 110 143'
+SERVICESTCP2='22'
SERVICESUDP2=''
# Private services (IPv4)
@@ -55,7 +55,7 @@ DNSSERVEURS='0.0.0.0/0'
# HTTP authorizations
# (you can use DNS names but set cron to reload minifirewall regularly)
# (if you have HTTP proxy, set 0.0.0.0/0)
-# HTTPSITES='security.debian.org security-cdn.debian.org pub.evolix.net volatile.debian.org mirror.evolix.org backports.debian.org hwraid.le-vert.net antispam00.evolix.org spamassassin.apache.org sa-update.space-pro.be sa-update.secnap.net www.sa-update.pccc.com sa-update.dnswl.org'
+# HTTPSITES='security.debian.org pub.evolix.net security-cdn.debian.org mirror.evolix.org backports.debian.org hwraid.le-vert.net antispam00.evolix.org spamassassin.apache.org sa-update.space-pro.be sa-update.secnap.net www.sa-update.pccc.com sa-update.dnswl.org ocsp.int-x3.letsencrypt.org'
HTTPSITES='0.0.0.0/0'
# HTTPS authorizations
diff --git a/nagios-nrpe/files/plugins/check_phpfpm_multi b/nagios-nrpe/files/plugins/check_phpfpm_multi
new file mode 100644
index 00000000..b2788d86
--- /dev/null
+++ b/nagios-nrpe/files/plugins/check_phpfpm_multi
@@ -0,0 +1,82 @@
+#!/bin/bash
+
+
+function detect_pool_dir() {
+ # Try to autodetect pool directory
+ php_version=$(php --version |head -n1 | sed -E 's/^PHP ([0-9]\.[0-9]).*/\1/g')
+
+ if [[ $php_version =~ ^5. ]]; then
+ echo "/etc/php5/fpm/pool.d/";
+ else
+ echo "/etc/php/$php_version/fpm/pool.d/";
+ fi
+}
+
+return=0
+nb_crit=0
+nb_warn=0
+nb_ok=0
+nb_unchk=0
+output=""
+
+readonly POOL_FOLDER=${1:-$(detect_pool_dir)}
+
+if [[ ! -d "$POOL_FOLDER" ]]; then
+ echo "CRITICAL - $POOL_FOLDER does not exists"
+ exit 2
+fi;
+
+readonly POOL_FILES=$(find "$POOL_FOLDER" -name "*.conf")
+
+for pool_file in $POOL_FILES; do
+
+ pool_name=$(grep "^\[" "$pool_file" | sed -E 's/^\[(.*)\].*$/\1/g')
+ pool_status_path=$(grep -E "^pm.status_path\s?=" "$pool_file" | sed -E "s/.*=\s?'?([^']*)'?\s?$/\1/g")
+ pool_listen=$(grep -E "^listen\s?=" "$pool_file" | sed -E 's/.*=\s?(.*)\s?$/\1/g')
+
+ if [[ "$pool_status_path" == '' ]]; then
+ nb_unchk=$((nb_unchk + 1))
+ output="${output}UNCHK - ${pool_name} (missing pm.status_path definition)\n"
+
+ continue;
+ fi;
+
+ if [[ -S "$pool_listen" ]] || [[ ! "$pool_listen" =~ ':' ]]; then
+ target=(-H 127.0.0.1 --unix "$pool_listen")
+ else
+ target=(-H "$(echo "$pool_listen" | cut -d':' -f1)" -p "$(echo "$pool_listen" | cut -d':' -f2 )")
+ fi
+
+ result=$(perl /usr/local/lib/nagios/plugins/check_phpfpm_status.pl "${target[@]}" -u "$pool_status_path")
+ ret="${?}"
+
+ if [ "${ret}" -ge 2 ]; then
+ nb_crit=$((nb_crit + 1))
+ output="${output}${result}\n"
+ [ "${return}" -le 2 ] && return=2
+ elif [ "${ret}" -ge 1 ]; then
+ nb_warn=$((nb_warn + 1))
+ output="${output}${result}\n"
+ [ "${return}" -le 1 ] && return=1
+ else
+ nb_ok=$((nb_ok + 1))
+ output="${output}$(echo "$result" | cut -d '|' -f1)\n"
+ [ "${return}" -le 0 ] && return=0
+ fi
+
+
+done;
+
+
+[ "${return}" -ge 0 ] && header="OK"
+[ "${return}" -ge 1 ] && header="WARNING"
+[ "${return}" -ge 2 ] && header="CRITICAL"
+
+printf "%s - %s UNCHK / %s CRIT / %s WARN / %s OK\n\n" "${header}" "${nb_unchk}" "${nb_crit}" "${nb_warn}" "${nb_ok}"
+
+printf "%b" "${output}" | grep -E "(CRITICAL|UNKNOWN)"
+printf "%b" "${output}" | grep -E "WARNING"
+printf "%b" "${output}" | grep -E "OK"
+printf "%b" "${output}" | grep -E "^UNCHK"
+
+exit "${return}"
diff --git a/nagios-nrpe/files/plugins/check_phpfpm_status.pl b/nagios-nrpe/files/plugins/check_phpfpm_status.pl
index 4fc47fde..fb8ba65c 100755
--- a/nagios-nrpe/files/plugins/check_phpfpm_status.pl
+++ b/nagios-nrpe/files/plugins/check_phpfpm_status.pl
@@ -1,18 +1,19 @@
#!/usr/bin/env perl
# check_phpfpm_status.pl
-# Version : 1.0
+# Version : 1.1
# Author : regis.leroy at makina-corpus.com
# based on previous apache status work by Dennis D. Spreen (dennis at spreendigital.de)
-# Based on check_apachestatus.pl v1.4 by
-# De Bodt Lieven (Lieven.DeBodt at gmail.com)
-# Karsten Behrens (karsten at behrens dot in)
-# Geoff McQueen (geoff.mcqueen at hiivesystems dot com )
-# Dave Steinberg (dave at redterror dot net)
+# Based on check_apachestatus.pl v1.4 by
+# De Bodt Lieven (Lieven.DeBodt at gmail.com)
+# Karsten Behrens (karsten at behrens dot in)
+# Geoff McQueen (geoff.mcqueen at hiivesystems dot com )
+# Dave Steinberg (dave at redterror dot net)
# Licence : GNU GPL v3 - http://www.fsf.org/licenses/gpl.txt
#
# help : ./check_phpfpm_status.pl -h
#
# issues & updates: http://github.com/regilero/check_phpfpm_status
+# Unix socket support from https://github.com/magenbrot/check_phpfpm_status/tree/unix-socket-support
use strict;
use warnings;
use Getopt::Long;
@@ -26,52 +27,53 @@ package main;
binmode(STDOUT, ":utf8");
# Globals
-my $Version='1.0';
-my $Name=$0;
+my $Version= '1.1';
+my $Name= $0;
-my $o_host = undef; # hostname
-my $o_help= undef; # want some help ?
-my $o_port= undef; # port
-my $o_url = undef; # url to use, if not the default
-my $o_user= undef; # user for auth
-my $o_pass= ''; # password for auth
-my $o_realm= ''; # password for auth
-my $o_version= undef; # print version
-my $o_warn_p_level= -1; # Min number of idle workers that will cause a warning
-my $o_crit_p_level= -1; # Min number of idle workersthat will cause an error
-my $o_warn_q_level= -1; # Number of Max Queue Reached that will cause a warning
-my $o_crit_q_level= -1; # Number of Max Queue Reached that will cause an error
-my $o_warn_m_level= -1; # Number of Max Processes Reached that will cause a warning
-my $o_crit_m_level= -1; # Number of Max Processes Reached that will cause an error
-my $o_timeout= 15; # Default 15s Timeout
-my $o_warn_thresold=undef; # warning thresolds entry
-my $o_crit_thresold=undef; # critical thresolds entry
-my $o_debug= undef; # debug mode
-my $o_fastcgi= undef; # direct fastcgi mode (without an http->fastcgi proxy)
-my $o_servername= undef; # ServerName (host header in http request)
-my $o_https= undef; # SSL (HTTPS) mode
-my $o_verify_ssl= 0; # SSL verification, False by default
-my $o_cacert_file= undef; # Path to cacert.pem file
+my $o_host= undef; # hostname
+my $o_help= undef; # want some help ?
+my $o_port= undef; # port
+my $o_url= undef; # url to use, if not the default
+my $o_user= undef; # user for auth
+my $o_pass= ''; # password for auth
+my $o_realm= ''; # password for auth
+my $o_version= undef; # print version
+my $o_warn_p_level= -1; # Min number of idle workers that will cause a warning
+my $o_crit_p_level= -1; # Min number of idle workersthat will cause an error
+my $o_warn_q_level= -1; # Number of Max Queue Reached that will cause a warning
+my $o_crit_q_level= -1; # Number of Max Queue Reached that will cause an error
+my $o_warn_m_level= -1; # Number of Max Processes Reached that will cause a warning
+my $o_crit_m_level= -1; # Number of Max Processes Reached that will cause an error
+my $o_timeout= 15; # Default 15s Timeout
+my $o_warn_threshold= undef; # warning thresholds entry
+my $o_crit_threshold= undef; # critical thresholds entry
+my $o_debug= undef; # debug mode
+my $o_fastcgi= undef; # direct fastcgi mode (without an http->fastcgi proxy)
+my $o_unixsocket= undef; # use a UNIX socket (in direct fastcgi mode)
+my $o_servername= undef; # ServerName (host header in http request)
+my $o_https= undef; # SSL (HTTPS) mode
+my $o_verify_ssl= 0; # SSL verification, False by default
+my $o_cacert_file= undef; # Path to cacert.pem file
-my $TempPath = '/tmp/'; # temp path
-my $MaxUptimeDif = 60*30; # Maximum uptime difference (seconds), default 30 minutes
+my $TempPath= '/tmp/'; # temp path
+my $MaxUptimeDif= 60*30; # Maximum uptime difference (seconds), default 30 minutes
-my $phpfpm = 'PHP-FPM'; # Could be used to store version also
+my $phpfpm= 'PHP-FPM'; # Could be used to store version also
# functions
sub show_versioninfo { print "$Name version : $Version\n"; }
sub print_usage {
- print "Usage: $Name -H [-p ] [-s servername] [-t ] [-w -c ] [-V] [-d] [-f] [-u ] [-U user -P pass -r realm]\n";
+ print "Usage: $Name -H [-p ] [-s servername] [-t ] [-w -c ] [-V] [-d] [-f] [-u ] [-U user -P pass -r realm]\n";
}
sub nagios_exit {
my ( $nickname, $status, $message, $perfdata , $silent) = @_;
my %STATUSCODE = (
- 'OK' => 0
- , 'WARNING' => 1
- , 'CRITICAL' => 2
- , 'UNKNOWN' => 3
- , 'PENDING' => 4
+ 'OK' => 0,
+ 'WARNING' => 1,
+ 'CRITICAL' => 2,
+ 'UNKNOWN' => 3,
+ 'PENDING' => 4,
);
if(!defined($silent)) {
my $output = undef;
@@ -100,13 +102,15 @@ sub help {
-H, --hostname=HOST
name or IP address of host to check
-p, --port=PORT
- Http port, or Fastcgi port when using --fastcgi
+ HTTP port, or Fastcgi port when using --fastcgi
-u, --url=URL
Specific URL (only the path part of it in fact) to use, instead of the default "/fpm-status"
-s, --servername=SERVERNAME
ServerName, (host header of HTTP request) use it if you specified an IP in -H to match the good Virtualhost in your target
-f, --fastcgi
Connect directly to php-fpm via network or local socket, using fastcgi protocol instead of HTTP.
+--unixsocket
+ Connect to php-fpm via UNIX socket, implies --fastcgi
-U, --user=user
Username for basic auth
-P, --pass=PASS
@@ -114,7 +118,7 @@ sub help {
-r, --realm=REALM
Realm for basic auth
-d, --debug
- Debug mode (show http request response)
+ Debug mode (show request response)
-t, --timeout=INTEGER
timeout in seconds (Default: $o_timeout)
-S, --ssl
@@ -136,50 +140,56 @@ sub help {
prints version number
Note :
- 3 items can be managed on this check, this is why -w and -c parameters are using 3 values thresolds
- - MIN_AVAILABLE_PROCESSES: Working with the number of available (Idle) and working process (Busy).
+ 3 items can be managed on this check, this is why -w and -c parameters are using 3 values thresholds
+ - MIN_AVAILABLE_PROCESSES: Working with the number of available (Idle) and working processes (Busy).
Generating WARNING and CRITICAL if you do not have enough Idle processes.
- - PROC_MAX_REACHED: the fpm-status report will show us how many times the max processes were reached sinc start,
- this script will record how many time this happended since last check, letting you fix thresolds for alerts
+ - PROC_MAX_REACHED: the fpm-status report will show us how many times the max processes were reached since start,
+ this script will record how many times this happened since last check, letting you fix thresholds for alerts
- QUEUE_MAX_REACHED: the php-fpm report will show us how many times the max queue was reached since start,
- this script will record how many time this happended since last check, letting you fix thresolds for alerts
+ this script will record how many times this happened since last check, letting you fix thresholds for alerts
Examples:
- This will lead to CRITICAL if you have 0 Idle process, or you have reached the max processes 2 times between last check,
- or you have reached the max queue len 5 times. A Warning will be reached for 1 Idle process only:
+ This will lead to CRITICAL if you have 0 idle processes or you reached the max processes 2 times between last check,
+ or you reached the max queue len 5 times. A warning will be generated for 1 idle process only:
check_phpfpm_status.pl -H 10.0.0.10 -u /foo/my-fpm-status -s mydomain.example.com -t 8 -w 1,-1,-1 -c 0,2,5
- this will generate WARNING and CRITICAL alerts only on the number of times you have reached the max process:
+ This will generate WARNING and CRITICAL alerts only on the number of times you have reached the max processes:
check_phpfpm_status.pl -H 10.0.0.10 -u /foo/my-fpm-status -s mydomain.example.com -t 8 -w -1,10,-1 -c -1,20,-1
- theses two equivalents will not generate any alert (if the php-fpm page is reachable) but could be used for graphics:
+ These two equivalents will not generate any alert (if the php-fpm page is reachable) but could be used for graphing:
check_phpfpm_status.pl -H 10.0.0.10 -s mydomain.example.com -w -1,-1,-1 -c -1,-1,-1
check_phpfpm_status.pl -H 10.0.0.10 -s mydomain.example.com
- And this one is a basic starting example :
+ And this one is a basic starting example:
check_phpfpm_status.pl -H 127.0.0.1 -s nagios.example.com -w 1,1,1 -c 0,2,2
All these examples used an HTTP proxy (like Nginx or Apache) in front of php-fpm. If php-fpm is listening on a tcp/ip socket
- you can also make a direct request on this port (9000 by default) using the fastcgi protocol. You'll need the FastCGI client
- tools enabled in Perl (check the README) and the command would use the -f or --fastcgi option (note that SSL or servername
- options are useless in this mode).
- This can be especially usefull if you use php-fpm in an isolated env, without the HTTP proxy support (like in a docker container):
+ you can also make a direct request on this port (9000 by default) or to an UNIX socket by using the fastcgi protocol. You'll
+ need the FastCGI client tools enabled in Perl (check the README) and the command would use the -f or --fastcgi and eventually
+ the --unixsocket option (note that SSL or servername options are useless in this mode).
+ This can be especially usefull if you use php-fpm in an isolated env, without the HTTP proxy support (like in a docker container).
+
+ Connect to an INET socket on port 9002:
check_phpfpm_status.pl -H 127.0.0.1 --fastcgi -p 9002 -w 1,1,1 -c 0,2,2
+ Connect to the UNIX socket listening in /run/php/php-fpm.sock with a non-standard URL:
+
+check_phpfpm_status.pl -H 127.0.0.1 --fastcgi --unixsocket /run/php/php-fpm.sock -u /secret-status -w 1,1,1 -c 0,2,2
+
HTTPS/SSL:
Adding --ssl you can reach an https host:
check_phpfpm_status.pl -H 10.0.0.10 -s mydomain.example.com --ssl
- Check --verify-ssl (false by defaut) --cacert and --sl for more options, like below
- (note that certificate checks never wortked on my side, add -d for full debug and
+ Check --verify-ssl (false by default) --cacert and --sl for more options, like below
+ (note that certificate checks never worked on my side, add -d for full debug and
tell me if it worked for you, you may need up to date CPAN adn openSSL libs)
check_phpfpm_status.pl -H 10.0.0.10 -s mydomain.example.com --ssl TLSv1_2 --verify-ssl 1 --cacert /etc/ssl/cacert.pem
@@ -193,6 +203,7 @@ sub check_options {
'h' => \$o_help, 'help' => \$o_help,
'd' => \$o_debug, 'debug' => \$o_debug,
'f' => \$o_fastcgi, 'fastcgi' => \$o_fastcgi,
+ 'unixsocket:s' => \$o_unixsocket,
'H:s' => \$o_host, 'hostname:s' => \$o_host,
's:s' => \$o_servername, 'servername:s' => \$o_servername,
'S:s' => \$o_https, 'ssl:s' => \$o_https,
@@ -202,12 +213,12 @@ sub check_options {
'r:s' => \$o_realm, 'realm:s' => \$o_realm,
'p:i' => \$o_port, 'port:i' => \$o_port,
'V' => \$o_version, 'version' => \$o_version,
- 'w=s' => \$o_warn_thresold, 'warn=s' => \$o_warn_thresold,
- 'c=s' => \$o_crit_thresold, 'critical=s' => \$o_crit_thresold,
+ 'w=s' => \$o_warn_threshold, 'warn=s' => \$o_warn_threshold,
+ 'c=s' => \$o_crit_threshold, 'critical=s' => \$o_crit_threshold,
't:i' => \$o_timeout, 'timeout:i' => \$o_timeout,
'x:i' => \$o_verify_ssl, 'verifyhostname:i' => \$o_verify_ssl,
'verifyssl:i' => \$o_verify_ssl,
- 'X:s' => \$o_cacert_file, 'cacert:s' => \$o_cacert_file,
+ 'X:s' => \$o_cacert_file, 'cacert:s' => \$o_cacert_file,
);
if (defined ($o_help)) {
@@ -219,34 +230,37 @@ sub check_options {
nagios_exit($phpfpm,"UNKNOWN","leaving","",1);
};
- if (defined($o_warn_thresold)) {
- ($o_warn_p_level,$o_warn_m_level,$o_warn_q_level) = split(',', $o_warn_thresold);
+ if (defined($o_warn_threshold)) {
+ ($o_warn_p_level,$o_warn_m_level,$o_warn_q_level) = split(',', $o_warn_threshold);
} else {
- $o_warn_thresold = 'undefined'
+ $o_warn_threshold = 'undefined'
}
- if (defined($o_crit_thresold)) {
- ($o_crit_p_level,$o_crit_m_level,$o_crit_q_level) = split(',', $o_crit_thresold);
+ if (defined($o_crit_threshold)) {
+ ($o_crit_p_level,$o_crit_m_level,$o_crit_q_level) = split(',', $o_crit_threshold);
} else {
- $o_crit_thresold = 'undefined'
+ $o_crit_threshold = 'undefined'
}
if (defined($o_fastcgi) && defined($o_https)) {
nagios_exit($phpfpm,"UNKNOWN","You cannot use both --fastcgi and --ssl options, we do not use http (nor https) when we use direct fastcgi access!");
}
+ if (defined($o_unixsocket) && not defined($o_fastcgi)) {
+ $o_fastcgi = 1;
+ }
if (defined($o_debug)) {
- print("\nDebug thresolds: \nWarning: ($o_warn_thresold) => Min Idle: $o_warn_p_level Max Reached :$o_warn_m_level MaxQueue: $o_warn_q_level");
- print("\nCritical ($o_crit_thresold) => : Min Idle: $o_crit_p_level Max Reached: $o_crit_m_level MaxQueue : $o_crit_q_level\n");
+ print("\nDEBUG thresholds: \nWarning: ($o_warn_threshold) => Min Idle: $o_warn_p_level Max Reached :$o_warn_m_level MaxQueue: $o_warn_q_level");
+ print("\nCritical ($o_crit_threshold) => : Min Idle: $o_crit_p_level Max Reached: $o_crit_m_level MaxQueue : $o_crit_q_level\n");
}
if ((defined($o_warn_p_level) && defined($o_crit_p_level)) &&
(($o_warn_p_level != -1) && ($o_crit_p_level != -1) && ($o_warn_p_level <= $o_crit_p_level)) ) {
- nagios_exit($phpfpm,"UNKNOWN","Check warning and critical values for IdleProcesses (1st part of thresold), warning level must be > crit level!");
+ nagios_exit($phpfpm,"UNKNOWN","Check warning and critical values for IdleProcesses (1st part of threshold), warning level must be > crit level!");
}
if ((defined($o_warn_m_level) && defined($o_crit_m_level)) &&
(($o_warn_m_level != -1) && ($o_crit_m_level != -1) && ($o_warn_m_level >= $o_crit_m_level)) ) {
- nagios_exit($phpfpm,"UNKNOWN","Check warning and critical values for MaxProcesses (2nd part of thresold), warning level must be < crit level!");
+ nagios_exit($phpfpm,"UNKNOWN","Check warning and critical values for MaxProcesses (2nd part of threshold), warning level must be < crit level!");
}
if ((defined($o_warn_q_level) && defined($o_crit_q_level)) &&
(($o_warn_q_level != -1) && ($o_crit_q_level != -1) && ($o_warn_q_level >= $o_crit_q_level)) ) {
- nagios_exit($phpfpm,"UNKNOWN","Check warning and critical values for MaxQueue (3rd part of thresold), warning level must be < crit level!");
+ nagios_exit($phpfpm,"UNKNOWN","Check warning and critical values for MaxQueue (3rd part of threshold), warning level must be < crit level!");
}
# Check compulsory attributes
if (!defined($o_host)) {
@@ -280,21 +294,37 @@ if (!defined($o_url)) {
}
if (defined($o_fastcgi)) {
- # -- FASTCGI
+ # -- FASTCGI
eval "use FCGI::Client::Connection;";
nagios_exit($phpfpm,"UNKNOWN","You need to activate FCGI::Client::Connection CPAN module for this feature: " . $@) if $@;
- eval "use IO::Socket::INET";
- nagios_exit($phpfpm,"UNKNOWN","You need to activate IO::Socket::INET CPAN module for this feature: " . $@) if $@;
- if (!defined($o_port)) {
- $o_port = 9000;
- }
- my $sock = IO::Socket::INET->new(
- PeerAddr => $override_ip,
- PeerPort => $o_port,
- );
- if (!$sock) {
- nagios_exit($phpfpm,"CRITICAL", "Cannot connect to $override_ip : $o_port !");
+ my $sock;
+ if (defined($o_unixsocket)) {
+ eval "use IO::Socket::UNIX;";
+ nagios_exit($phpfpm,"UNKNOWN","You need to activate IO::Socket::UNIX CPAN module for this feature: " . $@) if $@;
+ if (!-S $o_unixsocket) {
+ nagios_exit($phpfpm,"UNKNOWN","$o_unixsocket is not an UNIX socket");
+ }
+ $sock = IO::Socket::UNIX->new(
+ Type => SOCK_STREAM(),
+ Peer => $o_unixsocket,
+ );
+ if (!$sock) {
+ nagios_exit($phpfpm,"CRITICAL", "Cannot connect to UNIX socket $o_unixsocket !");
+ }
+ } else {
+ eval "use IO::Socket::INET;";
+ nagios_exit($phpfpm,"UNKNOWN","You need to activate IO::Socket::INET CPAN module for this feature: " . $@) if $@;
+ if (!defined($o_port)) {
+ $o_port = 9000;
+ }
+ $sock = IO::Socket::INET->new(
+ PeerAddr => $override_ip,
+ PeerPort => $o_port,
+ );
+ if (!$sock) {
+ nagios_exit($phpfpm,"CRITICAL", "Cannot connect to $override_ip : $o_port !");
+ }
}
my $fastcgiClient = FCGI::Client::Connection->new(sock => $sock);
$url = $o_url;
@@ -315,16 +345,16 @@ if (defined($o_fastcgi)) {
''
);
if (defined ($o_debug)) {
- print "\nDEBUG: FASCGI requested url\n";
+ print "\nDEBUG: FASTCGI requested url\n";
print $url;
- print "\nDEBUG: FASCGI response: STDERR\n";
+ print "\nDEBUG: FASTCGI response: STDERR\n";
print $stderr;
}
$response = fcgi_response->new($stdout, $o_debug);
} else {
- # -- HTTP
+ # -- HTTP
eval "use LWP::UserAgent;";
- nagios_exit($phpfpm,"UNKNOWN","You need to activate LWP::UserAgent CPAN module for this feature: " . $@) if $@;
+ nagios_exit($phpfpm,"UNKNOWN","You need to install LWP::UserAgent CPAN module for this feature: " . $@) if $@;
#use LWP::UserAgent;
my $proto='http://';
@@ -397,7 +427,7 @@ if (defined($o_fastcgi)) {
if (!defined($o_cacert_file)) {
eval "use Mozilla::CA;";
- nagios_exit($phpfpm,"UNKNOWN","You need to activate Mozilla::CA CPAN module for this feature, or use --cacert option: " . $@) if $@;
+ nagios_exit($phpfpm,"UNKNOWN","You need to install Mozilla::CA CPAN module for this feature, or use --cacert option: " . $@) if $@;
$o_cacert_file = Mozilla::CA::SSL_ca_file();
}
#$ssl_opts{"SSL_ca_path"} = '/usr/share/ca-certificates/mozilla/';
@@ -572,7 +602,7 @@ if ($response->is_success) {
$LastMaxListenQueue = <$FH>;
close ($FH);
if (defined ($o_debug)) {
- print ("\nDebug: data from temporary file:\n");
+ print ("\nDEBUG: data from temporary file:\n");
print ("LastUptime: $LastUptime LastAcceptedConn: $LastAcceptedConn LastMaxChildrenReached: $LastMaxChildrenReached LastMaxListenQueue: $LastMaxListenQueue \n");
}
}
diff --git a/nagios-nrpe/tasks/main.yml b/nagios-nrpe/tasks/main.yml
index 7125cbf7..065ffacc 100644
--- a/nagios-nrpe/tasks/main.yml
+++ b/nagios-nrpe/tasks/main.yml
@@ -1,5 +1,5 @@
---
-- name: packages are installed
+- name: base nrpe & plugins packages are installed
apt:
name:
- nagios-nrpe-server
@@ -12,6 +12,18 @@
tags:
- nagios-nrpe
+
+- name: custom plugin dependencies packages are installed
+ apt:
+ name:
+ - libfcgi-client-perl
+ state: present
+ when:
+ - ansible_distribution == "Debian"
+ - ansible_distribution_major_version is version('10', '>=')
+ tags:
+ - nagios-plugins
+
- name: custom configuration is present
template:
src: evolix.cfg.j2
diff --git a/nagios-nrpe/templates/evolix.cfg.j2 b/nagios-nrpe/templates/evolix.cfg.j2
index 3fda958f..7306b9cb 100644
--- a/nagios-nrpe/templates/evolix.cfg.j2
+++ b/nagios-nrpe/templates/evolix.cfg.j2
@@ -70,6 +70,11 @@ command[check_haproxy]=sudo {{ nagios_plugins_directory }}/check_haproxy_stats -
command[check_minifirewall]=sudo {{ nagios_plugins_directory }}/check_minifirewall
command[check_redis_instances]={{ nagios_plugins_directory }}/check_redis_instances
command[check_hpraid]={{ nagios_plugins_directory }}/check_hpraid
+command[check_php-fpm]={{ nagios_plugins_directory }}/check_phpfpm_multi
+command[check_php-fpm56]=sudo {{ nagios_plugins_directory }}/check_phpfpm_multi /var/lib/lxc/php56/rootfs/etc/php5/fpm/pool.d/
+command[check_php-fpm70]=sudo {{ nagios_plugins_directory }}/check_phpfpm_multi /var/lib/lxc/php70/rootfs/etc/php/7.0/fpm/pool.d/
+command[check_php-fpm73]=sudo {{ nagios_plugins_directory }}/check_phpfpm_multi /var/lib/lxc/php73/rootfs/etc/php/7.3/fpm/pool.d/
+command[check_php-fpm74]=sudo {{ nagios_plugins_directory }}/check_phpfpm_multi /var/lib/lxc/php74/rootfs/etc/php/7.4/fpm/pool.d/
# Check HTTP "many". Use this to check many websites (http, https, ports, sockets and SSL certificates).
# Beware! All checks must not take more than 10s!
diff --git a/nginx/defaults/main.yml b/nginx/defaults/main.yml
index d59da758..832fe3bb 100644
--- a/nginx/defaults/main.yml
+++ b/nginx/defaults/main.yml
@@ -4,7 +4,7 @@ nginx_minimal: False
# backward compatibility with a previously used variable
nginx_backports: "{{ nginx_jessie_backports | default(false, true) }}"
-nginx_package_name: "nginx-full"
+nginx_default_package_name: "nginx-full"
nginx_default_ipaddr_whitelist_ips: []
nginx_additional_ipaddr_whitelist_ips: []
diff --git a/nginx/tasks/main.yml b/nginx/tasks/main.yml
index e1144a39..6fe9a94e 100644
--- a/nginx/tasks/main.yml
+++ b/nginx/tasks/main.yml
@@ -1,7 +1,152 @@
---
-- include: main_minimal.yml
+- debug:
+ msg: "Nginx minimal mode has been removed, falling back to normal mode."
when: nginx_minimal
-- include: main_regular.yml
- when: not nginx_minimal
+- include: packages.yml
+
+- include: server_status_read.yml
+ tags:
+ - nginx
+
+# TODO: find a way to override the main configuration
+# without touching the main file
+
+- name: customize worker_connections
+ lineinfile:
+ dest: /etc/nginx/nginx.conf
+ regexp: '^(\s*worker_connections)\s+.+;'
+ line: ' worker_connections 1024;'
+ insertafter: 'events \{'
+ tags:
+ - nginx
+
+- name: use epoll
+ lineinfile:
+ dest: /etc/nginx/nginx.conf
+ regexp: '^(\s*use)\s+.+;'
+ line: ' use epoll;'
+ insertafter: 'events \{'
+ tags:
+ - nginx
+
+- name: Install Nginx http configuration
+ copy:
+ src: nginx/evolinux-defaults.conf
+ dest: /etc/nginx/conf.d/z-evolinux-defaults.conf
+ mode: "0640"
+ # force: yes
+ notify: reload nginx
+ tags:
+ - nginx
+
+# TODO: verify that those permissions are correct :
+# not too strict for ipaddr_whitelist
+# and not too loose for private_htpasswd
+
+- name: Copy ipaddr_whitelist
+ copy:
+ src: nginx/snippets/ipaddr_whitelist
+ dest: /etc/nginx/snippets/ipaddr_whitelist
+ owner: www-data
+ group: www-data
+ directory_mode: "0640"
+ mode: "0640"
+ force: no
+ notify: reload nginx
+ tags:
+ - nginx
+ - ips
+
+- name: Include IP address whitelist task
+ include: ip_whitelist.yml
+
+- name: Copy private_htpasswd
+ copy:
+ src: nginx/snippets/private_htpasswd
+ dest: /etc/nginx/snippets/private_htpasswd
+ owner: www-data
+ group: www-data
+ directory_mode: "0640"
+ mode: "0640"
+ force: no
+ notify: reload nginx
+ tags:
+ - nginx
+
+- name: add user:pwd to private htpasswd
+ lineinfile:
+ dest: /etc/nginx/snippets/private_htpasswd
+ line: "{{ item }}"
+ state: present
+ with_items: "{{ nginx_private_htpasswd_present }}"
+ notify: reload nginx
+ tags:
+ - nginx
+
+- name: remove user:pwd from private htpasswd
+ lineinfile:
+ dest: /etc/nginx/snippets/private_htpasswd
+ line: "{{ item }}"
+ state: absent
+ with_items: "{{ nginx_private_htpasswd_absent }}"
+ notify: reload nginx
+ tags:
+ - nginx
+
+- name: nginx vhost is installed
+ template:
+ src: "{{ nginx_default_template_regular }}"
+ dest: /etc/nginx/sites-available/evolinux-default.conf
+ mode: "0640"
+ force: "{{ nginx_force_default_template | default(False) }}"
+ notify: reload nginx
+ tags:
+ - nginx
+
+- name: default vhost is enabled
+ file:
+ src: /etc/nginx/sites-available/evolinux-default.conf
+ dest: /etc/nginx/sites-enabled/default
+ state: link
+ force: yes
+ notify: reload nginx
+ when: nginx_evolinux_default_enabled
+ tags:
+ - nginx
+
+- include: server_status_write.yml
+ tags:
+ - nginx
+
+- name: Verify that the service is enabled and started
+ service:
+ name: nginx
+ enabled: yes
+ state: started
+ tags:
+ - nginx
+
+- name: Check if Munin is installed
+ stat:
+ path: /etc/munin/plugin-conf.d/munin-node
+ check_mode: no
+ register: stat_munin_node
+ tags:
+ - nginx
+ - munin
+
+- include: munin_vhost.yml
+ when: stat_munin_node.stat.exists
+ tags:
+ - nginx
+ - munin
+
+- include: munin_graphs.yml
+ when: stat_munin_node.stat.exists
+ tags:
+ - nginx
+ - munin
+
+- include: logrotate.yml
diff --git a/nginx/tasks/main_minimal.yml b/nginx/tasks/main_minimal.yml
deleted file mode 100644
index 798cf055..00000000
--- a/nginx/tasks/main_minimal.yml
+++ /dev/null
@@ -1,40 +0,0 @@
----
-- name: Ensure Nginx is installed
- apt:
- name:
- - nginx-light
- - ssl-cert
- state: present
- notify: reload nginx
- tags:
- - nginx
- - packages
-
-- name: Copy default vhost
- template:
- src: "{{ nginx_default_template_minimal }}"
- dest: /etc/nginx/sites-available/evolinux-default.minimal.conf
- mode: 0644
- force: "{{ nginx_force_default_template | default(False) }}"
- notify: reload nginx
- tags:
- - nginx
- - packages
-
-- name: Enable default vhost
- file:
- src: /etc/nginx/sites-available/evolinux-default.minimal.conf
- dest: /etc/nginx/sites-enabled/default
- state: link
- notify: reload nginx
- tags:
- - nginx
- - packages
-
-- name: Ensure Nginx is enabled
- service:
- name: nginx
- state: started
- enabled: yes
- tags:
- - nginx
diff --git a/nginx/tasks/main_regular.yml b/nginx/tasks/main_regular.yml
deleted file mode 100644
index c7989bee..00000000
--- a/nginx/tasks/main_regular.yml
+++ /dev/null
@@ -1,182 +0,0 @@
----
-
-- include: packages.yml
-
-- include: server_status_read.yml
- tags:
- - nginx
-
-# TODO: find a way to override the main configuration
-# without touching the main file
-
-- name: customize worker_connections
- lineinfile:
- dest: /etc/nginx/nginx.conf
- regexp: '^(\s*worker_connections)\s+.+;'
- line: ' worker_connections 1024;'
- insertafter: 'events \{'
- tags:
- - nginx
-
-- name: use epoll
- lineinfile:
- dest: /etc/nginx/nginx.conf
- regexp: '^(\s*use)\s+.+;'
- line: ' use epoll;'
- insertafter: 'events \{'
- tags:
- - nginx
-
-- name: Install Nginx http configuration
- copy:
- src: nginx/evolinux-defaults.conf
- dest: /etc/nginx/conf.d/z-evolinux-defaults.conf
- mode: "0640"
- # force: yes
- notify: reload nginx
- tags:
- - nginx
-
-# TODO: verify that those permissions are correct :
-# not too strict for ipaddr_whitelist
-# and not too loose for private_htpasswd
-
-- name: Copy ipaddr_whitelist
- copy:
- src: nginx/snippets/ipaddr_whitelist
- dest: /etc/nginx/snippets/ipaddr_whitelist
- owner: www-data
- group: www-data
- directory_mode: "0640"
- mode: "0640"
- force: no
- notify: reload nginx
- tags:
- - nginx
- - ips
-
-- name: Include IP address whitelist task
- include: ip_whitelist.yml
-
-- name: Copy private_htpasswd
- copy:
- src: nginx/snippets/private_htpasswd
- dest: /etc/nginx/snippets/private_htpasswd
- owner: www-data
- group: www-data
- directory_mode: "0640"
- mode: "0640"
- force: no
- notify: reload nginx
- tags:
- - nginx
-
-- name: add user:pwd to private htpasswd
- lineinfile:
- dest: /etc/nginx/snippets/private_htpasswd
- line: "{{ item }}"
- state: present
- with_items: "{{ nginx_private_htpasswd_present }}"
- notify: reload nginx
- tags:
- - nginx
-
-- name: remove user:pwd from private htpasswd
- lineinfile:
- dest: /etc/nginx/snippets/private_htpasswd
- line: "{{ item }}"
- state: absent
- with_items: "{{ nginx_private_htpasswd_absent }}"
- notify: reload nginx
- tags:
- - nginx
-
-- name: nginx vhost is installed
- template:
- src: "{{ nginx_default_template_regular }}"
- dest: /etc/nginx/sites-available/evolinux-default.conf
- mode: "0640"
- force: "{{ nginx_force_default_template | default(False) }}"
- notify: reload nginx
- tags:
- - nginx
-
-- name: default vhost is enabled
- file:
- src: /etc/nginx/sites-available/evolinux-default.conf
- dest: /etc/nginx/sites-enabled/default
- state: link
- force: yes
- notify: reload nginx
- when: nginx_evolinux_default_enabled
- tags:
- - nginx
-
-- include: server_status_write.yml
- tags:
- - nginx
-
-# - block:
-# - name: generate random string for phpmyadmin suffix
-# command: "apg -a 1 -M N -n 1"
-# changed_when: False
-# register: random_phpmyadmin_suffix
-#
-# - name: overwrite nginx_phpmyadmin_suffix
-# set_fact:
-# nginx_phpmyadmin_suffix: "{{ random_phpmyadmin_suffix.stdout }}"
-# when: nginx_phpmyadmin_suffix == ""
-#
-# - name: replace phpmyadmin suffix in default site index
-# replace:
-# dest: /var/www/index.html
-# regexp: '__PHPMYADMIN_SUFFIX__'
-# replace: "{{ nginx_phpmyadmin_suffix }}"
-#
-# - block:
-# - name: generate random string for serverstatus suffix
-# command: "apg -a 1 -M N -n 1"
-# changed_when: False
-# register: random_serverstatus_suffix
-#
-# - name: overwrite nginx_serverstatus_suffix
-# set_fact:
-# nginx_serverstatus_suffix: "{{ random_phpmyadmin_suffix.stdout }}"
-# when: nginx_serverstatus_suffix == ""
-#
-# - name: replace server-status suffix in default site index
-# replace:
-# dest: /var/www/index.html
-# regexp: '__SERVERSTATUS_SUFFIX__'
-# replace: "{{ nginx_serverstatus_suffix }}"
-
-- name: Verify that the service is enabled and started
- service:
- name: nginx
- enabled: yes
- state: started
- tags:
- - nginx
-
-- name: Check if Munin is installed
- stat:
- path: /etc/munin/plugin-conf.d/munin-node
- check_mode: no
- register: stat_munin_node
- tags:
- - nginx
- - munin
-
-- include: munin_vhost.yml
- when: stat_munin_node.stat.exists
- tags:
- - nginx
- - munin
-
-- include: munin_graphs.yml
- when: stat_munin_node.stat.exists
- tags:
- - nginx
- - munin
-
-- include: logrotate.yml
diff --git a/nginx/tasks/packages.yml b/nginx/tasks/packages.yml
index 76350424..05c033b4 100644
--- a/nginx/tasks/packages.yml
+++ b/nginx/tasks/packages.yml
@@ -1,3 +1,9 @@
+
+
+- set_fact:
+ nginx_package_name_default: nginx-light
+ when: nginx_minimal
+
- include: packages_backports.yml
when: nginx_backports
@@ -5,12 +11,12 @@
- name: Ensure Nginx is installed
apt:
- name: "{{ nginx_package_name }}"
+ name: "{{ nginx_package_name | default(nginx_default_package_name) }}"
state: present
tags:
- nginx
- packages
-
+
- name: Ensure nginx service is running as configured.
service:
name: nginx
diff --git a/nginx/templates/evolinux-default.conf.j2 b/nginx/templates/evolinux-default.conf.j2
index ab4f8803..1a385e17 100644
--- a/nginx/templates/evolinux-default.conf.j2
+++ b/nginx/templates/evolinux-default.conf.j2
@@ -44,6 +44,11 @@ server {
fastcgi_pass unix:/var/run/munin/spawn-fcgi-munin-graph.sock;
include fastcgi_params;
}
+
+ location /server-status-{{ nginx_serverstatus_suffix | mandatory }} {
+ stub_status on;
+ access_log off;
+ }
}
server {
diff --git a/nginx/templates/evolinux-default.minimal.conf.j2 b/nginx/templates/evolinux-default.minimal.conf.j2
deleted file mode 100644
index 919a7a1f..00000000
--- a/nginx/templates/evolinux-default.minimal.conf.j2
+++ /dev/null
@@ -1,31 +0,0 @@
-server {
- listen 80 default_server;
- listen [::]:80 default_server;
-
- listen 443 ssl default_server;
- listen [::]:443 ssl default_server;
-
- if ($host != "{{ ansible_fqdn }}") {
- rewrite ^ https://{{ ansible_fqdn }}$request_uri permanent;
- }
-
- include snippets/snakeoil.conf;
-
- if ($https != "on") {
- return 301 https://{{ ansible_fqdn }}$request_uri;
- }
-
- root /var/www/;
-
- location /munin {
- alias /var/cache/munin/www;
- }
-
- index index.html;
-
- server_name _;
-
- location / {
- try_files $uri $uri/ =404;
- }
-}
diff --git a/nodejs/files/yarnpkg.gpg.key b/nodejs/files/yarnpkg.gpg.key
index 4d5f77ef..530eb5e3 100644
--- a/nodejs/files/yarnpkg.gpg.key
+++ b/nodejs/files/yarnpkg.gpg.key
@@ -1,220 +1,243 @@
------BEGIN PGP PUBLIC KEY BLOCK-----
-
-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
-=nmWU
------END PGP PUBLIC KEY BLOCK-----
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+=IgpF
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/packweb-apache/meta/main.yml b/packweb-apache/meta/main.yml
index f98442a6..cd4b4f94 100644
--- a/packweb-apache/meta/main.yml
+++ b/packweb-apache/meta/main.yml
@@ -24,8 +24,8 @@ dependencies:
- { role: evolix/squid, squid_localproxy_enable: True }
- { role: evolix/mysql, when: packweb_mysql_variant == "debian" }
- { role: evolix/mysql-oracle, when: packweb_mysql_variant == "oracle" }
- - { role: evolix/lxc-php, lxc_php_version: php56, when: "'php56' in packweb_multiphp_versions" }
- - { role: evolix/lxc-php, lxc_php_version: php70, when: "'php70' in packweb_multiphp_versions" }
- - { role: evolix/lxc-php, lxc_php_version: php73, when: "'php73' in packweb_multiphp_versions" }
+ - { role: evolix/lxc-php, lxc_php_version: php56, lxc_php_create_mysql_link: True, when: "'php56' in packweb_multiphp_versions" }
+ - { role: evolix/lxc-php, lxc_php_version: php70, lxc_php_create_mysql_link: True, when: "'php70' in packweb_multiphp_versions" }
+ - { role: evolix/lxc-php, lxc_php_version: php73, lxc_php_create_mysql_link: True, when: "'php73' in packweb_multiphp_versions" }
- { role: evolix/webapps/evoadmin-web, evoadmin_enable_vhost: "{{ packweb_enable_evoadmin_vhost }}", evoadmin_multiphp_versions: "{{ packweb_multiphp_versions }}" }
- { role: evolix/evoacme }
diff --git a/postfix/templates/evolinux_main.cf.j2 b/postfix/templates/evolinux_main.cf.j2
index b4499958..0c871546 100644
--- a/postfix/templates/evolinux_main.cf.j2
+++ b/postfix/templates/evolinux_main.cf.j2
@@ -20,6 +20,8 @@ smtp_tls_protocols=!SSLv2,!SSLv3
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtp_tls_loglevel = 1
+smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
+
{% if postfix_slow_transport_include == True %}
# Slow transports configuration
minimal_backoff_time = 2h
diff --git a/postfix/templates/packmail_main.cf.j2 b/postfix/templates/packmail_main.cf.j2
index bee7fe53..397abc0d 100644
--- a/postfix/templates/packmail_main.cf.j2
+++ b/postfix/templates/packmail_main.cf.j2
@@ -384,6 +384,7 @@ strict_rfc821_envelopes = yes
#par defaut, = yes
#smtpd_reject_unlisted_recipient =
+smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
#######################
# Section : Chiffrement
diff --git a/postgresql/files/postgresql.service.override.conf b/postgresql/files/postgresql.service.override.conf
new file mode 100644
index 00000000..8e79f883
--- /dev/null
+++ b/postgresql/files/postgresql.service.override.conf
@@ -0,0 +1,5 @@
+[Service]
+OOMScoreAdjust=-1000
+Environment=PG_OOM_ADJUST_FILE=/proc/self/oom_score_adj
+Environment=PG_MASTER_OOM_SCORE_ADJ=-1000
+Environment=PG_CHILD_OOM_SCORE_ADJ=0
diff --git a/postgresql/tasks/config.yml b/postgresql/tasks/config.yml
index dc3fc1b1..83b10e25 100644
--- a/postgresql/tasks/config.yml
+++ b/postgresql/tasks/config.yml
@@ -1,8 +1,14 @@
---
+- name: Ensure /etc/systemd/system/postgresql.service.d exists
+ file:
+ path: /etc/systemd/system/postgresql@.service.d
+ state: directory
+ recurse: true
+
- name: Override PostgreSQL systemd unit
- template:
- src: postgresql.service.j2
- dest: /etc/systemd/system/multi-user.target.wants/postgresql.service
+ copy:
+ src: postgresql.service.override.conf
+ dest: /etc/systemd/system/postgresql@.service.d/override.conf
force: yes
notify:
- reload systemd
diff --git a/postgresql/templates/postgresql.service.j2 b/postgresql/templates/postgresql.service.j2
deleted file mode 100644
index b5bd2730..00000000
--- a/postgresql/templates/postgresql.service.j2
+++ /dev/null
@@ -1,19 +0,0 @@
-# systemd service for managing all PostgreSQL clusters on the system. This
-# service is actually a systemd target, but we are using a service since
-# targets cannot be reloaded.
-
-[Unit]
-Description=PostgreSQL RDBMS
-
-[Service]
-OOMScoreAdjust=-1000
-Environment=PG_OOM_ADJUST_FILE=/proc/self/oom_score_adj
-Environment=PG_MASTER_OOM_SCORE_ADJ=-1000
-Environment=PG_CHILD_OOM_SCORE_ADJ=0
-Type=oneshot
-ExecStart=/bin/true
-ExecReload=/bin/true
-RemainAfterExit=on
-
-[Install]
-WantedBy=multi-user.target
diff --git a/redis/defaults/main.yml b/redis/defaults/main.yml
index 5cd311ce..93bbc741 100644
--- a/redis/defaults/main.yml
+++ b/redis/defaults/main.yml
@@ -9,10 +9,10 @@ redis_port: 6379
redis_bind_interface: 127.0.0.1
redis_socket_enabled: True
-redis_socket_dir_prefix: '/var/run/redis'
+redis_socket_dir_prefix: '/run/redis'
redis_socket_perms: 770
-redis_pid_dir_prefix: "/var/run/redis"
+redis_pid_dir_prefix: "/run/redis"
redis_timeout: 300
diff --git a/redis/tasks/instance-log2mail.yml b/redis/tasks/instance-log2mail.yml
index 8f853199..a20e1a0a 100644
--- a/redis/tasks/instance-log2mail.yml
+++ b/redis/tasks/instance-log2mail.yml
@@ -8,7 +8,7 @@
mode: "0640"
create: yes
marker: "# {mark} ANSIBLE MANAGED RULES FOR INSTANCE {{ redis_instance_name }}"
- content: |
+ block: |
file = {{ redis_log_dir }}/redis-server.log
pattern = "Cannot allocate memory"
mailto = {{ log2mail_alert_email or general_alert_email | mandatory }}
diff --git a/redis/tasks/instance-server.yml b/redis/tasks/instance-server.yml
index 5f4b2601..1b491d9a 100644
--- a/redis/tasks/instance-server.yml
+++ b/redis/tasks/instance-server.yml
@@ -70,7 +70,21 @@
tags:
- redis
-- name: "Instance '{{ redis_instance_name }}' other directories are present"
+- name: "Instance '{{ redis_instance_name }}' socket/pid directories are present"
+ file:
+ dest: "{{ item }}"
+ mode: "0755"
+ owner: "redis-{{ redis_instance_name }}"
+ group: "redis-{{ redis_instance_name }}"
+ follow: yes
+ state: directory
+ with_items:
+ - "{{ redis_pid_dir }}"
+ - "{{ redis_socket_dir }}"
+ tags:
+ - redis
+
+- name: "Instance '{{ redis_instance_name }}' data/log directories are present"
file:
dest: "{{ item }}"
mode: "0750"
@@ -79,8 +93,6 @@
follow: yes
state: directory
with_items:
- - "{{ redis_pid_dir }}"
- - "{{ redis_socket_dir }}"
- "{{ redis_data_dir }}"
- "{{ redis_log_dir }}"
tags:
@@ -110,7 +122,7 @@
tags:
- redis
-- name: Systemd template for redis instances is installed (Debian 9 or later)
+- name: Systemd template for redis instances is installed (Debian 9)
template:
src: 'redis-server@stretch.service.j2'
dest: '/etc/systemd/system/redis-server@.service'
@@ -119,7 +131,20 @@
group: "root"
when:
- ansible_distribution == "Debian"
- - ansible_distribution_major_version is version('9', '>=')
+ - ansible_distribution_major_version is version('9', '=')
+ tags:
+ - redis
+
+- name: Systemd template for redis instances is installed (Debian 10 or later)
+ template:
+ src: 'redis-server@buster.service.j2'
+ dest: '/etc/systemd/system/redis-server@.service'
+ mode: "0644"
+ owner: "root"
+ group: "root"
+ when:
+ - ansible_distribution == "Debian"
+ - ansible_distribution_major_version is version('10', '>=')
tags:
- redis
diff --git a/redis/templates/munin-plugin-instances.conf.j2 b/redis/templates/munin-plugin-instances.conf.j2
index 7e6a00ac..3a0551fc 100644
--- a/redis/templates/munin-plugin-instances.conf.j2
+++ b/redis/templates/munin-plugin-instances.conf.j2
@@ -4,5 +4,5 @@
env.title_prefix Instance {{ redis_instance_name }}
env.port {{ redis_port }}
{% if redis_password %}
-env.password {{ redis_password }}
+env.password {{ redis_password | replace("#", "\#") }}
{% endif %}
diff --git a/redis/templates/redis-server@buster.service.j2 b/redis/templates/redis-server@buster.service.j2
new file mode 100644
index 00000000..3742e589
--- /dev/null
+++ b/redis/templates/redis-server@buster.service.j2
@@ -0,0 +1,35 @@
+[Unit]
+Description=Advanced key-value store
+After=network.target
+
+[Service]
+Type=forking
+ExecStart=/usr/bin/redis-server {{ redis_conf_dir_prefix }}-%i/redis.conf
+PIDFile=/run/redis-%i/redis-server.pid
+TimeoutStopSec=0
+Restart=always
+User=redis-%i
+Group=redis-%i
+RuntimeDirectory=redis-%i
+
+ExecStop=/bin/kill -s TERM $MAINPID
+
+UMask=007
+PrivateTmp=yes
+LimitNOFILE=65535
+PrivateDevices=yes
+ProtectHome={{ redis_data_dir_prefix is match('/home') | ternary('no', 'yes') }}
+ReadOnlyDirectories=/
+ReadWriteDirectories=-{{ redis_data_dir_prefix }}-%i
+ReadWriteDirectories=-{{ redis_log_dir_prefix }}-%i
+ReadWriteDirectories=-{{ redis_pid_dir_prefix }}-%i
+ReadWriteDirectories=-{{ redis_socket_dir_prefix }}-%i
+CapabilityBoundingSet=~CAP_SYS_PTRACE
+
+# redis-server writes its own config file when in cluster mode so we allow
+# writing there (NB. ProtectSystem=true over ProtectSystem=full)
+ProtectSystem=true
+ReadWriteDirectories=-{{ redis_conf_dir_prefix }}-%i
+
+[Install]
+WantedBy=multi-user.target
diff --git a/redis/templates/redis-server@stretch.service.j2 b/redis/templates/redis-server@stretch.service.j2
index 3f14a296..5126caad 100644
--- a/redis/templates/redis-server@stretch.service.j2
+++ b/redis/templates/redis-server@stretch.service.j2
@@ -5,7 +5,7 @@ After=network.target
[Service]
Type=forking
ExecStart=/usr/bin/redis-server {{ redis_conf_dir_prefix }}-%i/redis.conf
-PIDFile=/var/run/redis-%i/redis-server.pid
+PIDFile=/run/redis-%i/redis-server.pid
TimeoutStopSec=0
Restart=always
User=redis-%i
diff --git a/spamassasin/files/spamassassin.cf b/spamassasin/files/spamassassin.cf
index 821f51d4..56608559 100644
--- a/spamassasin/files/spamassassin.cf
+++ b/spamassasin/files/spamassassin.cf
@@ -94,7 +94,7 @@ score ADDRESS_IN_SUBJECT 0.1
score HELO_LH_HOME 1.0
#internal_networks 192.168.XXX/24
-trusted_networks 62.212.111.216 88.179.18.233 85.118.59.50 31.170.8.0/21
+trusted_networks 62.212.121.90 82.65.34.85 31.170.8.0/21
#score ALL_TRUSTED 0.3
score HELO_DYNAMIC_IPADDR 0.3