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