From 06a1e919da1fbf5e81a1c82b6362f08b889e66a9 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Fri, 20 Sep 2019 14:21:42 +0200 Subject: [PATCH 01/52] Add UFW stack --- docs/wo.8 | 4 +-- install | 8 ++--- wo/cli/plugins/stack.py | 24 +++++++++++++ wo/cli/plugins/stack_pref.py | 18 ++++++++++ wo/cli/templates/ufw.mustache | 65 +++++++++++++++++++++++++++++++++++ 5 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 wo/cli/templates/ufw.mustache diff --git a/docs/wo.8 b/docs/wo.8 index 47b17f6..b6f23b7 100644 --- a/docs/wo.8 +++ b/docs/wo.8 @@ -11,9 +11,9 @@ wo stack [ status | start | stop | reload | restart ] [--all | --nginx | --php | .TP wo site [ list | info | show | enable | disable | edit | cd | show ] [ example.com ] .TP -wo site create example.com [ --html | --php | --php73 | --mysql] [[--wp | --wpsubdir | --wpsubdomain ] [--wpsc | --wpfc | --wpredis | --letsencrypt/-le/--letsencrypt=subdomain/wildcard][--dns/--dns=dns_cf/dns_do]] +wo site create example.com [ --html | --php | --php73 | --mysql] [[--wp | --wpsubdir | --wpsubdomain ] [--wpsc | --wpfc | --wpredis | --letsencrypt/-le/--letsencrypt=wildcard][--dns/--dns=dns_cf/dns_do]] .TP -wo site update example.com [ --php | --php73 |--mysql] [[--wp | --wpsubdir | --wpsubdomain ] [--wpsc | --wpfc | --wpredis ] [--password] [-le/--letsencrypt/--letsencrypt=on/off/subdomain/renew/wildcard/clean/purge] [--dns/--dns=dns_cf/dns_do]] +wo site update example.com [ --php | --php73 |--mysql] [[--wp | --wpsubdir | --wpsubdomain ] [--wpsc | --wpfc | --wpredis ] [--password] [-le/--letsencrypt/--letsencrypt=on/off/wildcard/clean/purge] [--dns/--dns=dns_cf/dns_do]] .TP wo site delete example.com [--db | --files | --all | --no-prompt | --force/-f ] .TP diff --git a/install b/install index b81a667..a1c2c0c 100755 --- a/install +++ b/install @@ -669,7 +669,7 @@ wo_backup_ee() { } wo_backup_wo() { - /bin/tar -I pigz -cf "$WO_BACKUP_FILE" "$WO_NGINX" /etc/wo /var/lib/wo "$WO_LE" + /bin/tar -I pigz -cf "$WO_BACKUP_FILE" /etc/nginx /etc/wo /var/lib/wo "$WO_LE" return 0 } @@ -856,9 +856,9 @@ else fi _run wo_install_dep "Installing wo dependencies" _run wo_timesync - if [ "$ufw" = "y" ]; then - _run wo_ufw_setup "Configuring UFW" - fi + #if [ "$ufw" = "y" ]; then + # _run wo_ufw_setup "Configuring UFW" + #fi # skip steps if travis if [ -z "$wo_travis" ]; then _run wo_dist_upgrade diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index 5f0b022..27da795 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -88,6 +88,8 @@ class WOStackController(CementBaseController): dict(help='Install Fail2ban stack', action='store_true')), (['--clamav'], dict(help='Install ClamAV stack', action='store_true')), + (['--ufw'], + dict(help='Install UFW stack', action='store_true')), (['--sendmail'], dict(help='Install Sendmail stack', action='store_true')), (['--utils'], @@ -129,11 +131,13 @@ class WOStackController(CementBaseController): (not pargs.adminer) and (not pargs.utils) and (not pargs.redis) and (not pargs.proftpd) and (not pargs.extplorer) and (not pargs.clamav) and + (not pargs.ufw) and (not pargs.phpredisadmin) and (not pargs.sendmail) and (not pargs.php73)): pargs.web = True pargs.admin = True pargs.fail2ban = True + pargs.ufw = True if pargs.all: pargs.web = True @@ -164,6 +168,7 @@ class WOStackController(CementBaseController): if pargs.security: pargs.fail2ban = True pargs.clamav = True + pargs.ufw = True # Nginx if pargs.nginx: @@ -270,6 +275,11 @@ class WOStackController(CementBaseController): Log.debug(self, "ClamAV already installed") Log.info(self, "ClamAV already installed") + # UFW + if pargs.ufw: + Log.debug(self, "Setting apt_packages variable for UFW") + apt_packages = apt_packages + ["ufw"] + # sendmail if pargs.sendmail: Log.debug(self, "Setting apt_packages variable for Sendmail") @@ -518,6 +528,7 @@ class WOStackController(CementBaseController): (not pargs.adminer) and (not pargs.utils) and (not pargs.redis) and (not pargs.proftpd) and (not pargs.extplorer) and (not pargs.clamav) and + (not pargs.ufw) and (not pargs.phpredisadmin) and (not pargs.sendmail) and (not pargs.php73)): pargs.web = True @@ -551,6 +562,7 @@ class WOStackController(CementBaseController): if pargs.security: pargs.fail2ban = True pargs.clamav = True + pargs.ufw = True # NGINX if pargs.nginx: @@ -620,6 +632,11 @@ class WOStackController(CementBaseController): Log.debug(self, "Remove apt_packages variable for ProFTPd") apt_packages = apt_packages + ["proftpd-basic"] + # UFW + if pargs.ufw: + Log.debug(self, "Remove apt_packages variable for UFW") + apt_packages = apt_packages + ["ufw"] + # WPCLI if pargs.wpcli: Log.debug(self, "Removing package variable of WPCLI ") @@ -739,6 +756,7 @@ class WOStackController(CementBaseController): (not pargs.adminer) and (not pargs.utils) and (not pargs.redis) and (not pargs.proftpd) and (not pargs.extplorer) and (not pargs.clamav) and + (not pargs.ufw) and (not pargs.phpredisadmin) and (not pargs.sendmail) and (not pargs.php73)): pargs.web = True @@ -771,6 +789,7 @@ class WOStackController(CementBaseController): if pargs.security: pargs.fail2ban = True pargs.clamav = True + pargs.ufw = True # NGINX if pargs.nginx: @@ -829,6 +848,11 @@ class WOStackController(CementBaseController): if WOAptGet.is_installed(self, 'clamav'): apt_packages = apt_packages + WOVariables.wo_clamav + # UFW + if pargs.ufw: + Log.debug(self, "Remove apt_packages variable for UFW") + apt_packages = apt_packages + ["ufw"] + # sendmail if pargs.sendmail: Log.debug(self, "Setting apt_packages variable for Sendmail") diff --git a/wo/cli/plugins/stack_pref.py b/wo/cli/plugins/stack_pref.py index 3aa4c24..1d05be8 100644 --- a/wo/cli/plugins/stack_pref.py +++ b/wo/cli/plugins/stack_pref.py @@ -975,6 +975,24 @@ def post_pref(self, apt_packages, packages, upgrade=False): msg="Adding ProFTPd into Git") WOService.reload_service(self, 'proftpd') + if "ufw" in apt_packages: + # check if ufw is already enabled + if not WOFileUtils.grep(self, + '/etc/ufw/ufw.conf', 'ENABLED=yes'): + Log.wait(self, "Configuring UFW") + # check if ufw script is already created + if not os.path.isfile("/opt/ufw.sh"): + data = dict() + WOTemplate.render(self, '/opt/ufw.sh', + 'ufw.mustache', + data, overwrite=False) + WOFileUtils.chmod(self, "/opt/ufw.sh", 0o700) + # setup ufw rules + WOShellExec.cmd_exec(self, "bash /opt/ufw.sh") + Log.valide(self, "Configuring UFW") + else: + Log.info(self, "UFW is already installed and enabled") + # Redis configuration if "redis-server" in apt_packages: if os.path.isfile("/etc/nginx/conf.d/upstream.conf"): diff --git a/wo/cli/templates/ufw.mustache b/wo/cli/templates/ufw.mustache new file mode 100644 index 0000000..aa8d344 --- /dev/null +++ b/wo/cli/templates/ufw.mustache @@ -0,0 +1,65 @@ +#!/bin/bash + +wo_ufw_setup() { + # get custom ssh port + if [ -f /etc/ssh/sshd_config ]; then + CURRENT_SSH_PORT=$(grep "Port" /etc/ssh/sshd_config | awk -F " " '{print $2}') + fi + # define firewall rules + if ! grep -q "LOGLEVEL=low" /etc/ufw/ufw.conf; then + ufw logging low + fi + if ! grep -q 'DEFAULT_OUTPUT_POLICY="ACCEPT"' /etc/default/ufw; then + ufw default allow outgoing + fi + if ! grep -q 'DEFAULT_INPUT_POLICY="DROP"' /etc/default/ufw; then + ufw default deny incoming + fi + if ! grep -q "\-\-dport 22 -j" /etc/ufw/user.rules; then + # default ssh port + ufw limit 22 + fi + + # custom ssh port + if [ "$CURRENT_SSH_PORT" != "22" ]; then + if ! grep -q "\-\-dport $CURRENT_SSH_PORT -j" /etc/ufw/user.rules; then + ufw limit "$CURRENT_SSH_PORT" + fi + fi + + # nginx + if ! grep -q "\-\-dport 80 -j" /etc/ufw/user.rules; then + # http + ufw allow http + fi + if ! grep -q "\-\-dport 443 -j" /etc/ufw/user.rules; then + # https + ufw allow https + fi + + # ntp + if ! grep -q "\-\-dport 123 -j" /etc/ufw/user.rules; then + ufw allow 123 + fi + + if ! grep -q "\-\-dport 22222 -j" /etc/ufw/user.rules; then + # wordops backend + ufw limit 22222 + fi + # enable ufw + if [ -n "$CURRENT_SSH_PORT" ]; then + ufw --force enable + fi + + # remove ufw from syslog + if [ -f /etc/rsyslog.d/20-ufw.conf ]; then + sed -i 's/\#\& stop/\& stop/' /etc/rsyslog.d/20-ufw.conf + service rsyslog restart + fi +} + +if { wo_ufw_setup; }; then + exit 0 +else + exit 1 +fi From 75ec3b7a314e9230f4f548c81cd900bc3d3807eb Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Fri, 20 Sep 2019 16:24:26 +0200 Subject: [PATCH 02/52] Add more tests into travis --- install | 35 ++--------------------------------- tests/travis.sh | 43 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 36 deletions(-) diff --git a/install b/install index a1c2c0c..e425ff1 100755 --- a/install +++ b/install @@ -623,37 +623,6 @@ wo_update_latest() { } -# Do git intialisation -wo_git_init() { - # Nginx under git version control - [ -d /etc/nginx ] && { - cd /etc/nginx || exit 1 - [ ! -d /etc/nginx/.git ] && { - git init - } - git add -A . - git commit -am "Updated Nginx" - } - # WordOps under git version control - [ -d /etc/wo ] && { - cd /etc/wo || exit 1 - [ ! -d /etc/wo/.git ] && { - git init - } - git add -A . - git commit -am "Installed/Updated to WordOps" - } - # PHP under git version control - [ -d /etc/php ] && { - cd /etc/php || exit 1 - [ ! -d /etc/php/.git ] && { - git init - } - git add -A . - git commit -am "Updated PHP" - } -} - wo_backup_ee() { if [ -d /etc/nginx ]; then local EE_NGINX="/etc/nginx" @@ -860,10 +829,10 @@ else # _run wo_ufw_setup "Configuring UFW" #fi # skip steps if travis + _run wo_download "Downloading WordOps" if [ -z "$wo_travis" ]; then _run wo_dist_upgrade - _run wo_download "Downloading WordOps" - wo_git_config + wo_git_config _run wo_install "Installing WordOps" else _run wo_install_travis "Installing WordOps" diff --git a/tests/travis.sh b/tests/travis.sh index 5bc2929..c32dfca 100644 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -17,7 +17,7 @@ exit_script() { echo -e "${CGREEN}#############################################${CEND}" echo -e ' stack install ' echo -e "${CGREEN}#############################################${CEND}" -stack_list='nginx php php73 mysql redis fail2ban clamav proftpd netdata phpmyadmin composer dashboard extplorer adminer redis phpredisadmin mysqltuner utils' +stack_list='nginx php php73 mysql redis fail2ban clamav proftpd netdata phpmyadmin composer dashboard extplorer adminer redis phpredisadmin mysqltuner utils ufw' for stack in $stack_list; do echo -ne " Installing $stack [..]\r" if { @@ -150,9 +150,46 @@ for stack in $stack_upgrade; do fi done +echo -e "${CGREEN}#############################################${CEND}" +echo -e ' wo clean ' +echo -e "${CGREEN}#############################################${CEND}" +stack_clean='fastcgi redis opcache all' +for stack in $stack_clean; do + echo -ne " cleaning $stack cache [..]\r" + if { + wo stack clean --${stack} + } >> /var/log/wo/test.log; then + echo -ne " cleaning $stack cache [${CGREEN}OK${CEND}]\\r" + echo -ne '\n' + else + echo -e " cleaning $stack cache [${CRED}FAIL${CEND}]" + echo -ne '\n' + exit_script + + fi +done + +echo -e "${CGREEN}#############################################${CEND}" +echo -e ' wo stack purge ' +echo -e "${CGREEN}#############################################${CEND}" +stack_purge='nginx php php73 mysql redis fail2ban clamav proftpd netdata phpmyadmin composer dashboard extplorer adminer redis ufw' +for stack in $stack_purge; do + echo -ne " purging $stack [..]\r" + if { + wo stack purge --${stack} --force + } >> /var/log/wo/test.log; then + echo -ne " purging $stack [${CGREEN}OK${CEND}]\\r" + echo -ne '\n' + else + echo -e " purging $stack [${CRED}FAIL${CEND}]" + echo -ne '\n' + exit_script + + fi +done + echo -e "${CGREEN}#############################################${CEND}" echo -e ' various informations ' echo -e "${CGREEN}#############################################${CEND}" wp --allow-root --info -wo site info wp1.com -wo stack purge --all --force +wo site info wp.net \ No newline at end of file From c9eef0dc687922c149c55b512707b8a012d97218 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Fri, 20 Sep 2019 17:29:23 +0200 Subject: [PATCH 03/52] Fix travis --- tests/travis.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/travis.sh b/tests/travis.sh index c32dfca..8dfe54e 100644 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -157,7 +157,7 @@ stack_clean='fastcgi redis opcache all' for stack in $stack_clean; do echo -ne " cleaning $stack cache [..]\r" if { - wo stack clean --${stack} + wo clean --${stack} } >> /var/log/wo/test.log; then echo -ne " cleaning $stack cache [${CGREEN}OK${CEND}]\\r" echo -ne '\n' From 941d10dd09200d7c8974f57eb07db234749943b7 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Fri, 20 Sep 2019 18:22:22 +0200 Subject: [PATCH 04/52] Fix travis --- .travis.yml | 3 --- install | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4d263aa..e950bde 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,9 +37,6 @@ script: - lsb_release -a - sudo bash -c 'echo -e "[user]\n\tname = abc\n\temail = root@localhost.com" > /home/travis/.gitconfig' - sudo echo "Travis Banch = $TRAVIS_BRANCH" - - sed -i 's/# "nose"/"nose"/g' setup.py - - sed -i 's/# "coverage"/"coverage"/g' setup.py - - sed -i 's/# "Sphinx >= 1.0"/"Sphinx >= 1.0"/g' setup.py - sudo time bash install --travis -b "$TRAVIS_BRANCH" - sudo time bash tests/travis.sh diff --git a/install b/install index e425ff1..d559a9a 100755 --- a/install +++ b/install @@ -826,13 +826,13 @@ else _run wo_install_dep "Installing wo dependencies" _run wo_timesync #if [ "$ufw" = "y" ]; then - # _run wo_ufw_setup "Configuring UFW" + # _run wo_ufw_setup "Configuring UFW" #fi # skip steps if travis - _run wo_download "Downloading WordOps" if [ -z "$wo_travis" ]; then + _run wo_download "Downloading WordOps" _run wo_dist_upgrade - wo_git_config + wo_git_config _run wo_install "Installing WordOps" else _run wo_install_travis "Installing WordOps" From 04eb403f83942bf5d179a3c701f9515674d95116 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Fri, 20 Sep 2019 21:22:43 +0200 Subject: [PATCH 05/52] Switch back to xenial du to issues with postgresql repo --- .travis.yml | 9 +++++---- tests/travis.sh | 4 ++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index e950bde..a09084a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ sudo: required -dist: bionic +dist: xenial language: bash @@ -21,10 +21,11 @@ before_install: before_script: - sudo rm -rf /etc/mysql - sudo bash -c 'echo example.com > /etc/hostname' - - sudo apt-get -qq purge mysql* graphviz* redis* - - sudo apt-get -qq autoremove --purge - unset LANG - - sudo apt-get install --assume-yes --quiet git python3-setuptools python3-dev python3-apt ccze tree + - sudo apt-get -qq purge mysql* graphviz* redis* + - sudo apt-get install -qq git python3-setuptools python3-dev python3-apt ccze tree + - sudo apt-get -qq autoremove --purge + after_script: - sudo cat /etc/nginx/nginx.conf | ccze -A diff --git a/tests/travis.sh b/tests/travis.sh index 8dfe54e..9e324c7 100644 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -9,6 +9,10 @@ CRED="${CSI}1;31m" CGREEN="${CSI}1;32m" CEND="${CSI}0m" +apt-get -qq purge mysql* graphviz* redis* +apt-get install -qq git python3-setuptools python3-dev python3-apt ccze tree +sudo apt-get -qq autoremove --purge + exit_script() { curl --progress-bar --upload-file /var/log/wo/wordops.log https://transfer.vtbox.net/"$(basename wordops.log)" && echo "" exit 1 From 03228e08b9318f084867a5ee3c038a0d18073a5c Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Fri, 20 Sep 2019 21:58:23 +0200 Subject: [PATCH 06/52] Fix travis --- .travis.yml | 1 - tests/travis.sh | 12 ++++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index a09084a..3694e4d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,6 +40,5 @@ script: - sudo echo "Travis Banch = $TRAVIS_BRANCH" - sudo time bash install --travis -b "$TRAVIS_BRANCH" - sudo time bash tests/travis.sh - - sudo wo update --travis - sudo wo stack status diff --git a/tests/travis.sh b/tests/travis.sh index 9e324c7..882ebe9 100644 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -173,6 +173,12 @@ for stack in $stack_clean; do fi done +echo -e "${CGREEN}#############################################${CEND}" +echo -e ' various informations ' +echo -e "${CGREEN}#############################################${CEND}" +wp --allow-root --info +wo site info wp.net + echo -e "${CGREEN}#############################################${CEND}" echo -e ' wo stack purge ' echo -e "${CGREEN}#############################################${CEND}" @@ -191,9 +197,3 @@ for stack in $stack_purge; do fi done - -echo -e "${CGREEN}#############################################${CEND}" -echo -e ' various informations ' -echo -e "${CGREEN}#############################################${CEND}" -wp --allow-root --info -wo site info wp.net \ No newline at end of file From 5cc723550e6dd805dfb52ab18ceddc2e3afae113 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Sat, 21 Sep 2019 01:08:51 +0200 Subject: [PATCH 07/52] Fix wo stack purge --- CHANGELOG.md | 4 ++++ wo/cli/plugins/stack.py | 41 +++++++++++++++++++++++------------------ wo/cli/plugins/sync.py | 5 +++-- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a87bdb8..cd9cd5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### v3.9.x - [Unreleased] +#### Fixed + +- `wo stack purge --all` failure if mysql isn't installed + ### v3.9.8.12 - 2019-09-20 #### Changed diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index 27da795..67048db 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -794,12 +794,12 @@ class WOStackController(CementBaseController): # NGINX if pargs.nginx: if WOAptGet.is_installed(self, 'nginx-custom'): - Log.debug(self, "Purge apt_packages variable of Nginx") + Log.debug(self, "Add Nginx to apt_packages list") apt_packages = apt_packages + WOVariables.wo_nginx # PHP if pargs.php: - Log.debug(self, "Purge apt_packages variable PHP") + Log.debug(self, "Add PHP to apt_packages list") if WOAptGet.is_installed(self, 'php7.2-fpm'): if not (WOAptGet.is_installed(self, 'php7.3-fpm')): apt_packages = apt_packages + WOVariables.wo_php + \ @@ -819,50 +819,53 @@ class WOStackController(CementBaseController): # REDIS if pargs.redis: - Log.debug(self, "Remove apt_packages variable of Redis") - apt_packages = apt_packages + ["redis-server"] + if WOAptGet.is_installed(self, 'redis-server'): + Log.debug(self, "Remove apt_packages variable of Redis") + apt_packages = apt_packages + ["redis-server"] # MariaDB if pargs.mysql: - Log.debug(self, "Removing apt_packages variable of MySQL") - apt_packages = apt_packages + ['mariadb-server', 'mysql-common', - 'mariadb-client'] - packages = packages + ['/etc/mysql', '/var/lib/mysql'] + if WOAptGet.is_installed(self, 'mariadb-server'): + Log.debug(self, "Removing apt_packages variable of MySQL") + apt_packages = apt_packages + ['mariadb-server', + 'mysql-common', + 'mariadb-client'] + packages = packages + ['/etc/mysql', '/var/lib/mysql'] # mysqlclient if pargs.mysqlclient: - Log.debug(self, "Removing apt_packages variable " - "for MySQL Client") if WOShellExec.cmd_exec(self, "mysqladmin ping"): + Log.debug(self, "Add MySQL client to apt_packages list") apt_packages = apt_packages + WOVariables.wo_mysql_client # fail2ban if pargs.fail2ban: if WOAptGet.is_installed(self, 'fail2ban'): - Log.debug(self, "Remove apt_packages variable of Fail2ban") + Log.debug(self, "Add Fail2ban to apt_packages list") apt_packages = apt_packages + WOVariables.wo_fail2ban # ClamAV if pargs.clamav: - Log.debug(self, "Setting apt_packages variable for ClamAV") + Log.debug(self, "Add ClamAV to apt_packages list") if WOAptGet.is_installed(self, 'clamav'): apt_packages = apt_packages + WOVariables.wo_clamav # UFW if pargs.ufw: - Log.debug(self, "Remove apt_packages variable for UFW") - apt_packages = apt_packages + ["ufw"] + if WOAptGet.is_installed(self, 'ufw'): + Log.debug(self, "Add UFW to apt_packages list") + apt_packages = apt_packages + ["ufw"] # sendmail if pargs.sendmail: - Log.debug(self, "Setting apt_packages variable for Sendmail") if WOAptGet.is_installed(self, 'sendmail'): + Log.debug(self, "Add sendmail to apt_packages list") apt_packages = apt_packages + ["sendmail"] # proftpd if pargs.proftpd: if WOAptGet.is_installed(self, 'proftpd-basic'): - Log.debug(self, "Purge apt_packages variable for ProFTPd") + Log.debug(self, "Add Proftpd to apt_packages list") apt_packages = apt_packages + ["proftpd-basic"] # WP-CLI @@ -945,8 +948,10 @@ class WOStackController(CementBaseController): WOService.stop_service(self, 'fail2ban') if (set(["mariadb-server"]).issubset(set(apt_packages))): - WOMysql.backupAll(self) - WOService.stop_service(self, 'mysql') + if (os.path.isfile('/usr/bin/mysql') and + os.path.isdir('/var/lib/mysql')): + WOMysql.backupAll(self) + WOService.stop_service(self, 'mysql') # Netdata uninstaller if (set(['/var/lib/wo/tmp/' diff --git a/wo/cli/plugins/sync.py b/wo/cli/plugins/sync.py index b2f1d91..732d9ee 100644 --- a/wo/cli/plugins/sync.py +++ b/wo/cli/plugins/sync.py @@ -45,8 +45,9 @@ class WOSyncController(CementBaseController): Log.debug(self, "Config files not found in {0}/ " .format(wo_site_webroot)) if site.site_type != 'mysql': - Log.debug(self, "Searching wp-config.php in {0}/htdocs/ " - .format(wo_site_webroot)) + Log.debug(self, + "Searching wp-config.php in {0}/htdocs/" + .format(wo_site_webroot)) configfiles = glob.glob( wo_site_webroot + '/htdocs/wp-config.php') From daad6dbfe741cdf1af47045b8afc25ebfeee48c2 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Sat, 21 Sep 2019 16:42:49 +0200 Subject: [PATCH 08/52] Fix phpmyadmin --- wo/cli/plugins/stack.py | 9 ++++++++- wo/cli/plugins/stack_pref.py | 18 ++++++++---------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index 67048db..3299759 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -796,6 +796,8 @@ class WOStackController(CementBaseController): if WOAptGet.is_installed(self, 'nginx-custom'): Log.debug(self, "Add Nginx to apt_packages list") apt_packages = apt_packages + WOVariables.wo_nginx + else: + Log.info(self, "Nginx is not installed") # PHP if pargs.php: @@ -822,15 +824,20 @@ class WOStackController(CementBaseController): if WOAptGet.is_installed(self, 'redis-server'): Log.debug(self, "Remove apt_packages variable of Redis") apt_packages = apt_packages + ["redis-server"] + else: + Log.info(self, "Redis is not installed") + # MariaDB if pargs.mysql: if WOAptGet.is_installed(self, 'mariadb-server'): - Log.debug(self, "Removing apt_packages variable of MySQL") + Log.debug(self, "Add MySQL to apt_packages list") apt_packages = apt_packages + ['mariadb-server', 'mysql-common', 'mariadb-client'] packages = packages + ['/etc/mysql', '/var/lib/mysql'] + else: + Log.info(self, "MariaDB is not installed") # mysqlclient if pargs.mysqlclient: diff --git a/wo/cli/plugins/stack_pref.py b/wo/cli/plugins/stack_pref.py index 1d05be8..58c5770 100644 --- a/wo/cli/plugins/stack_pref.py +++ b/wo/cli/plugins/stack_pref.py @@ -21,9 +21,9 @@ from wo.core.logging import Log from wo.core.mysql import WOMysql from wo.core.services import WOService from wo.core.shellexec import CommandExecutionError, WOShellExec +from wo.core.sslutils import SSL from wo.core.template import WOTemplate from wo.core.variables import WOVariables -from wo.core.sslutils import SSL def pre_pref(self, apt_packages): @@ -1170,9 +1170,8 @@ def post_pref(self, apt_packages, packages, upgrade=False): Log.info(self, "Updating phpMyAdmin, please wait...") WOShellExec.cmd_exec( self, "/usr/local/bin/composer update " - "--no-plugins --no-scripts " - "-n --no-dev -d " - "/var/www/22222/htdocs/db/pma/ &") + "--no-plugins --no-scripts -n --no-dev -d " + "/var/www/22222/htdocs/db/pma/") WOFileUtils.chown( self, '{0}22222/htdocs/db/pma' .format(WOVariables.wo_webroot), @@ -1189,12 +1188,11 @@ def post_pref(self, apt_packages, packages, upgrade=False): .format(WOVariables.wo_webroot)) if not os.path.isfile('/var/www/22222/htdocs/cache/redis/' 'phpRedisAdmin/composer.lock'): - WOShellExec.cmd_exec(self, "/usr/local/bin/composer " - "create-project --no-plugins " - "--no-scripts -n -s dev " - "erik-dubbelboer/php-redis-admin " - "/var/www/22222/htdocs/cache" - "/redis/phpRedisAdmin &") + WOShellExec.cmd_exec( + self, "/usr/local/bin/composer " + "create-project --no-plugins --no-scripts -n -s dev " + "erik-dubbelboer/php-redis-admin " + "/var/www/22222/htdocs/cache/redis/phpRedisAdmin") WOFileUtils.chown(self, '{0}22222/htdocs' .format(WOVariables.wo_webroot), 'www-data', From e7cdc8f2cf5a890722fe6f1a3b5775ead1ba3c4c Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Sat, 21 Sep 2019 16:52:58 +0200 Subject: [PATCH 09/52] Fix ufw config --- wo/cli/templates/ufw.mustache | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wo/cli/templates/ufw.mustache b/wo/cli/templates/ufw.mustache index aa8d344..8ff7150 100644 --- a/wo/cli/templates/ufw.mustache +++ b/wo/cli/templates/ufw.mustache @@ -59,7 +59,7 @@ wo_ufw_setup() { } if { wo_ufw_setup; }; then - exit 0 + return 0 else - exit 1 + return 1 fi From 757ad73e4658548075b460a5898cd7c8535e8adf Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Sat, 21 Sep 2019 19:08:53 +0200 Subject: [PATCH 10/52] Fix UFW install --- wo/cli/plugins/site.py | 5 ---- wo/cli/plugins/stack.py | 2 ++ wo/cli/plugins/stack_config.py | 1 - wo/cli/plugins/stack_services.py | 1 - wo/cli/plugins/stack_upgrade.py | 1 - wo/cli/templates/sshd.mustache | 45 ++++++++++++++++++++++++++++++++ wo/cli/templates/ufw.mustache | 6 +---- 7 files changed, 48 insertions(+), 13 deletions(-) create mode 100644 wo/cli/templates/sshd.mustache diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index 668941c..29ed6b1 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -261,7 +261,6 @@ class WOSiteEditController(CementBaseController): label = 'edit' stacked_on = 'site' stacked_type = 'nested' - exit_on_close = True description = ('Edit Nginx configuration of site') arguments = [ (['site_name'], @@ -316,7 +315,6 @@ class WOSiteCreateController(CementBaseController): label = 'create' stacked_on = 'site' stacked_type = 'nested' - exit_on_close = True description = ('this commands set up configuration and installs ' 'required files as options are provided') arguments = [ @@ -792,7 +790,6 @@ class WOSiteUpdateController(CementBaseController): label = 'update' stacked_on = 'site' stacked_type = 'nested' - exit_on_close = True description = ('This command updates websites configuration to ' 'another as per the options are provided') arguments = [ @@ -1826,7 +1823,6 @@ class WOSiteDeleteController(CementBaseController): label = 'delete' stacked_on = 'site' stacked_type = 'nested' - exit_on_close = True description = 'delete an existing website' arguments = [ (['site_name'], @@ -1966,7 +1962,6 @@ class WOSiteListController(CementBaseController): label = 'list' stacked_on = 'site' stacked_type = 'nested' - exit_on_close = True description = 'List websites' arguments = [ (['--enabled'], diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index 3299759..de5b0b0 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -502,7 +502,9 @@ class WOStackController(CementBaseController): Log.debug(self, "Downloading following: {0}".format(packages)) WODownload.download(self, packages) Log.debug(self, "Calling post_pref") + Log.wait(self, "Configuring packages") post_pref(self, [], packages) + Log.valide(self, "Configuring packages") if disp_msg: if (self.msg): diff --git a/wo/cli/plugins/stack_config.py b/wo/cli/plugins/stack_config.py index 3c5166a..e2e85a1 100644 --- a/wo/cli/plugins/stack_config.py +++ b/wo/cli/plugins/stack_config.py @@ -20,7 +20,6 @@ class WOStackUpgradeController(CementBaseController): label = 'config' stacked_on = 'stack' stacked_type = 'nested' - exit_on_close = True description = ('Upgrade stack safely') arguments = [ (['--nginx'], diff --git a/wo/cli/plugins/stack_services.py b/wo/cli/plugins/stack_services.py index 0fd60ee..26408f6 100644 --- a/wo/cli/plugins/stack_services.py +++ b/wo/cli/plugins/stack_services.py @@ -14,7 +14,6 @@ class WOStackStatusController(CementBaseController): label = 'stack_services' stacked_on = 'stack' stacked_type = 'embedded' - exit_on_close = True description = 'Check the stack status' @expose(help="Start stack services") diff --git a/wo/cli/plugins/stack_upgrade.py b/wo/cli/plugins/stack_upgrade.py index 694a198..6ea8107 100644 --- a/wo/cli/plugins/stack_upgrade.py +++ b/wo/cli/plugins/stack_upgrade.py @@ -20,7 +20,6 @@ class WOStackUpgradeController(CementBaseController): label = 'upgrade' stacked_on = 'stack' stacked_type = 'nested' - exit_on_close = True description = ('Upgrade stack safely') arguments = [ (['--all'], diff --git a/wo/cli/templates/sshd.mustache b/wo/cli/templates/sshd.mustache new file mode 100644 index 0000000..6f19a03 --- /dev/null +++ b/wo/cli/templates/sshd.mustache @@ -0,0 +1,45 @@ +# Use a custom port in the following range : 1024-65536 +Port {{sshport}} + +#Prefer ed25519 & ECDSA keys rather than 2048 bit RSA +HostKey /etc/ssh/ssh_host_rsa_key +HostKey /etc/ssh/ssh_host_ecdsa_key +HostKey /etc/ssh/ssh_host_ed25519_key + +# Allow root access with ssh keys +PermitRootLogin without-password + +# Allow ssh access to some users only +AllowUsers root ubuntu + +# allow ssh key Authentication +PubkeyAuthentication yes + +# ssh keys path in ~/.ssh/authorized_keys +AuthorizedKeysFile %h/.ssh/authorized_keys + +# No password or empty passwords Authentication +PasswordAuthentication {{allowpass}} +PermitEmptyPasswords no + +# No challenge response Authentication +ChallengeResponseAuthentication no + +UsePAM yes +X11Forwarding yes + +#PrintMotd no + +# Allow client to pass locale environment variables +AcceptEnv LANG LC_* + +# override default of no subsystems +Subsystem sftp /usr/lib/openssh/sftp-server + +# Host keys the client accepts - order here is honored by OpenSSH +HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ssh-ed25519,ssh-rsa,ecdsa-sha2-nistp521-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256 + +# use strong ciphers +KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256 +Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr +MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com \ No newline at end of file diff --git a/wo/cli/templates/ufw.mustache b/wo/cli/templates/ufw.mustache index 8ff7150..f64fc39 100644 --- a/wo/cli/templates/ufw.mustache +++ b/wo/cli/templates/ufw.mustache @@ -58,8 +58,4 @@ wo_ufw_setup() { fi } -if { wo_ufw_setup; }; then - return 0 -else - return 1 -fi +wo_ufw_setup \ No newline at end of file From a3e05cbbaffced2be28c94a9d54b9f7a3ac0930e Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Sat, 21 Sep 2019 19:12:46 +0200 Subject: [PATCH 11/52] Update bash-completion --- config/bash_completion.d/wo_auto.rc | 2 +- install | 2 +- wo/cli/plugins/secure.py | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/config/bash_completion.d/wo_auto.rc b/config/bash_completion.d/wo_auto.rc index b0ffd54..db3df87 100644 --- a/config/bash_completion.d/wo_auto.rc +++ b/config/bash_completion.d/wo_auto.rc @@ -74,7 +74,7 @@ _wo_complete() # HANDLE EVERYTHING AFTER THE THIRD LEVEL NAMESPACE "install" | "purge" | "remove" ) COMPREPLY=( $(compgen \ - -W "--recommended --web --admin --security --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --redis --phpredisadmin --composer --netdata --fail2ban --dashboard --proftpd --clamav --mysqlclient --mysqltuner --extplorer --all" \ + -W "--recommended --web --admin --security --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --redis --phpredisadmin --composer --netdata --fail2ban --dashboard --proftpd --clamav --mysqlclient --mysqltuner --extplorer --all --force" \ -- $cur) ) ;; "upgrade" ) diff --git a/install b/install index d559a9a..9df4add 100755 --- a/install +++ b/install @@ -877,7 +877,7 @@ else wo_lib_echo "WordOps (wo) installed successfully" echo wo_lib_echo "To enable bash-completion, just use the command:" - wo_lib_echo_info "bash" + wo_lib_echo_info "bash -l" echo wo_lib_echo "To install WordOps recommended stacks, you can use the command:" wo_lib_echo_info "wo stack install" diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index ec3d916..3a073a6 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -11,6 +11,7 @@ from wo.core.services import WOService from wo.core.shellexec import WOShellExec from wo.core.variables import WOVariables from wo.core.random import RANDOM +from wo.core.template import WOTemplate def wo_secure_hook(app): @@ -33,6 +34,13 @@ class WOSecureController(CementBaseController): dict(help='set backend port', action='store_true')), (['--ip'], dict(help='set backend whitelisted ip', action='store_true')), + (['--ssh-port'], dict( + help='set custom ssh port', action='store_true')), + (['--ssh-strict'], dict(help='harden ssh security', + action='store_true')), + (['--ufw'], + dict(help='setup and configure ufw firewall', + action='store_true')), (['user_input'], dict(help='user input', nargs='?', default=None)), (['user_pass'], From d717e553843405e4a480a28f8be4f3ea8c5eb3e8 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Sun, 22 Sep 2019 01:02:04 +0200 Subject: [PATCH 12/52] Cleanup stack import --- wo/cli/plugins/stack.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index de5b0b0..843e227 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -1,16 +1,6 @@ """Stack Plugin for WordOps""" -import codecs -import configparser import os -import pwd -import random -import re -import shutil -import string - -import psutil -import requests from cement.core import handler, hook from cement.core.controller import CementBaseController, expose @@ -21,18 +11,13 @@ from wo.cli.plugins.stack_migrate import WOStackMigrateController from wo.cli.plugins.stack_pref import post_pref, pre_pref from wo.cli.plugins.stack_services import WOStackStatusController from wo.cli.plugins.stack_upgrade import WOStackUpgradeController -from wo.core.apt_repo import WORepo from wo.core.aptget import WOAptGet -from wo.core.cron import WOCron from wo.core.download import WODownload -from wo.core.extract import WOExtract from wo.core.fileutils import WOFileUtils -from wo.core.git import WOGit from wo.core.logging import Log from wo.core.mysql import WOMysql from wo.core.services import WOService -from wo.core.shellexec import CommandExecutionError, WOShellExec -from wo.core.template import WOTemplate +from wo.core.shellexec import WOShellExec from wo.core.variables import WOVariables From 1950764dc80b1a3d8ee976eccf5a9d9f70dceeba Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Sun, 22 Sep 2019 14:11:12 +0200 Subject: [PATCH 13/52] Updating WordOps dashboard --- CHANGELOG.md | 4 + config/bash_completion.d/wo_auto.rc | 4 +- wo/cli/plugins/stack_config.py | 51 ---------- wo/cli/plugins/stack_pref.py | 7 +- wo/cli/plugins/stack_upgrade.py | 34 ++++--- wo/cli/templates/nextcloud.mustache | 147 +++++++++++++--------------- wo/core/variables.py | 2 +- 7 files changed, 98 insertions(+), 151 deletions(-) delete mode 100644 wo/cli/plugins/stack_config.py diff --git a/CHANGELOG.md b/CHANGELOG.md index cd9cd5b..f91bfad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### v3.9.x - [Unreleased] +#### Changed + +- [APP] WordOps dashboard updated to v1.2. Shipped as a html file, it can be used without PHP stack + #### Fixed - `wo stack purge --all` failure if mysql isn't installed diff --git a/config/bash_completion.d/wo_auto.rc b/config/bash_completion.d/wo_auto.rc index db3df87..f9bd70b 100644 --- a/config/bash_completion.d/wo_auto.rc +++ b/config/bash_completion.d/wo_auto.rc @@ -269,9 +269,9 @@ _wo_complete() "--web" | "--admin" | "--nginx" | "--php" | "--php73" | "--mysql" | "--wpcli" | "--phpmyadmin" | "--adminer" | "--utils" | "--fail2ban" | "--redis | --phpredisadmin | --netdata") if [[ "${COMP_WORDS[2]}" == "install" || "${COMP_WORDS[2]}" == "purge" || "${COMP_WORDS[2]}" == "remove" ]]; then - retlist="--web --admin --security --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --redis --fail2ban --phpredisadmin --netdata -f --force" + retlist="--web --admin --security --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --redis --fail2ban --phpredisadmin --netdata --force" elif [[ "${COMP_WORDS[2]}" == "start" || "${COMP_WORDS[2]}" == "reload" || "${COMP_WORDS[2]}" == "restart" || "${COMP_WORDS[2]}" == "stop" ]]; then - retlist="--nginx --php --php73 --mysql --redis --netdata" + retlist="--nginx --php --php73 --mysql --redis --netdata --fail2ban" elif [[ "${COMP_WORDS[1]}" == "debug" ]]; then retlist="--start --nginx --php --php73 --fpm --fpm7 --mysql -i --interactive -stop --import-slow-log --import-slow-log-interval= -" if [[ $prev == '--mysql' ]]; then diff --git a/wo/cli/plugins/stack_config.py b/wo/cli/plugins/stack_config.py deleted file mode 100644 index e2e85a1..0000000 --- a/wo/cli/plugins/stack_config.py +++ /dev/null @@ -1,51 +0,0 @@ -import os -import shutil - -from cement.core import handler, hook -from cement.core.controller import CementBaseController, expose - -from wo.cli.plugins.stack_pref import post_pref, pre_pref -from wo.core.aptget import WOAptGet -from wo.core.download import WODownload -from wo.core.extract import WOExtract -from wo.core.fileutils import WOFileUtils -from wo.core.logging import Log -from wo.core.services import WOService -from wo.core.shellexec import WOShellExec -from wo.core.variables import WOVariables - - -class WOStackUpgradeController(CementBaseController): - class Meta: - label = 'config' - stacked_on = 'stack' - stacked_type = 'nested' - description = ('Upgrade stack safely') - arguments = [ - (['--nginx'], - dict(help='Upgrade all stack', action='store_true')), - (['--php'], - dict(help='Upgrade PHP 7.2 stack', action='store_true')), - (['--php73'], - dict(help='Upgrade PHP 7.3 stack', action='store_true')), - (['--mysql'], - dict(help='Upgrade MySQL stack', action='store_true')), - (['--wpcli'], - dict(help='Upgrade WPCLI', action='store_true')), - (['--redis'], - dict(help='Upgrade Redis', action='store_true')), - (['--netdata'], - dict(help='Upgrade Netdata', action='store_true')), - (['--dashboard'], - dict(help='Upgrade WordOps Dashboard', action='store_true')), - (['--composer'], - dict(help='Upgrade Composer', action='store_true')), - (['--phpmyadmin'], - dict(help='Upgrade phpMyAdmin', action='store_true')), - (['--no-prompt'], - dict(help="Upgrade Packages without any prompt", - action='store_true')), - (['--force'], - dict(help="Force Packages upgrade without any prompt", - action='store_true')), - ] diff --git a/wo/cli/plugins/stack_pref.py b/wo/cli/plugins/stack_pref.py index 58c5770..aca2225 100644 --- a/wo/cli/plugins/stack_pref.py +++ b/wo/cli/plugins/stack_pref.py @@ -950,10 +950,11 @@ def post_pref(self, apt_packages, packages, upgrade=False): WOService.restart_service(self, 'proftpd') # add rule for proftpd with UFW - if os.path.isdir('/etc/ufw'): + if WOFileUtils.grepcheck( + self, '/etc/ufw/ufw.conf', 'ENABLED=yes'): try: WOShellExec.cmd_exec( - self, "ufw allow 21") + self, "ufw limit 21") WOShellExec.cmd_exec( self, "ufw allow 49000:50000/tcp") WOShellExec.cmd_exec( @@ -1270,7 +1271,7 @@ def post_pref(self, apt_packages, packages, upgrade=False): "| cut -d ' ' -f 2").read() if (wo_wan != 'eth0' and wo_wan != ''): WOFileUtils.searchreplace(self, - "{0}22222/htdocs/index.php" + "{0}22222/htdocs/index.html" .format(WOVariables.wo_webroot), "eth0", "{0}".format(wo_wan)) diff --git a/wo/cli/plugins/stack_upgrade.py b/wo/cli/plugins/stack_upgrade.py index 6ea8107..77cbd28 100644 --- a/wo/cli/plugins/stack_upgrade.py +++ b/wo/cli/plugins/stack_upgrade.py @@ -148,7 +148,8 @@ class WOStackUpgradeController(CementBaseController): 'Netdata']] if pargs.dashboard: - if os.path.isfile('/var/www/22222/htdocs/index.php'): + if (os.path.isfile('/var/www/22222/htdocs/index.php') or + os.path.isfile('/var/www/22222/htdocs/index.html')): packages = packages + \ [["https://github.com/WordOps/wordops-dashboard/" "releases/download/v{0}/wordops-dashboard.tar.gz" @@ -233,7 +234,11 @@ class WOStackUpgradeController(CementBaseController): WOFileUtils.rm(self, '/var/lib/wo/tmp/kickstart.sh') if pargs.dashboard: - WOFileUtils.rm(self, '/var/www/22222/htdocs/index.php') + if os.path.isfile('/var/www/22222/htdocs/index.php'): + WOFileUtils.rm(self, '/var/www/22222/htdocs/index.php') + if os.path.isfile('/var/www/22222/htdocs/index.html'): + WOFileUtils.rm( + self, '/var/www/22222/htdocs/index.html') Log.debug(self, "Downloading following: {0}".format(packages)) WODownload.download(self, packages) @@ -256,20 +261,17 @@ class WOStackUpgradeController(CementBaseController): Log.valide(self, "Upgrading Netdata") if pargs.dashboard: - Log.debug(self, "Extracting wo-dashboard.tar.gz " - "to location {0}22222/htdocs/" - .format(WOVariables.wo_webroot)) - WOExtract.extract(self, '/var/lib/wo/tmp/' - 'wo-dashboard.tar.gz', - '{0}22222/htdocs' - .format(WOVariables.wo_webroot)) - WOFileUtils.chown(self, "{0}22222/htdocs" - .format(WOVariables.wo_webroot), - 'www-data', - 'www-data', recursive=True) + post_pref( + self, [], [["https://github.com/WordOps" + "/wordops-dashboard/" + "releases/download/v{0}/" + "wordops-dashboard.tar.gz" + .format(WOVariables.wo_dashboard), + "/var/lib/wo/tmp/wo-dashboard.tar.gz", + "WordOps Dashboard"]]) if pargs.composer: - Log.wait(self, "Upgrading Composer ") + Log.wait(self, "Upgrading Composer") WOShellExec.cmd_exec( self, "php -q /var/lib/wo" "/tmp/composer-install " @@ -280,7 +282,7 @@ class WOStackUpgradeController(CementBaseController): Log.valide(self, "Upgrading Composer ") if pargs.phpmyadmin: - Log.wait(self, "Upgrading phpMyAdmin ") + Log.wait(self, "Upgrading phpMyAdmin") WOExtract.extract(self, '/var/lib/wo/tmp/pma.tar.gz', '/var/lib/wo/tmp/') shutil.copyfile(('{0}22222/htdocs/db/pma' @@ -301,6 +303,6 @@ class WOStackUpgradeController(CementBaseController): .format(WOVariables.wo_webroot), 'www-data', 'www-data', recursive=True) - Log.valide(self, "Upgrading phpMyAdmin ") + Log.valide(self, "Upgrading phpMyAdmin") Log.info(self, "Successfully updated packages") diff --git a/wo/cli/templates/nextcloud.mustache b/wo/cli/templates/nextcloud.mustache index f907de7..48a7df1 100644 --- a/wo/cli/templates/nextcloud.mustache +++ b/wo/cli/templates/nextcloud.mustache @@ -1,82 +1,73 @@ # WordOps nextcloud configuration +add_header X-Robots-Tag none; +add_header X-Permitted-Cross-Domain-Policies none; +add_header Referrer-Policy no-referrer; +location = /robots.txt { + allow all; + log_not_found off; + access_log off; +} +location / { + rewrite ^ /index.php$request_uri; +} +location ~ ^\/(?:build|tests|config|lib|3rdparty|templates|data)\/ { + deny all; +} +location ~ ^\/(?:\.|autotest|occ|issue|indie|db_|console) { + deny all; +} +location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) { + fastcgi_split_path_info ^(.+?\.php)(\/.*|)$; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param HTTPS on; +# Avoid sending the security headers twice + fastcgi_param modHeadersAvailable true; +# Enable pretty urls + fastcgi_param front_controller_active true; + fastcgi_pass {{upstream}}; + fastcgi_intercept_errors on; + fastcgi_request_buffering off; +} +location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) { + try_files $uri/ =404; + index index.php; +} +# Adding the cache control header for js, css and map files +# Make sure it is BELOW the PHP block +location ~ \.(?:css|js|woff2?|svg|gif|map)$ { + try_files $uri /index.php$request_uri; + add_header Cache-Control "public, max-age=15778463"; +# Add headers to serve security related headers (It is intended to +# have those duplicated to the ones above) +# Before enabling Strict-Transport-Security headers please read into +# this topic first. +#add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;"; +# +# WARNING: Only add the preload option once you read about +# the consequences in https://hstspreload.org/. This option +# will add the domain to a hardcoded list that is shipped +# in all major browsers and getting removed from this list +# could take several months. + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; add_header X-Robots-Tag none; + add_header X-Download-Options noopen; add_header X-Permitted-Cross-Domain-Policies none; add_header Referrer-Policy no-referrer; - - location = /robots.txt { - allow all; - log_not_found off; - access_log off; - } - - # Enable gzip but do not remove ETag headers - gzip on; - gzip_vary on; - gzip_comp_level 4; - gzip_min_length 256; - gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; - gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; - - location / { - rewrite ^ /index.php$request_uri; - } - - location ~ ^\/(?:build|tests|config|lib|3rdparty|templates|data)\/ { - deny all; - } - location ~ ^\/(?:\.|autotest|occ|issue|indie|db_|console) { - deny all; - } - - location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) { - fastcgi_split_path_info ^(.+?\.php)(\/.*|)$; - include fastcgi_params; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param PATH_INFO $fastcgi_path_info; - fastcgi_param HTTPS on; - # Avoid sending the security headers twice - fastcgi_param modHeadersAvailable true; - # Enable pretty urls - fastcgi_param front_controller_active true; - fastcgi_pass {{upstream}}; - fastcgi_intercept_errors on; - fastcgi_request_buffering off; - } - - location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) { - try_files $uri/ =404; - index index.php; - } - - # Adding the cache control header for js, css and map files - # Make sure it is BELOW the PHP block - location ~ \.(?:css|js|woff2?|svg|gif|map)$ { - try_files $uri /index.php$request_uri; - add_header Cache-Control "public, max-age=15778463"; - # Add headers to serve security related headers (It is intended to - # have those duplicated to the ones above) - # Before enabling Strict-Transport-Security headers please read into - # this topic first. - #add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;"; - # - # WARNING: Only add the preload option once you read about - # the consequences in https://hstspreload.org/. This option - # will add the domain to a hardcoded list that is shipped - # in all major browsers and getting removed from this list - # could take several months. - add_header X-Content-Type-Options nosniff; - add_header X-XSS-Protection "1; mode=block"; - add_header X-Robots-Tag none; - add_header X-Download-Options noopen; - add_header X-Permitted-Cross-Domain-Policies none; - add_header Referrer-Policy no-referrer; - - # Optional: Don't log access to assets - access_log off; - } - - location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap)$ { - try_files $uri /index.php$request_uri; - # Optional: Don't log access to other assets - access_log off; - } +# Optional: Don't log access to assets + access_log off; +} +location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap)$ { + try_files $uri /index.php$request_uri; +# Optional: Don't log access to other assets + access_log off; +} +# Enable gzip but do not remove ETag headers +gzip on; +gzip_vary on; +gzip_comp_level 4; +gzip_min_length 256; +gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; +gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; diff --git a/wo/core/variables.py b/wo/core/variables.py index aa39058..2db38dd 100644 --- a/wo/core/variables.py +++ b/wo/core/variables.py @@ -17,7 +17,7 @@ class WOVariables(): wo_adminer = "4.7.2" wo_phpmyadmin = "4.9.0.1" wo_extplorer = "2.1.13" - wo_dashboard = "1.1" + wo_dashboard = "1.2" # Get WPCLI path wo_wpcli_path = '/usr/local/bin/wp' From c6b081868f99139aad7a4a3531aadea9af1da9b4 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Sun, 22 Sep 2019 17:00:35 +0200 Subject: [PATCH 14/52] Fix ee cleanup --- install | 2 +- wo/cli/plugins/secure.py | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/install b/install index 9df4add..3e13ba4 100755 --- a/install +++ b/install @@ -643,7 +643,7 @@ wo_backup_wo() { } wo_clean_ee() { - rm -f /usr/local/bin/ee /etc/bash_completion.d/ee_auto.rc /usr/lib/ee/templates /usr/local/lib/python3.*/dist-packages/ee-*.egg /etc/ee /var/lib/ee + rm -rf /usr/local/bin/ee /etc/bash_completion.d/ee_auto.rc /usr/lib/ee/templates /usr/local/lib/python3.*/dist-packages/ee-*.egg /etc/ee /var/lib/ee return 0 } diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index 3a073a6..8977707 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -1,17 +1,14 @@ import getpass -import random -import string from cement.core import handler, hook from cement.core.controller import CementBaseController, expose from wo.core.git import WOGit from wo.core.logging import Log +from wo.core.random import RANDOM from wo.core.services import WOService from wo.core.shellexec import WOShellExec from wo.core.variables import WOVariables -from wo.core.random import RANDOM -from wo.core.template import WOTemplate def wo_secure_hook(app): From e817a4086c7c0f396166f5e9afa924032df84509 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Sun, 22 Sep 2019 19:30:39 +0200 Subject: [PATCH 15/52] Refactor acme.sh integration --- CHANGELOG.md | 3 +- wo/cli/plugins/site.py | 186 +++++++++++++++++++------------ wo/cli/plugins/site_functions.py | 157 ++------------------------ wo/core/acme.py | 129 +++++++++++++++++++++ 4 files changed, 258 insertions(+), 217 deletions(-) create mode 100644 wo/core/acme.py diff --git a/CHANGELOG.md b/CHANGELOG.md index f91bfad..3307fcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), #### Changed -- [APP] WordOps dashboard updated to v1.2. Shipped as a html file, it can be used without PHP stack +- [APP] WordOps dashboard updated to v1.2, shipped as a html file, it can be used without PHP stack #### Fixed - `wo stack purge --all` failure if mysql isn't installed +- Fix EEv3 files cleanup ### v3.9.8.12 - 2019-09-20 diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index 29ed6b1..4e8a32e 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -1,9 +1,7 @@ -# """WordOps site controller.""" import glob import json import os import subprocess -from subprocess import Popen from cement.core import handler, hook from cement.core.controller import CementBaseController, expose @@ -11,6 +9,7 @@ from cement.core.controller import CementBaseController, expose from wo.cli.plugins.site_functions import * from wo.cli.plugins.sitedb import (addNewSite, deleteSiteInfo, getAllsites, getSiteInfo, updateSiteInfo) +from wo.core.acme import WOAcme from wo.core.domainvalidate import WODomain from wo.core.fileutils import WOFileUtils from wo.core.git import WOGit @@ -58,7 +57,8 @@ class WOSiteController(CementBaseController): pargs.site_name = pargs.site_name.strip() # validate domain name - (wo_domain, wo_www_domain) = WODomain.validatedomain(self, pargs.site_name) + (wo_domain, + wo_www_domain) = WODomain.validatedomain(self, pargs.site_name) # check if site exists if not check_domain_exists(self, wo_domain): @@ -136,8 +136,10 @@ class WOSiteController(CementBaseController): Log.debug(self, str(e)) Log.error(self, 'could not input site name') pargs.site_name = pargs.site_name.strip() - (wo_domain, wo_www_domain) = WODomain.validatedomain(self, pargs.site_name) - (wo_domain_type, wo_root_domain) = WODomain.getdomainlevel(self, wo_domain) + (wo_domain, + wo_www_domain) = WODomain.validatedomain(self, pargs.site_name) + (wo_domain_type, + wo_root_domain) = WODomain.getdomainlevel(self, wo_domain) wo_db_name = '' wo_db_user = '' wo_db_pass = '' @@ -188,7 +190,8 @@ class WOSiteController(CementBaseController): def log(self): pargs = self.app.pargs pargs.site_name = pargs.site_name.strip() - (wo_domain, wo_www_domain) = WODomain.validatedomain(self, pargs.site_name) + (wo_domain, + wo_www_domain) = WODomain.validatedomain(self, pargs.site_name) wo_site_webroot = getSiteInfo(self, wo_domain).site_path if not check_domain_exists(self, wo_domain): @@ -210,7 +213,8 @@ class WOSiteController(CementBaseController): Log.error(self, 'could not input site name') # TODO Write code for wo site edit command here pargs.site_name = pargs.site_name.strip() - (wo_domain, wo_www_domain) = WODomain.validatedomain(self, pargs.site_name) + (wo_domain, + wo_www_domain) = WODomain.validatedomain(self, pargs.site_name) if not check_domain_exists(self, wo_domain): Log.error(self, "site {0} does not exist".format(wo_domain)) @@ -241,7 +245,8 @@ class WOSiteController(CementBaseController): Log.error(self, 'Unable to read input, please try again') pargs.site_name = pargs.site_name.strip() - (wo_domain, wo_www_domain) = WODomain.validatedomain(self, pargs.site_name) + (wo_domain, + wo_www_domain) = WODomain.validatedomain(self, pargs.site_name) if not check_domain_exists(self, wo_domain): Log.error(self, "site {0} does not exist".format(wo_domain)) @@ -281,7 +286,8 @@ class WOSiteEditController(CementBaseController): Log.error(self, 'Unable to read input, Please try again') pargs.site_name = pargs.site_name.strip() - (wo_domain, wo_www_domain) = WODomain.validatedomain(self, pargs.site_name) + (wo_domain, + wo_www_domain) = WODomain.validatedomain(self, pargs.site_name) if not check_domain_exists(self, wo_domain): Log.error(self, "site {0} does not exist".format(wo_domain)) @@ -422,7 +428,8 @@ class WOSiteCreateController(CementBaseController): Log.error(self, "Unable to input site name, Please try again!") pargs.site_name = pargs.site_name.strip() - (wo_domain, wo_www_domain) = WODomain.validatedomain(self, pargs.site_name) + (wo_domain, + wo_www_domain) = WODomain.validatedomain(self, pargs.site_name) if not wo_domain.strip(): Log.error(self, "Invalid domain name, " "Provide valid domain name") @@ -715,31 +722,52 @@ class WOSiteCreateController(CementBaseController): "`tail /var/log/wo/wordops.log` and please try again") if pargs.letsencrypt: - (wo_domain_type, wo_root_domain) = WODomain.getdomainlevel(self, - wo_domain) + acme_domains = [] + (wo_domain_type, + wo_root_domain) = WODomain.getdomainlevel(self, + wo_domain) data['letsencrypt'] = True letsencrypt = True if data['letsencrypt'] is True: + Log.debug(self, "Going to issue Let's Encrypt certificate") + acmedata = dict(acme_domains, dns=False, acme_dns='dns_cf') if pargs.dns: - wo_acme_dns = pargs.dns - wo_dns = True - else: - wo_acme_dns = '' - wo_dns = False + Log.debug(self, "DNS validation enabled") + acmedata['dns'] = True + if not pargs.dns == 'dns_cf': + Log.debug(self, "DNS API : {0}".format(pargs.dns)) + acmedata['acme_dns'] = pargs.dns + + # detect subdomain and set subdomain variable if pargs.letsencrypt == "subdomain": - wo_subdomain = True - wo_wildcard = False + Log.warn( + self, 'Flag --letsencrypt=subdomain is ' + 'deprecated and not required anymore.') + acme_subdomain = True + acme_wildcard = False elif pargs.letsencrypt == "wildcard": - wo_wildcard = True - wo_subdomain = False + acme_wildcard = True + acme_subdomain = False + acmedata['dns'] = True else: - wo_wildcard = False - wo_subdomain = False - Log.debug(self, "Domain type = {0}" - .format(wo_domain_type)) - if ((wo_domain_type == 'subdomain') and - (not pargs.letsencrypt == 'wildcard')): - wo_subdomain = True + if ((wo_domain_type == 'subdomain')): + Log.debug(self, "Domain type = {0}" + .format(wo_domain_type)) + acme_subdomain = True + else: + acme_subdomain = False + acme_wildcard = False + + if acme_subdomain is True: + acme_domains = acme_domains + ['{0}'.format(wo_domain)] + elif acme_wildcard is True: + acme_domains = acme_domains + ['{0}'.format(wo_domain), + '*.{0}'.format(wo_domain)] + else: + acme_domains = acme_domains + ['{0}'.format(wo_domain), + 'www.{0}'.format(wo_domain)] + + if acme_subdomain is True: # check if a wildcard cert for the root domain exist Log.debug(self, "checkWildcardExist on *.{0}" .format(wo_root_domain)) @@ -757,12 +785,15 @@ class WOSiteCreateController(CementBaseController): else: Log.debug(self, "Setup Cert with acme.sh for {0}" .format(wo_domain)) - setupLetsEncrypt(self, wo_domain, wo_subdomain, - wo_wildcard, wo_dns, wo_acme_dns) + Log.info(self, "Certificate type: Subdomain") + if WOAcme.setupletsencrypt( + self, acme_domains, acmedata): + WOAcme.deploycert(self, wo_domain) else: - setupLetsEncrypt(self, wo_domain, wo_subdomain, - wo_wildcard, wo_dns, wo_acme_dns) - httpsRedirect(self, wo_domain, True, wo_wildcard) + if WOAcme.setupletsencrypt( + self, acme_domains, acmedata): + WOAcme.deploycert(self, wo_domain) + httpsRedirect(self, wo_domain, True, acme_wildcard) if pargs.hsts: SSL.setuphsts(self, wo_domain) @@ -928,7 +959,8 @@ class WOSiteUpdateController(CementBaseController): Log.error(self, 'Unable to input site name, Please try again!') pargs.site_name = pargs.site_name.strip() - (wo_domain, wo_www_domain) = WODomain.validatedomain(self, pargs.site_name) + (wo_domain, + wo_www_domain) = WODomain.validatedomain(self, pargs.site_name) wo_site_webroot = WOVariables.wo_webroot + wo_domain check_site = getSiteInfo(self, wo_domain) @@ -1126,44 +1158,47 @@ class WOSiteUpdateController(CementBaseController): pargs.php73 = False if pargs.letsencrypt: - (wo_domain_type, wo_root_domain) = WODomain.getdomainlevel(self, - wo_domain) + acme_domains = [] + acmedata = dict(acme_domains, dns=False, acme_dns='dns_cf') + (wo_domain_type, + wo_root_domain) = WODomain.getdomainlevel(self, wo_domain) + if pargs.letsencrypt == 'on': data['letsencrypt'] = True letsencrypt = True - if ((wo_domain_type == 'subdomain') and - (not pargs.letsencrypt == 'wildcard')): - wo_subdomain = True + if (wo_domain_type == 'subdomain'): + acme_subdomain = True else: - wo_subdomain = False - wo_wildcard = False + acme_subdomain = False + acme_wildcard = False elif pargs.letsencrypt == 'subdomain': data['letsencrypt'] = True letsencrypt = True - wo_subdomain = True - wo_wildcard = False + acme_subdomain = True + acme_wildcard = False elif pargs.letsencrypt == 'wildcard': data['letsencrypt'] = True letsencrypt = True - wo_wildcard = True - wo_subdomain = False + acme_wildcard = True + acme_subdomain = False + acmedata['dns'] = True elif pargs.letsencrypt == 'off': data['letsencrypt'] = False letsencrypt = False - wo_subdomain = False - wo_wildcard = False + acme_subdomain = False + acme_wildcard = False elif pargs.letsencrypt == 'clean': data['letsencrypt'] = False letsencrypt = False - wo_subdomain = False - wo_wildcard = False + acme_subdomain = False + acme_wildcard = False elif pargs.letsencrypt == 'purge': data['letsencrypt'] = False letsencrypt = False - wo_subdomain = False - wo_wildcard = False + acme_subdomain = False + acme_wildcard = False - if not wo_subdomain: + if not (acme_subdomain is True): if letsencrypt is check_ssl: if letsencrypt is False: Log.error(self, "SSl is not configured for given " @@ -1268,13 +1303,12 @@ class WOSiteUpdateController(CementBaseController): data['php73'] = False php73 = False - if pargs.letsencrypt == "on" or pargs.php73 == "on": - if pargs.php73 == "on": - data['php73'] = True - php73 = True - else: - data['php73'] = False - php73 = False + if pargs.php73 == "on": + data['php73'] = True + php73 = True + else: + data['php73'] = False + php73 = False if pargs.wpredis and data['currcachetype'] != 'wpredis': data['wpredis'] = True @@ -1345,20 +1379,31 @@ class WOSiteUpdateController(CementBaseController): if pargs.letsencrypt: if data['letsencrypt'] is True: + # DNS API configuration if pargs.dns: - wo_acme_dns = pargs.dns - wo_dns = True + Log.debug(self, "DNS validation enabled") + acmedata['dns'] = True + if not pargs.dns == 'dns_cf': + Log.debug(self, "DNS API : {0}".format(pargs.dns)) + acmedata['acme_dns'] = pargs.dns + # Set list of domains to secure + if acme_subdomain is True: + acme_domains = acme_domains + ['{0}'.format(wo_domain)] + elif acme_wildcard is True: + acme_domains = acme_domains + ['{0}'.format(wo_domain), + '*.{0}'.format(wo_domain)] else: - wo_acme_dns = '' - wo_dns = False - if wo_subdomain: + acme_domains = acme_domains + ['{0}'.format(wo_domain), + 'www.{0}'.format(wo_domain)] + + if acme_subdomain: # check if a wildcard cert for the root domain exist Log.debug(self, "checkWildcardExist on *.{0}" .format(wo_root_domain)) iswildcard = SSL.checkwildcardexist(self, wo_root_domain) Log.debug(self, "iswildcard = {0}".format(iswildcard)) if not os.path.isfile("{0}/conf/nginx/ssl.conf.disabled"): - if wo_subdomain: + if acme_subdomain: if iswildcard: Log.info(self, "Using existing Wildcard SSL " "certificate from {0} to secure {1}" @@ -1371,11 +1416,10 @@ class WOSiteUpdateController(CementBaseController): else: Log.debug(self, "Setup Cert with acme.sh for {0}" .format(wo_domain)) - setupLetsEncrypt(self, wo_domain, wo_subdomain, - wo_wildcard, wo_dns, wo_acme_dns) + WOAcme.setupletsencrypt( + self, acme_domains, acmedata) else: - setupLetsEncrypt(self, wo_domain, wo_subdomain, - wo_wildcard, wo_dns, wo_acme_dns) + WOAcme.setupletsencrypt(self, acme_domains, acmedata) else: WOFileUtils.mvfile(self, "{0}/conf/nginx/ssl.conf.disabled" .format(wo_site_webroot), @@ -1387,7 +1431,7 @@ class WOSiteUpdateController(CementBaseController): '/etc/nginx/conf.d/force-ssl-{0}.conf' .format(wo_domain)) - httpsRedirect(self, wo_domain, True, wo_wildcard) + httpsRedirect(self, wo_domain, True, acme_wildcard) SSL.siteurlhttps(self, wo_domain) if not WOService.reload_service(self, 'nginx'): @@ -1396,7 +1440,7 @@ class WOSiteUpdateController(CementBaseController): Log.info(self, "Congratulations! Successfully " "Configured SSL for Site " " https://{0}".format(wo_domain)) - if wo_subdomain and iswildcard: + if acme_subdomain and iswildcard: if (SSL.getexpirationdays(self, wo_root_domain) > 0): Log.info( self, "Your cert will expire within " + diff --git a/wo/cli/plugins/site_functions.py b/wo/cli/plugins/site_functions.py index 8156f11..20376b9 100644 --- a/wo/cli/plugins/site_functions.py +++ b/wo/cli/plugins/site_functions.py @@ -20,6 +20,7 @@ from wo.core.services import WOService from wo.core.shellexec import CommandExecutionError, WOShellExec from wo.core.sslutils import SSL from wo.core.variables import WOVariables +from wo.core.acme import WOAcme class SiteError(Exception): @@ -1348,129 +1349,6 @@ def doCleanupAction(self, domain='', webroot='', dbname='', dbuser='', # setup letsencrypt for domain + www.domain - -def setupLetsEncrypt(self, wo_domain_name, subdomain=False, wildcard=False, - wo_dns=False, wo_acme_dns='dns_cf'): - - if os.path.isfile("/etc/letsencrypt/" - "renewal/{0}_ecc/" - "fullchain.cer".format(wo_domain_name)): - Log.debug(self, "Let's Encrypt certificate " - "found for the domain: {0}" - .format(wo_domain_name)) - ssl = archivedCertificateHandle(self, wo_domain_name) - else: - keylenght = "{0}".format(self.app.config.get('letsencrypt', - 'keylength')) - wo_acme_exec = ("/etc/letsencrypt/acme.sh --config-home " - "'/etc/letsencrypt/config'") - if wo_dns: - acme_mode = "--dns {0}".format(wo_acme_dns) - validation_mode = "DNS with {0}".format(wo_acme_dns) - Log.debug( - self, "Validation : DNS mode with {0}".format(wo_acme_dns)) - else: - acme_mode = "-w /var/www/html" - validation_mode = "Webroot challenge" - Log.debug(self, "Validation : Webroot mode") - if subdomain: - Log.info(self, "Certificate type: Subdomain") - Log.info(self, "Validation mode : {0}".format(validation_mode)) - Log.wait(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "{0} ".format(wo_acme_exec) + - "--issue " - "-d {0} {1} " - "-k {2} -f" - .format(wo_domain_name, - acme_mode, - keylenght)) - elif wildcard: - Log.info(self, "Certificate type: Wildcard") - Log.info(self, "Validation mode : {0}".format(validation_mode)) - Log.wait(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "{0} ".format(wo_acme_exec) + - "--issue " - "-d {0} -d '*.{0}' --dns {1} " - "-k {2} -f" - .format(wo_domain_name, - wo_acme_dns, - keylenght)) - else: - Log.info(self, "Certificate type: Domain + www") - Log.info(self, "Validation mode : {0}".format(validation_mode)) - Log.wait(self, "Issuing SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "{0} ".format(wo_acme_exec) + - "--issue " - "-d {0} -d www.{0} {1} " - "-k {2} -f" - .format(wo_domain_name, - acme_mode, keylenght)) - if ssl: - Log.valide(self, "Issuing SSL cert with acme.sh") - Log.wait(self, "Deploying SSL cert") - Log.debug(self, "Cert deployment for domain: {0}" - .format(wo_domain_name)) - try: - - WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " - "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--install-cert -d {1} --ecc " - "--cert-file {0}/{1}/cert.pem " - "--key-file {0}/{1}/key.pem " - "--fullchain-file " - "{0}/{1}/fullchain.pem " - "--ca-file {0}/{1}/ca.pem " - "--reloadcmd " - "\"nginx -t && " - "service nginx restart\" " - .format(WOVariables.wo_ssl_live, - wo_domain_name)) - Log.valide(self, "Deploying SSL cert") - if os.path.isdir('/var/www/{0}/conf/nginx' - .format(wo_domain_name)): - - sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" - .format(wo_domain_name), - encoding='utf-8', mode='w') - sslconf.write( - "listen 443 ssl http2;\n" - "listen [::]:443 ssl http2;\n" - "ssl_certificate {0}/{1}/fullchain.pem;\n" - "ssl_certificate_key {0}/{1}/key.pem;\n" - "ssl_trusted_certificate {0}/{1}/ca.pem;\n" - "ssl_stapling_verify on;\n" - .format(WOVariables.wo_ssl_live, wo_domain_name)) - sslconf.close() - # updateSiteInfo(self, wo_domain_name, ssl=True) - if not WOFileUtils.grep(self, '/var/www/22222/conf/nginx/ssl.conf', - '/etc/letsencrypt'): - Log.info(self, "Securing WordOps backend with {0} certificate" - .format(wo_domain_name)) - sslconf = open("/var/www/22222/conf/nginx/ssl.conf", - encoding='utf-8', mode='w') - sslconf.write("ssl_certificate {0}/{1}/fullchain.pem;\n" - "ssl_certificate_key {0}/{1}/key.pem;\n" - "ssl_trusted_certificate {0}/{1}/ca.pem;\n" - "ssl_stapling_verify on;\n" - .format(WOVariables.wo_ssl_live, wo_domain_name)) - sslconf.close() - - WOGit.add(self, ["/etc/letsencrypt"], - msg="Adding letsencrypt folder") - - except IOError as e: - Log.debug(self, str(e)) - Log.debug(self, "Error occured while generating " - "ssl.conf") - else: - Log.error(self, "Unable to install certificate", False) - Log.error(self, "Please make sure that your site is pointed to \n" - "same server on which " - "you are running Let\'s Encrypt Client " - "\n to allow it to verify the site automatically.") - # copy wildcard certificate to a subdomain @@ -1616,14 +1494,16 @@ def httpsRedirect(self, wo_domain_name, redirect=True, wildcard=False): def archivedCertificateHandle(self, domain): - Log.warn(self, "You already have an existing certificate " - "for the domain requested.\n" - "(ref: {0}/" - "{1}_ecc/{1}.conf)".format(WOVariables.wo_ssl_archive, domain) + - "\nPlease select an option from below?" - "\n\t1: Reinstall existing certificate" - "\n\t2: Renew & replace the certificate (limit ~5 per 7 days)" - "") + Log.warn( + self, "You already have an existing certificate " + "for the domain requested.\n" + "(ref: {0}/" + "{1}_ecc/{1}.conf)".format(WOVariables.wo_ssl_archive, domain) + + "\nPlease select an option from below?" + "\n\t1: Reinstall existing certificate" + "\n\t2: Issue a new certificate to replace " + "the current one (limit ~5 per 7 days)" + "") check_prompt = input( "\nType the appropriate number [1-2] or any other key to cancel: ") if not os.path.isfile("{0}/{1}/fullchain.pem" @@ -1634,20 +1514,7 @@ def archivedCertificateHandle(self, domain): if check_prompt == "1": Log.info(self, "Reinstalling SSL cert with acme.sh") - ssl = WOShellExec.cmd_exec(self, "mkdir -p {0}/{1} && " - "/etc/letsencrypt/acme.sh " - "--config-home " - "'/etc/letsencrypt/config' " - "--install-cert -d {1} --ecc " - "--cert-file {0}/{1}/cert.pem " - "--key-file {0}/{1}/key.pem " - "--fullchain-file " - "{0}/{1}/fullchain.pem " - "--ca-file {0}/{1}/ca.pem " - "--reloadcmd " - "\"nginx -t && service nginx restart\" " - .format(WOVariables.wo_ssl_live, - domain)) + ssl = WOAcme.deploycert(self, domain) if ssl: try: diff --git a/wo/core/acme.py b/wo/core/acme.py new file mode 100644 index 0000000..987d570 --- /dev/null +++ b/wo/core/acme.py @@ -0,0 +1,129 @@ +import os + +import requests + +from wo.core.fileutils import WOFileUtils +from wo.core.git import WOGit +from wo.core.logging import Log +from wo.core.shellexec import WOShellExec +from wo.core.variables import WOVariables + + +class WOAcme: + """Acme.sh utilities for WordOps""" + + def setupletsencrypt(self, acme_domain, acmedata): + """issue SSL certificates with acme.sh""" + all_domains = '\' -d \''.join(acme_domain) + wo_acme_dns = acmedata['acme_dns'] + keylenght = "{0}".format(self.app.config.get('letsencrypt', + 'keylength')) + wo_acme_exec = ("/etc/letsencrypt/acme.sh --config-home " + "'/etc/letsencrypt/config'") + if acmedata['dns'] is True: + acme_mode = "--dns {0}".format(wo_acme_dns) + validation_mode = "DNS mode with {0}".format(wo_acme_dns) + else: + acme_mode = "-w /var/www/html" + validation_mode = "Webroot challenge" + Log.debug(self, "Validation : Webroot mode") + + Log.info(self, "Validation mode : {0}".format(validation_mode)) + Log.wait(self, "Issuing SSL cert with acme.sh") + try: + WOShellExec.cmd_exec( + self, "{0} ".format(wo_acme_exec) + + "--issue -d '{0}' {1} -k {2} -f" + .format(all_domains, acme_mode, keylenght)) + except Exception as e: + Log.failed(self, "Issuing SSL cert with acme.sh") + Log.debug(self, str(e)) + Log.error(self, "Unable to issue certificate", False) + if acmedata['dns'] is True: + Log.warn( + self, "Please make sure your properly " + "set your DNS API credentials for acme.sh") + else: + server_ip = requests.get('http://v4.wordops.eu/').text + for domain in wo_domain: + domain_ip = requests.get('http://v4.wordops.eu/dns/{0}/' + .format(domain)).text + if(not domain_ip == server_ip): + Log.warn( + self, "{0} is not pointing to your server IP" + .format(domain)) + Log.error( + self, "You have to add the " + "proper DNS record", False) + break + else: + Log.error( + self, "Your domain is properly configured " + "but acme.sh was unable to issue certificate.\n" + "You can find more informations in " + "/var/log/wo/wordops.log", False) + return False + Log.valide(self, "Issuing SSL cert with acme.sh") + return True + + def deploycert(self, wo_domain_name): + wo_acme_exec = ("/etc/letsencrypt/acme.sh --config-home " + "'/etc/letsencrypt/config'") + if not os.path.isfile('/etc/letsencrypt/{0}_ecc/fullchain.cer' + .format(wo_domain_name)): + Log.error(self, 'Certificate not found. Deployment canceled') + + Log.debug(self, "Cert deployment for domain: {0}" + .format(wo_domain_name)) + + try: + Log.wait(self, "Deploying SSL cert") + if WOShellExec.cmd_exec( + self, "mkdir -p {0}/{1} && {2} --install-cert -d {1} --ecc " + "--cert-file {0}/{1}/cert.pem --key-file {0}/{1}/key.pem " + "--fullchain-file {0}/{1}/fullchain.pem " + "--ca-file {0}/{1}/ca.pem --reloadcmd \"nginx -t && " + "service nginx restart\" " + .format(WOVariables.wo_ssl_live, + wo_domain_name, wo_acme_exec)): + Log.valide(self, "Deploying SSL cert") + else: + Log.failed(self, "Deploying SSL cert") + Log.error(self, "Unable to deploy certificate") + + if os.path.isdir('/var/www/{0}/conf/nginx' + .format(wo_domain_name)): + + sslconf = open("/var/www/{0}/conf/nginx/ssl.conf" + .format(wo_domain_name), + encoding='utf-8', mode='w') + sslconf.write( + "listen 443 ssl http2;\n" + "listen [::]:443 ssl http2;\n" + "ssl_certificate {0}/{1}/fullchain.pem;\n" + "ssl_certificate_key {0}/{1}/key.pem;\n" + "ssl_trusted_certificate {0}/{1}/ca.pem;\n" + "ssl_stapling_verify on;\n" + .format(WOVariables.wo_ssl_live, wo_domain_name)) + sslconf.close() + + if not WOFileUtils.grep(self, '/var/www/22222/conf/nginx/ssl.conf', + '/etc/letsencrypt'): + Log.info(self, "Securing WordOps backend with current cert" + .format(wo_domain_name)) + sslconf = open("/var/www/22222/conf/nginx/ssl.conf", + encoding='utf-8', mode='w') + sslconf.write("ssl_certificate {0}/{1}/fullchain.pem;\n" + "ssl_certificate_key {0}/{1}/key.pem;\n" + "ssl_trusted_certificate {0}/{1}/ca.pem;\n" + "ssl_stapling_verify on;\n" + .format(WOVariables.wo_ssl_live, wo_domain_name)) + sslconf.close() + + WOGit.add(self, ["/etc/letsencrypt"], + msg="Adding letsencrypt folder") + + except IOError as e: + Log.debug(self, str(e)) + Log.debug(self, "Error occured while generating " + "ssl.conf") From c99f429f3cdc88121bd66316c9b9fbdba715000b Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 01:00:58 +0200 Subject: [PATCH 16/52] Fix cert deployment --- wo/core/acme.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wo/core/acme.py b/wo/core/acme.py index 987d570..6632806 100644 --- a/wo/core/acme.py +++ b/wo/core/acme.py @@ -69,7 +69,7 @@ class WOAcme: def deploycert(self, wo_domain_name): wo_acme_exec = ("/etc/letsencrypt/acme.sh --config-home " "'/etc/letsencrypt/config'") - if not os.path.isfile('/etc/letsencrypt/{0}_ecc/fullchain.cer' + if not os.path.isfile('/etc/letsencrypt/renewal/{0}_ecc/fullchain.cer' .format(wo_domain_name)): Log.error(self, 'Certificate not found. Deployment canceled') From 7207948727665e25e4c13356edb1c399d58dff5b Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 01:02:44 +0200 Subject: [PATCH 17/52] Fix incorrect variable --- wo/core/acme.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wo/core/acme.py b/wo/core/acme.py index 6632806..f7d423b 100644 --- a/wo/core/acme.py +++ b/wo/core/acme.py @@ -12,9 +12,9 @@ from wo.core.variables import WOVariables class WOAcme: """Acme.sh utilities for WordOps""" - def setupletsencrypt(self, acme_domain, acmedata): + def setupletsencrypt(self, acme_domains, acmedata): """issue SSL certificates with acme.sh""" - all_domains = '\' -d \''.join(acme_domain) + all_domains = '\' -d \''.join(acme_domains) wo_acme_dns = acmedata['acme_dns'] keylenght = "{0}".format(self.app.config.get('letsencrypt', 'keylength')) @@ -45,7 +45,7 @@ class WOAcme: "set your DNS API credentials for acme.sh") else: server_ip = requests.get('http://v4.wordops.eu/').text - for domain in wo_domain: + for domain in acme_domains: domain_ip = requests.get('http://v4.wordops.eu/dns/{0}/' .format(domain)).text if(not domain_ip == server_ip): From cfb7a98c7b71dcd4eef60b4c1198bf4ffe434e71 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 01:06:44 +0200 Subject: [PATCH 18/52] Add missing deploy cert --- wo/cli/plugins/site.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index 4e8a32e..cdda922 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -1416,10 +1416,13 @@ class WOSiteUpdateController(CementBaseController): else: Log.debug(self, "Setup Cert with acme.sh for {0}" .format(wo_domain)) - WOAcme.setupletsencrypt( - self, acme_domains, acmedata) + if WOAcme.setupletsencrypt( + self, acme_domains, acmedata): + WOAcme.deploycert(self, wo_domain) else: - WOAcme.setupletsencrypt(self, acme_domains, acmedata) + if WOAcme.setupletsencrypt( + self, acme_domains, acmedata): + WOAcme.deploycert(self, wo_domain) else: WOFileUtils.mvfile(self, "{0}/conf/nginx/ssl.conf.disabled" .format(wo_site_webroot), From da23fc88aea18219528ab630533ee35e39b2fc4c Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 01:11:27 +0200 Subject: [PATCH 19/52] Improve acme logging --- wo/cli/plugins/site.py | 4 ++++ wo/core/acme.py | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index cdda922..0823eb7 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -1419,10 +1419,14 @@ class WOSiteUpdateController(CementBaseController): if WOAcme.setupletsencrypt( self, acme_domains, acmedata): WOAcme.deploycert(self, wo_domain) + else: + Log.error(self, "Unable to issue certificate") else: if WOAcme.setupletsencrypt( self, acme_domains, acmedata): WOAcme.deploycert(self, wo_domain) + else: + Log.error(self, "Unable to issue certificate") else: WOFileUtils.mvfile(self, "{0}/conf/nginx/ssl.conf.disabled" .format(wo_site_webroot), diff --git a/wo/core/acme.py b/wo/core/acme.py index f7d423b..e870681 100644 --- a/wo/core/acme.py +++ b/wo/core/acme.py @@ -38,7 +38,6 @@ class WOAcme: except Exception as e: Log.failed(self, "Issuing SSL cert with acme.sh") Log.debug(self, str(e)) - Log.error(self, "Unable to issue certificate", False) if acmedata['dns'] is True: Log.warn( self, "Please make sure your properly " From 2ca057e7cedbaa81c453fcc171b6e8b87429d3fe Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 01:14:59 +0200 Subject: [PATCH 20/52] Fix acme return true --- wo/core/acme.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wo/core/acme.py b/wo/core/acme.py index e870681..4acbaa7 100644 --- a/wo/core/acme.py +++ b/wo/core/acme.py @@ -62,8 +62,9 @@ class WOAcme: "You can find more informations in " "/var/log/wo/wordops.log", False) return False - Log.valide(self, "Issuing SSL cert with acme.sh") - return True + else: + Log.valide(self, "Issuing SSL cert with acme.sh") + return True def deploycert(self, wo_domain_name): wo_acme_exec = ("/etc/letsencrypt/acme.sh --config-home " From edde5e4043382778346ac445c924ec08fb9690bc Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 01:16:52 +0200 Subject: [PATCH 21/52] Refactor acme validation --- wo/core/acme.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/wo/core/acme.py b/wo/core/acme.py index 4acbaa7..bc42ee3 100644 --- a/wo/core/acme.py +++ b/wo/core/acme.py @@ -30,12 +30,10 @@ class WOAcme: Log.info(self, "Validation mode : {0}".format(validation_mode)) Log.wait(self, "Issuing SSL cert with acme.sh") - try: - WOShellExec.cmd_exec( + if not WOShellExec.cmd_exec( self, "{0} ".format(wo_acme_exec) + "--issue -d '{0}' {1} -k {2} -f" - .format(all_domains, acme_mode, keylenght)) - except Exception as e: + .format(all_domains, acme_mode, keylenght)): Log.failed(self, "Issuing SSL cert with acme.sh") Log.debug(self, str(e)) if acmedata['dns'] is True: From 818e36afbc046300754fb23a9ee8ad839f167bab Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 01:17:28 +0200 Subject: [PATCH 22/52] Fix debug --- wo/core/acme.py | 1 - 1 file changed, 1 deletion(-) diff --git a/wo/core/acme.py b/wo/core/acme.py index bc42ee3..209cba7 100644 --- a/wo/core/acme.py +++ b/wo/core/acme.py @@ -35,7 +35,6 @@ class WOAcme: "--issue -d '{0}' {1} -k {2} -f" .format(all_domains, acme_mode, keylenght)): Log.failed(self, "Issuing SSL cert with acme.sh") - Log.debug(self, str(e)) if acmedata['dns'] is True: Log.warn( self, "Please make sure your properly " From 5249479c893a9ddccbd84173015237844a1ef66e Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 01:40:26 +0200 Subject: [PATCH 23/52] Display domain type to acme process --- CHANGELOG.md | 2 ++ wo/cli/plugins/site.py | 6 ++++++ wo/cli/plugins/site_functions.py | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3307fcf..84590ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), #### Changed - [APP] WordOps dashboard updated to v1.2, shipped as a html file, it can be used without PHP stack +- [STACK] Refactor Let's Encrypt with acme.sh +- [STACK] Log error improved with acme.sh depending on the acme challenge (DNS API or Webroot) #### Fixed diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index 0823eb7..d48fdd9 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -759,11 +759,14 @@ class WOSiteCreateController(CementBaseController): acme_wildcard = False if acme_subdomain is True: + Log.info(self, "Certificate type : subdomain") acme_domains = acme_domains + ['{0}'.format(wo_domain)] elif acme_wildcard is True: + Log.info(self, "Certificate type : wildcard") acme_domains = acme_domains + ['{0}'.format(wo_domain), '*.{0}'.format(wo_domain)] else: + Log.info(self, "Certificate type : domain") acme_domains = acme_domains + ['{0}'.format(wo_domain), 'www.{0}'.format(wo_domain)] @@ -1388,11 +1391,14 @@ class WOSiteUpdateController(CementBaseController): acmedata['acme_dns'] = pargs.dns # Set list of domains to secure if acme_subdomain is True: + Log.info(self, "Certificate type : subdomain") acme_domains = acme_domains + ['{0}'.format(wo_domain)] elif acme_wildcard is True: + Log.info(self, "Certificate type : wildcard") acme_domains = acme_domains + ['{0}'.format(wo_domain), '*.{0}'.format(wo_domain)] else: + Log.info(self, "Certificate type : domain") acme_domains = acme_domains + ['{0}'.format(wo_domain), 'www.{0}'.format(wo_domain)] diff --git a/wo/cli/plugins/site_functions.py b/wo/cli/plugins/site_functions.py index 20376b9..282021a 100644 --- a/wo/cli/plugins/site_functions.py +++ b/wo/cli/plugins/site_functions.py @@ -1594,7 +1594,7 @@ def setuprocketchat(self): if ((not WOVariables.wo_platform_codename == 'bionic') and (not WOVariables.wo_platform_codename == 'xenial')): Log.info(self, "Rocket.chat is only available on Ubuntu 16.04 " - "& 18.04 LTS") + "& 18.04 LTS") return False else: if not WOAptGet.is_installed(self, 'snapd'): From 5abc051901e5b490b767035b008e7f5486f9e13f Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 12:11:15 +0200 Subject: [PATCH 24/52] Add `wo secure --ssh` --- CHANGELOG.md | 6 ++ config/bash_completion.d/wo_auto.rc | 2 +- wo/cli/plugins/secure.py | 33 +++++++++- wo/cli/plugins/stack.py | 98 ++++++++++++++++++----------- 4 files changed, 97 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84590ce..7d700c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### v3.9.x - [Unreleased] +#### Added + +- [STACK] UFW as a STACK available with `--ufw` + #### Changed - [APP] WordOps dashboard updated to v1.2, shipped as a html file, it can be used without PHP stack - [STACK] Refactor Let's Encrypt with acme.sh - [STACK] Log error improved with acme.sh depending on the acme challenge (DNS API or Webroot) +- [INSTALL] Removed UFW setup from install script +- [APP] phpMyAdmin updated to v4.9.1 #### Fixed diff --git a/config/bash_completion.d/wo_auto.rc b/config/bash_completion.d/wo_auto.rc index f9bd70b..b6abc9e 100644 --- a/config/bash_completion.d/wo_auto.rc +++ b/config/bash_completion.d/wo_auto.rc @@ -74,7 +74,7 @@ _wo_complete() # HANDLE EVERYTHING AFTER THE THIRD LEVEL NAMESPACE "install" | "purge" | "remove" ) COMPREPLY=( $(compgen \ - -W "--recommended --web --admin --security --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --redis --phpredisadmin --composer --netdata --fail2ban --dashboard --proftpd --clamav --mysqlclient --mysqltuner --extplorer --all --force" \ + -W "--web --admin --security --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --redis --phpredisadmin --composer --netdata --fail2ban --dashboard --proftpd --clamav --mysqlclient --mysqltuner --extplorer --all --force" \ -- $cur) ) ;; "upgrade" ) diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index 8977707..b8bf8b8 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -1,4 +1,5 @@ import getpass +import os from cement.core import handler, hook from cement.core.controller import CementBaseController, expose @@ -8,7 +9,9 @@ from wo.core.logging import Log from wo.core.random import RANDOM from wo.core.services import WOService from wo.core.shellexec import WOShellExec +from wo.core.template import WOTemplate from wo.core.variables import WOVariables +from wo.core.services import WOService def wo_secure_hook(app): @@ -31,10 +34,10 @@ class WOSecureController(CementBaseController): dict(help='set backend port', action='store_true')), (['--ip'], dict(help='set backend whitelisted ip', action='store_true')), - (['--ssh-port'], dict( + (['--sshport'], dict( help='set custom ssh port', action='store_true')), - (['--ssh-strict'], dict(help='harden ssh security', - action='store_true')), + (['--ssh'], dict( + help='harden ssh security', action='store_true')), (['--ufw'], dict(help='setup and configure ufw firewall', action='store_true')), @@ -53,6 +56,10 @@ class WOSecureController(CementBaseController): self.secure_port() if pargs.ip: self.secure_ip() + if pargs.sshport: + self.secure_ssh_port() + if pargs.ssh: + self.secure_ssh() @expose(hide=True) def secure_auth(self): @@ -139,6 +146,26 @@ class WOSecureController(CementBaseController): Log.info(self, "Successfully added IP address in acl.conf file") + @expose(hide=True) + def secure_ssh(self): + """Harden ssh security""" + pargs = self.app.pargs + if pargs.user_input: + current_ssh_port = pargs.user_input + else: + if os.path.isfile('/etc/ssh/sshd_config'): + for line in open('/etc/ssh/sshd_config', encoding='utf-8'): + if 'Port' in line: + ssh_line = line.strip() + break + port = (ssh_line).split(' ') + current_ssh_port = port[1] + else: + Log.error(self, "SSH config file not found") + data = dict(sshport=current_ssh_port, allowpass='no') + WOTemplate.render(self, '/etc/ssh/sshd_config', 'sshd.mustache', data) + WOService.restart_service(self, 'ssh') + def load(app): handler.register(WOSecureController) diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index 843e227..c5072a1 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -122,7 +122,6 @@ class WOStackController(CementBaseController): pargs.web = True pargs.admin = True pargs.fail2ban = True - pargs.ufw = True if pargs.all: pargs.web = True @@ -262,8 +261,10 @@ class WOStackController(CementBaseController): # UFW if pargs.ufw: - Log.debug(self, "Setting apt_packages variable for UFW") - apt_packages = apt_packages + ["ufw"] + if not WOFileUtils.grep( + self, '/etc/ufw/ufw.conf', 'ENABLED=yes'): + Log.debug(self, "Setting apt_packages variable for UFW") + apt_packages = apt_packages + ["ufw"] # sendmail if pargs.sendmail: @@ -579,14 +580,17 @@ class WOStackController(CementBaseController): # REDIS if pargs.redis: - Log.debug(self, "Remove apt_packages variable of Redis") - apt_packages = apt_packages + ["redis-server"] + if WOAptGet.is_installed(self, 'redis-server'): + Log.debug(self, "Remove apt_packages variable of Redis") + apt_packages = apt_packages + ["redis-server"] # MariaDB if pargs.mysql: - Log.debug(self, "Removing apt_packages variable of MySQL") - apt_packages = apt_packages + ['mariadb-server', 'mysql-common', - 'mariadb-client'] + if WOAptGet.is_installed(self, 'mariadb-server'): + Log.debug(self, "Removing apt_packages variable of MySQL") + apt_packages = apt_packages + ['mariadb-server', + 'mysql-common', + 'mariadb-client'] # mysqlclient if pargs.mysqlclient: @@ -621,8 +625,9 @@ class WOStackController(CementBaseController): # UFW if pargs.ufw: - Log.debug(self, "Remove apt_packages variable for UFW") - apt_packages = apt_packages + ["ufw"] + if WOAptGet.is_installed(self, 'ufw'): + Log.debug(self, "Remove apt_packages variable for UFW") + apt_packages = apt_packages + ["ufw"] # WPCLI if pargs.wpcli: @@ -632,18 +637,22 @@ class WOStackController(CementBaseController): # PHPMYADMIN if pargs.phpmyadmin: - Log.debug(self, "Removing package of phpMyAdmin ") - packages = packages + ['{0}22222/htdocs/db/pma' - .format(WOVariables.wo_webroot)] + if os.path.isdir('{0}22222/htdocs/db/pma' + .format(WOVariables.wo_webroot)): + Log.debug(self, "Removing package of phpMyAdmin ") + packages = packages + ['{0}22222/htdocs/db/pma' + .format(WOVariables.wo_webroot)] # Composer if pargs.composer: Log.debug(self, "Removing package of Composer ") if os.path.isfile('/usr/local/bin/composer'): packages = packages + ['/usr/local/bin/composer'] + # MySQLTuner if pargs.mysqltuner: - Log.debug(self, "Removing packages for MySQLTuner ") - packages = packages + ['/usr/bin/mysqltuner'] + if os.path.isfile(/usr/bin/mysqltuner): + Log.debug(self, "Removing packages for MySQLTuner ") + packages = packages + ['/usr/bin/mysqltuner'] # PHPREDISADMIN if pargs.phpredisadmin: @@ -655,9 +664,11 @@ class WOStackController(CementBaseController): .format(WOVariables.wo_webroot)] # ADMINER if pargs.adminer: - Log.debug(self, "Removing package variable of Adminer ") - packages = packages + ['{0}22222/htdocs/db/adminer' - .format(WOVariables.wo_webroot)] + if os.path.isdir('{0}22222/htdocs/db/adminer' + .format(WOVariables.wo_webroot)): + Log.debug(self, "Removing package variable of Adminer ") + packages = packages + ['{0}22222/htdocs/db/adminer' + .format(WOVariables.wo_webroot)] if pargs.utils: Log.debug(self, "Removing package variable of utils ") packages = packages + ['{0}22222/htdocs/php/webgrind/' @@ -677,11 +688,17 @@ class WOStackController(CementBaseController): packages = packages + ['/var/lib/wo/tmp/kickstart.sh'] if pargs.dashboard: - Log.debug(self, "Removing Wo-Dashboard") - packages = packages + ['{0}22222/htdocs/assets' - .format(WOVariables.wo_webroot), - '{0}22222/htdocs/index.php' - .format(WOVariables.wo_webroot)] + if (os.path.isfile('{0}22222/htdocs/index.php' + .format(WOVariables.wo_webroot)) or + os.path.isfile('{0}22222/htdocs/index.html' + .format(WOVariables.wo_webroot))): + Log.debug(self, "Removing Wo-Dashboard") + packages = packages + ['{0}22222/htdocs/assets' + .format(WOVariables.wo_webroot), + '{0}22222/htdocs/index.php' + .format(WOVariables.wo_webroot), + '{0}22222/htdocs/index.html' + .format(WOVariables.wo_webroot)] if (packages) or (apt_packages): if (not pargs.force): @@ -693,10 +710,10 @@ class WOStackController(CementBaseController): if start_remove != "Y" and start_remove != "y": Log.error(self, "Not starting stack removal") - if (set(["nginx-custom"]).issubset(set(apt_packages))): + if 'nginx-custom' in apt_packages: WOService.stop_service(self, 'nginx') - if (set(["mariadb-server"]).issubset(set(apt_packages))): + if 'mariadb-server' in apt_packages: WOMysql.backupAll(self) WOService.stop_service(self, 'mysql') @@ -814,7 +831,6 @@ class WOStackController(CementBaseController): else: Log.info(self, "Redis is not installed") - # MariaDB if pargs.mysql: if WOAptGet.is_installed(self, 'mariadb-server'): @@ -840,8 +856,8 @@ class WOStackController(CementBaseController): # ClamAV if pargs.clamav: - Log.debug(self, "Add ClamAV to apt_packages list") if WOAptGet.is_installed(self, 'clamav'): + Log.debug(self, "Add ClamAV to apt_packages list") apt_packages = apt_packages + WOVariables.wo_clamav # UFW @@ -864,25 +880,29 @@ class WOStackController(CementBaseController): # WP-CLI if pargs.wpcli: - Log.debug(self, "Purge package variable WPCLI") if os.path.isfile('/usr/local/bin/wp'): + Log.debug(self, "Purge package variable WPCLI") packages = packages + ['/usr/local/bin/wp'] # PHPMYADMIN if pargs.phpmyadmin: - packages = packages + ['{0}22222/htdocs/db/pma'. - format(WOVariables.wo_webroot)] - Log.debug(self, "Purge package variable phpMyAdmin") + if os.path.isdir('{0}22222/htdocs/db/pma' + .format(WOVariables.wo_webroot)): + Log.debug(self, "Removing package of phpMyAdmin ") + packages = packages + ['{0}22222/htdocs/db/pma' + .format(WOVariables.wo_webroot)] # Composer if pargs.composer: - Log.debug(self, "Removing package variable of Composer ") if os.path.isfile('/usr/local/bin/composer'): + Log.debug(self, "Removing package variable of Composer ") packages = packages + ['/usr/local/bin/composer'] + # MySQLTuner if pargs.mysqltuner: - Log.debug(self, "Removing packages for MySQLTuner ") - packages = packages + ['/usr/bin/mysqltuner'] + if os.path.isfile(/usr/bin/mysqltuner): + Log.debug(self, "Removing packages for MySQLTuner ") + packages = packages + ['/usr/bin/mysqltuner'] # PHPREDISADMIN if pargs.phpredisadmin: @@ -892,11 +912,13 @@ class WOStackController(CementBaseController): packages = packages + ['{0}22222/htdocs/' 'cache/redis' .format(WOVariables.wo_webroot)] - # Adminer + # ADMINER if pargs.adminer: - Log.debug(self, "Purge package variable Adminer") - packages = packages + ['{0}22222/htdocs/db/adminer' - .format(WOVariables.wo_webroot)] + if os.path.isdir('{0}22222/htdocs/db/adminer' + .format(WOVariables.wo_webroot)): + Log.debug(self, "Removing package variable of Adminer ") + packages = packages + ['{0}22222/htdocs/db/adminer' + .format(WOVariables.wo_webroot)] # utils if pargs.utils: Log.debug(self, "Purge package variable utils") From 986ed19f64aeb1ecef59948f56b418201dc48b96 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 12:24:10 +0200 Subject: [PATCH 25/52] Fix typo in stack --- CHANGELOG.md | 1 + wo/cli/plugins/secure.py | 1 - wo/cli/plugins/stack.py | 4 ++-- wo/core/variables.py | 37 +++++++++++++++---------------------- 4 files changed, 18 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d700c2..e8da00f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), #### Added - [STACK] UFW as a STACK available with `--ufw` +- [SECURE] `wo stack secure --ssh` to harden ssh security #### Changed diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index b8bf8b8..fc42521 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -11,7 +11,6 @@ from wo.core.services import WOService from wo.core.shellexec import WOShellExec from wo.core.template import WOTemplate from wo.core.variables import WOVariables -from wo.core.services import WOService def wo_secure_hook(app): diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index c5072a1..eee1e0b 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -650,7 +650,7 @@ class WOStackController(CementBaseController): # MySQLTuner if pargs.mysqltuner: - if os.path.isfile(/usr/bin/mysqltuner): + if os.path.isfile('/usr/bin/mysqltuner'): Log.debug(self, "Removing packages for MySQLTuner ") packages = packages + ['/usr/bin/mysqltuner'] @@ -900,7 +900,7 @@ class WOStackController(CementBaseController): # MySQLTuner if pargs.mysqltuner: - if os.path.isfile(/usr/bin/mysqltuner): + if os.path.isfile('/usr/bin/mysqltuner'): Log.debug(self, "Removing packages for MySQLTuner ") packages = packages + ['/usr/bin/mysqltuner'] diff --git a/wo/core/variables.py b/wo/core/variables.py index 2db38dd..9ad512b 100644 --- a/wo/core/variables.py +++ b/wo/core/variables.py @@ -15,7 +15,7 @@ class WOVariables(): # WordOps packages versions wo_wp_cli = "2.3.0" wo_adminer = "4.7.2" - wo_phpmyadmin = "4.9.0.1" + wo_phpmyadmin = "4.9.1" wo_extplorer = "2.1.13" wo_dashboard = "1.2" @@ -111,15 +111,6 @@ class WOVariables(): wo_nginx = ["nginx-custom", "nginx-wo"] wo_nginx_key = '188C9FB063F0247A' - # PHP repo and packages - if wo_distro == 'ubuntu': - wo_php_repo = "ppa:ondrej/php" - else: - wo_php_repo = ( - "deb https://packages.sury.org/php/ {codename} main" - .format(codename=wo_platform_codename)) - wo_php_key = 'AC0E47584A7A714D' - wo_php = ["php7.2-fpm", "php7.2-curl", "php7.2-gd", "php7.2-imap", "php7.2-readline", "php7.2-common", "php7.2-recode", "php7.2-cli", "php7.2-mbstring", "php7.2-intl", @@ -133,18 +124,6 @@ class WOVariables(): wo_php_extra = ["php-memcached", "php-imagick", "graphviz", "php-xdebug", "php-msgpack", "php-redis"] - # MySQL repo and packages - if wo_distro == 'ubuntu': - wo_mysql_repo = ("deb [arch=amd64,ppc64el] " - "http://sfo1.mirrors.digitalocean.com/mariadb/repo/" - "10.3/ubuntu {codename} main" - .format(codename=wo_platform_codename)) - else: - wo_mysql_repo = ("deb [arch=amd64,ppc64el] " - "http://sfo1.mirrors.digitalocean.com/mariadb/repo/" - "10.3/debian {codename} main" - .format(codename=wo_platform_codename)) - if not wo_distro == 'raspbian': if (not wo_platform_codename == 'jessie'): wo_mysql = ["mariadb-server", "percona-toolkit", @@ -166,11 +145,25 @@ class WOVariables(): # Redis repo details if wo_distro == 'ubuntu': + wo_php_repo = "ppa:ondrej/php" wo_redis_repo = ("ppa:chris-lea/redis-server") + wo_goaccess_repo = ("ppa:alex-p/goaccess") + wo_mysql_repo = ("deb [arch=amd64,ppc64el] " + "http://sfo1.mirrors.digitalocean.com/mariadb/repo/" + "10.3/ubuntu {codename} main" + .format(codename=wo_platform_codename)) else: + wo_php_repo = ( + "deb https://packages.sury.org/php/ {codename} main" + .format(codename=wo_platform_codename)) + wo_php_key = 'AC0E47584A7A714D' wo_redis_repo = ("deb https://packages.sury.org/php/ {codename} all" .format(codename=wo_platform_codename)) + wo_mysql_repo = ("deb [arch=amd64,ppc64el] " + "http://sfo1.mirrors.digitalocean.com/mariadb/repo/" + "10.3/debian {codename} main" + .format(codename=wo_platform_codename)) wo_redis = ['redis-server'] From 050365ace6bf2c29fd76dabaab219785dc19be2d Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 12:32:28 +0200 Subject: [PATCH 26/52] Fix secure --- wo/cli/plugins/secure.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index fc42521..fe50e28 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -157,10 +157,13 @@ class WOSecureController(CementBaseController): if 'Port' in line: ssh_line = line.strip() break - port = (ssh_line).split(' ') - current_ssh_port = port[1] + else: + port = (ssh_line).split(' ') + current_ssh_port = port[1] else: Log.error(self, "SSH config file not found") + if not current_ssh_port: + current_ssh_port = '22' data = dict(sshport=current_ssh_port, allowpass='no') WOTemplate.render(self, '/etc/ssh/sshd_config', 'sshd.mustache', data) WOService.restart_service(self, 'ssh') From 7f11e2404477cbdc8cd38fb974ba3864f2c92b5b Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 12:35:11 +0200 Subject: [PATCH 27/52] Another fix for secure --- wo/cli/plugins/secure.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index fe50e28..e71ef5e 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -148,20 +148,16 @@ class WOSecureController(CementBaseController): @expose(hide=True) def secure_ssh(self): """Harden ssh security""" - pargs = self.app.pargs - if pargs.user_input: - current_ssh_port = pargs.user_input - else: - if os.path.isfile('/etc/ssh/sshd_config'): - for line in open('/etc/ssh/sshd_config', encoding='utf-8'): - if 'Port' in line: - ssh_line = line.strip() + if os.path.isfile('/etc/ssh/sshd_config'): + for line in open('/etc/ssh/sshd_config', encoding='utf-8'): + if 'Port' in line: + ssh_line = line.strip() break - else: - port = (ssh_line).split(' ') - current_ssh_port = port[1] else: - Log.error(self, "SSH config file not found") + port = (ssh_line).split(' ') + current_ssh_port = port[1] + else: + Log.error(self, "SSH config file not found") if not current_ssh_port: current_ssh_port = '22' data = dict(sshport=current_ssh_port, allowpass='no') From fcab2079df6dea31daaa3dc2289863b285ddeb94 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 12:38:16 +0200 Subject: [PATCH 28/52] Fix secure identation --- wo/cli/plugins/secure.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index e71ef5e..b7d18c4 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -148,7 +148,9 @@ class WOSecureController(CementBaseController): @expose(hide=True) def secure_ssh(self): """Harden ssh security""" + Log.debug(self, "check if /etc/ssh/sshd_config exist") if os.path.isfile('/etc/ssh/sshd_config'): + Log.debug(self, "looking for the current ssh port") for line in open('/etc/ssh/sshd_config', encoding='utf-8'): if 'Port' in line: ssh_line = line.strip() @@ -156,13 +158,12 @@ class WOSecureController(CementBaseController): else: port = (ssh_line).split(' ') current_ssh_port = port[1] + data = dict(sshport=current_ssh_port, allowpass='no') + WOTemplate.render(self, '/etc/ssh/sshd_config', + 'sshd.mustache', data) + WOService.restart_service(self, 'ssh') else: Log.error(self, "SSH config file not found") - if not current_ssh_port: - current_ssh_port = '22' - data = dict(sshport=current_ssh_port, allowpass='no') - WOTemplate.render(self, '/etc/ssh/sshd_config', 'sshd.mustache', data) - WOService.restart_service(self, 'ssh') def load(app): From f1cead141d096e5ce657954dfb64584b592d271c Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 12:42:58 +0200 Subject: [PATCH 29/52] change secure --- wo/cli/plugins/secure.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index b7d18c4..358af5c 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -155,13 +155,13 @@ class WOSecureController(CementBaseController): if 'Port' in line: ssh_line = line.strip() break - else: - port = (ssh_line).split(' ') - current_ssh_port = port[1] - data = dict(sshport=current_ssh_port, allowpass='no') - WOTemplate.render(self, '/etc/ssh/sshd_config', - 'sshd.mustache', data) - WOService.restart_service(self, 'ssh') + + port = (ssh_line).split(' ') + current_ssh_port = (port[1]).strip() + data = dict(sshport=current_ssh_port, allowpass='no') + WOTemplate.render(self, '/etc/ssh/sshd_config', + 'sshd.mustache', data) + WOService.restart_service(self, 'ssh') else: Log.error(self, "SSH config file not found") From a93f735fca856eba6f1b53f0c3161adc99ff377b Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 13:09:15 +0200 Subject: [PATCH 30/52] Add `wo secure --sshport` --- CHANGELOG.md | 2 ++ wo/cli/plugins/secure.py | 56 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8da00f..e12a47e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - [STACK] UFW as a STACK available with `--ufw` - [SECURE] `wo stack secure --ssh` to harden ssh security +- [SECURE] `wo stack secure --sshport` to change ssh port #### Changed @@ -20,6 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - [STACK] Log error improved with acme.sh depending on the acme challenge (DNS API or Webroot) - [INSTALL] Removed UFW setup from install script - [APP] phpMyAdmin updated to v4.9.1 +- [STACK] Commit possible Nginx configuration changes into Git before and after performing tasks (in `wo secure` for example) #### Fixed diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index 358af5c..7016c4d 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -63,6 +63,8 @@ class WOSecureController(CementBaseController): @expose(hide=True) def secure_auth(self): """This function secures authentication""" + WOGit.add(self, ["/etc/nginx"], + msg="Add Nginx to into Git") pargs = self.app.pargs passwd = RANDOM.long(self) if not pargs.user_input: @@ -94,16 +96,19 @@ class WOSecureController(CementBaseController): @expose(hide=True) def secure_port(self): """This function Secures port""" + WOGit.add(self, ["/etc/nginx"], + msg="Add Nginx to into Git") pargs = self.app.pargs if pargs.user_input: - while not pargs.user_input.isdigit(): + while ((not pargs.user_input.isdigit()) and + (not pargs.user_input < 65556)): Log.info(self, "Please enter a valid port number ") pargs.user_input = input("WordOps " "admin port [22222]:") if not pargs.user_input: port = input("WordOps admin port [22222]:") if port == "": - pargs.user_input = 22222 + port = 22222 while (not port.isdigit()) and (port != "") and (not port < 65556): Log.info(self, "Please Enter valid port number :") port = input("WordOps admin port [22222]:") @@ -123,6 +128,8 @@ class WOSecureController(CementBaseController): @expose(hide=True) def secure_ip(self): """IP whitelisting""" + WOGit.add(self, ["/etc/nginx"], + msg="Add Nginx to into Git") pargs = self.app.pargs if not pargs.user_input: ip = input("Enter the comma separated IP addresses " @@ -148,6 +155,14 @@ class WOSecureController(CementBaseController): @expose(hide=True) def secure_ssh(self): """Harden ssh security""" + start_secure = input('Are you sure you to want to' + ' harden SSH security ?' + '\nSSH login with password will not ' + 'be possible anymore. Please make sure ' + 'you are already using SSH Keys.\n' + 'Harden SSH security [y/N]') + if start_secure != "Y" and start_secure != "y": + Log.error(self, "Not hardening SSH security") Log.debug(self, "check if /etc/ssh/sshd_config exist") if os.path.isfile('/etc/ssh/sshd_config'): Log.debug(self, "looking for the current ssh port") @@ -155,16 +170,49 @@ class WOSecureController(CementBaseController): if 'Port' in line: ssh_line = line.strip() break - port = (ssh_line).split(' ') current_ssh_port = (port[1]).strip() data = dict(sshport=current_ssh_port, allowpass='no') WOTemplate.render(self, '/etc/ssh/sshd_config', 'sshd.mustache', data) - WOService.restart_service(self, 'ssh') + WOGit.add(self, ["/etc/ssh"], + msg="Adding changed SSH port into Git") + if not WOService.restart_service(self, 'ssh'): + Log.error(self, "service SSH restart failed.") + Log.info(self, "Successfully harden SSH security") else: Log.error(self, "SSH config file not found") + @expose(hide=True) + def secure_ssh_port(self): + """Change SSH port""" + WOGit.add(self, ["/etc/ssh"], + msg="Adding changed SSH port into Git") + pargs = self.app.pargs + if pargs.user_input: + while ((not pargs.user_input.isdigit()) and + (not pargs.user_input < 65556)): + Log.info(self, "Please enter a valid port number ") + pargs.user_input = input("Server " + "SSH port [22]:") + if not pargs.user_input: + port = input("Server SSH port [22]:") + if port == "": + port = 22 + while (not port.isdigit()) and (port != "") and (not port < 65556): + Log.info(self, "Please Enter valid port number :") + port = input("Server SSH port [22]:") + pargs.user_input = port + WOShellExec.cmd_exec(self, "sed -i \"s/Port.*/Port " + "{port}\" /etc/ssh/sshd_config" + .format(port=pargs.user_input)) + WOGit.add(self, ["/etc/ssh"], + msg="Adding changed SSH port into Git") + if not WOService.restart_service(self, 'ssh'): + Log.error(self, "service SSH restart failed.") + Log.info(self, "Successfully changed SSH port to {port}" + .format(port=pargs.user_input)) + def load(app): handler.register(WOSecureController) From b91059942f718998b7f076a453edb24f16666eb5 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 13:09:32 +0200 Subject: [PATCH 31/52] Update bash-completion --- config/bash_completion.d/wo_auto.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/bash_completion.d/wo_auto.rc b/config/bash_completion.d/wo_auto.rc index b6abc9e..b7675a2 100644 --- a/config/bash_completion.d/wo_auto.rc +++ b/config/bash_completion.d/wo_auto.rc @@ -47,7 +47,7 @@ _wo_complete() "secure") COMPREPLY=( $(compgen \ - -W "--auth --port --ip" \ + -W "--auth --port --ip --ssh --sshport" \ -- $cur) ) ;; From e3d65ad458d93f359453cf277edeeb911fdd6a67 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 13:15:07 +0200 Subject: [PATCH 32/52] Add debian to ssh users list --- CHANGELOG.md | 3 ++- wo/cli/templates/sshd.mustache | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e12a47e..3e5e3ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), #### Added -- [STACK] UFW as a STACK available with `--ufw` +- [STACK] UFW now available as a stack with flag `--ufw` - [SECURE] `wo stack secure --ssh` to harden ssh security - [SECURE] `wo stack secure --sshport` to change ssh port @@ -27,6 +27,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - `wo stack purge --all` failure if mysql isn't installed - Fix EEv3 files cleanup +- Incorrect variable usage in `wo secure --port` ### v3.9.8.12 - 2019-09-20 diff --git a/wo/cli/templates/sshd.mustache b/wo/cli/templates/sshd.mustache index 6f19a03..cdd2939 100644 --- a/wo/cli/templates/sshd.mustache +++ b/wo/cli/templates/sshd.mustache @@ -10,7 +10,7 @@ HostKey /etc/ssh/ssh_host_ed25519_key PermitRootLogin without-password # Allow ssh access to some users only -AllowUsers root ubuntu +AllowUsers root ubuntu debian # allow ssh key Authentication PubkeyAuthentication yes From 2a41abbce71411e13e2fe63a5c6ecbe965bd7bfd Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 13:46:31 +0200 Subject: [PATCH 33/52] Add current user to allowed ssh users --- wo/cli/plugins/secure.py | 7 ++++++- wo/cli/templates/sshd.mustache | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index 7016c4d..a0ac649 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -172,7 +172,12 @@ class WOSecureController(CementBaseController): break port = (ssh_line).split(' ') current_ssh_port = (port[1]).strip() - data = dict(sshport=current_ssh_port, allowpass='no') + if os.getenv('SUDO_USER'): + sudo_user = os.environ['SUDO_USER'] + else: + sudo_user = '' + data = dict(sshport=current_ssh_port, allowpass='no', + user=sudo_user) WOTemplate.render(self, '/etc/ssh/sshd_config', 'sshd.mustache', data) WOGit.add(self, ["/etc/ssh"], diff --git a/wo/cli/templates/sshd.mustache b/wo/cli/templates/sshd.mustache index cdd2939..2266ee2 100644 --- a/wo/cli/templates/sshd.mustache +++ b/wo/cli/templates/sshd.mustache @@ -10,7 +10,7 @@ HostKey /etc/ssh/ssh_host_ed25519_key PermitRootLogin without-password # Allow ssh access to some users only -AllowUsers root ubuntu debian +AllowUsers root ubuntu debian {{user}} # allow ssh key Authentication PubkeyAuthentication yes From 65bab99ddad0a4c4d280bb938bc1f1f6ec874121 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 14:24:16 +0200 Subject: [PATCH 34/52] Fix EEv3 migration --- CHANGELOG.md | 1 + install | 16 +++++----------- wo/cli/plugins/secure.py | 8 ++++---- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e5e3ad..4f79ca5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - `wo stack purge --all` failure if mysql isn't installed - Fix EEv3 files cleanup - Incorrect variable usage in `wo secure --port` +- Fix backup_ee function in install script ### v3.9.8.12 - 2019-09-20 diff --git a/install b/install index 3e13ba4..a380e30 100755 --- a/install +++ b/install @@ -624,17 +624,11 @@ wo_update_latest() { } wo_backup_ee() { - if [ -d /etc/nginx ]; then - local EE_NGINX="/etc/nginx" - else - local EE_NGINX="" - fi - if [ -d /etc/letsencrypt ]; then - local EE_LE="/etc/letsencrypt" - else - local EE_LE="" - fi - /bin/tar -I pigz -cf "$EE_BACKUP_FILE" "$EE_NGINX" /usr/local/bin/ee /usr/lib/ee/templates /usr/local/lib/python3.*/dist-packages/ee-*.egg /etc/ee /var/lib/ee "$EE_LE" + local BACKUP_EE="" + [ -d /etc/nginx ] && { BACKUP_EE="$BACKUP_EE /etc/nginx"; } + [ -d /etc/letsencrypt ] && { BACKUP_EE="$BACKUP_EE /etc/letsencrypt"; } + /bin/tar -I pigz -cf "$EE_BACKUP_FILE" /usr/local/bin/ee /usr/lib/ee/templates /usr/local/lib/python3.*/dist-packages/ee-*.egg /etc/ee /var/lib/ee "$BACKUP_EE" + return 0 } wo_backup_wo() { diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index a0ac649..31e176b 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -101,7 +101,7 @@ class WOSecureController(CementBaseController): pargs = self.app.pargs if pargs.user_input: while ((not pargs.user_input.isdigit()) and - (not pargs.user_input < 65556)): + (not pargs.user_input < 65536)): Log.info(self, "Please enter a valid port number ") pargs.user_input = input("WordOps " "admin port [22222]:") @@ -109,7 +109,7 @@ class WOSecureController(CementBaseController): port = input("WordOps admin port [22222]:") if port == "": port = 22222 - while (not port.isdigit()) and (port != "") and (not port < 65556): + while (not port.isdigit()) and (port != "") and (not port < 65536): Log.info(self, "Please Enter valid port number :") port = input("WordOps admin port [22222]:") pargs.user_input = port @@ -196,7 +196,7 @@ class WOSecureController(CementBaseController): pargs = self.app.pargs if pargs.user_input: while ((not pargs.user_input.isdigit()) and - (not pargs.user_input < 65556)): + (not pargs.user_input < 65536)): Log.info(self, "Please enter a valid port number ") pargs.user_input = input("Server " "SSH port [22]:") @@ -204,7 +204,7 @@ class WOSecureController(CementBaseController): port = input("Server SSH port [22]:") if port == "": port = 22 - while (not port.isdigit()) and (port != "") and (not port < 65556): + while (not port.isdigit()) and (port != "") and (not port < 65536): Log.info(self, "Please Enter valid port number :") port = input("Server SSH port [22]:") pargs.user_input = port From 490a7969e1afe2870b904ed10f9448479a89b412 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 15:43:23 +0200 Subject: [PATCH 35/52] Fix signal for cement --- wo/cli/controllers/base.py | 2 ++ wo/cli/ext/wo_outputhandler.py | 3 ++- wo/cli/main.py | 2 +- wo/cli/plugins/debug.py | 2 +- wo/cli/plugins/secure.py | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/wo/cli/controllers/base.py b/wo/cli/controllers/base.py index ac0e34f..62c9587 100644 --- a/wo/cli/controllers/base.py +++ b/wo/cli/controllers/base.py @@ -1,7 +1,9 @@ """WordOps base controller.""" from cement.core.controller import CementBaseController, expose + from wo.core.variables import WOVariables + VERSION = WOVariables.wo_version BANNER = """ diff --git a/wo/cli/ext/wo_outputhandler.py b/wo/cli/ext/wo_outputhandler.py index a39b5f9..c472420 100644 --- a/wo/cli/ext/wo_outputhandler.py +++ b/wo/cli/ext/wo_outputhandler.py @@ -2,8 +2,9 @@ # To avoid encoding releated error,we defined our custom output handler # I hope we will remove this when we upgarde to Cement 2.6 (Not released yet) import os -from cement.utils import fs + from cement.ext.ext_mustache import MustacheOutputHandler +from cement.utils import fs class WOOutputHandler(MustacheOutputHandler): diff --git a/wo/cli/main.py b/wo/cli/main.py index 864deff..00ddd6a 100644 --- a/wo/cli/main.py +++ b/wo/cli/main.py @@ -2,8 +2,8 @@ import os import sys -from cement.core.foundation import CementApp from cement.core.exc import CaughtSignal, FrameworkError +from cement.core.foundation import CementApp from cement.ext.ext_argparse import ArgParseArgumentHandler from cement.utils.misc import init_defaults diff --git a/wo/cli/plugins/debug.py b/wo/cli/plugins/debug.py index 9c2def0..8cc2482 100644 --- a/wo/cli/plugins/debug.py +++ b/wo/cli/plugins/debug.py @@ -588,7 +588,7 @@ class WODebugController(CementBaseController): " disabled".format(self.app.pargs.site_name)) @expose(hide=True) - def signal_handler(self, signal, frame): + def signal_handler(self, app, signal, frame): """Handle Ctrl+c hevent for -i option of debug""" self.start = False if self.app.pargs.nginx: diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index 31e176b..c042cda 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -173,7 +173,7 @@ class WOSecureController(CementBaseController): port = (ssh_line).split(' ') current_ssh_port = (port[1]).strip() if os.getenv('SUDO_USER'): - sudo_user = os.environ['SUDO_USER'] + sudo_user = os.getenv('SUDO_USER') else: sudo_user = '' data = dict(sshport=current_ssh_port, allowpass='no', From 907d1dc5f5a1b4597ae44f7aff50ef2378248417 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 16:12:01 +0200 Subject: [PATCH 36/52] Testing cement 2.10.12 --- requirements.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 2df9c0a..29f8528 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -cement>=2.8.2 +cement>=2.10.12 diff --git a/setup.py b/setup.py index 09aa70a..81601f1 100644 --- a/setup.py +++ b/setup.py @@ -46,7 +46,7 @@ setup(name='wo', # "nose", # "coverage", # Required to function - 'cement == 2.8.2', + 'cement == 2.10.12', 'pystache', 'python-apt', 'pynginxconfig', From 782cb2e440981e4c8c6b529d44dffe6b1224142e Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 16:35:20 +0200 Subject: [PATCH 37/52] Rename template function --- wo/cli/bootstrap.py | 1 + wo/cli/plugins/secure.py | 2 +- wo/cli/plugins/stack_pref.py | 72 ++++++++++++++++++------------------ wo/core/database.py | 3 +- wo/core/template.py | 3 +- 5 files changed, 42 insertions(+), 39 deletions(-) diff --git a/wo/cli/bootstrap.py b/wo/cli/bootstrap.py index c982bc0..6df352d 100644 --- a/wo/cli/bootstrap.py +++ b/wo/cli/bootstrap.py @@ -4,6 +4,7 @@ # in this file in the same way as WOBaseController. from cement.core import handler + from wo.cli.controllers.base import WOBaseController diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index c042cda..e86dd9f 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -178,7 +178,7 @@ class WOSecureController(CementBaseController): sudo_user = '' data = dict(sshport=current_ssh_port, allowpass='no', user=sudo_user) - WOTemplate.render(self, '/etc/ssh/sshd_config', + WOTemplate.deploy(self, '/etc/ssh/sshd_config', 'sshd.mustache', data) WOGit.add(self, ["/etc/ssh"], msg="Adding changed SSH port into Git") diff --git a/wo/cli/plugins/stack_pref.py b/wo/cli/plugins/stack_pref.py index aca2225..7681829 100644 --- a/wo/cli/plugins/stack_pref.py +++ b/wo/cli/plugins/stack_pref.py @@ -159,22 +159,22 @@ def post_pref(self, apt_packages, packages, upgrade=False): ["/etc/nginx"], msg="Adding Nginx into Git") data = dict(tls13=True) - WOTemplate.render(self, + WOTemplate.deploy(self, '/etc/nginx/nginx.conf', 'nginx-core.mustache', data) if not os.path.isfile('{0}/gzip.conf.disabled'.format(ngxcnf)): data = dict() - WOTemplate.render(self, '{0}/gzip.conf'.format(ngxcnf), + WOTemplate.deploy(self, '{0}/gzip.conf'.format(ngxcnf), 'gzip.mustache', data) if not os.path.isfile('{0}/brotli.conf'.format(ngxcnf)): - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/brotli.conf.disabled' .format(ngxcnf), 'brotli.mustache', data) - WOTemplate.render(self, '{0}/tweaks.conf'.format(ngxcnf), + WOTemplate.deploy(self, '{0}/tweaks.conf'.format(ngxcnf), 'tweaks.mustache', data) # Fix for white screen death with NGINX PLUS @@ -187,26 +187,26 @@ def post_pref(self, apt_packages, packages, upgrade=False): try: data = dict(php="9000", debug="9001", php7="9070", debug7="9170") - WOTemplate.render( + WOTemplate.deploy( self, '{0}/upstream.conf'.format(ngxcnf), 'upstream.mustache', data, overwrite=True) data = dict(phpconf=True if WOAptGet.is_installed(self, 'php7.2-fpm') else False) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/stub_status.conf'.format(ngxcnf), 'stub_status.mustache', data) data = dict() - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/webp.conf'.format(ngxcnf), 'webp.mustache', data, overwrite=False) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/cloudflare.conf'.format(ngxcnf), 'cloudflare.mustache', data) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/map-wp-fastcgi-cache.conf'.format( ngxcnf), 'map-wp.mustache', data) @@ -223,83 +223,83 @@ def post_pref(self, apt_packages, packages, upgrade=False): data = dict() # Common Configuration - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/locations-wo.conf' .format(ngxcom), 'locations.mustache', data) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/wpsubdir.conf' .format(ngxcom), 'wpsubdir.mustache', data) data = dict(upstream="php72") # PHP 7.2 conf - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/php72.conf' .format(ngxcom), 'php.mustache', data) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/redis-php72.conf' .format(ngxcom), 'redis.mustache', data) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/wpcommon-php72.conf' .format(ngxcom), 'wpcommon.mustache', data) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/wpfc-php72.conf' .format(ngxcom), 'wpfc.mustache', data) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/wpsc-php72.conf' .format(ngxcom), 'wpsc.mustache', data) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/wprocket-php72.conf' .format(ngxcom), 'wprocket.mustache', data) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/wpce-php72.conf' .format(ngxcom), 'wpce.mustache', data) # PHP 7.3 conf data = dict(upstream="php73") - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/php73.conf' .format(ngxcom), 'php.mustache', data) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/redis-php73.conf' .format(ngxcom), 'redis.mustache', data) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/wpcommon-php73.conf' .format(ngxcom), 'wpcommon.mustache', data) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/wpfc-php73.conf' .format(ngxcom), 'wpfc.mustache', data) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/wpsc-php73.conf' .format(ngxcom), 'wpsc.mustache', data) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/wprocket-php73.conf' .format(ngxcom), 'wprocket.mustache', data) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/wpce-php73.conf' .format(ngxcom), 'wpce.mustache', data) @@ -315,15 +315,15 @@ def post_pref(self, apt_packages, packages, upgrade=False): # Following files should not be overwrited data = dict(webroot=ngxroot) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/acl.conf' .format(ngxcom), 'acl.mustache', data, overwrite=False) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/blockips.conf' .format(ngxcnf), 'blockips.mustache', data, overwrite=False) - WOTemplate.render(self, + WOTemplate.deploy(self, '{0}/fastcgi.conf' .format(ngxcnf), 'fastcgi.mustache', data, overwrite=True) @@ -361,7 +361,7 @@ def post_pref(self, apt_packages, packages, upgrade=False): # 22222 port settings data = dict(webroot=ngxroot) - WOTemplate.render( + WOTemplate.deploy( self, '/etc/nginx/sites-available/22222', '22222.mustache', data, overwrite=True) @@ -466,7 +466,7 @@ def post_pref(self, apt_packages, packages, upgrade=False): if not os.path.isfile("/opt/cf-update.sh"): data = dict() - WOTemplate.render(self, '/opt/cf-update.sh', + WOTemplate.deploy(self, '/opt/cf-update.sh', 'cf-update.mustache', data, overwrite=False) WOFileUtils.chmod(self, "/opt/cf-update.sh", 0o775) @@ -868,7 +868,7 @@ def post_pref(self, apt_packages, packages, upgrade=False): inno_buffer=wo_ram_innodb, inno_log_buffer=wo_ram_log_buffer, innodb_instances=wo_innodb_instance) - WOTemplate.render( + WOTemplate.deploy( self, '/etc/mysql/my.cnf', 'my.mustache', data) # replacing default values Log.debug(self, "Tuning MySQL configuration") @@ -892,17 +892,17 @@ def post_pref(self, apt_packages, packages, upgrade=False): if not os.path.isfile("/etc/fail2ban/jail.d/custom.conf"): Log.info(self, "Configuring Fail2Ban") data = dict() - WOTemplate.render( + WOTemplate.deploy( self, '/etc/fail2ban/jail.d/custom.conf', 'fail2ban.mustache', data, overwrite=False) - WOTemplate.render( + WOTemplate.deploy( self, '/etc/fail2ban/filter.d/wo-wordpress.conf', 'fail2ban-wp.mustache', data, overwrite=False) - WOTemplate.render( + WOTemplate.deploy( self, '/etc/fail2ban/filter.d/nginx-forbidden.conf', 'fail2ban-forbidden.mustache', @@ -984,7 +984,7 @@ def post_pref(self, apt_packages, packages, upgrade=False): # check if ufw script is already created if not os.path.isfile("/opt/ufw.sh"): data = dict() - WOTemplate.render(self, '/opt/ufw.sh', + WOTemplate.deploy(self, '/opt/ufw.sh', 'ufw.mustache', data, overwrite=False) WOFileUtils.chmod(self, "/opt/ufw.sh", 0o700) @@ -1077,7 +1077,7 @@ def post_pref(self, apt_packages, packages, upgrade=False): Log.debug(self, "Setting up freshclam cronjob") if not os.path.isfile("/opt/freshclam.sh"): data = dict() - WOTemplate.render(self, '/opt/freshclam.sh', + WOTemplate.deploy(self, '/opt/freshclam.sh', 'freshclam.mustache', data, overwrite=False) WOFileUtils.chmod(self, "/opt/freshclam.sh", 0o775) diff --git a/wo/core/database.py b/wo/core/database.py index 2fe0631..c7fbff9 100644 --- a/wo/core/database.py +++ b/wo/core/database.py @@ -1,7 +1,8 @@ """WordOps generic database creation module""" from sqlalchemy import create_engine -from sqlalchemy.orm import scoped_session, sessionmaker from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import scoped_session, sessionmaker + from wo.core.variables import WOVariables # db_path = self.app.config.get('site', 'db_path') diff --git a/wo/core/template.py b/wo/core/template.py index 0d50224..e827149 100644 --- a/wo/core/template.py +++ b/wo/core/template.py @@ -9,7 +9,8 @@ Render Templates class WOTemplate(): - def render(self, fileconf, template, data, overwrite=True): + def deploy(self, fileconf, template, data, overwrite=True): + """Deploy template with render()""" data = dict(data) if (not os.path.isfile('{0}.custom' .format(fileconf))): From b9c0850c9a3f55bea1babde0bc0c60520fb519b2 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 18:32:38 +0200 Subject: [PATCH 38/52] Set proper output --- wo/cli/plugins/site_functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wo/cli/plugins/site_functions.py b/wo/cli/plugins/site_functions.py index 282021a..920a4a9 100644 --- a/wo/cli/plugins/site_functions.py +++ b/wo/cli/plugins/site_functions.py @@ -11,6 +11,7 @@ from subprocess import CalledProcessError from wo.cli.plugins.sitedb import getSiteInfo from wo.cli.plugins.stack import WOStackController from wo.cli.plugins.stack_pref import post_pref +from wo.core.acme import WOAcme from wo.core.aptget import WOAptGet from wo.core.fileutils import WOFileUtils from wo.core.git import WOGit @@ -20,7 +21,6 @@ from wo.core.services import WOService from wo.core.shellexec import CommandExecutionError, WOShellExec from wo.core.sslutils import SSL from wo.core.variables import WOVariables -from wo.core.acme import WOAcme class SiteError(Exception): @@ -39,8 +39,8 @@ def pre_run_checks(self): Log.wait(self, "Running pre-update checks") try: Log.debug(self, "checking NGINX configuration ...") - FNULL = open('/dev/null', 'w') - subprocess.check_call(["/usr/sbin/nginx", "-t"], stdout=FNULL, + fnull = open('/dev/null', 'w') + subprocess.check_call(["/usr/sbin/nginx", "-t"], stdout=fnull, stderr=subprocess.STDOUT) except CalledProcessError as e: Log.failed(self, "Running pre-update checks") From 8b6c9a7c70b67d4914e63764fc6c9eff30386d33 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 18:34:56 +0200 Subject: [PATCH 39/52] set back cement 2.8.2 --- requirements.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 29f8528..2df9c0a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -cement>=2.10.12 +cement>=2.8.2 diff --git a/setup.py b/setup.py index 81601f1..09aa70a 100644 --- a/setup.py +++ b/setup.py @@ -46,7 +46,7 @@ setup(name='wo', # "nose", # "coverage", # Required to function - 'cement == 2.10.12', + 'cement == 2.8.2', 'pystache', 'python-apt', 'pynginxconfig', From b4fcb78f6b5aaa502cc1dfe3c48d20cb35250f30 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Mon, 23 Sep 2019 23:55:04 +0200 Subject: [PATCH 40/52] Remove deprecated handler register --- wo/cli/plugins/stack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index eee1e0b..1f1acc2 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -997,7 +997,7 @@ class WOStackController(CementBaseController): def load(app): # register the plugin class.. this only happens if the plugin is enabled - handler.register(WOStackController) + app.handler.register(WOStackController) handler.register(WOStackStatusController) handler.register(WOStackMigrateController) handler.register(WOStackUpgradeController) From 547b356a76c48232fad54b537211ed6d0aed71f4 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Tue, 24 Sep 2019 00:01:20 +0200 Subject: [PATCH 41/52] Remove deprecated handler.register --- wo/cli/bootstrap.py | 2 +- wo/cli/plugins/clean.py | 2 +- wo/cli/plugins/debug.py | 2 +- wo/cli/plugins/import_slow_log.py | 2 +- wo/cli/plugins/info.py | 2 +- wo/cli/plugins/log.py | 10 +++++----- wo/cli/plugins/maintenance.py | 2 +- wo/cli/plugins/secure.py | 2 +- wo/cli/plugins/site.py | 12 ++++++------ wo/cli/plugins/stack.py | 6 +++--- wo/cli/plugins/sync.py | 2 +- wo/cli/plugins/update.py | 2 +- 12 files changed, 23 insertions(+), 23 deletions(-) diff --git a/wo/cli/bootstrap.py b/wo/cli/bootstrap.py index 6df352d..60d5c4a 100644 --- a/wo/cli/bootstrap.py +++ b/wo/cli/bootstrap.py @@ -9,4 +9,4 @@ from wo.cli.controllers.base import WOBaseController def load(app): - handler.register(WOBaseController) + app.handler.register(WOBaseController) diff --git a/wo/cli/plugins/clean.py b/wo/cli/plugins/clean.py index a1b1231..3c00862 100644 --- a/wo/cli/plugins/clean.py +++ b/wo/cli/plugins/clean.py @@ -90,6 +90,6 @@ class WOCleanController(CementBaseController): def load(app): # register the plugin class.. this only happens if the plugin is enabled - handler.register(WOCleanController) + app.handler.register(WOCleanController) # register a hook (function) to run after arguments are parsed. hook.register('post_argument_parsing', wo_clean_hook) diff --git a/wo/cli/plugins/debug.py b/wo/cli/plugins/debug.py index 8cc2482..9d0b5f4 100644 --- a/wo/cli/plugins/debug.py +++ b/wo/cli/plugins/debug.py @@ -847,6 +847,6 @@ class WODebugController(CementBaseController): def load(app): # register the plugin class.. this only happens if the plugin is enabled - handler.register(WODebugController) + app.handler.register(WODebugController) # register a hook (function) to run after arguments are parsed. hook.register('post_argument_parsing', wo_debug_hook) diff --git a/wo/cli/plugins/import_slow_log.py b/wo/cli/plugins/import_slow_log.py index bc13ff8..63a1896 100644 --- a/wo/cli/plugins/import_slow_log.py +++ b/wo/cli/plugins/import_slow_log.py @@ -26,7 +26,7 @@ class WOImportslowlogController(CementBaseController): def load(app): # register the plugin class.. this only happens if the plugin is enabled - handler.register(WOImportslowlogController) + app.handler.register(WOImportslowlogController) # register a hook (function) to run after arguments are parsed. hook.register('post_argument_parsing', wo_import_slow_log_hook) diff --git a/wo/cli/plugins/info.py b/wo/cli/plugins/info.py index 0a6a145..764a1e2 100644 --- a/wo/cli/plugins/info.py +++ b/wo/cli/plugins/info.py @@ -292,7 +292,7 @@ class WOInfoController(CementBaseController): def load(app): # register the plugin class.. this only happens if the plugin is enabled - handler.register(WOInfoController) + app.handler.register(WOInfoController) # register a hook (function) to run after arguments are parsed. hook.register('post_argument_parsing', wo_info_hook) diff --git a/wo/cli/plugins/log.py b/wo/cli/plugins/log.py index d2d2fe3..8035eb7 100644 --- a/wo/cli/plugins/log.py +++ b/wo/cli/plugins/log.py @@ -571,10 +571,10 @@ class WOLogMailController(CementBaseController): def load(app): # register the plugin class.. this only happens if the plugin is enabled - handler.register(WOLogController) - handler.register(WOLogShowController) - handler.register(WOLogResetController) - handler.register(WOLogGzipController) - handler.register(WOLogMailController) + app.handler.register(WOLogController) + app.handler.register(WOLogShowController) + app.handler.register(WOLogResetController) + app.handler.register(WOLogGzipController) + app.handler.register(WOLogMailController) # register a hook (function) to run after arguments are parsed. hook.register('post_argument_parsing', wo_log_hook) diff --git a/wo/cli/plugins/maintenance.py b/wo/cli/plugins/maintenance.py index 8d45967..1a62fe0 100644 --- a/wo/cli/plugins/maintenance.py +++ b/wo/cli/plugins/maintenance.py @@ -40,6 +40,6 @@ class WOMaintenanceController(CementBaseController): def load(app): # register the plugin class.. this only happens if the plugin is enabled - handler.register(WOMaintenanceController) + app.handler.register(WOMaintenanceController) # register a hook (function) to run after arguments are parsed. hook.register('post_argument_parsing', wo_maintenance_hook) diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index e86dd9f..6f41ed1 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -220,5 +220,5 @@ class WOSecureController(CementBaseController): def load(app): - handler.register(WOSecureController) + app.handler.register(WOSecureController) hook.register('post_argument_parsing', wo_secure_hook) diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index d48fdd9..912207a 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -2049,11 +2049,11 @@ class WOSiteListController(CementBaseController): def load(app): # register the plugin class.. this only happens if the plugin is enabled - handler.register(WOSiteController) - handler.register(WOSiteCreateController) - handler.register(WOSiteUpdateController) - handler.register(WOSiteDeleteController) - handler.register(WOSiteListController) - handler.register(WOSiteEditController) + app.handler.register(WOSiteController) + app.handler.register(WOSiteCreateController) + app.handler.register(WOSiteUpdateController) + app.handler.register(WOSiteDeleteController) + app.handler.register(WOSiteListController) + app.handler.register(WOSiteEditController) # register a hook (function) to run after arguments are parsed. hook.register('post_argument_parsing', wo_site_hook) diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index 1f1acc2..dff3623 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -998,9 +998,9 @@ class WOStackController(CementBaseController): def load(app): # register the plugin class.. this only happens if the plugin is enabled app.handler.register(WOStackController) - handler.register(WOStackStatusController) - handler.register(WOStackMigrateController) - handler.register(WOStackUpgradeController) + app.handler.register(WOStackStatusController) + app.handler.register(WOStackMigrateController) + app.handler.register(WOStackUpgradeController) # register a hook (function) to run after arguments are parsed. hook.register('post_argument_parsing', wo_stack_hook) diff --git a/wo/cli/plugins/sync.py b/wo/cli/plugins/sync.py index 732d9ee..287a05d 100644 --- a/wo/cli/plugins/sync.py +++ b/wo/cli/plugins/sync.py @@ -94,6 +94,6 @@ class WOSyncController(CementBaseController): def load(app): # register the plugin class.. this only happens if the plugin is enabled - handler.register(WOSyncController) + app.handler.register(WOSyncController) # register a hook (function) to run after arguments are parsed. hook.register('post_argument_parsing', wo_sync_hook) diff --git a/wo/cli/plugins/update.py b/wo/cli/plugins/update.py index 0372853..119f499 100644 --- a/wo/cli/plugins/update.py +++ b/wo/cli/plugins/update.py @@ -78,6 +78,6 @@ class WOUpdateController(CementBaseController): def load(app): # register the plugin class.. this only happens if the plugin is enabled - handler.register(WOUpdateController) + app.handler.register(WOUpdateController) # register a hook (function) to run after arguments are parsed. hook.register('post_argument_parsing', wo_update_hook) From baada0473e0b6a58309e3b8bc1c9f33301a66244 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Tue, 24 Sep 2019 00:04:32 +0200 Subject: [PATCH 42/52] Remove deprecated hook.register --- wo/cli/plugins/clean.py | 2 +- wo/cli/plugins/debug.py | 2 +- wo/cli/plugins/import_slow_log.py | 2 +- wo/cli/plugins/info.py | 2 +- wo/cli/plugins/log.py | 2 +- wo/cli/plugins/maintenance.py | 2 +- wo/cli/plugins/secure.py | 2 +- wo/cli/plugins/site.py | 2 +- wo/cli/plugins/stack.py | 2 +- wo/cli/plugins/sync.py | 2 +- wo/cli/plugins/update.py | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/wo/cli/plugins/clean.py b/wo/cli/plugins/clean.py index 3c00862..ceb1f21 100644 --- a/wo/cli/plugins/clean.py +++ b/wo/cli/plugins/clean.py @@ -92,4 +92,4 @@ def load(app): # register the plugin class.. this only happens if the plugin is enabled app.handler.register(WOCleanController) # register a hook (function) to run after arguments are parsed. - hook.register('post_argument_parsing', wo_clean_hook) + app.hook.register('post_argument_parsing', wo_clean_hook) diff --git a/wo/cli/plugins/debug.py b/wo/cli/plugins/debug.py index 9d0b5f4..51744ee 100644 --- a/wo/cli/plugins/debug.py +++ b/wo/cli/plugins/debug.py @@ -849,4 +849,4 @@ def load(app): # register the plugin class.. this only happens if the plugin is enabled app.handler.register(WODebugController) # register a hook (function) to run after arguments are parsed. - hook.register('post_argument_parsing', wo_debug_hook) + app.hook.register('post_argument_parsing', wo_debug_hook) diff --git a/wo/cli/plugins/import_slow_log.py b/wo/cli/plugins/import_slow_log.py index 63a1896..570b59a 100644 --- a/wo/cli/plugins/import_slow_log.py +++ b/wo/cli/plugins/import_slow_log.py @@ -29,4 +29,4 @@ def load(app): app.handler.register(WOImportslowlogController) # register a hook (function) to run after arguments are parsed. - hook.register('post_argument_parsing', wo_import_slow_log_hook) + app.hook.register('post_argument_parsing', wo_import_slow_log_hook) diff --git a/wo/cli/plugins/info.py b/wo/cli/plugins/info.py index 764a1e2..87d22c0 100644 --- a/wo/cli/plugins/info.py +++ b/wo/cli/plugins/info.py @@ -295,4 +295,4 @@ def load(app): app.handler.register(WOInfoController) # register a hook (function) to run after arguments are parsed. - hook.register('post_argument_parsing', wo_info_hook) + app.hook.register('post_argument_parsing', wo_info_hook) diff --git a/wo/cli/plugins/log.py b/wo/cli/plugins/log.py index 8035eb7..8c6ee23 100644 --- a/wo/cli/plugins/log.py +++ b/wo/cli/plugins/log.py @@ -577,4 +577,4 @@ def load(app): app.handler.register(WOLogGzipController) app.handler.register(WOLogMailController) # register a hook (function) to run after arguments are parsed. - hook.register('post_argument_parsing', wo_log_hook) + app.hook.register('post_argument_parsing', wo_log_hook) diff --git a/wo/cli/plugins/maintenance.py b/wo/cli/plugins/maintenance.py index 1a62fe0..9444fc4 100644 --- a/wo/cli/plugins/maintenance.py +++ b/wo/cli/plugins/maintenance.py @@ -42,4 +42,4 @@ def load(app): # register the plugin class.. this only happens if the plugin is enabled app.handler.register(WOMaintenanceController) # register a hook (function) to run after arguments are parsed. - hook.register('post_argument_parsing', wo_maintenance_hook) + app.hook.register('post_argument_parsing', wo_maintenance_hook) diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index 6f41ed1..470fcb6 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -221,4 +221,4 @@ class WOSecureController(CementBaseController): def load(app): app.handler.register(WOSecureController) - hook.register('post_argument_parsing', wo_secure_hook) + app.hook.register('post_argument_parsing', wo_secure_hook) diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index 912207a..36044a1 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -2056,4 +2056,4 @@ def load(app): app.handler.register(WOSiteListController) app.handler.register(WOSiteEditController) # register a hook (function) to run after arguments are parsed. - hook.register('post_argument_parsing', wo_site_hook) + app.hook.register('post_argument_parsing', wo_site_hook) diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py index dff3623..32eab17 100644 --- a/wo/cli/plugins/stack.py +++ b/wo/cli/plugins/stack.py @@ -1003,4 +1003,4 @@ def load(app): app.handler.register(WOStackUpgradeController) # register a hook (function) to run after arguments are parsed. - hook.register('post_argument_parsing', wo_stack_hook) + app.hook.register('post_argument_parsing', wo_stack_hook) diff --git a/wo/cli/plugins/sync.py b/wo/cli/plugins/sync.py index 287a05d..209003b 100644 --- a/wo/cli/plugins/sync.py +++ b/wo/cli/plugins/sync.py @@ -96,4 +96,4 @@ def load(app): # register the plugin class.. this only happens if the plugin is enabled app.handler.register(WOSyncController) # register a hook (function) to run after arguments are parsed. - hook.register('post_argument_parsing', wo_sync_hook) + app.hook.register('post_argument_parsing', wo_sync_hook) diff --git a/wo/cli/plugins/update.py b/wo/cli/plugins/update.py index 119f499..6ef7fb8 100644 --- a/wo/cli/plugins/update.py +++ b/wo/cli/plugins/update.py @@ -80,4 +80,4 @@ def load(app): # register the plugin class.. this only happens if the plugin is enabled app.handler.register(WOUpdateController) # register a hook (function) to run after arguments are parsed. - hook.register('post_argument_parsing', wo_update_hook) + app.hook.register('post_argument_parsing', wo_update_hook) From f4bfd927e317958d619ddc36309a1fb60dba8707 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Tue, 24 Sep 2019 00:11:26 +0200 Subject: [PATCH 43/52] Improve logging in stack_pref --- wo/cli/plugins/stack_pref.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/wo/cli/plugins/stack_pref.py b/wo/cli/plugins/stack_pref.py index 7681829..f55f0da 100644 --- a/wo/cli/plugins/stack_pref.py +++ b/wo/cli/plugins/stack_pref.py @@ -1158,17 +1158,18 @@ def post_pref(self, apt_packages, packages, upgrade=False): # composer install and phpmyadmin update if any('/var/lib/wo/tmp/composer-install' == x[1] for x in packages): - Log.info(self, "Installing composer, please wait...") + Log.wait(self, "Installing composer") WOShellExec.cmd_exec(self, "php -q /var/lib/wo" "/tmp/composer-install " "--install-dir=/var/lib/wo/tmp/") shutil.copyfile('/var/lib/wo/tmp/composer.phar', '/usr/local/bin/composer') WOFileUtils.chmod(self, "/usr/local/bin/composer", 0o775) + Log.valide(self, "Installing composer") if ((os.path.isdir("/var/www/22222/htdocs/db/pma")) and (not os.path.isfile('/var/www/22222/htdocs/db/' 'pma/composer.lock'))): - Log.info(self, "Updating phpMyAdmin, please wait...") + Log.wait(self, "Updating phpMyAdmin") WOShellExec.cmd_exec( self, "/usr/local/bin/composer update " "--no-plugins --no-scripts -n --no-dev -d " @@ -1179,6 +1180,7 @@ def post_pref(self, apt_packages, packages, upgrade=False): 'www-data', 'www-data', recursive=True) + Log.valide(self, "Updating phpMyAdmin") if not os.path.exists('{0}22222/htdocs/cache/' 'redis/phpRedisAdmin' .format(WOVariables.wo_webroot)): @@ -1209,11 +1211,11 @@ def post_pref(self, apt_packages, packages, upgrade=False): # netdata install if any('/var/lib/wo/tmp/kickstart.sh' == x[1] for x in packages): - Log.info(self, "Installing Netdata, please wait...") - WOShellExec.cmd_exec(self, "bash /var/lib/wo/tmp/" - "kickstart.sh " - "--dont-wait", - errormsg='', log=False) + Log.wait(self, "Installing Netdata") + WOShellExec.cmd_exec( + self, "bash /var/lib/wo/tmp/kickstart.sh " + "--dont-wait", errormsg='', log=False) + Log.valide(self, "Installing Netdata") if os.path.isdir('/etc/netdata'): wo_netdata = "/" elif os.path.isdir('/opt/netdata'): @@ -1245,7 +1247,7 @@ def post_pref(self, apt_packages, packages, upgrade=False): WOMysql.execute( self, "flush privileges;", log=False) - except CommandExecutionError as e: + except Exception as e: Log.debug(self, "{0}".format(e)) Log.info( self, "fail to setup mysql user for netdata") From 429e10fe8cfd9a1c7996102eabe49a772220bc0c Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Tue, 24 Sep 2019 00:16:01 +0200 Subject: [PATCH 44/52] Update changelog et bash-completion --- CHANGELOG.md | 1 + config/bash_completion.d/wo_auto.rc | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f79ca5..71aa525 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - [INSTALL] Removed UFW setup from install script - [APP] phpMyAdmin updated to v4.9.1 - [STACK] Commit possible Nginx configuration changes into Git before and after performing tasks (in `wo secure` for example) +- [CORE] Update deprecated handlers and hooks registration #### Fixed diff --git a/config/bash_completion.d/wo_auto.rc b/config/bash_completion.d/wo_auto.rc index b7675a2..53d6856 100644 --- a/config/bash_completion.d/wo_auto.rc +++ b/config/bash_completion.d/wo_auto.rc @@ -74,17 +74,17 @@ _wo_complete() # HANDLE EVERYTHING AFTER THE THIRD LEVEL NAMESPACE "install" | "purge" | "remove" ) COMPREPLY=( $(compgen \ - -W "--web --admin --security --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --redis --phpredisadmin --composer --netdata --fail2ban --dashboard --proftpd --clamav --mysqlclient --mysqltuner --extplorer --all --force" \ + -W "--web --admin --security --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --redis --phpredisadmin --composer --netdata --fail2ban --ufw --dashboard --proftpd --clamav --mysqlclient --mysqltuner --extplorer --all --force" \ -- $cur) ) ;; "upgrade" ) COMPREPLY=( $(compgen \ - -W "--web --admin --utils --nginx --php --php73 --mysql --all --netdata --composer --phpmyadmin --dashboard --no-prompt --mysqtuner --wpcli" \ + -W "--web --admin --utils --nginx --php --php73 --mysql --all --netdata --composer --phpmyadmin --dashboard --no-prompt --mysqtuner --wpcli --force" \ -- $cur) ) ;; "start" | "stop" | "reload" | "restart" | "status") COMPREPLY=( $(compgen \ - -W "--nginx --php --php73 --mysql --redis --fail2ban --netdata -proftpd" \ + -W "--nginx --php --php73 --mysql --redis --fail2ban --ufw --netdata -proftpd" \ -- $cur) ) ;; "list") @@ -267,11 +267,11 @@ _wo_complete() -- $cur) ) ;; - "--web" | "--admin" | "--nginx" | "--php" | "--php73" | "--mysql" | "--wpcli" | "--phpmyadmin" | "--adminer" | "--utils" | "--fail2ban" | "--redis | --phpredisadmin | --netdata") + "--web" | "--admin" | "--nginx" | "--php" | "--php73" | "--mysql" | "--wpcli" | "--phpmyadmin" | "--adminer" | "--utils" | "--fail2ban" | "--ufw" | "--redis | --phpredisadmin | --netdata") if [[ "${COMP_WORDS[2]}" == "install" || "${COMP_WORDS[2]}" == "purge" || "${COMP_WORDS[2]}" == "remove" ]]; then - retlist="--web --admin --security --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --redis --fail2ban --phpredisadmin --netdata --force" + retlist="--web --admin --security --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --redis --fail2ban --ufw --phpredisadmin --netdata --force" elif [[ "${COMP_WORDS[2]}" == "start" || "${COMP_WORDS[2]}" == "reload" || "${COMP_WORDS[2]}" == "restart" || "${COMP_WORDS[2]}" == "stop" ]]; then - retlist="--nginx --php --php73 --mysql --redis --netdata --fail2ban" + retlist="--nginx --php --php73 --mysql --redis --netdata --fail2ban --ufw" elif [[ "${COMP_WORDS[1]}" == "debug" ]]; then retlist="--start --nginx --php --php73 --fpm --fpm7 --mysql -i --interactive -stop --import-slow-log --import-slow-log-interval= -" if [[ $prev == '--mysql' ]]; then From dd5fe9f416322c6319be9a142390a05d095661d4 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Tue, 24 Sep 2019 00:24:42 +0200 Subject: [PATCH 45/52] update requirements --- requirements-dev-py3.txt | 21 +++++++++++++++++++++ setup.cfg | 5 ++++- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 requirements-dev-py3.txt diff --git a/requirements-dev-py3.txt b/requirements-dev-py3.txt new file mode 100644 index 0000000..38fec85 --- /dev/null +++ b/requirements-dev-py3.txt @@ -0,0 +1,21 @@ +# The following are only required in development, not production +nose +coverage +sphinx +pep8 +autopep8 +mock +pyinotify + +# Required for optional extensions (only the ones supported on py3) +argcomplete +pystache +pyYaml +colorlog +configobj +tabulate +pylibmc +redis +jinja2 +watchdog +pybars3 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 2313b24..0e6f9dd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,9 +1,12 @@ + [nosetests] -verbosity=3 +verbosity=2 debug=0 detailed-errors=1 with-coverage=1 cover-package=wo +cover-inclusive=1 cover-erase=1 cover-html=1 cover-html-dir=coverage_report/ +where=tests/ \ No newline at end of file From 1d8164e5836d439397da56ab7e9c95670e002442 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Tue, 24 Sep 2019 01:59:49 +0200 Subject: [PATCH 46/52] Improve acme and check dns before issuing cert --- tests/cli/3_test_stack_services_status.py | 6 ---- tests/cli/plugins/test_example.py | 1 + wo/cli/plugins/site.py | 27 +++++++++++++-- wo/core/acme.py | 42 +++++++++++++---------- 4 files changed, 49 insertions(+), 27 deletions(-) diff --git a/tests/cli/3_test_stack_services_status.py b/tests/cli/3_test_stack_services_status.py index 904bd6d..f0441a4 100644 --- a/tests/cli/3_test_stack_services_status.py +++ b/tests/cli/3_test_stack_services_status.py @@ -27,12 +27,6 @@ class CliTestCaseStack(test.WOTestCase): self.app.run() self.app.close() - def test_wo_cli_stack_services_status_memcached(self): - self.app = get_test_app(argv=['stack', 'status', '--memcache']) - self.app.setup() - self.app.run() - self.app.close() - def test_wo_cli_stack_services_status_all(self): self.app = get_test_app(argv=['stack', 'status']) self.app.setup() diff --git a/tests/cli/plugins/test_example.py b/tests/cli/plugins/test_example.py index d3745b4..0f9d21d 100644 --- a/tests/cli/plugins/test_example.py +++ b/tests/cli/plugins/test_example.py @@ -2,6 +2,7 @@ from wo.utils import test + class ExamplePluginTestCase(test.WOTestCase): def test_load_example_plugin(self): self.app.setup() diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index 36044a1..d62f2fb 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -786,6 +786,11 @@ class WOSiteCreateController(CementBaseController): # copy the cert from the root domain copyWildcardCert(self, wo_domain, wo_root_domain) else: + # check DNS records before issuing cert + if not acmedata['dns'] is True: + if not WOAcme.check_dns(self, acme_domains): + Log.error(self, + "Aborting SSL certificate issuance") Log.debug(self, "Setup Cert with acme.sh for {0}" .format(wo_domain)) Log.info(self, "Certificate type: Subdomain") @@ -793,8 +798,12 @@ class WOSiteCreateController(CementBaseController): self, acme_domains, acmedata): WOAcme.deploycert(self, wo_domain) else: + if not acmedata['dns'] is True: + if not WOAcme.check_dns(self, acme_domains): + Log.error(self, + "Aborting SSL certificate issuance") if WOAcme.setupletsencrypt( - self, acme_domains, acmedata): + self, acme_domains, acmedata): WOAcme.deploycert(self, wo_domain) httpsRedirect(self, wo_domain, True, acme_wildcard) @@ -1420,16 +1429,28 @@ class WOSiteUpdateController(CementBaseController): # copy the cert from the root domain copyWildcardCert(self, wo_domain, wo_root_domain) else: + # check DNS records before issuing cert + if not acmedata['dns'] is True: + if not WOAcme.check_dns(self, acme_domains): + Log.error( + self, + "Aborting SSL certificate issuance") Log.debug(self, "Setup Cert with acme.sh for {0}" .format(wo_domain)) if WOAcme.setupletsencrypt( - self, acme_domains, acmedata): + self, acme_domains, acmedata): WOAcme.deploycert(self, wo_domain) else: Log.error(self, "Unable to issue certificate") else: + # check DNS records before issuing cert + if not acmedata['dns'] is True: + if not WOAcme.check_dns(self, acme_domains): + Log.error( + self, + "Aborting SSL certificate issuance") if WOAcme.setupletsencrypt( - self, acme_domains, acmedata): + self, acme_domains, acmedata): WOAcme.deploycert(self, wo_domain) else: Log.error(self, "Unable to issue certificate") diff --git a/wo/core/acme.py b/wo/core/acme.py index 209cba7..d9e68f1 100644 --- a/wo/core/acme.py +++ b/wo/core/acme.py @@ -40,24 +40,11 @@ class WOAcme: self, "Please make sure your properly " "set your DNS API credentials for acme.sh") else: - server_ip = requests.get('http://v4.wordops.eu/').text - for domain in acme_domains: - domain_ip = requests.get('http://v4.wordops.eu/dns/{0}/' - .format(domain)).text - if(not domain_ip == server_ip): - Log.warn( - self, "{0} is not pointing to your server IP" - .format(domain)) - Log.error( - self, "You have to add the " - "proper DNS record", False) - break - else: - Log.error( - self, "Your domain is properly configured " - "but acme.sh was unable to issue certificate.\n" - "You can find more informations in " - "/var/log/wo/wordops.log", False) + Log.error( + self, "Your domain is properly configured " + "but acme.sh was unable to issue certificate.\n" + "You can find more informations in " + "/var/log/wo/wordops.log", False) return False else: Log.valide(self, "Issuing SSL cert with acme.sh") @@ -124,3 +111,22 @@ class WOAcme: Log.debug(self, str(e)) Log.debug(self, "Error occured while generating " "ssl.conf") + + def check_dns(self, acme_domains): + """Check if a list of domains point to the server IP""" + server_ip = requests.get('http://v4.wordops.eu/').text + for domain in acme_domains: + domain_ip = requests.get('http://v4.wordops.eu/dns/{0}/' + .format(domain)).text + if(not domain_ip == server_ip): + Log.warn( + self, "{0} is not pointing to your server IP" + .format(domain)) + Log.error( + self, "You have to add the " + "proper DNS record", False) + return False + break + else: + Log.debug(self, "DNS record are properly set") + return True From 0170366ce49405acffa66bffccc7a7a95dc992b3 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Tue, 24 Sep 2019 02:36:46 +0200 Subject: [PATCH 47/52] Add DNS alias mode --- CHANGELOG.md | 12 ++++++++---- setup.py | 2 +- wo/cli/plugins/site.py | 18 +++++++++++++++--- wo/core/acme.py | 3 +++ wo/core/variables.py | 2 +- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71aa525..b222577 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### v3.9.x - [Unreleased] +### v3.9.9 - 2019-09-24 + #### Added - [STACK] UFW now available as a stack with flag `--ufw` - [SECURE] `wo stack secure --ssh` to harden ssh security - [SECURE] `wo stack secure --sshport` to change ssh port +- [SITE] check domain DNS records before issuing a new certificate without DNS API +- [STACK] Acme challenge with DNS Alias mode [acme.sh wiki](https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode) #### Changed @@ -26,10 +30,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), #### Fixed -- `wo stack purge --all` failure if mysql isn't installed -- Fix EEv3 files cleanup -- Incorrect variable usage in `wo secure --port` -- Fix backup_ee function in install script +- [STACK] `wo stack purge --all` failure if mysql isn't installed +- [INSTALL] Fix EEv3 files cleanup +- [SECURE] Incorrect variable usage in `wo secure --port` +- [INSTALL] Fix backup_ee function in install script ### v3.9.8.12 - 2019-09-20 diff --git a/setup.py b/setup.py index 09aa70a..cf655c2 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ if not os.path.exists('/var/lib/wo/'): os.makedirs('/var/lib/wo/') setup(name='wo', - version='3.9.8.12', + version='3.9.9', description=long_description, long_description=long_description, classifiers=[], diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index d62f2fb..af17533 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -372,6 +372,9 @@ class WOSiteCreateController(CementBaseController): dict(help="choose dns provider api for letsencrypt", action='store' or 'store_const', const='dns_cf', nargs='?')), + (['--dnsalias'], + dict(help="set domain used for acme dns alias validation", + action='store', nargs='?')), (['--hsts'], dict(help="enable HSTS for site secured with letsencrypt", action='store_true')), @@ -730,13 +733,18 @@ class WOSiteCreateController(CementBaseController): letsencrypt = True if data['letsencrypt'] is True: Log.debug(self, "Going to issue Let's Encrypt certificate") - acmedata = dict(acme_domains, dns=False, acme_dns='dns_cf') + acmedata = dict(acme_domains, dns=False, acme_dns='dns_cf', + dnsalias=False, acme_alias='') if pargs.dns: Log.debug(self, "DNS validation enabled") acmedata['dns'] = True if not pargs.dns == 'dns_cf': Log.debug(self, "DNS API : {0}".format(pargs.dns)) acmedata['acme_dns'] = pargs.dns + if pargs.dnsalias: + Log.debug(self, "DNS Alias enabled") + acmedata['dnsalias'] = True + acmedata['acme_alias'] = pargs.dnsalias # detect subdomain and set subdomain variable if pargs.letsencrypt == "subdomain": @@ -793,7 +801,6 @@ class WOSiteCreateController(CementBaseController): "Aborting SSL certificate issuance") Log.debug(self, "Setup Cert with acme.sh for {0}" .format(wo_domain)) - Log.info(self, "Certificate type: Subdomain") if WOAcme.setupletsencrypt( self, acme_domains, acmedata): WOAcme.deploycert(self, wo_domain) @@ -1171,7 +1178,8 @@ class WOSiteUpdateController(CementBaseController): if pargs.letsencrypt: acme_domains = [] - acmedata = dict(acme_domains, dns=False, acme_dns='dns_cf') + acmedata = dict(acme_domains, dns=False, acme_dns='dns_cf', + dnsalias=False, acme_alias='') (wo_domain_type, wo_root_domain) = WODomain.getdomainlevel(self, wo_domain) @@ -1398,6 +1406,10 @@ class WOSiteUpdateController(CementBaseController): if not pargs.dns == 'dns_cf': Log.debug(self, "DNS API : {0}".format(pargs.dns)) acmedata['acme_dns'] = pargs.dns + if pargs.dnsalias: + Log.debug(self, "DNS Alias enabled") + acmedata['dnsalias'] = True + acmedata['acme_alias'] = pargs.dnsalias # Set list of domains to secure if acme_subdomain is True: Log.info(self, "Certificate type : subdomain") diff --git a/wo/core/acme.py b/wo/core/acme.py index d9e68f1..d640eed 100644 --- a/wo/core/acme.py +++ b/wo/core/acme.py @@ -23,6 +23,9 @@ class WOAcme: if acmedata['dns'] is True: acme_mode = "--dns {0}".format(wo_acme_dns) validation_mode = "DNS mode with {0}".format(wo_acme_dns) + if acmedata['dnsalias'] is True: + acme_mode = acme_mode + \ + " --challenge-alias {0}".format(acmedata['acme_alias']) else: acme_mode = "-w /var/www/html" validation_mode = "Webroot challenge" diff --git a/wo/core/variables.py b/wo/core/variables.py index 9ad512b..af40727 100644 --- a/wo/core/variables.py +++ b/wo/core/variables.py @@ -11,7 +11,7 @@ class WOVariables(): """Intialization of core variables""" # WordOps version - wo_version = "3.9.8.12" + wo_version = "3.9.9" # WordOps packages versions wo_wp_cli = "2.3.0" wo_adminer = "4.7.2" From 7c9d6b6dffe0da6d9c805bd9a4b28de01c9c06b5 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Tue, 24 Sep 2019 02:42:40 +0200 Subject: [PATCH 48/52] Fix replacing current SSH port --- wo/cli/plugins/secure.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index 470fcb6..069d777 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -208,9 +208,18 @@ class WOSecureController(CementBaseController): Log.info(self, "Please Enter valid port number :") port = input("Server SSH port [22]:") pargs.user_input = port - WOShellExec.cmd_exec(self, "sed -i \"s/Port.*/Port " + if os.path.isfile('/etc/ssh/sshd_config'): + Log.debug(self, "looking for the current ssh port") + for line in open('/etc/ssh/sshd_config', encoding='utf-8'): + if 'Port' in line: + ssh_line = line.strip() + break + sshport = (ssh_line).split(' ') + current_ssh_port = (sshport[1]).strip() + WOShellExec.cmd_exec(self, "sed -i \"s/Port {current}/Port " "{port}\" /etc/ssh/sshd_config" - .format(port=pargs.user_input)) + .format(current=current_ssh_port, + port=pargs.user_input)) WOGit.add(self, ["/etc/ssh"], msg="Adding changed SSH port into Git") if not WOService.restart_service(self, 'ssh'): From d048ebadf848cd3bf51943c4b71b1567e17c316d Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Tue, 24 Sep 2019 02:44:33 +0200 Subject: [PATCH 49/52] Add force argument to secure ssh --- wo/cli/plugins/secure.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index 069d777..759db6c 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -37,8 +37,8 @@ class WOSecureController(CementBaseController): help='set custom ssh port', action='store_true')), (['--ssh'], dict( help='harden ssh security', action='store_true')), - (['--ufw'], - dict(help='setup and configure ufw firewall', + (['--force'], + dict(help='force execution without being prompt', action='store_true')), (['user_input'], dict(help='user input', nargs='?', default=None)), @@ -155,14 +155,16 @@ class WOSecureController(CementBaseController): @expose(hide=True) def secure_ssh(self): """Harden ssh security""" - start_secure = input('Are you sure you to want to' - ' harden SSH security ?' - '\nSSH login with password will not ' - 'be possible anymore. Please make sure ' - 'you are already using SSH Keys.\n' - 'Harden SSH security [y/N]') - if start_secure != "Y" and start_secure != "y": - Log.error(self, "Not hardening SSH security") + pargs = self.app.pargs + if not pargs.force: + start_secure = input('Are you sure you to want to' + ' harden SSH security ?' + '\nSSH login with password will not ' + 'be possible anymore. Please make sure ' + 'you are already using SSH Keys.\n' + 'Harden SSH security [y/N]') + if start_secure != "Y" and start_secure != "y": + Log.error(self, "Not hardening SSH security") Log.debug(self, "check if /etc/ssh/sshd_config exist") if os.path.isfile('/etc/ssh/sshd_config'): Log.debug(self, "looking for the current ssh port") From d512542b4a7283a4d1ad6a7fda5e39cce2819138 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Tue, 24 Sep 2019 02:47:06 +0200 Subject: [PATCH 50/52] Fix changing ssh port --- wo/cli/plugins/secure.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index 759db6c..1290285 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -218,8 +218,8 @@ class WOSecureController(CementBaseController): break sshport = (ssh_line).split(' ') current_ssh_port = (sshport[1]).strip() - WOShellExec.cmd_exec(self, "sed -i \"s/Port {current}/Port " - "{port}\" /etc/ssh/sshd_config" + WOShellExec.cmd_exec(self, "sed -i \"s/Port.*/Port " + "{port}/\" /etc/ssh/sshd_config" .format(current=current_ssh_port, port=pargs.user_input)) WOGit.add(self, ["/etc/ssh"], From 08d9ea7a57e2ca8512d9622a6f5988a238fb75ad Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Tue, 24 Sep 2019 02:47:49 +0200 Subject: [PATCH 51/52] simplify secure --- wo/cli/plugins/secure.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/wo/cli/plugins/secure.py b/wo/cli/plugins/secure.py index 1290285..89e4012 100644 --- a/wo/cli/plugins/secure.py +++ b/wo/cli/plugins/secure.py @@ -210,18 +210,9 @@ class WOSecureController(CementBaseController): Log.info(self, "Please Enter valid port number :") port = input("Server SSH port [22]:") pargs.user_input = port - if os.path.isfile('/etc/ssh/sshd_config'): - Log.debug(self, "looking for the current ssh port") - for line in open('/etc/ssh/sshd_config', encoding='utf-8'): - if 'Port' in line: - ssh_line = line.strip() - break - sshport = (ssh_line).split(' ') - current_ssh_port = (sshport[1]).strip() WOShellExec.cmd_exec(self, "sed -i \"s/Port.*/Port " "{port}/\" /etc/ssh/sshd_config" - .format(current=current_ssh_port, - port=pargs.user_input)) + .format(port=pargs.user_input)) WOGit.add(self, ["/etc/ssh"], msg="Adding changed SSH port into Git") if not WOService.restart_service(self, 'ssh'): From 225c30d298d010bd70decfad5896117a75f8c2c2 Mon Sep 17 00:00:00 2001 From: VirtuBox Date: Tue, 24 Sep 2019 02:51:36 +0200 Subject: [PATCH 52/52] Fix dnsalias --- wo/cli/plugins/site.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py index af17533..cf6176a 100644 --- a/wo/cli/plugins/site.py +++ b/wo/cli/plugins/site.py @@ -889,6 +889,9 @@ class WOSiteUpdateController(CementBaseController): dict(help="choose dns provider api for letsencrypt", action='store' or 'store_const', const='dns_cf', nargs='?')), + (['--dnsalias'], + dict(help="set domain used for acme dns alias validation", + action='store', nargs='?')), (['--hsts'], dict(help="configure hsts for the site", action='store' or 'store_const',