diff --git a/.travis.yml b/.travis.yml
index ecad218..2ee76d3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -51,8 +51,7 @@ script:
- sudo wo site create 1.com --html && sudo wo site create 2.com --php && sudo wo site create 3.com --mysql || sudo tail -n50 /var/log/wo/wordops.log
- sudo wo site update 1.com --wp && sudo wo site update 2.com --php73 && sudo wo site update 3.com --php73 && sudo wo site update 1.com --wpfc && sudo wo site update 1.com --wpsc && sudo wo site update 1.com --wpredis || sudo tail -n50 /var/log/wo/wordops.log
- - sudo ls /var/www/
- - sudo ls /var/www/22222/htdocs/
- sudo wp --allow-root --info
- sudo wo info || sudo tail -n50 /var/log/wo/wordops.log
- sudo tree -L 2 /etc/nginx
+ - sudo tree -L 3 /var/www/22222/htdocs/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ead24bb..ab727f3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,7 +8,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
---
-### v3.9.5 - [Unreleased]
+### v3.9.6 - [Unreleased]
+
+### v3.9.5 - 2019-05-02
#### Added
@@ -28,13 +30,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- Support for Netdata on backend : https://server.hostname:22222/netdata/
- New Stacks : composer and netdata
- additional argument for letsencrypt : --hsts
-- Theme for adminer
+- Clean Theme for adminer
- Credits for tools shipped with WordOps
- Cache exception for Easy Digital Download
-- Additional cache exception for Woocommerce
+- Additional cache exceptions for Woocommerce
- MySQL monitoring with Netdata
-- WordOps-dashboard on 22222
-- Extplorer filemanager
+- WordOps-dashboard on 22222, can be installed with `wo stack install`
+- Extplorer filemanager in WordOps backend
+- Enable OSCP Stapling with Let's Encrypt
+- Compress database backup with pigz (faster than gzip) before updating sites
+- Support for Ubuntu 19.04 (disco) - few php extensions missing
+- Support for Raspbian 9 (stretch) - tested on Raspberry Pi 3b+
+- backup letsencrypt certificate before upgrade
+- directives emergency_restart_threshold & emergency_restart_interval to restart php-fpm in case of failure
+- EasyEngine cronjob removal during install
+- Kernel tweaks via systctl.conf
+- open_basedir on php-fpm process to forbid access with php outside of /var/www & /run/nginx-cache
#### Changed
@@ -42,12 +53,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- letsencrypt validation with webroot folder
- hardened nginx ssl_ecdh_curve
- Update phpredisadmin
-- Increase MySQL root password size to 16 characters
-- Increase MySQL users password size to 16 characters
+- Increase MySQL root password size to 24 characters
+- Increase MySQL users password size to 24 characters
- Nginx locations template is the same for php7.2 & 7.3
- backend SSL configuration now stored in /var/www/22222/conf/nginx/ssl.conf
- Install Netdata with static pre-built binaries instead of having to compile it from source
- Nginx updated to new stable release (1.16.0)
+- New packages (phpmyadmin, adminer, composer) are not download in /tmp anymore
#### Fixed
@@ -67,6 +79,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- phpredisadmin setup
- --hsts flag with basic html site
- hsts flag on site not secure with letsencrypt
+- fix import of previous acme.sh certificate
+- fix proxy webroot folder creation
### v3.9.4 - 2019-03-15
diff --git a/README.md b/README.md
index e379646..12dd5c6 100644
--- a/README.md
+++ b/README.md
@@ -11,10 +11,14 @@
-
+
-
+
+
+
+
+
@@ -26,11 +30,9 @@
License
- WordOps site •
-Documentation •
-Community forum •
-Slack
-
+ WordOps.net •
+Documentation •
+Community forum
---
@@ -52,8 +54,10 @@
- Ubuntu 16.04 LTS (Xenial)
- Ubuntu 18.04 LTS (Bionic)
+- Ubuntu 19.04 (Disco)
- Debian 8 (Jessie)
- Debian 9 (Stretch)
+- Raspbian 9 (Stretch)
### Ports requirements
diff --git a/config/bash_completion.d/wo_auto.rc b/config/bash_completion.d/wo_auto.rc
index 6a6e3a9..ead7ffc 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 "--web --admin --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --all --redis --phpredisadmin --composer --netdata --fail2ban" \
+ -W "--web --admin --nginx --php --php73 --mysql --wpcli --phpmyadmin --adminer --utils --all --redis --phpredisadmin --composer --netdata --fail2ban --dashboard" \
-- $cur) )
;;
"upgrade" )
diff --git a/docs/wo.8 b/docs/wo.8
index 5c62929..f0d2891 100644
--- a/docs/wo.8
+++ b/docs/wo.8
@@ -5,7 +5,7 @@
.SH SYNOPSIS
wo [ --version | --help | info | stack | site | debug | update | clean | import_slow_log | log | secure | sync | maintenance]
.TP
-wo stack [ install | remove | purge | migrate | upgrade] [ --web | --all | --nginx | --php | --php73 | --mysql | --admin | --adminer | --redis | --phpmyadmin | --phpredisadmin | --wpcli | --utils ]
+wo stack [ install | remove | purge | migrate | upgrade] [ --web | --all | --nginx | --php | --php73 | --mysql | --admin | --adminer | --redis | --phpmyadmin | --phpredisadmin | --wpcli | --utils | --dashboard | --netdata ]
.TP
wo stack [ status | start | stop | reload | restart ] [--all | --nginx | --php | --php73 |--mysql | --web | --memcached | --redis]
.TP
diff --git a/install b/install
index 1ea406b..0d5e9b7 100755
--- a/install
+++ b/install
@@ -7,10 +7,10 @@
# Copyright (c) 2019 - WordOps
# This script is licensed under M.I.T
# -------------------------------------------------------------------------
-# Version 3.9.5 - 2019-04-22
+# Version 3.9.5 - 2019-05-02
# -------------------------------------------------------------------------
readonly wo_version_old="2.2.3"
-readonly wo_version_new="3.9.4.6"
+readonly wo_version_new="3.9.5"
# CONTENTS
# ---
# 1. VARIABLES AND DECLARATIONS
@@ -113,6 +113,7 @@ if [ -z "$wo_branch" ]; then
fi
readonly wo_log_dir=/var/log/wo/
readonly wo_backup_dir=/var/lib/wo-backup/
+readonly wo_tmp_dir=/var/lib/wo/tmp
readonly wo_install_log=/var/log/wo/install.log
readonly wo_linux_distro=$(lsb_release -is)
readonly wo_distro_version=$(lsb_release -sc)
@@ -122,6 +123,7 @@ TIME=$(date +"$TIME_FORMAT")
NGINX_BACKUP_FILE="/var/lib/wo-backup/nginx-backup.$TIME.tar.gz"
EE_BACKUP_FILE="/var/lib/wo-backup/ee-backup.$TIME.tar.gz"
WO_BACKUP_FILE="/var/lib/wo-backup/wo-backup.$TIME.tar.gz"
+WO_ARCH="$(uname -m)"
if [ -x /usr/local/bin/ee ]; then
ee_migration=1
@@ -133,15 +135,15 @@ fi
# 1 - Checking linux distro
###
if [ -z "$wo_force_install" ]; then
- if [ "$wo_linux_distro" != "Ubuntu" ] && [ "$wo_linux_distro" != "Debian" ]; then
- wo_lib_echo_fail "WordOps (wo) only supports Ubuntu and Debian at the moment."
+ if [ "$wo_linux_distro" != "Ubuntu" ] && [ "$wo_linux_distro" != "Debian" ] && [ "$wo_linux_distro" != "Raspbian" ]; then
+ wo_lib_echo_fail "WordOps (wo) only supports Ubuntu, Debian & Raspbian at the moment."
wo_lib_echo_fail "If you are feeling adventurous, you are free to fork WordOps to support"
wo_lib_echo_fail "other Linux distributions and perhaps even Unix deratives."
exit 100
else
- check_wo_linux_distro=$(lsb_release -sc | grep -E "trusty|xenial|bionic|jessie|stretch")
+ check_wo_linux_distro=$(lsb_release -sc | grep -E "trusty|xenial|bionic|disco|jessie|stretch")
if [ -z "$check_wo_linux_distro" ]; then
- wo_lib_echo_fail "WordOps (wo) only supports Ubuntu 14.04/16.04/18.04, Debian 8.x and Debian 9.x"
+ wo_lib_echo_fail "WordOps (wo) only supports Ubuntu 14.04/16.04/18.04/19.04 LTS, Debian 8.x, Debian 9.x and Raspbian 9.x"
exit 100
fi
fi
@@ -150,23 +152,15 @@ fi
###
# 1 - To prevent errors or unexpected behaviour, create the log and ACL it
###
-if [ ! -d "$wo_log_dir" ]; then
+if [ ! -d "$wo_log_dir" ] || [ ! -d "$wo_backup_dir" ] || [ ! -d "$wo_tmp_dir" ]; then
- wo_lib_echo "Creating WordOps log directory, just a second..."
- mkdir -p "$wo_log_dir" || wo_lib_error "Whoops - seems we are unable to create the log directory $wo_log_dir, exit status " $?
+ wo_lib_echo "Creating WordOps backup, tmp & log directory, just a second..."
+ mkdir -p "$wo_backup_dir" "$wo_log_dir" "$wo_tmp_dir" || wo_lib_error "Whoops - seems we are unable to create the log directory $wo_log_dir, exit status " $?
# create wordops log files
touch /var/log/wo/{wordops.log,install.log}
- chmod -R 700 /var/log/wo || wo_lib_error "Whoops, there was an error setting the permissions on the WordOps log folder, exit status " $?
-fi
-
-if [ ! -d "$wo_backup_dir" ]; then
-
- wo_lib_echo "Creating WordOps backup directory, just a second..."
- mkdir -p "$wo_backup_dir" || wo_lib_error "Whoops - seems we are unable to create the backup directory $wo_backup_dir, exit status " $?
- chmod -R 600 "$wo_backup_dir"
-
+ chmod -R 700 "$wo_log_dir" "$wo_backup_dir" "$wo_tmp_dir" || wo_lib_error "Whoops, there was an error setting the permissions on the WordOps log folder, exit status " $?
fi
###
@@ -178,7 +172,7 @@ wo_install_dep() {
if [ "$wo_linux_distro" == "Ubuntu" ]; then
DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confmiss" -o Dpkg::Options::="--force-confold" -y install build-essential curl gzip python3 python3-apt python3-setuptools python3-dev sqlite3 git tar software-properties-common pigz gnupg2 cron ccze rsync tree haveged ufw > /dev/null 2>&1
else
- wget -qO /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
+ [ -d /etc/apt/trusted.gpg.d ] && { wget -qO /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg; }
DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confmiss" -o Dpkg::Options::="--force-confold" -y install build-essential curl gzip dirmngr sudo python3 python3-apt python3-setuptools python3-dev ca-certificates sqlite3 git tar software-properties-common pigz apt-transport-https gnupg2 cron ccze rsync tree haveged ufw > /dev/null 2>&1
fi
@@ -216,9 +210,6 @@ wo_sync_db() {
# Copy the EasyEngine database
cp /var/lib/ee/ee.db /var/lib/wo/dbase-ee.db
- # Set the migration variable for the closing text
- migration=1
-
###
# Clean WO installation
###
@@ -327,9 +318,9 @@ wo_sync_db() {
secure_wo_db() {
# The owner is root
- chown -R root:root /var/lib/wo/
+ chown -R root:root /var/lib/wo
# Only allow access by root, block others
- chmod -R 600 /var/lib/wo/
+ chmod -R 600 /var/lib/wo
}
@@ -357,21 +348,17 @@ wo_update_wp_cli() {
wo_install_acme_sh() {
# check if acme.sh is already installed
- if [ ! -d /opt/acme.sh ]; then
+ if [ ! -x /etc/letsencrypt/acme.sh ]; then
{
# clone the git repository
- git clone https://github.com/Neilpang/acme.sh.git /opt/acme.sh -q
+ if [ -d /opt/acme.sh/.git ]; then
+ git -C /opt/acme.sh pull origin master
+ else
+ git clone https://github.com/Neilpang/acme.sh.git /opt/acme.sh -q
+ fi
cd /opt/acme.sh || exit 1
# create conf directories
- [ ! -d /etc/letsencrypt/config ] && {
- mkdir -p /etc/letsencrypt/config
- }
- [ ! -d /etc/letsencrypt/live ] && {
- mkdir -p /etc/letsencrypt/live
- }
- [ ! -d /etc/letsencrypt/renewal ] && {
- mkdir -p /etc/letsencrypt/renewal
- }
+ mkdir -p /etc/letsencrypt/{config,live,renewal}
# install acme.sh
./acme.sh --install \
--home /etc/letsencrypt \
@@ -380,19 +367,13 @@ wo_install_acme_sh() {
# enable auto-upgrade
/etc/letsencrypt/acme.sh --config-home /etc/letsencrypt/config --upgrade --auto-upgrade
- # Let's Encrypt .well-known folder setup
- if [ ! -d /var/www/html/.well-known/acme-challenge ]; then
- mkdir -p /var/www/html/.well-known/acme-challenge
- chown -R www-data:www-data /var/www/html /var/www/html/.well-known
- chmod 750 /var/www/html /var/www/html/.well-known
- else
- chmod 750 /var/www/html /var/www/html/.well-known
- fi
-
} >> "$wo_install_log" 2>&1
fi
- if [ -d "$HOME/.acme.sh" ]; then
+ if [ -x "$HOME/.acme.sh/acme.sh" ]; then
{
+ # backup acme.sh folder
+ /bin/tar -I pigz -cf /var/lib/wo-backup/acme.sh.tar.gz "$HOME/.acme.sh"
+ # rsync previous certificates to new acme.sh location
/usr/bin/rsync -rltgoDpz --exclude="account.conf" \
--exclude="acme.sh" \
--exclude="acme.sh.env" \
@@ -400,12 +381,24 @@ wo_install_acme_sh() {
--exclude="dnsapi" \
--exclude="http.header" \
--exclude="ca" \
- --del \
"$HOME/.acme.sh/" \
/etc/letsencrypt/renewal/
+ # remove previous acme.sh folder
+ rm -rf "$HOME/.acme.sh"
+ # create acme.sh.env file inlcuded in .bashrc to avoid error when logging in
+ mkdir -p "$HOME/.acme.sh"
+ echo '' > "$HOME/.acme.sh/acme.sh.env"
} >> "$wo_install_log" 2>&1
fi
+ # Let's Encrypt .well-known folder setup
+ if [ ! -d /var/www/html/.well-known/acme-challenge ]; then
+ mkdir -p /var/www/html/.well-known/acme-challenge
+ chown -R www-data:www-data /var/www/html /var/www/html/.well-known
+ chmod 750 /var/www/html /var/www/html/.well-known
+ else
+ chmod 750 /var/www/html /var/www/html/.well-known
+ fi
}
# Clone Github repository if it doesn't exist
@@ -551,7 +544,7 @@ wo_update_latest() {
if [ -f /etc/ImageMagick/policy.xml ]; then
if [ ! -f /etc/ImageMagick/patch.txt ]; then
- echo -e "\t\n\t\n\t\n\t\n\t" >> /etc/ImageMagick/patch.txt
+ echo -e "\t\n\t\n\t\n\t\n\t" >> /etc/ImageMagick/patch.txt
sed -i '//r /etc/ImageMagick/patch.txt' /etc/ImageMagick/policy.xml
fi
fi
@@ -581,10 +574,10 @@ wo_update_latest() {
# Fix Redis-server security issue
# http://redis.io/topics/security
if [ -f /etc/redis/redis.conf ]; then
- grep -0 -v "#" /etc/redis/redis.conf | grep 'bind' >> /dev/null 2>&1
+ CHECK_REDIS_BIND=$(grep -0 -v "#" /etc/redis/redis.conf | grep 'bind' >> /dev/null 2>&1)
- if [ "$?" -ne 0 ]; then
- sed -i '$ a bind 127.0.0.1' /etc/redis/redis.conf &
+ if [ -z "$CHECK_REDIS_BIND" ]; then
+ echo 'bind 127.0.0.1 ::1' >> /etc/redis/redis.conf
service redis-server restart > /dev/null 2>&1
@@ -598,9 +591,9 @@ wo_git_init() {
# Nginx under git version control
[ -d /etc/nginx ] && {
cd /etc/nginx || exit 1
- if [ ! -d /etc/nginx/.git ]; then
+ [ ! -d /etc/nginx/.git ] && {
git init
- fi
+ }
git add -A .
git commit -am "Updated Nginx"
} >> /var/log/wo/install.log 2>&1
@@ -616,36 +609,66 @@ wo_git_init() {
# PHP under git version control
[ -d /etc/php ] && {
cd /etc/php || exit 1
- if [ ! -d /etc/php/.git ]; then
+ [ ! -d /etc/php/.git ] && {
git init
- fi
+ }
git add -A .
git commit -am "Updated PHP"
} >> /var/log/wo/install.log 2>&1
}
wo_backup_ee() {
- tar -I pigz -cf "$EE_BACKUP_FILE" /etc/nginx /usr/local/bin/ee /usr/lib/ee/templates /usr/local/lib/python3.6/dist-packages/ee-*.egg /etc/ee /var/lib/ee >> /var/log/wo/install.log 2>&1
+ /bin/tar -I pigz -cf "$EE_BACKUP_FILE" /etc/nginx /usr/local/bin/ee /usr/lib/ee/templates /usr/local/lib/python3.6/dist-packages/ee-*.egg /etc/ee /var/lib/ee /etc/letsencrypt >> /var/log/wo/install.log 2>&1
}
wo_backup_wo() {
- tar -I pigz -cf "$WO_BACKUP_FILE" /etc/nginx/ /usr/local/lib/python3.6/dist-packages/wo-*.egg /etc/wo /var/lib/wo >> /var/log/wo/install.log 2>&1
+ /bin/tar -I pigz -cf "$WO_BACKUP_FILE" /etc/nginx /usr/local/lib/python3.6/dist-packages/wo-*.egg /etc/wo /var/lib/wo /etc/letsencrypt >> /var/log/wo/install.log 2>&1
}
wo_clean_ee() {
rm -f /usr/local/bin/ee /etc/bash_completion.d/ee_auto.rc /usr/lib/ee/templates /usr/local/lib/python3.6/dist-packages/ee-*.egg /etc/ee /var/lib/ee >> /var/log/wo/install.log 2>&1
}
+wo_remove_ee_cron() {
+
+ crontab -l | sed '/ee site update --le=renew --all 2> \/dev\/null/d' | crontab -
+
+}
+
+wo_tweak_kernel() {
+
+ if [ ! -f /etc/sysctl.d/60-wo-tweaks.conf ]; then
+ if [ "$WO_ARCH" = "x86_64" ]; then
+ rm -f /etc/sysctl.d/60-ubuntu-nginx-web-server.conf
+ wget -qO /etc/sysctl.d/60-wo-tweaks.conf https://raw.githubusercontent.com/WordOps/WordOps/updating-configuration/wo/cli/templates/sysctl.mustache
+ if [ "$wo_distro_version" = "bionic" ] || [ "$wo_distro_version" = "disco" ]; then
+ modprobe tcp_bbr && echo 'tcp_bbr' >> /etc/modules-load.d/bbr.conf
+ echo -e '\nnet.ipv4.tcp_congestion_control = bbr\nnet.ipv4.tcp_notsent_lowat = 16384' >> /etc/sysctl.d/60-wo-tweaks.conf
+ else
+ modprobe tcp_htcp && echo 'tcp_htcp' >> /etc/modules-load.d/htcp.conf
+ echo 'net.ipv4.tcp_congestion_control = htcp' >> /etc/sysctl.d/60-wo-tweaks.conf
+ fi
+
+ fi
+ LIMIT_CHECK=$(grep "500000" /etc/security/limits.conf)
+ if [ -z "$LIMIT_CHECK" ]; then
+ echo -e "* hard nofile 500000\n* soft nofile 500000\nroot hard nofile 500000\nroot soft nofile 500000\n" >> /etc/security/limits.conf
+ fi
+ fi
+
+}
+
###
# 4 - WO MAIN SETUP
###
# 1 - WO already installed
if [ -x /usr/local/bin/wo ]; then
- wo -v 2>&1 | grep $wo_version_new
- if [[ $? -ne 0 ]]; then
- read -p "Update WordOps to $wo_version_new (y/n): " wo_ans
- if [ "$wo_ans" = "y" ] || [ "$wo_ans" = "Y" ]; then
+ if ! {
+ wo -v 2>&1 | grep $wo_version_new
+ }; then
+ echo -e "Update WordOps to $wo_version_new (y/n): " && read -r WO_ANSWER
+ if [ "$WO_ANSWER" = "y" ] || [ "$WO_ANSWER" = "Y" ]; then
wo_lib_echo "Installing wo dependencies " | tee -ai $wo_install_log
wo_install_dep | tee -ai $wo_install_log
wo_lib_echo "Backing-up WO install" | tee -ai $wo_install_log
@@ -653,14 +676,20 @@ if [ -x /usr/local/bin/wo ]; then
wo_lib_echo "Syncing WO database" | tee -ai $wo_install_log
secure_wo_db | tee -ai $wo_install_log
wo_lib_echo "Installing WordOps " | tee -ai $wo_install_log
- wo_install >> wo_install_log 2>&1
+ if [ -f "$HOME/.gitconfig" ]; then
+ wo_install >> wo_install_log 2>&1
+ else
+ wo_install | tee -ai $wo_install_log
+ fi
if [ -x "$(command -v nginx)" ]; then
wo_lib_echo "Upgrading Nginx" | tee -ai $wo_install_log
wo_upgrade_nginx | tee -ai $wo_install_log
fi
wo_update_latest | tee -ai $wo_install_log
- wo_lib_echo "Installing acme.sh" | tee -ai $wo_install_log
+ wo_lib_echo "Updating acme.sh" | tee -ai $wo_install_log
wo_install_acme_sh | tee -ai $wo_install_log
+ wo_lib_echo "Applying Kernel tweaks" | tee -ai $wo_install_log
+ wo_tweak_kernel | tee -ai $wo_install_log
wo_lib_echo "Running post-install steps " | tee -ai $wo_install_log
wo_git_init | tee -ai $wo_install_log
wo_update_wp_cli | tee -ai $wo_install_log
@@ -673,17 +702,23 @@ if [ -x /usr/local/bin/wo ]; then
else
# 2 - Migration from EEv3
if [ -x /usr/local/bin/ee ]; then
- read -p "Migrate from EasyEngine to WordOps (y/n): " wo_ans
- if [ "$wo_ans" = "y" ] || [ "$wo_ans" = "Y" ]; then
+ echo -e "Migrate from EasyEngine to WordOps (y/n): " && read -r WO_ANSWER
+ if [ "$WO_ANSWER" = "y" ] || [ "$WO_ANSWER" = "Y" ]; then
wo_lib_echo "Installing wo dependencies " | tee -ai $wo_install_log
wo_install_dep | tee -ai $wo_install_log
wo_lib_echo "Backing-up EE install" | tee -ai $wo_install_log
wo_backup_ee | tee -ai $wo_install_log
+ wo_lib_echo "Removing EasyEngine cronjob" | tee -ai $wo_install_log
+ wo_remove_ee_cron | tee -ai $wo_install_log
wo_lib_echo "Syncing WO database" | tee -ai $wo_install_log
wo_sync_db | tee -ai $wo_install_log
secure_wo_db | tee -ai $wo_install_log
wo_lib_echo "Installing WordOps " | tee -ai $wo_install_log
- wo_install >> wo_install_log 2>&1
+ if [ -f "$HOME/.gitconfig" ]; then
+ wo_install >> wo_install_log 2>&1
+ else
+ wo_install | tee -ai $wo_install_log
+ fi
if [ -x "$(command -v nginx)" ]; then
wo_lib_echo "Upgrading Nginx" | tee -ai $wo_install_log
wo_upgrade_nginx | tee -ai $wo_install_log
@@ -691,6 +726,8 @@ else
wo_update_latest | tee -ai $wo_install_log
wo_lib_echo "Installing acme.sh" | tee -ai $wo_install_log
wo_install_acme_sh | tee -ai $wo_install_log
+ wo_lib_echo "Applying Kernel tweaks" | tee -ai $wo_install_log
+ wo_tweak_kernel | tee -ai $wo_install_log
wo_lib_echo "Running post-install steps " | tee -ai $wo_install_log
wo_git_init | tee -ai $wo_install_log
wo_update_wp_cli | tee -ai $wo_install_log
@@ -705,6 +742,8 @@ else
wo_install_dep | tee -ai $wo_install_log
wo_lib_echo "Installing WordOps " | tee -ai $wo_install_log
wo_install | tee -ai $wo_install_log
+ wo_lib_echo "Applying Kernel tweaks" | tee -ai $wo_install_log
+ wo_tweak_kernel | tee -ai $wo_install_log
wo_lib_echo "Installing acme.sh" | tee -ai $wo_install_log
wo_install_acme_sh | tee -ai $wo_install_log
wo_lib_echo "Running post-install steps " | tee -ai $wo_install_log
diff --git a/setup.py b/setup.py
index 39ecda6..c302674 100644
--- a/setup.py
+++ b/setup.py
@@ -38,7 +38,7 @@ except Exception as e:
print("Your informations will ONLY be stored locally")
wo_user = input("Enter your name: ")
- while wo_user is "":
+ while wo_user == "":
print("Unfortunately, this can't be left blank")
wo_user = input("Enter your name: ")
@@ -57,7 +57,7 @@ if not os.path.isfile('/root/.gitconfig'):
shutil.copy2(os.path.expanduser("~")+'/.gitconfig', '/root/.gitconfig')
setup(name='wo',
- version='3.9.4',
+ version='3.9.5',
description=long_description,
long_description=long_description,
classifiers=[],
diff --git a/smtp-cli.pl b/smtp-cli.pl
deleted file mode 100644
index 0eeb426..0000000
--- a/smtp-cli.pl
+++ /dev/null
@@ -1,1074 +0,0 @@
-#!/usr/bin/perl
-
-#
-# Command line SMTP client with SSL, STARTTLS, SMTP-AUTH and IPv6 support.
-# Michal Ludvig, 2003-2018
-# See http://smtp-cli.logix.cz for details
-# and https://github.com/mludvig/smtp-cli for code.
-# Thanks to all contributors for ideas and fixes!
-#
-
-my $version = "3.9";
-
-#
-# ChangeLog:
-# * Version 3.9 (2018-04-06)
-# - Don't attempt to use IO::Socket::INET6 with --ipv4.
-#
-# * Version 3.8 (2017-07-05)
-# - New parameter --local-addr
-# - Support body and attachment reading from non-regular files
-# - Various protocol fixes
-#
-# * Version 3.7 (2014-11-21)
-# - Support STDIN input with --body-plain=- or --body-html=-
-#
-# * Version 3.6 (2013-07-11)
-# - Improved compatibility with perl < 5.10 and perl >= 5.18
-# - Added support for more chars in user-part of email address.
-#
-# * Version 3.5 (2013-05-08)
-# - Improved compliance with SMTP RFC 5321
-# - New parameter --text-encoding
-#
-# * Version 3.4 (2013-02-05)
-# - Ok, ok, support both File::Type and File::LibMagic
-#
-# * Version 3.3 (2012-07-30)
-# - Moved from File::Type to File::LibMagic
-# (File::Type is no longer maintained and not available
-# in EPEL for RHEL 6)
-#
-# * Version 3.2 (2012-06-26)
-# - Fixed syntax error
-#
-# * Version 3.1 (2012-06-25)
-# - New --add-header, --replace-header and --remove-header options.
-# - Improved compatibility with new IO::Socket::SSL releases.
-#
-# * Version 3.0 (2012-01-24)
-# - Support for server SSL verification agains CA root cert.
-# - Use "Content-Disposition: attachment" for all attachments
-# unless --attach-inline was used.
-# - No longer default to --server=localhost
-# - Support for --charset= affecting all text/* parts.
-# - Ensure "To: undisclosed-recipients:;" if sending only to Bcc.
-#
-# * Version 2.9 (2011-09-02)
-# - Fixed problem when using IPv6 addresses with --server.
-# For example with --server 2001:db8::123 it was connecting
-# to server 2001:db8:: port 123. Fixed now.
-#
-# * Version 2.8 (2011-01-05)
-# - Added --ssl to support for SSMTP (SMTP over SSL). This is
-# turned on by default when --port=465.
-#
-# * Version 2.7 (2010-09-08)
-# - Added support for Cc header (--cc=...)
-# - Addressess (From, To, Cc) can now contain a "display name",
-# for example --from="Michal Ludvig "
-# - Support for --mail-from and --rcpt-to addresses independent
-# on --from, --to, --cc and --bcc
-# - Fixed warnings in Perl 5.12
-#
-# * Version 2.6 (2009-08-05)
-# - Message building fixed for plaintext+attachment case.
-# - Auto-enable AUTH as soon as --user parameter is used.
-# (previously --enable-auth or --auth-plain had to be used
-# together with --user, that was confusing).
-# - New --print-only parameter for displaying the composed
-# MIME message without sending.
-# - All(?) non-standard modules are now optional.
-# - Displays local and remote address on successfull connect.
-#
-# * Version 2.5 (2009-07-21)
-# - IPv6 support provided the required modules are
-# available.
-#
-# * Version 2.1 (2008-12-08)
-# - Make the MIME modules optional. Simply disable
-# the required functionality if they're not available.
-#
-# * Version 2.0 (2008-11-18)
-# - Support for message building through MIME::Lite,
-# including attachments, multipart, etc.
-#
-# * Version 1.1 (2006-08-26)
-# - STARTTLS and AUTH support
-#
-# * Version 1.0
-# - First public version
-#
-# This program is licensed under GNU Public License v3 (GPLv3)
-#
-
-## Require Perl 5.8 or higher -> we need open(.., .., \$variable) construct
-require 5.008;
-
-use strict;
-use IO::Socket::INET;
-use MIME::Base64 qw(encode_base64 decode_base64);
-use Getopt::Long;
-use Socket qw(:DEFAULT :crlf);
-
-my @valid_encodings = ("7bit", "8bit", "binary", "base64", "quoted-printable");
-
-my ($user, $pass, $host, $port, $addr_family, $localaddr,
- $use_login, $use_plain, $use_cram_md5,
- $ehlo_ok, $auth_ok, $starttls_ok, $ssl, $verbose,
- $hello_host, $datasrc,
- $mail_from, @rcpt_to, $from, @to, @cc, @bcc,
- $missing_modules_ok, $missing_modules_count,
- $subject, $body_plain, $body_html, $charset, $text_encoding, $print_only,
- @attachments, @attachments_inline,
- @add_headers, @replace_headers, @remove_headers,
- $ssl_ca_file, $ssl_ca_path,
- $sock, $built_message);
-
-$host = undef;
-$port = 'smtp(25)';
-$addr_family = AF_UNSPEC;
-$localaddr = undef;
-$hello_host = 'localhost';
-$verbose = 0;
-$use_login = 0;
-$use_plain = 0;
-$use_cram_md5 = 0;
-$starttls_ok = 1;
-$ssl = undef;
-$auth_ok = 0;
-$ehlo_ok = 1;
-$missing_modules_ok = 0;
-$missing_modules_count = 0;
-$charset = undef;
-$text_encoding = "quoted-printable";
-$print_only = 0;
-
-# Get command line options.
-GetOptions (
- 'host|server=s' => \$host,
- 'port=i' => \$port,
- '4|ipv4' => sub { $addr_family = AF_INET; },
- '6|ipv6' => sub { $addr_family = AF_INET6; },
- 'local-addr=s' => \$localaddr,
- 'user=s' => \$user, 'password=s' => \$pass,
- 'auth-login' => \$use_login,
- 'auth-plain' => \$use_plain,
- 'auth-cram-md5' => \$use_cram_md5,
- 'disable-ehlo' => sub { $ehlo_ok = 0; },
- 'force-ehlo' => sub { $ehlo_ok = 2; },
- 'hello-host|ehlo-host|helo-host=s' => \$hello_host,
- 'auth|enable-auth' => \$auth_ok,
- 'disable-starttls|disable-tls|disable-ssl' =>
- sub { $starttls_ok = 0; },
- 'ssl' => sub { $ssl = 1 },
- 'disable-ssl' => sub { $ssl = 0 },
- 'mail-from=s' => \$mail_from,
- 'rcpt-to=s' => \@rcpt_to,
- 'from=s' => \$from,
- 'to=s' => \@to,
- 'cc=s' => \@cc,
- 'bcc=s' => \@bcc,
- 'data=s' => \$datasrc,
- 'subject=s' => \$subject,
- 'body|body-plain=s' => \$body_plain,
- 'body-html=s' => \$body_html,
- 'charset=s' => \$charset,
- 'text-encoding=s' => \$text_encoding,
- 'attachment|attach=s' => \@attachments,
- 'attachment-inline|attach-inline=s' => \@attachments_inline,
- 'add-header=s' => \@add_headers,
- 'replace-header=s' => \@replace_headers,
- 'remove-header=s' => \@remove_headers,
- 'print-only' => \$print_only,
- 'missing-modules-ok' => \$missing_modules_ok,
- 'ssl-ca-file=s' => \$ssl_ca_file,
- 'ssl-ca-path=s' => \$ssl_ca_path,
- 'v|verbose+' => \$verbose,
- 'version' => sub { &version() },
- 'help' => sub { &usage() } );
-
-#### Try to load optional modules
-
-## IO::Socket::SSL and Net::SSLeay are optional
-my $have_ssl = eval { require IO::Socket::SSL; require Net::SSLeay; 1; };
-if (not $have_ssl and not $missing_modules_ok) {
- warn("!!! IO::Socket::SSL and/or Net::SSLeay modules are not found\n");
- warn("!!! These modules are required for SSL and STARTTLS support\n");
- $missing_modules_count += 2;
-}
-
-## IO::Socket::INET6 and Socket6 are optional
-my $socket6 = eval { require IO::Socket::INET6; require Socket6; 1; };
-if (not $socket6 and not ($addr_family == AF_INET)) {
- if ($addr_family == AF_INET6) {
- die("!!! IO::Socket::INET6 and Socket6 modules are not found\nIPv6 support is not available\n");
- }
- if (not $missing_modules_ok) {
- warn("!!! IO::Socket::INET6 -- optional module not found\n");
- warn("!!! Socket6 -- optional module not found\n");
- warn("!!! These modules are required for IPv6 support\n\n");
- $missing_modules_count += 2;
- }
-}
-
-## MIME::Lite dependency is optional
-my $mime_lite = eval { require MIME::Lite; 1; };
-if (not $mime_lite and not $missing_modules_ok) {
- warn("!!! MIME::Lite -- optional module not found\n");
- warn("!!! Used for composing messages from --subject, --body, --attachment, etc.\n\n");
- $missing_modules_count++;
-}
-
-## File::LibMagic dependency is optional
-my $file_libmagic = eval { require File::LibMagic; File::LibMagic->new(); };
-
-## File::Type dependency is optional
-## Not needed if File::LibMagic is available
-my $file_type = eval { require File::Type; File::Type->new(); };
-
-if (not $file_libmagic and not $file_type and not $missing_modules_ok) {
- warn("!!! Neither File::LibMagic nor File::Type module found.\n");
- warn("!!! Used for guessing MIME types of attachments. Optional.\n\n");
- $missing_modules_count++;
-}
-
-## Term::ReadKey dependency is optional
-my $have_term_readkey = eval { require Term::ReadKey; 1; };
-if (not $have_term_readkey and not $missing_modules_ok) {
- warn("!!! Term::ReadKey -- optional module not found\n");
- warn("!!! Used for hidden reading SMTP password from the terminal\n\n");
- $missing_modules_count++;
-}
-
-my $have_hmac_md5 = eval { require Digest::HMAC_MD5; 1; };
-if (not $have_hmac_md5 and not $missing_modules_ok) {
- if ($use_cram_md5) {
- die("!!! CRAM-MD5 authentication is not available because Digest::HMAC_MD5 module is missing\n");
- }
- warn("!!! Digest::HMAC_MD5 -- optional module missing\n");
- warn("!!! Used for CRAM-MD5 authentication method\n");
- $missing_modules_count++;
-}
-
-## Advise about --missing-modules-ok parameter
-if ($missing_modules_count) {
- warn("!!! Use --missing-modules-ok if you don't need the above listed modules\n");
- warn("!!! and don't want to see this message again.\n\n");
-}
-
-## Make sure we've got a server name to connect to
-if (not defined($host)) {
- if (not $print_only) {
- die("Error: Specify the SMTP server with --server=hostname[:port]\n");
- } else {
- # We're printing to stdout only, let's assign just about any
- # hostname to satisfy the next few tests.
- $host = "localhost";
- }
-}
-
-## Make sure the --text-encoding value is valid
-if (not grep(/^$text_encoding$/, @valid_encodings))
-{
- die ("The --text-encoding value is invalid: $text_encoding\nMust be one of: " . join(', ', @valid_encodings) . "\n");
-}
-
-## Accept hostname with port number as host:port
-## Either it's a hostname:port or 1.2.3.4:port or [2001:db8::1]:port.
-## Don't parse 2001:db8::1 as $host=2001:db8:: and $port=1!
-if (($host =~ /^([^:]+):([:alnum:]+)$/) or
- ($host =~ /^\[([[:xdigit:]:]+)\]:([:alnum:]+)$/))
-{
- $host = $1;
- $port = $2;
-}
-
-## Automatically start in SSL mode if port == 465 (SSMTP)
-if (not defined($ssl)) {
- $ssl = ($port == 465);
-}
-
-# Extract $mail_from address from $from
-if (not defined($mail_from) and defined($from)) {
- $mail_from = &find_email_addr($from) or
- die ("The --from string does not contain a valid email address: $from\n");
-}
-
-# Extract @rcpt_to list from @to, @cc and @bcc
-if (not @rcpt_to) {
- foreach my $rcpt (@to, @cc, @bcc) {
- my $rcpt_addr = &find_email_addr($rcpt);
- if (not defined($rcpt_addr)) {
- warn("No valid email address found in: $rcpt\n");
- next;
- }
- push(@rcpt_to, $rcpt_addr);
- }
-}
-
-# Ensure "To: undisclosed-recipients:;" when sending only to Bcc's
-if (not @to and not @cc) {
- push(@to, "undisclosed-recipients:;");
-}
-
-# Build the MIME message if required
-if (defined($subject) or defined($body_plain) or defined($body_html) or
- @attachments or @attachments_inline) {
- if (not $mime_lite) {
- die("Module MIME::Lite is not available. Unable to build the message, sorry.\n".
- "Use --data and provide a complete email payload including headers instead.\n");
- }
- if (defined($datasrc)) {
- die("Requested building a message and at the same time used --data parameter.\n".
- "That's not possible, sorry.\n");
- }
- if (defined($body_plain)) {
- if (-e $body_plain) {
- local $/=undef;
- open(FILE, $body_plain);
- $body_plain = ;
- close(FILE);
- } elsif ($body_plain eq "-") {
- local $/=undef;
- $body_plain = ;
- }
- }
- if (defined($body_html)) {
- if (-e $body_html) {
- local $/=undef;
- open(FILE, $body_html);
- $body_html = ;
- close(FILE);
- } elsif ($body_html eq "-") {
- local $/=undef;
- $body_html = ;
- }
- }
- my $message = &build_message();
-
- open(BUILT_MESSAGE, "+>", \$built_message);
- $datasrc = "///built_message";
- if ($print_only) {
- $message->print();
- exit(0);
- } else {
- $message->print(\*BUILT_MESSAGE);
- }
- seek(BUILT_MESSAGE, 0, 0);
-}
-
-# Username was given -> enable AUTH
-if ($user)
- { $auth_ok = 1; }
-
-# If at least one --auth-* option was given, enable AUTH.
-if ($use_login + $use_plain + $use_cram_md5 > 0)
- { $auth_ok = 1; }
-
-# If --enable-auth was given, enable all AUTH methods.
-elsif ($auth_ok && ($use_login + $use_plain + $use_cram_md5 == 0))
-{
- $use_login = 1;
- $use_plain = 1;
- $use_cram_md5 = 1 if ($have_hmac_md5);
-}
-
-# Exit if user haven't specified username for AUTH.
-if ($auth_ok && !defined ($user))
- { die ("SMTP AUTH support requested without --user\n"); }
-
-# Ask for password if it wasn't supplied on the command line.
-if ($auth_ok && defined ($user) && !defined ($pass))
-{
- if ($have_term_readkey) {
- # Set echo off.
- Term::ReadKey::ReadMode (2);
- } else {
- warn ("Module Term::ReadKey not available - password WILL NOT be hidden!!!\n");
- }
- printf ("Enter password for %s@%s : ", $user, $host);
- $pass = <>;
- if ($have_term_readkey) {
- # Restore echo.
- Term::ReadKey::ReadMode (0);
- printf ("\n");
- }
- exit if (! defined ($pass));
- chop ($pass);
-}
-
-# Connect to the SMTP server.
-my %connect_args = (
- PeerAddr => $host,
- PeerPort => $port,
- Proto => 'tcp',
- Timeout => 5);
-
-if (defined($localaddr)) {
- $connect_args{'LocalAddr'} = $localaddr;
-}
-
-if ($addr_family == AF_INET) {
- # If the user requested --ipv4 don't even bother with INET6 module
- # (although it should work some users reported problems)
- $sock = IO::Socket::INET->new(%connect_args) or die ("Connect failed: $@\n");
-} else {
- # Either --ipv6 or no preference - do the best we can
- $connect_args{'Domain'} = $addr_family;
- $sock = IO::Socket::INET6->new(%connect_args) or die ("Connect failed: $@\n");
-}
-
-if ($verbose >= 1) {
- my $addr_fmt = "%s";
- $addr_fmt = "[%s]" if ($sock->sockhost() =~ /:/); ## IPv6 connection
-
- printf ("Connection from $addr_fmt:%s to $addr_fmt:%s\n",
- $sock->sockhost(), $sock->sockport(),
- $sock->peerhost(), $sock->peerport());
-}
-
-if ($ssl) {
- printf ("Starting SMTP/SSL...\n") if ($verbose >= 1);
- &socket_to_ssl($sock);
-}
-
-my ($code, $text);
-my (%features);
-
-# Wait for the welcome message of the server.
-($code, $text) = &get_line ($sock);
-die ("Unknown welcome string: '$code $text'\n") if ($code != 220);
-$ehlo_ok-- if ($text !~ /ESMTP/);
-
-# Send EHLO
-&say_hello ($sock, $ehlo_ok, $hello_host, \%features) or exit (1);
-
-# Run the SMTP session
-my $exitcode = &run_smtp ();
-
-# Good bye...
-&send_line ($sock, "QUIT\n");
-($code, $text) = &get_line ($sock);
-die ("Unknown QUIT response '$code'.\n") if ($code != 221);
-
-exit $exitcode;
-
-# This is the main SMTP "engine".
-sub run_smtp
-{
- # See if we could start encryption
- if ((defined ($features{'STARTTLS'}) || defined ($features{'TLS'})) && $starttls_ok && !$have_ssl)
- {
- warn ("Module IO::Socket::SSL is missing - STARTTLS support disabled.\n");
- warn ("Use --disable-starttls or install the modules to avoid this warning.\n");
- undef ($features{'STARTTLS'});
- undef ($features{'TLS'});
- }
-
- if ((defined ($features{'STARTTLS'}) || defined ($features{'TLS'})) && $starttls_ok)
- {
- printf ("Starting TLS...\n") if ($verbose >= 1);
-
- &send_line ($sock, "STARTTLS\n");
- ($code, $text) = &get_line ($sock);
- die ("Unknown STARTTLS response '$code'.\n") if ($code != 220);
-
- &socket_to_ssl($sock);
-
- # Send EHLO again (required by the SMTP standard).
- &say_hello ($sock, $ehlo_ok, $hello_host, \%features) or return 0;
- }
-
- # See if we should authenticate ourself
- if (defined ($features{'AUTH'}) && $auth_ok)
- {
- printf ("AUTH method (%s): ", $features{'AUTH'}) if ($verbose >= 1);
-
- ## Try DIGEST-MD5 first
- # Actually we won't. It never worked reliably here.
- # After all DIGEST-MD5 is on a way to deprecation
- # see this thread: http://www.imc.org/ietf-sasl/mail-archive/msg02996.html
-
- # Instead use CRAM-MD5 if supported by the server
- if ($features{'AUTH'} =~ /CRAM-MD5/i && $use_cram_md5)
- {
- printf ("using CRAM-MD5\n") if ($verbose >= 1);
- &send_line ($sock, "AUTH CRAM-MD5\n");
- ($code, $text) = &get_line ($sock);
- if ($code != 334)
- { die ("AUTH CRAM-MD5 failed: $code $text\n"); }
-
- my $response = &encode_cram_md5 ($text, $user, $pass);
- &send_line ($sock, "%s\n", $response);
- ($code, $text) = &get_line ($sock);
- if ($code != 235)
- { die ("AUTH CRAM-MD5 failed: $code $text\n"); }
- }
- # Eventually try LOGIN method
- elsif ($features{'AUTH'} =~ /LOGIN/i && $use_login)
- {
- printf ("using LOGIN\n") if ($verbose >= 1);
- &send_line ($sock, "AUTH LOGIN\n");
- ($code, $text) = &get_line ($sock);
- if ($code != 334)
- { die ("AUTH LOGIN failed: $code $text\n"); }
-
- &send_line ($sock, "%s\n", encode_base64 ($user, ""));
-
- ($code, $text) = &get_line ($sock);
- if ($code != 334)
- { die ("AUTH LOGIN failed: $code $text\n"); }
-
- &send_line ($sock, "%s\n", encode_base64 ($pass, ""));
-
- ($code, $text) = &get_line ($sock);
- if ($code != 235)
- { die ("AUTH LOGIN failed: $code $text\n"); }
- }
- # Or finally PLAIN if nothing else was supported.
- elsif ($features{'AUTH'} =~ /PLAIN/i && $use_plain)
- {
- printf ("using PLAIN\n") if ($verbose >= 1);
- &send_line ($sock, "AUTH PLAIN %s\n",
- encode_base64 ("$user\0$user\0$pass", ""));
- ($code, $text) = &get_line ($sock);
- if ($code != 235)
- { die ("AUTH PLAIN failed: $code $text\n"); }
- }
- # Complain otherwise.
- else
- {
- warn ("No supported authentication method\n".
- "advertised by the server.\n");
- return 1;
- }
-
- printf ("Authentication of $user\@$host succeeded\n") if ($verbose >= 1);
- }
-
- # We can do a relay-test now if a recipient was set.
- if ($#rcpt_to >= 0)
- {
- if (!defined ($mail_from))
- {
- warn ("From: address not set. Using empty one.\n");
- $mail_from = "";
- }
- &send_line ($sock, "MAIL FROM:<%s>\n", $mail_from);
- ($code, $text) = &get_line ($sock);
- if ($code != 250)
- {
- warn ("MAIL FROM <$mail_from> failed: '$code $text'\n");
- return 1;
- }
-
- my $i;
- for ($i=0; $i <= $#rcpt_to; $i++)
- {
- &send_line ($sock, "RCPT TO:<%s>\n", $rcpt_to[$i]);
- ($code, $text) = &get_line ($sock);
- if ($code != 250)
- {
- warn ("RCPT TO <".$rcpt_to[$i]."> ".
- "failed: '$code $text'\n");
- return 0;
- }
- }
- }
-
- # Wow, we should even send something!
- if (defined ($datasrc))
- {
- if ($datasrc eq "///built_message")
- {
- *MAIL = *BUILT_MESSAGE;
- }
- elsif ($datasrc eq "-")
- {
- *MAIL = *STDIN;
- }
- elsif (!open (MAIL, $datasrc))
- {
- warn ("Can't open file '$datasrc'\n");
- return 0;
- }
-
- &send_line ($sock, "DATA\n");
- ($code, $text) = &get_line ($sock);
- if ($code != 354)
- {
- warn ("DATA failed: '$code $text'\n");
- return 0;
- }
-
- while ()
- {
- my $line = $_;
- # RFC 5321 section 4.5.2 - leading dot must be doubled
- $line =~ s/^\./\.\./;
- # RFC 5321 section 2.3.8 - ensure CR-LF line ending
- $line =~ s/[\r\n]+$/$CRLF/;
- $sock->print ($line);
- }
-
- close (MAIL);
-
- $sock->printf ("$CRLF.$CRLF");
-
- ($code, $text) = &get_line ($sock);
- if ($code != 250)
- {
- warn ("DATA not send: '$code $text'\n");
- return 0;
- }
- }
-
- # Perfect. Everything succeeded!
- return 1;
-}
-
-# Get one line of response from the server.
-sub get_one_line ($)
-{
- my $sock = shift;
- my ($code, $sep, $text) = ($sock->getline() =~ /(\d+)(.)([^\r]*)/);
- my $more;
- $more = ($sep eq "-");
- if ($verbose)
- { printf ("[%d] '%s'\n", $code, $text); }
- return ($code, $text, $more);
-}
-
-# Get concatenated lines of response from the server.
-sub get_line ($)
-{
- my $sock = shift;
- my ($code, $text, $more) = &get_one_line ($sock);
- while ($more) {
- my ($code2, $line);
- ($code2, $line, $more) = &get_one_line ($sock);
- $text .= " $line";
- die ("Error code changed from $code to $code2. That's illegal.\n") if ($code ne $code2);
- }
- return ($code, $text);
-}
-
-# Send one line back to the server
-sub send_line ($@)
-{
- my $socket = shift;
- my @args = @_;
-
- if ($verbose)
- { printf ("> "); printf (@args); }
- $args[0] =~ s/\n/$CRLF/g;
- $socket->printf (@args);
-}
-
-sub socket_to_ssl($)
-{
- if (!$have_ssl) {
- die ("SSL/TLS support is not available due to missing modules. Sorry.\n");
- }
-
- # Do Net::SSLeay initialization
- Net::SSLeay::load_error_strings();
- Net::SSLeay::SSLeay_add_ssl_algorithms();
- Net::SSLeay::randomize();
-
- if (! IO::Socket::SSL->start_SSL($sock, {
- SSL_ca_file => $ssl_ca_file,
- SSL_ca_path => $ssl_ca_path,
- SSL_verify_mode => (defined($ssl_ca_file) or defined($ssl_ca_path)) ? 0x01 : 0x00,
- }))
- {
- die ("SSL/TLS: ".IO::Socket::SSL::errstr()."\n");
- }
-
- if ($verbose >= 1)
- {
- printf ("Using cipher: %s\n", $sock->get_cipher ());
- printf ("%s", $sock->dump_peer_certificate());
- }
-}
-
-# Helper function to encode CRAM-MD5 challenge
-sub encode_cram_md5 ($$$)
-{
- my ($ticket64, $username, $password) = @_;
- my $ticket = decode_base64($ticket64) or
- die ("Unable to decode Base64 encoded string '$ticket64'\n");
-
- print "Decoded CRAM-MD5 challenge: $ticket\n" if ($verbose > 1);
- my $password_md5 = Digest::HMAC_MD5::hmac_md5_hex($ticket, $password);
- return encode_base64 ("$username $password_md5", "");
-}
-
-# Store all server's ESMTP features to a hash.
-sub say_hello ($$$$)
-{
- my ($sock, $ehlo_ok, $hello_host, $featref) = @_;
- my ($feat, $param);
- my $hello_cmd = $ehlo_ok > 0 ? "EHLO" : "HELO";
-
- &send_line ($sock, "$hello_cmd $hello_host\n");
- my ($code, $text, $more) = &get_one_line ($sock);
-
- if ($code != 250)
- {
- warn ("$hello_cmd failed: '$code $text'\n");
- return 0;
- }
-
- # Empty the hash
- %{$featref} = ();
-
- ($feat, $param) = ($text =~ /^(\w+)[= ]*(.*)$/);
- $featref->{$feat} = $param;
-
- # Load all features presented by the server into the hash
- while ($more == 1)
- {
- ($code, $text, $more) = &get_one_line ($sock);
- ($feat, $param) = ($text =~ /^(\w+)[= ]*(.*)$/);
- $featref->{$feat} = $param;
- }
-
- return 1;
-}
-
-sub find_email_addr($)
-{
- my $addr = shift;
- if ($addr =~ /([A-Z0-9._%=#+-]+@(?:[A-Z0-9-]+\.)+[A-Z]+)\b/i) {
- return $1;
- }
- return undef;
-}
-
-sub guess_mime_type($)
-{
- my $filename = shift;
- if (defined($file_libmagic)) {
- ## Use File::LibMagic if possible
- return $file_libmagic->checktype_filename($filename);
- } elsif (defined($file_type)) {
- ## Use File::Type if possible
- return $file_type->mime_type($filename);
- } else {
- ## Module File::LibMagic is not available
- ## Still recognise some common extensions
- return "image/jpeg" if ($filename =~ /\.jpe?g/i);
- return "image/gif" if ($filename =~ /\.gif/i);
- return "image/png" if ($filename =~ /\.png/i);
- return "text/plain" if ($filename =~ /\.txt/i);
- return "application/zip" if ($filename =~ /\.zip/i);
- return "application/x-gzip" if ($filename =~ /\.t?gz/i);
- return "application/x-bzip" if ($filename =~ /\.t?bz2?/i);
- }
- return "application/octet-stream";
-}
-
-sub basename($)
-{
- my $path = shift;
- my @parts = split(/\//, $path);
- return $parts[$#parts];
-}
-
-sub prepare_attachment($)
-{
- my $attachment = shift;
- my ($path, $mime_type);
-
- if (-e $attachment) {
- $path = $attachment;
- $mime_type = guess_mime_type($attachment);
- } elsif ($attachment =~ /(.*)@([^@]*)$/ and -e $1) {
- $path = $1;
- $mime_type = $2;
- }
- return ($path, $mime_type);
-}
-
-sub attach_attachments($$@)
-{
- my $message = shift;
- my $disposition = shift;
- my @attachments = @_;
-
- foreach my $attachment (@attachments) {
- my ($path, $mime_type) = prepare_attachment($attachment);
- if (not defined($path)) {
- warn("$attachment: File not found. Ignoring.\n");
- next;
- }
- $message->attach(
- Type => $mime_type,
- Path => $path,
- Id => basename($path),
- Disposition => $disposition,
- );
- }
-}
-
-sub safe_attach($$)
-{
- my ($message, $part) = @_;
- ## Remove some headers when $part is becoming a subpart of $message
- $part->delete("Date");
- $part->delete("X-Mailer");
- $part->attr("MIME-Version" => undef);
- $message->attach($part);
- return $message;
-}
-
-sub mime_message($$)
-{
- my ($type, $data) = @_;
-
- ## Set QP encoding for text/* types, let MIME::Lite decide for all other types.
- my $encoding = $type =~ /^text\// ? $text_encoding : undef;
- my $message = MIME::Lite->new(
- Type => $type,
- Encoding=> $encoding,
- Data => $data);
- $message->attr('content-type.charset' => $charset) if (($type =~ /^text\//i) and defined($charset));
- return $message;
-}
-
-sub build_message
-{
- my ($part_plain, $part_html, $part_body, $message);
-
- if (@attachments_inline) {
- if (not defined($body_html)) {
- die("Inline attachments (--attach-inline) must be used with --body-html\n");
- }
- $part_html = MIME::Lite->new(Type => 'multipart/related');
- $part_html->attach(Type => 'text/html', Data => $body_html);
- attach_attachments($part_html, "inline", @attachments_inline);
- $message = $part_html;
- # undefine $body_html to prevent confusion in the next if()
- undef($body_html);
- }
-
- if (defined($body_html)) {
- $part_html = mime_message('text/html', $body_html);
- $message = $part_html;
- }
-
- if (defined($body_plain)) {
- $part_plain = mime_message('text/plain', $body_plain);
- $message = $part_plain;
- }
-
- if (defined($part_plain) and defined($part_html)) {
- $part_body = mime_message("multipart/alternative", undef);
- safe_attach($part_body, $part_plain);
- safe_attach($part_body, $part_html);
- $message = $part_body;
- }
-
- if (@attachments) {
- if (defined($message)) {
- # We already have some plaintext and/or html content built
- # => make it the first part of multipart/mixed
- my $message_body = $message;
- $message = mime_message("multipart/mixed", undef);
- safe_attach($message, $message_body);
- attach_attachments($message, "attachment", @attachments);
- } elsif ($#attachments == 0) {
- # Only one single attachment - let it be the body
- my ($path, $mime_type) = prepare_attachment($attachments[0]);
- if (not defined($path)) {
- die($attachments[0].": File not found. No other message parts defined. Aborting.\n");
- }
- $message = MIME::Lite->new(
- Type => $mime_type,
- Path => $path);
- } else {
- # Message consisting only of attachments
- $message = mime_message("multipart/mixed", undef);
- attach_attachments($message, "attachment", @attachments);
- }
- }
-
- # Last resort - empty plaintext message
- if (!defined($message)) {
- $message = mime_message("TEXT", "");
- }
-
- $message->replace("From" => $from);
- $message->replace("To" => join(", ", @to));
- $message->replace("Cc" => join(", ", @cc));
- $message->replace("Subject" => $subject);
- $message->replace("X-Mailer" => "smtp-cli $version, see http://smtp-cli.logix.cz");
- $message->replace("Message-ID" => "<".time()."-".int(rand(999999))."\@smtp-cli>");
-
- for my $header (@add_headers) {
- my ($hdr, $val) = ($header =~ /^([^:]+):\s*(.*)$/);
- die("Not a valid header format: ${header}\n") if (not $hdr or not $val);
- $message->add($hdr => $val);
- }
- for my $header (@replace_headers) {
- my ($hdr, $val) = ($header =~ /^([^:]+):\s*(.*)$/);
- die("Not a valid header format: ${header}\n") if (not $hdr or not $val);
- $message->replace($hdr => $val);
- }
- for my $header (@remove_headers) {
- my ($hdr) = ($header =~ /^([^:\s]+)/);
- $message->replace($header => "");
- }
-
- return $message;
-}
-
-sub version ()
-{
- print "smtp-cli version $version\n";
- exit (0);
-}
-
-sub usage ()
-{
- printf (
-"Simple SMTP client written in Perl that supports advanced
-features like STARTTLS and SMTP-AUTH and IPv6. It can also
-create messages from components (files, text snippets) and
-attach files.
-
-Version: smtp-cli v$version
-
-Author: Michal Ludvig (c) 2003-2017
- http://smtp-cli.logix.cz
-
-Usage: smtp-cli [--options]
-
- --server=[:]
- Host name or IP address of the SMTP server.
- May include the port after colon, alternatively
- use --port.
- --port= Port where the SMTP server is listening.
- (default: 25)
- -4 or --ipv4 Use standard IP (IPv4) protocol.
- -6 or --ipv6 Use IPv6 protocol. For hosts that have
- both IPv6 and IPv4 addresses the IPv6
- connection is tried first.
- --local-addr= Specify local address (by default the OS chooses)
-
- --hello-host= String to use in the EHLO/HELO command.
- --disable-ehlo Don't use ESMTP EHLO command, only HELO.
- --force-ehlo Use EHLO even if server doesn't say ESMTP.
-
- Transport encryption (TLS)
- --disable-starttls Don't use encryption even if the remote
- host offers it.
- --ssl Start in SMTP/SSL mode (aka SSMTP).
- Default when --port=465
- --disable-ssl Don't start SSMTP even if --port=465
- --ssl-ca-file=
- Verify the server's SSL certificate against
- a trusted CA root certificate file.
- --ssl-ca-path= Similar to --ssl-ca-file but will look for
- the appropriate root certificate file in
- the given directory. The certificates must
- must be stored one per file with hash-links
- generated by, for example, c_rehash script
- from OpenSSL.
-
- Authentication options (AUTH)
- --user= Username for SMTP authentication.
- --pass= Corresponding password.
- --auth-login Enable only AUTH LOGIN method.
- --auth-plain Enable only AUTH PLAIN method.
- --auth-cram-md5 Enable only AUTH CRAM-MD5 method.
- --auth Enable all supported methods. This is
- normally not needed, --user enables
- everything as well.
-
- Sender / recipient
- --from=\"Display Name \"
- Sender's name address (or address only).
- --to=\"Display Name \"
- --cc=\"Display Name \"
- --bcc=\"Display Name \"
- Message recipients. Each parameter can be
- used multiple times.
- The --bcc addresses won't apprear in
- the composed message.
-
- SMTP Envelope sender / recipient
- (rarely needed, use --from, --to, --cc and --bcc instead)
- --mail-from= Address to use in MAIL FROM command.
- Use --from instead, unless you want
- a different address in the envelope and
- in the headers.
- --rcpt-to= Address to use in RCPT TO command. Can be
- used multiple times. Normally not needed,
- use --to, --cc and --bcc instead.
- If set the --to, --cc and --bcc will only
- be used for composing the message body and
- not for delivering the messages.
-
- Send a complete RFC822-compliant email message:
- --data= Name of file to send after DATA command.
- With \"--data=-\" the script will read
- standard input (useful e.g. for pipes).
-
- Alternatively build email a message from provided components:
- --subject= Subject of the message
- --body-plain=
- --body-html=
- Plaintext and/or HTML body of the message
- If both are provided the message is sent
- as multipart.
- --charset= Character set used for Subject and Body,
- for example UTF-8, ISO-8859-2, KOI8-R, etc.
- --text-encoding=
- Enforce Content-Transfer-Encoding for text
- parts of the email, including body and
- attachments. Must be one of:
- ".join(", ", @valid_encodings)."
- The default is: quoted-printable
- --attach=[\@]
- Attach a given filename.
- MIME-Type of the attachment is guessed
- by default guessed but can optionally
- be specified after '\@' delimiter.
- For instance: --attach mail.log\@text/plain
- Parameter can be used multiple times.
- --attach-inline=[\@]
- Attach a given filename (typically a picture)
- as a 'related' part to the above 'body-html'.
- Refer to these pictures as
- in the 'body-html' contents.
- See --attach for details about MIME-Type.
- Can be used multiple times.
- --add-header=\"Header: value\"
- --replace-header=\"Header: value\"
- --remove-header=\"Header\"
- Add, Replace or Remove pretty much any header
- in the email. For example to set a different
- Mailer use --replace-header=\"X-Mailer: Blah\",
- to remove it altogether --remove-header=X-Mailer
- or to add a completely custom header use
- --add-header=\"X-Something: foo bar\".
- --print-only Dump the composed MIME message to standard
- output. This is useful mainly for debugging
- or in the case you need to run the message
- through some filter before sending.
-
- Other options
- --verbose[=] Be more verbose, print the SMTP session.
- --missing-modules-ok Don't complain about missing optional modules.
- --version Print: smtp-cli version $version
- --help Guess what is this option for ;-)
-
-PayPal donations: http://smtp-cli.logix.cz/donate
- Thanks in advance for your support!
-
-");
- exit (0);
-}
diff --git a/wo/cli/main.py b/wo/cli/main.py
index e8d9812..230257d 100644
--- a/wo/cli/main.py
+++ b/wo/cli/main.py
@@ -86,7 +86,8 @@ def main():
# if not root...kick out
if not os.geteuid() == 0:
- print("\nNon-privileged users cant use WordOps. Switch to root or invoke sudo.\n")
+ print("\nNon-privileged users cant use WordOps. "
+ "Switch to root or invoke sudo.\n")
app.close(1)
# Setup the application
@@ -130,5 +131,6 @@ def get_test_app(**kw):
app = WOApp(**kw)
return app
+
if __name__ == '__main__':
main()
diff --git a/wo/cli/plugins/debug.py b/wo/cli/plugins/debug.py
index af1cfda..51178ae 100644
--- a/wo/cli/plugins/debug.py
+++ b/wo/cli/plugins/debug.py
@@ -191,22 +191,22 @@ class WODebugController(CementBaseController):
nc.savef('/etc/nginx/conf.d/upstream.conf')
# Enable xdebug
- WOFileUtils.searchreplace(self, "/etc/{0}/mods-available/".format("php/7.2" if (WOVariables.wo_platform_distro == 'ubuntu') else "php/7.2") +
+ WOFileUtils.searchreplace(self, "/etc/{0}/"
+ "mods-available/".format("php/7.2") +
"xdebug.ini",
";zend_extension",
"zend_extension")
# Fix slow log is not enabled default in PHP5.6
config = configparser.ConfigParser()
- config.read('/etc/{0}/fpm/pool.d/debug.conf'.format(
- "php/7.2" if (WOVariables.wo_platform_distro == 'ubuntu') else "php5"))
- config['debug']['slowlog'] = '/var/log/{0}/slow.log'.format("php/7.2" if (
- WOVariables.wo_platform_distro == 'ubuntu') else "php5")
+ config.read('/etc/{0}/fpm/pool.d/debug.conf'.format("php/7.2"))
+ config['debug']['slowlog'] = '/var/log/{0}/slow.log'.format(
+ "php/7.2")
config['debug']['request_slowlog_timeout'] = '10s'
- with open('/etc/{0}/fpm/pool.d/debug.conf'.format("php/7.2" if (WOVariables.wo_platform_distro == 'ubuntu') else "php5"),
+ with open('/etc/{0}/fpm/pool.d/debug.conf'.format("php/7.2"),
encoding='utf-8', mode='w') as confifile:
Log.debug(self, "Writting debug.conf configuration into "
- "/etc/{0}/fpm/pool.d/debug.conf".format("php/7.2" if (WOVariables.wo_platform_distro == 'ubuntu') else "php5"))
+ "/etc/{0}/fpm/pool.d/debug.conf".format("php/7.2"))
config.write(confifile)
self.trigger_php = True
@@ -214,8 +214,7 @@ class WODebugController(CementBaseController):
else:
Log.info(self, "PHP debug is already enabled")
- self.msg = self.msg + ['/var/log/{0}/slow.log'.format("php/7.2" if (
- WOVariables.wo_platform_distro == 'ubuntu') else "php5")]
+ self.msg = self.msg + ['/var/log/{0}/slow.log'.format("php/7.2")]
# PHP global debug stop
elif (self.app.pargs.php == 'off' and not self.app.pargs.site_name):
@@ -231,7 +230,8 @@ class WODebugController(CementBaseController):
nc.savef('/etc/nginx/conf.d/upstream.conf')
# Disable xdebug
- WOFileUtils.searchreplace(self, "/etc/{0}/mods-available/".format("php/7.2" if (WOVariables.wo_platform_distro == 'ubuntu') else "php5") +
+ WOFileUtils.searchreplace(self, "/etc/{0}/"
+ "mods-available/".format("php/7.2") +
"xdebug.ini",
"zend_extension",
";zend_extension")
@@ -247,43 +247,41 @@ class WODebugController(CementBaseController):
# PHP5-FPM start global debug
if (self.app.pargs.fpm == 'on' and not self.app.pargs.site_name):
if not WOShellExec.cmd_exec(self, "grep \"log_level = debug\" "
- "/etc/{0}/fpm/php-fpm.conf".format("php/7.2" if (WOVariables.wo_platform_distro == 'ubuntu') else "php5")):
+ "/etc/{0}/"
+ "fpm/php-fpm.conf".format("php/7.2")):
Log.info(self, "Setting up PHP5-FPM log_level = debug")
config = configparser.ConfigParser()
- config.read('/etc/{0}/fpm/php-fpm.conf'.format("php/7.2" if (
- WOVariables.wo_platform_distro == 'ubuntu') else "php5"))
+ config.read('/etc/{0}/fpm/php-fpm.conf'.format("php/7.2"))
config.remove_option('global', 'include')
config['global']['log_level'] = 'debug'
- config['global']['include'] = '/etc/{0}/fpm/pool.d/*.conf'.format("php/7.2" if (
- WOVariables.wo_platform_distro == 'ubuntu') else "php5")
- with open('/etc/{0}/fpm/php-fpm.conf'.format("php/7.2" if (WOVariables.wo_platform_distro == 'ubuntu') else "php5"),
+ config['global']['include'] = '/etc/{0}/fpm/pool.d/*.conf'.format(
+ "php/7.2")
+ with open('/etc/{0}/fpm/php-fpm.conf'.format("php/7.2"),
encoding='utf-8', mode='w') as configfile:
Log.debug(self, "Writting php5-FPM configuration into "
- "/etc/{0}/fpm/php-fpm.conf".format("php/7.2" if (WOVariables.wo_platform_distro == 'ubuntu') else "php5"))
+ "/etc/{0}/fpm/php-fpm.conf".format("php/7.2"))
config.write(configfile)
self.trigger_php = True
else:
Log.info(self, "PHP5-FPM log_level = debug already setup")
- self.msg = self.msg + ['/var/log/{0}/fpm.log'.format("php/7.2" if (
- WOVariables.wo_platform_distro == 'ubuntu') else "php5")]
+ self.msg = self.msg + ['/var/log/{0}/fpm.log'.format("php/7.2")]
# PHP5-FPM stop global debug
elif (self.app.pargs.fpm == 'off' and not self.app.pargs.site_name):
if WOShellExec.cmd_exec(self, "grep \"log_level = debug\" "
- "/etc/{0}/fpm/php-fpm.conf".format("php/7.2" if (WOVariables.wo_platform_distro == 'ubuntu') else "php5")):
+ "/etc/{0}/fpm/php-fpm.conf".format("php/7.2")):
Log.info(self, "Disabling PHP5-FPM log_level = debug")
config = configparser.ConfigParser()
- config.read('/etc/{0}/fpm/php-fpm.conf'.format("php/7.2" if (
- WOVariables.wo_platform_distro == 'ubuntu') else "php5"))
+ config.read('/etc/{0}/fpm/php-fpm.conf'.format("php/7.2"))
config.remove_option('global', 'include')
config['global']['log_level'] = 'notice'
- config['global']['include'] = '/etc/{0}/fpm/pool.d/*.conf'.format("php/7.2" if (
- WOVariables.wo_platform_distro == 'ubuntu') else "php5")
- with open('/etc/{0}/fpm/php-fpm.conf'.format("php/7.2" if (WOVariables.wo_platform_distro == 'ubuntu') else "php5"),
+ config['global']['include'] = '/etc/{0}/fpm/pool.d/*.conf'.format(
+ "php/7.2")
+ with open('/etc/{0}/fpm/php-fpm.conf'.format("php/7.2"),
encoding='utf-8', mode='w') as configfile:
Log.debug(self, "writting php5 configuration into "
- "/etc/{0}/fpm/php-fpm.conf".format("php/7.2" if (WOVariables.wo_platform_distro == 'ubuntu') else "php5"))
+ "/etc/{0}/fpm/php-fpm.conf".format("php/7.2"))
config.write(configfile)
self.trigger_php = True
@@ -335,7 +333,8 @@ class WODebugController(CementBaseController):
# PHP global debug stop
elif (self.app.pargs.php73 == 'off' and not self.app.pargs.site_name):
- if WOShellExec.cmd_exec(self, " sed -n \"/upstream php72 {/,/}/p\" "
+ if WOShellExec.cmd_exec(self, " sed -n \"/upstream "
+ "php72 {/,/}/p\" "
"/etc/nginx/conf.d/upstream.conf "
"| grep 9172"):
Log.info(self, "Disabling PHP 7.2 debug")
@@ -399,7 +398,8 @@ class WODebugController(CementBaseController):
config.write(configfile)
self.trigger_php = True
else:
- Log.info(self, "PHP7.3-FPM log_level = debug already disabled")
+ Log.info(self, "PHP7.3-FPM log_level "
+ "= debug already disabled")
@expose(hide=True)
def debug_mysql(self):
@@ -536,8 +536,8 @@ class WODebugController(CementBaseController):
self.msg = self.msg + ['/var/log/nginx/*.error.log']
# Stop Nginx rewrite debug globally
- elif (self.app.pargs.rewrite == 'off'
- and not self.app.pargs.site_name):
+ elif (self.app.pargs.rewrite == 'off' and
+ not self.app.pargs.site_name):
if WOShellExec.cmd_exec(self, "grep \"rewrite_log on;\" "
"/etc/nginx/nginx.conf"):
Log.info(self, "Disabling Nginx rewrite logs")
@@ -644,10 +644,10 @@ class WODebugController(CementBaseController):
(not self.app.pargs.fpm73) and (not self.app.pargs.mysql) and
(not self.app.pargs.wp) and (not self.app.pargs.rewrite) and
(not self.app.pargs.all) and (not self.app.pargs.site_name) and
- (not self.app.pargs.import_slow_log)
- and (not self.app.pargs.interval)):
+ (not self.app.pargs.import_slow_log) and
+ (not self.app.pargs.interval)):
if self.app.pargs.stop or self.app.pargs.start:
- print("--start/stop option is deprecated since ee v3.0.5")
+ print("--start/stop option is deprecated since wo v3.0.5")
self.app.args.print_help()
else:
self.app.args.print_help()
@@ -778,8 +778,8 @@ class WODebugController(CementBaseController):
if len(self.msg) > 0:
if not self.app.pargs.interactive:
disp_msg = ' '.join(self.msg)
- Log.info(self, "Use following command to check debug logs:\n"
- + Log.ENDC + "tail -f {0}".format(disp_msg))
+ Log.info(self, "Use following command to check debug logs:\n" +
+ Log.ENDC + "tail -f {0}".format(disp_msg))
else:
signal.signal(signal.SIGINT, self.signal_handler)
watch_list = []
@@ -797,18 +797,18 @@ class WODebugController(CementBaseController):
# Get Anemometer user name and password
Log.info(self, "Importing MySQL slow log to Anemometer")
host = os.popen("grep -e \"\'host\'\" {0}22222/htdocs/"
- .format(WOVariables.wo_webroot)
- + "db/anemometer/conf/config.inc.php "
+ .format(WOVariables.wo_webroot) +
+ "db/anemometer/conf/config.inc.php "
"| head -1 | cut -d\\\' -f4 | "
"tr -d '\n'").read()
user = os.popen("grep -e \"\'user\'\" {0}22222/htdocs/"
- .format(WOVariables.wo_webroot)
- + "db/anemometer/conf/config.inc.php "
+ .format(WOVariables.wo_webroot) +
+ "db/anemometer/conf/config.inc.php "
"| head -1 | cut -d\\\' -f4 | "
"tr -d '\n'").read()
password = os.popen("grep -e \"\'password\'\" {0}22222/"
- .format(WOVariables.wo_webroot)
- + "htdocs/db/anemometer/conf"
+ .format(WOVariables.wo_webroot) +
+ "htdocs/db/anemometer/conf"
"/config.inc.php "
"| head -1 | cut -d\\\' -f4 | "
"tr -d '\n'").read()
@@ -836,9 +836,9 @@ class WODebugController(CementBaseController):
" so not imported slow logs")
else:
Log.error(self, "Anemometer is not installed." +
- Log.ENDC + "\n Install Anemometer with:"
- + Log.BOLD + "\n `wo stack install --utils`"
- + Log.ENDC)
+ Log.ENDC + "\n Install Anemometer with:" +
+ Log.BOLD + "\n `wo stack install --utils`" +
+ Log.ENDC)
def load(app):
diff --git a/wo/cli/plugins/info.py b/wo/cli/plugins/info.py
index d20d5a6..9e071d7 100644
--- a/wo/cli/plugins/info.py
+++ b/wo/cli/plugins/info.py
@@ -41,7 +41,8 @@ class WOInfoController(CementBaseController):
@expose(hide=True)
def info_nginx(self):
"""Display Nginx information"""
- version = os.popen("nginx -v 2>&1 | awk -F '/' '{print $2}' | "
+ version = os.popen("/usr/sbin/nginx -v 2>&1 | "
+ "awk -F '/' '{print $2}' | "
"awk -F ' ' '{print $1}' | tr '\n' ' '").read()
allow = os.popen("grep ^allow /etc/nginx/common/acl.conf | "
"cut -d' ' -f2 | cut -d';' -f1 | tr '\n' ' '").read()
@@ -66,7 +67,8 @@ class WOInfoController(CementBaseController):
@expose(hide=True)
def info_php(self):
"""Display PHP information"""
- version = os.popen("php7.2 -v 2>/dev/null | head -n1 | cut -d' ' -f2 |"
+ version = os.popen("/usr/bin/php7.2 -v 2>/dev/null | "
+ "head -n1 | cut -d' ' -f2 |"
" cut -d'+' -f1 | tr -d '\n'").read
config = configparser.ConfigParser()
config.read('/etc/{0}/fpm/php.ini'.format("php/7.2"))
@@ -140,7 +142,8 @@ class WOInfoController(CementBaseController):
@expose(hide=True)
def info_php73(self):
"""Display PHP information"""
- version = os.popen("php7.3 -v 2>/dev/null | head -n1 | cut -d' ' -f2 |"
+ version = os.popen("/usr/bin/php7.3 -v 2>/dev/null | "
+ "head -n1 | cut -d' ' -f2 |"
" cut -d'+' -f1 | tr -d '\n'").read
config = configparser.ConfigParser()
config.read('/etc/php/7.3/fpm/php.ini')
@@ -214,23 +217,29 @@ class WOInfoController(CementBaseController):
@expose(hide=True)
def info_mysql(self):
"""Display MySQL information"""
- version = os.popen("mysql -V | awk '{print($5)}' | cut -d ',' "
+ version = os.popen("/usr/bin/mysql -V | awk '{print($5)}' | "
+ "cut -d ',' "
"-f1 | tr -d '\n'").read()
host = "localhost"
- port = os.popen("mysql -e \"show variables\" | grep ^port | awk "
+ port = os.popen("/usr/bin/mysql -e \"show variables\" | "
+ "grep ^port | awk "
"'{print($2)}' | tr -d '\n'").read()
- wait_timeout = os.popen("mysql -e \"show variables\" | grep "
+ wait_timeout = os.popen("/usr/bin/mysql -e \"show variables\" | grep "
"^wait_timeout | awk '{print($2)}' | "
"tr -d '\n'").read()
- interactive_timeout = os.popen("mysql -e \"show variables\" | grep "
+ interactive_timeout = os.popen("/usr/bin/mysql -e "
+ "\"show variables\" | grep "
"^interactive_timeout | awk "
"'{print($2)}' | tr -d '\n'").read()
- max_used_connections = os.popen("mysql -e \"show global status\" | "
+ max_used_connections = os.popen("/usr/bin/mysql -e "
+ "\"show global status\" | "
"grep Max_used_connections | awk "
"'{print($2)}' | tr -d '\n'").read()
- datadir = os.popen("mysql -e \"show variables\" | grep datadir | awk"
+ datadir = os.popen("/usr/bin/mysql -e \"show variables\" | "
+ "grep datadir | awk"
" '{print($2)}' | tr -d '\n'").read()
- socket = os.popen("mysql -e \"show variables\" | grep \"^socket\" | "
+ socket = os.popen("/usr/bin/mysql -e \"show variables\" | "
+ "grep \"^socket\" | "
"awk '{print($2)}' | tr -d '\n'").read()
data = dict(version=version, host=host, port=port,
wait_timeout=wait_timeout,
diff --git a/wo/cli/plugins/log.py b/wo/cli/plugins/log.py
index 5f7436f..5b08189 100644
--- a/wo/cli/plugins/log.py
+++ b/wo/cli/plugins/log.py
@@ -444,7 +444,8 @@ class WOLogMailController(CementBaseController):
(['--nginx'],
dict(help='Mail Nginx Error logs file', action='store_true')),
(['--php'],
- dict(help='Mail PHP 7.2 Error logs file', action='store_true')),
+ dict(help='Mail PHP 7.2 Error logs file',
+ action='store_true')),
(['--fpm'],
dict(help='Mail PHP 7.2-fpm slow logs file',
action='store_true')),
diff --git a/wo/cli/plugins/site.py b/wo/cli/plugins/site.py
index 86ecb10..6c27262 100644
--- a/wo/cli/plugins/site.py
+++ b/wo/cli/plugins/site.py
@@ -147,10 +147,6 @@ class WOSiteController(CementBaseController):
wo_db_user = siteinfo.db_user
wo_db_pass = siteinfo.db_password
wo_db_host = siteinfo.db_host
- if sitetype == "proxy":
- access_log = "/var/log/nginx/{0}.access.log".format(wo_domain)
- error_log = "/var/log/nginx/{0}.error.log".format(wo_domain)
- wo_site_webroot = ''
php_version = siteinfo.php_version
@@ -408,12 +404,14 @@ class WOSiteCreateController(CementBaseController):
"{0} already exists".format(wo_domain))
if stype == 'proxy':
- data['site_name'] = wo_domain
- data['www_domain'] = wo_www_domain
+ data = dict(site_name=wo_domain, www_domain=wo_www_domain,
+ static=True, basic=False, php73=False, wp=False,
+ wpfc=False, wpsc=False, multisite=False,
+ wpsubdir=False, webroot=wo_site_webroot)
data['proxy'] = True
data['host'] = host
data['port'] = port
- wo_site_webroot = WOVariables.wo_webroot + wo_domain
+ data['basic'] = True
if self.app.pargs.php73:
data = dict(site_name=wo_domain, www_domain=wo_www_domain,
@@ -587,10 +585,35 @@ class WOSiteCreateController(CementBaseController):
"and please try again")
# Setup WordPress if Wordpress site
- if data['wp']:
+ if (data['wp'] and (not self.app.pargs.vhostonly)):
try:
wo_wp_creds = setupwordpress(self, data)
# Add database information for site into database
+ updateSiteInfo(self, wo_domain,
+ db_name=data['wo_db_name'],
+ db_user=data['wo_db_user'],
+ db_password=data['wo_db_pass'],
+ db_host=data['wo_db_host'])
+ except SiteError as e:
+ # call cleanup actions on failure
+ Log.debug(self, str(e))
+ Log.info(self, Log.FAIL +
+ "There was a serious error encountered...")
+ Log.info(self, Log.FAIL + "Cleaning up afterwards...")
+ doCleanupAction(self, domain=wo_domain,
+ webroot=data['webroot'],
+ dbname=data['wo_db_name'],
+ dbuser=data['wo_db_user'],
+ dbhost=data['wo_mysql_grant_host'])
+ deleteSiteInfo(self, wo_domain)
+ Log.error(self, "Check the log for details: "
+ "`tail /var/log/wo/wordops.log` "
+ "and please try again")
+
+ if (data['wp'] and (self.app.pargs.vhostonly)):
+ try:
+ data = setupdatabase(self, data)
+ # Add database information for site into database
updateSiteInfo(self, wo_domain, db_name=data['wo_db_name'],
db_user=data['wo_db_user'],
db_password=data['wo_db_pass'],
@@ -605,7 +628,37 @@ class WOSiteCreateController(CementBaseController):
webroot=data['webroot'],
dbname=data['wo_db_name'],
dbuser=data['wo_db_user'],
- dbhost=data['wo_mysql_grant_host'])
+ dbhost=data['wo_db_host'])
+ deleteSiteInfo(self, wo_domain)
+ Log.error(self, "Check the log for details: "
+ "`tail /var/log/wo/wordops.log` "
+ "and please try again")
+ try:
+ wodbconfig = open("{0}/wo-config.php"
+ .format(wo_site_webroot),
+ encoding='utf-8', mode='w')
+ wodbconfig.write(""
+ .format(data['wo_db_name'],
+ data['wo_db_user'],
+ data['wo_db_pass'],
+ data['wo_db_host']))
+ wodbconfig.close()
+
+ except IOError as e:
+ Log.debug(self, str(e))
+ Log.debug(self, "Error occured while generating "
+ "wo-config.php")
+ Log.info(self, Log.FAIL +
+ "There was a serious error encountered...")
+ Log.info(self, Log.FAIL + "Cleaning up afterwards...")
+ doCleanupAction(self, domain=wo_domain,
+ webroot=data['webroot'],
+ dbname=data['wo_db_name'],
+ dbuser=data['wo_db_user'],
+ dbhost=data['wo_db_host'])
deleteSiteInfo(self, wo_domain)
Log.error(self, "Check the log for details: "
"`tail /var/log/wo/wordops.log` "
@@ -658,7 +711,7 @@ class WOSiteCreateController(CementBaseController):
for msg in wo_auth:
Log.info(self, Log.ENDC + msg, log=False)
- if data['wp']:
+ if data['wp'] and (not self.app.pargs.vhostonly):
Log.info(self, Log.ENDC + "WordPress admin user :"
" {0}".format(wo_wp_creds['wp_user']), log=False)
Log.info(self, Log.ENDC + "WordPress admin user password : {0}"
diff --git a/wo/cli/plugins/site_functions.py b/wo/cli/plugins/site_functions.py
index 818e23e..39cb072 100644
--- a/wo/cli/plugins/site_functions.py
+++ b/wo/cli/plugins/site_functions.py
@@ -49,8 +49,7 @@ def pre_run_checks(self):
def check_domain_exists(self, domain):
if getSiteInfo(self, domain):
return True
- else:
- return False
+ return False
def setupdomain(self, data):
@@ -60,7 +59,7 @@ def setupdomain(self, data):
# print (key, value)
wo_domain_name = data['site_name']
- wo_site_webroot = data['webroot'] if 'webroot' in data.keys() else ''
+ wo_site_webroot = data['webroot']
# Check if nginx configuration already exists
# if os.path.isfile('/etc/nginx/sites-available/{0}'
@@ -313,10 +312,12 @@ def setupwordpress(self, data):
"--dbname=\'{0}\' --dbprefix=\'{1}\' "
"--dbuser=\'{2}\' --dbhost=\'{3}\' "
.format(data['wo_db_name'], wo_wp_prefix,
- data['wo_db_user'], data['wo_db_host']
+ data['wo_db_user'],
+ data['wo_db_host']
) +
"--dbpass=\'{0}\' "
- "--extra-php< {1}/{0}.sql"
+ if not WOShellExec.cmd_exec(self, "mysqldump --single-transaction "
+ "{0} | pigz -9 -p\"$(nproc)\" "
+ "> {1}/{0}.gz"
.format(data['wo_db_name'],
backup_path)):
Log.info(self,
@@ -1147,7 +1150,7 @@ def detSitePar(opts):
def generate_random():
wo_random10 = (''.join(random.sample(string.ascii_uppercase +
string.ascii_lowercase +
- string.digits, 16)))
+ string.digits, 24)))
return wo_random10
@@ -1200,9 +1203,8 @@ def deleteWebRoot(self, webroot):
Log.debug(self, "Removing {0}".format(webroot))
WOFileUtils.rm(self, webroot)
return True
- else:
- Log.debug(self, "{0} does not exist".format(webroot))
- return False
+ Log.debug(self, "{0} does not exist".format(webroot))
+ return False
def removeNginxConf(self, domain):
@@ -1219,6 +1221,19 @@ def removeNginxConf(self, domain):
.format(domain))
+def removeAcmeConf(self, domain):
+ if os.path.isdir('/etc/letsencrypt/renewal/{0}_ecc'
+ .format(domain)):
+ Log.debug(self, "Removing Acme configuration")
+ WOFileUtils.rm(self, '/etc/letsencrypt/renewal/{0}_ecc'
+ .format(domain))
+ WOFileUtils.rm(self, '/etc/letsencrypt/live/{0}'
+ .format(domain))
+ WOGit.add(self, ["/etc/letsencrypt"],
+ msg="Deleted {0} "
+ .format(domain))
+
+
def doCleanupAction(self, domain='', webroot='', dbname='', dbuser='',
dbhost=''):
"""
@@ -1230,6 +1245,10 @@ def doCleanupAction(self, domain='', webroot='', dbname='', dbuser='',
if os.path.isfile('/etc/nginx/sites-available/{0}'
.format(domain)):
removeNginxConf(self, domain)
+ if os.path.isdir('/etc/letsencrypt/renewal/{0}_ecc'
+ .format(domain)):
+ removeAcmeConf(self, domain)
+
if webroot:
deleteWebRoot(self, webroot)
@@ -1287,6 +1306,7 @@ def setupLetsEncrypt(self, wo_domain_name):
"--key-file {0}/{1}/key.pem "
"--fullchain-file "
"{0}/{1}/fullchain.pem "
+ "--ca-file {0}/{1}/ca.pem "
"--reloadcmd "
"\"service nginx restart\" "
.format(WOVariables.wo_ssl_live,
@@ -1300,9 +1320,10 @@ def setupLetsEncrypt(self, wo_domain_name):
encoding='utf-8', mode='w')
sslconf.write("listen 443 ssl http2;\n"
"listen [::]:443 ssl http2;\n"
- "ssl on;\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)
@@ -1368,6 +1389,7 @@ def setupLetsEncryptSubdomain(self, wo_domain_name):
"--key-file {0}/{1}/key.pem "
"--fullchain-file "
"{0}/{1}/fullchain.pem "
+ "--ca-file {0}/{1}/ca.pem "
"--reloadcmd "
"\"service nginx restart\" "
.format(WOVariables.wo_ssl_live,
@@ -1382,9 +1404,10 @@ def setupLetsEncryptSubdomain(self, wo_domain_name):
encoding='utf-8', mode='w')
sslconf.write("listen 443 ssl http2;\n"
"listen [::]:443 ssl http2;\n"
- "ssl on;\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)
@@ -1548,6 +1571,7 @@ def archivedCertificateHandle(self, domain):
"--key-file {0}/{1}/key.pem "
"--fullchain-file "
"{0}/{1}/fullchain.pem "
+ "--ca-file {0}/{1}/ca.pem "
"--reloadcmd "
"\"service nginx restart\" "
.format(WOVariables.wo_ssl_live,
@@ -1567,10 +1591,11 @@ def archivedCertificateHandle(self, domain):
encoding='utf-8', mode='w')
sslconf.write("listen 443 ssl http2;\n"
"listen [::]:443 ssl http2;\n"
- "ssl on;\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, domain))
sslconf.close()
@@ -1614,6 +1639,8 @@ def archivedCertificateHandle(self, domain):
"--key-file {0}/{1}/key.pem "
"--fullchain-file "
"{0}/{1}/fullchain.pem "
+ "ssl_trusted_certificate "
+ "{0}/{1}/ca.pem;\n"
"--reloadcmd "
"\"service nginx restart\" "
.format(WOVariables.wo_ssl_live, domain))
@@ -1634,4 +1661,4 @@ def archivedCertificateHandle(self, domain):
'/var/www/{0}/conf/nginx/ssl.conf.bak'
.format(domain))
- return ssl
+ return ssl
diff --git a/wo/cli/plugins/stack.py b/wo/cli/plugins/stack.py
index 00ea021..14cb867 100644
--- a/wo/cli/plugins/stack.py
+++ b/wo/cli/plugins/stack.py
@@ -16,11 +16,9 @@ from wo.core.git import WOGit
from wo.core.checkfqdn import check_fqdn
from pynginxconfig import NginxConfig
from wo.core.services import WOService
-from wo.core.variables import WOVariables
import random
import string
import configparser
-import time
import shutil
import os
import pwd
@@ -72,6 +70,8 @@ class WOStackController(CementBaseController):
dict(help='Install WordOps dashboard', action='store_true')),
(['--adminer'],
dict(help='Install Adminer stack', action='store_true')),
+ (['--fail2ban'],
+ dict(help='Install Fail2ban stack', action='store_true')),
(['--utils'],
dict(help='Install Utils stack', action='store_true')),
(['--redis'],
@@ -91,50 +91,85 @@ class WOStackController(CementBaseController):
"""Pre settings to do before installation packages"""
if set(WOVariables.wo_mysql).issubset(set(apt_packages)):
- Log.info(self, "Adding repository for MySQL, please wait...")
- mysql_pref = ("Package: *\nPin: origin "
- "sfo1.mirrors.digitalocean.com"
- "\nPin-Priority: 1000\n")
- with open('/etc/apt/preferences.d/'
- 'MariaDB.pref', 'w') as mysql_pref_file:
- mysql_pref_file.write(mysql_pref)
- WORepo.add(self, repo_url=WOVariables.wo_mysql_repo)
- Log.debug(self, 'Adding key for {0}'
- .format(WOVariables.wo_mysql_repo))
- WORepo.add_key(self, '0xcbcb082a1bb943db',
- keyserver="keyserver.ubuntu.com")
- WORepo.add_key(self, '0xF1656F24C74CD1D8',
- keyserver="keyserver.ubuntu.com")
- chars = ''.join(random.sample(string.ascii_letters, 16))
- Log.debug(self, "Pre-seeding MySQL")
- Log.debug(self, "echo \"mariadb-server-10.3 "
- "mysql-server/root_password "
- "password \" | "
- "debconf-set-selections")
- try:
- WOShellExec.cmd_exec(self, "echo \"mariadb-server-10.3 "
- "mysql-server/root_password "
- "password {chars}\" | "
- "debconf-set-selections"
- .format(chars=chars),
- log=False)
- except CommandExecutionError as e:
- Log.error("Failed to initialize MySQL package")
+ # add mariadb repository excepted on raspbian and ubuntu 19.04
+ if ((not WOVariables.wo_platform_codename == 'disco') and
+ (not WOVariables.wo_platform_distro == 'raspbian')):
+ Log.info(self, "Adding repository for MySQL, please wait...")
+ mysql_pref = ("Package: *\nPin: origin "
+ "sfo1.mirrors.digitalocean.com"
+ "\nPin-Priority: 1000\n")
+ with open('/etc/apt/preferences.d/'
+ 'MariaDB.pref', 'w') as mysql_pref_file:
+ mysql_pref_file.write(mysql_pref)
+ WORepo.add(self, repo_url=WOVariables.wo_mysql_repo)
+ Log.debug(self, 'Adding key for {0}'
+ .format(WOVariables.wo_mysql_repo))
+ WORepo.add_key(self, '0xcbcb082a1bb943db',
+ keyserver="keyserver.ubuntu.com")
+ WORepo.add_key(self, '0xF1656F24C74CD1D8',
+ keyserver="keyserver.ubuntu.com")
+ # generate random 24 characters root password
+ chars = ''.join(random.sample(string.ascii_letters, 24))
+ # configure MySQL non-interactive install
+ if (not WOVariables.wo_platform_distro == 'raspbian'):
+ Log.debug(self, "Pre-seeding MySQL")
+ Log.debug(self, "echo \"mariadb-server-10.3 "
+ "mysql-server/root_password "
+ "password \" | "
+ "debconf-set-selections")
+ try:
+ WOShellExec.cmd_exec(self, "echo \"mariadb-server-10.3 "
+ "mysql-server/root_password "
+ "password {chars}\" | "
+ "debconf-set-selections"
+ .format(chars=chars),
+ log=False)
+ except CommandExecutionError as e:
+ Log.error("Failed to initialize MySQL package")
- Log.debug(self, "echo \"mariadb-server-10.3 "
- "mysql-server/root_password_again "
- "password \" | "
- "debconf-set-selections")
- try:
- WOShellExec.cmd_exec(self, "echo \"mariadb-server-10.3 "
- "mysql-server/root_password_again "
- "password {chars}\" | "
- "debconf-set-selections"
- .format(chars=chars),
- log=False)
- except CommandExecutionError as e:
- Log.error("Failed to initialize MySQL package")
+ Log.debug(self, "echo \"mariadb-server-10.3 "
+ "mysql-server/root_password_again "
+ "password \" | "
+ "debconf-set-selections")
+ try:
+ WOShellExec.cmd_exec(self, "echo \"mariadb-server-10.3 "
+ "mysql-server/root_password_again "
+ "password {chars}\" | "
+ "debconf-set-selections"
+ .format(chars=chars),
+ log=False)
+ except CommandExecutionError as e:
+ Log.error("Failed to initialize MySQL package")
+ else:
+ Log.debug(self, "Pre-seeding MySQL")
+ Log.debug(self, "echo \"mariadb-server-10.1 "
+ "mysql-server/root_password "
+ "password \" | "
+ "debconf-set-selections")
+ try:
+ WOShellExec.cmd_exec(self, "echo \"mariadb-server-10.1 "
+ "mysql-server/root_password "
+ "password {chars}\" | "
+ "debconf-set-selections"
+ .format(chars=chars),
+ log=False)
+ except CommandExecutionError as e:
+ Log.error("Failed to initialize MySQL package")
+ Log.debug(self, "echo \"mariadb-server-10.1 "
+ "mysql-server/root_password_again "
+ "password \" | "
+ "debconf-set-selections")
+ try:
+ WOShellExec.cmd_exec(self, "echo \"mariadb-server-10.1 "
+ "mysql-server/root_password_again "
+ "password {chars}\" | "
+ "debconf-set-selections"
+ .format(chars=chars),
+ log=False)
+ except CommandExecutionError as e:
+ Log.error("Failed to initialize MySQL package")
+ # generate my.cnf root credentials
mysql_config = """
[client]
user = root
@@ -151,12 +186,14 @@ class WOStackController(CementBaseController):
Log.debug(self, 'Setting my.cnf permission')
WOFileUtils.chmod(self, "/etc/mysql/conf.d/my.cnf", 0o600)
+ # add nginx repository
if set(WOVariables.wo_nginx).issubset(set(apt_packages)):
Log.info(self, "Adding repository for NGINX, please wait...")
WORepo.add(self, repo_url=WOVariables.wo_nginx_repo)
Log.debug(self, 'Adding repository for Nginx')
WORepo.add_key(self, WOVariables.wo_nginx_key)
+ # add php repository
if (set(WOVariables.wo_php73).issubset(set(apt_packages)) or
set(WOVariables.wo_php).issubset(set(apt_packages))):
if (WOVariables.wo_platform_distro == 'ubuntu'):
@@ -170,7 +207,7 @@ class WOStackController(CementBaseController):
WORepo.add(self, repo_url=WOVariables.wo_php_repo)
Log.debug(self, 'Adding deb.sury GPG key')
WORepo.add_key(self, WOVariables.wo_php_key)
-
+ # add redis repository
if set(WOVariables.wo_redis).issubset(set(apt_packages)):
Log.info(self, "Adding repository for Redis, please wait...")
if WOVariables.wo_platform_distro == 'ubuntu':
@@ -182,7 +219,7 @@ class WOStackController(CementBaseController):
@expose(hide=True)
def post_pref(self, apt_packages, packages):
"""Post activity after installation of packages"""
- if len(apt_packages):
+ if (apt_packages):
if set(WOVariables.wo_nginx).issubset(set(apt_packages)):
if set(["nginx"]).issubset(set(apt_packages)):
@@ -377,10 +414,12 @@ class WOStackController(CementBaseController):
self.app.render((data), 'locations.mustache',
out=wo_nginx)
wo_nginx.close()
+
if not os.path.isfile("/etc/nginx/common/release"):
with open("/etc/nginx/common/release",
"a") as release_file:
- release_file.write("v3.9.5")
+ release_file.write("v{0}"
+ .format(WOVariables.wo_version))
release_file.close()
# Nginx-Plus does not have nginx
@@ -640,7 +679,7 @@ class WOStackController(CementBaseController):
"/var/run/php/php73-fpm.sock;\n}\n"
"upstream debug73 {\nserver "
"127.0.0.1:9173;\n}\n")
-
+ # create nginx configuration for redis
if set(WOVariables.wo_redis).issubset(set(apt_packages)):
if (os.path.isfile("/etc/nginx/nginx.conf") and
not os.path.isfile("/etc/nginx/common/"
@@ -748,6 +787,12 @@ class WOStackController(CementBaseController):
"/etc/php/7.2/fpm/pool.d/www.conf")
config.write(configfile)
+ with open("/etc/php/7.2/fpm/pool.d/www.conf",
+ encoding='utf-8', mode='a') as myfile:
+ myfile.write("\nphp_admin_value[open_basedir] "
+ "= \"/var/www/:/usr/share/php/:"
+ "/tmp/:/var/run/nginx-cache/\"\n")
+
# Generate /etc/php/7.2/fpm/pool.d/www-two.conf
WOFileUtils.copyfile(self, "/etc/php/7.2/fpm/pool.d/www.conf",
"/etc/php/7.2/fpm/pool.d/www-two.conf")
@@ -903,6 +948,12 @@ class WOStackController(CementBaseController):
"/etc/php/7.3/fpm/pool.d/www.conf")
config.write(configfile)
+ with open("/etc/php/7.3/fpm/pool.d/www.conf",
+ encoding='utf-8', mode='a') as myfile:
+ myfile.write("\nphp_admin_value[open_basedir] "
+ "= \"/var/www/:/usr/share/php/:"
+ "/tmp/:/var/run/nginx-cache/\"\n")
+
# Generate /etc/php/7.3/fpm/pool.d/www-two.conf
WOFileUtils.copyfile(self, "/etc/php/7.3/fpm/pool.d/www.conf",
"/etc/php/7.3/fpm/pool.d/www-two.conf")
@@ -990,6 +1041,7 @@ class WOStackController(CementBaseController):
WOGit.add(self, ["/etc/php"], msg="Adding PHP into Git")
WOService.restart_service(self, 'php7.3-fpm')
+ # create mysql config if it doesn't exist
if set(WOVariables.wo_mysql).issubset(set(apt_packages)):
if not os.path.isfile("/etc/mysql/my.cnf"):
config = ("[mysqld]\nwait_timeout = 30\n"
@@ -1015,17 +1067,36 @@ class WOStackController(CementBaseController):
WOGit.add(self, ["/etc/mysql"], msg="Adding MySQL into Git")
WOService.reload_service(self, 'mysql')
- if len(packages):
+ # create fail2ban configuration files
+ if set(WOVariables.wo_fail2ban).issubset(set(apt_packages)):
+ if not os.path.isfile("/etc/fail2ban/jail.d/custom.conf"):
+ data = dict()
+ Log.debug(self, "Setting up fail2ban jails configuration")
+ wo_fail2ban = open('/etc/fail2ban/jail.d/custom.conf',
+ encoding='utf-8', mode='w')
+ self.app.render((data), 'fail2ban.mustache',
+ out=wo_fail2ban)
+ wo_fail2ban.close()
+
+ Log.debug(self, "Setting up fail2ban wp filter")
+ wo_fail2ban = open('/etc/fail2ban/filter.d/wo-wordpress.conf',
+ encoding='utf-8', mode='w')
+ self.app.render((data), 'fail2ban-wp.mustache',
+ out=wo_fail2ban)
+ wo_fail2ban.close()
+
+ if (packages):
if any('/usr/local/bin/wp' == x[1] for x in packages):
Log.debug(self, "Setting Privileges"
" to /usr/local/bin/wp file ")
WOFileUtils.chmod(self, "/usr/local/bin/wp", 0o775)
- if any('/tmp/pma.tar.gz' == x[1]
+ if any('/var/lib/wo/tmp/pma.tar.gz' == x[1]
for x in packages):
- WOExtract.extract(self, '/tmp/pma.tar.gz', '/tmp/')
- Log.debug(self, 'Extracting file /tmp/pma.tar.gz to '
- 'location /tmp/')
+ WOExtract.extract(
+ self, '/var/lib/wo/tmp/pma.tar.gz', '/var/lib/wo/tmp/')
+ Log.debug(self, 'Extracting file /var/lib/wo/tmp/pma.tar.gz to '
+ 'location /var/lib/wo/tmp/')
if not os.path.exists('{0}22222/htdocs/db'
.format(WOVariables.wo_webroot)):
Log.debug(self, "Creating new directory "
@@ -1035,7 +1106,7 @@ class WOStackController(CementBaseController):
.format(WOVariables.wo_webroot))
if not os.path.exists('{0}22222/htdocs/db/pma/'
.format(WOVariables.wo_webroot)):
- shutil.move('/tmp/phpmyadmin-STABLE/',
+ shutil.move('/var/lib/wo/tmp/phpmyadmin-STABLE/',
'{0}22222/htdocs/db/pma/'
.format(WOVariables.wo_webroot))
shutil.copyfile('{0}22222/htdocs/db/pma'
@@ -1079,12 +1150,12 @@ class WOStackController(CementBaseController):
recursive=True)
# composer install and phpmyadmin update
- if any('/tmp/composer-install' == x[1]
+ if any('/var/lib/wo/tmp/composer-install' == x[1]
for x in packages):
Log.info(self, "Installing composer, please wait...")
- WOShellExec.cmd_exec(self, "php -q /tmp/composer-install "
- "--install-dir=/tmp/")
- shutil.copyfile('/tmp/composer.phar',
+ 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.info(self, "Updating phpMyAdmin, please wait...")
@@ -1092,12 +1163,12 @@ class WOStackController(CementBaseController):
"update -n --no-dev -d "
"/var/www/22222/htdocs/db/pma/")
# netdata install
- if any('/tmp/kickstart.sh' == x[1]
+ if any('/var/lib/wo/tmp/kickstart.sh' == x[1]
for x in packages):
if ((not os.path.exists('/opt/netdata')) and
(not os.path.exists('/etc/netdata'))):
Log.info(self, "Installing Netdata, please wait...")
- WOShellExec.cmd_exec(self, "bash /tmp/kickstart.sh "
+ WOShellExec.cmd_exec(self, "bash /var/lib/wo/tmp/kickstart.sh "
"--dont-wait")
# disable mail notifications
WOFileUtils.searchreplace(self, "/opt/netdata/usr/"
@@ -1125,21 +1196,21 @@ class WOStackController(CementBaseController):
WOService.restart_service(self, 'netdata')
# WordOps Dashboard
- if any('/tmp/wo-dashboard.tar.gz' == x[1]
+ if any('/var/lib/wo/tmp/wo-dashboard.tar.gz' == x[1]
for x in packages):
if not os.path.isfile('{0}22222/htdocs/index.php'
.format(WOVariables.wo_webroot)):
Log.debug(self, "Extracting wo-dashboard.tar.gz "
"to location {0}22222/htdocs/"
.format(WOVariables.wo_webroot))
- WOExtract.extract(self, '/tmp/wo-dashboard.tar.gz',
+ WOExtract.extract(self, '/var/lib/wo/tmp/wo-dashboard.tar.gz',
'{0}22222/htdocs'
.format(WOVariables.wo_webroot))
- if WOVariables.wo_wan_interface != 'eth0':
+ if WOVariables.wo_wan != 'eth0':
WOFileUtils.searchreplace(self, "{0}22222/htdocs/index.php"
.format(WOVariables.wo_webroot),
"eth0",
- "{0}".format(WOVariables.wo_wan_interface))
+ "{0}".format(WOVariables.wo_wan))
Log.debug(self, "Setting Privileges to "
"{0}22222/htdocs"
@@ -1151,16 +1222,16 @@ class WOStackController(CementBaseController):
recursive=True)
# Extplorer FileManager
- if any('/tmp/extplorer.tar.gz' == x[1]
+ if any('/var/lib/wo/tmp/extplorer.tar.gz' == x[1]
for x in packages):
if not os.path.exists('{0}22222/htdocs/files'
.format(WOVariables.wo_webroot)):
Log.debug(self, "Extracting explorer.tar.gz "
"to location {0}22222/htdocs/files"
.format(WOVariables.wo_webroot))
- WOExtract.extract(self, '/tmp/extplorer.tar.gz',
- '/tmp/')
- shutil.move('/tmp/extplorer-2.1.11/',
+ WOExtract.extract(self, '/var/lib/wo/tmp/extplorer.tar.gz',
+ '/var/lib/wo/tmp/')
+ shutil.move('/var/lib/wo/tmp/extplorer-2.1.11',
'{0}22222/htdocs/files'
.format(WOVariables.wo_webroot))
Log.debug(self, "Setting Privileges to "
@@ -1173,16 +1244,16 @@ class WOStackController(CementBaseController):
recursive=True)
# phpmemcachedadmin
- if any('/tmp/memcached.tar.gz' == x[1]
+ if any('/var/lib/wo/tmp/memcached.tar.gz' == x[1]
for x in packages):
Log.debug(self, "Extracting memcached.tar.gz to location"
- " {0}22222/htdocs/cache/memcached "
+ " {0}22222/htdocs/cache/memcache "
.format(WOVariables.wo_webroot))
- WOExtract.extract(self, '/tmp/memcached.tar.gz',
- '{0}22222/htdocs/cache/memcached'
+ WOExtract.extract(self, '/var/lib/wo/tmp/memcached.tar.gz',
+ '{0}22222/htdocs/cache/memcache'
.format(WOVariables.wo_webroot))
Log.debug(self, "Setting Privileges to "
- "{0}22222/htdocs/cache/memcached file"
+ "{0}22222/htdocs/cache/memcache file"
.format(WOVariables.wo_webroot))
WOFileUtils.chown(self, '{0}22222'
.format(WOVariables.wo_webroot),
@@ -1190,11 +1261,12 @@ class WOStackController(CementBaseController):
WOVariables.wo_php_user,
recursive=True)
# webgrind
- if any('/tmp/webgrind.tar.gz' == x[1]
+ if any('/var/lib/wo/tmp/webgrind.tar.gz' == x[1]
for x in packages):
Log.debug(self, "Extracting file webgrind.tar.gz to "
- "location /tmp/ ")
- WOExtract.extract(self, '/tmp/webgrind.tar.gz', '/tmp/')
+ "location /var/lib/wo/tmp/ ")
+ WOExtract.extract(
+ self, '/var/lib/wo/tmp/webgrind.tar.gz', '/var/lib/wo/tmp/')
if not os.path.exists('{0}22222/htdocs/php'
.format(WOVariables.wo_webroot)):
Log.debug(self, "Creating directroy "
@@ -1204,7 +1276,7 @@ class WOStackController(CementBaseController):
.format(WOVariables.wo_webroot))
if not os.path.exists('{0}22222/htdocs/php/webgrind'
.format(WOVariables.wo_webroot)):
- shutil.move('/tmp/webgrind-master/',
+ shutil.move('/var/lib/wo/tmp/webgrind-master/',
'{0}22222/htdocs/php/webgrind'
.format(WOVariables.wo_webroot))
@@ -1232,11 +1304,12 @@ class WOStackController(CementBaseController):
WOVariables.wo_php_user,
recursive=True)
# anemometer
- if any('/tmp/anemometer.tar.gz' == x[1]
+ if any('/var/lib/wo/tmp/anemometer.tar.gz' == x[1]
for x in packages):
Log.debug(self, "Extracting file anemometer.tar.gz to "
- "location /tmp/ ")
- WOExtract.extract(self, '/tmp/anemometer.tar.gz', '/tmp/')
+ "location /var/lib/wo/tmp/ ")
+ WOExtract.extract(
+ self, '/var/lib/wo/tmp/anemometer.tar.gz', '/var/lib/wo/tmp/')
if not os.path.exists('{0}22222/htdocs/db/'
.format(WOVariables.wo_webroot)):
Log.debug(self, "Creating directory")
@@ -1244,7 +1317,7 @@ class WOStackController(CementBaseController):
.format(WOVariables.wo_webroot))
if not os.path.exists('{0}22222/htdocs/db/anemometer'
.format(WOVariables.wo_webroot)):
- shutil.move('/tmp/Anemometer-master',
+ shutil.move('/var/lib/wo/tmp/Anemometer-master',
'{0}22222/htdocs/db/anemometer'
.format(WOVariables.wo_webroot))
chars = ''.join(random.sample(string.ascii_letters, 8))
@@ -1288,8 +1361,8 @@ class WOStackController(CementBaseController):
if any('/usr/bin/pt-query-advisor' == x[1]
for x in packages):
WOFileUtils.chmod(self, "/usr/bin/pt-query-advisor", 0o775)
- # ph
- if any('/tmp/pra.tar.gz' == x[1]
+ # phpredisadmin
+ if any('/var/lib/wo/tmp/pra.tar.gz' == x[1]
for x in packages):
if not os.path.exists('{0}22222/htdocs/cache/redis'
.format(WOVariables.wo_webroot)):
@@ -1442,33 +1515,49 @@ class WOStackController(CementBaseController):
Log.debug(self, "WP-CLI is already installed")
Log.info(self, "WP-CLI is already installed")
+ # fail2ban
+ if self.app.pargs.fail2ban:
+ Log.debug(self, "Setting apt_packages variable for Fail2ban")
+ if not WOAptGet.is_installed(self, 'fail2ban'):
+ apt_packages = apt_packages + WOVariables.wo_fail2ban
+ else:
+ Log.debug(self, "Fail2ban already installed")
+ Log.info(self, "Fail2ban already installed")
+
# PHPMYADMIN
if self.app.pargs.phpmyadmin:
Log.debug(self, "Setting packages variable for phpMyAdmin ")
if (not self.app.pargs.composer):
packages = packages + [["https://github.com/phpmyadmin/"
"phpmyadmin/archive/STABLE.tar.gz",
- "/tmp/pma.tar.gz", "phpMyAdmin"],
- ["https://getcomposer.org/installer",
- "/tmp/composer-install", "Composer"]]
+ "/var/lib/wo/tmp/pma.tar.gz",
+ "phpMyAdmin"],
+ ["https://getcomposer.org/"
+ "installer",
+ "/var/lib/wo/tmp/composer-install",
+ "Composer"]]
else:
packages = packages + [["https://github.com/phpmyadmin/"
"phpmyadmin/archive/STABLE.tar.gz",
- "/tmp/pma.tar.gz", "phpMyAdmin"]]
+ "/var/lib/wo/tmp/pma.tar.gz",
+ "phpMyAdmin"]]
# Composer
if self.app.pargs.composer:
Log.debug(self, "Setting packages variable for Composer ")
packages = packages + [["https://getcomposer.org/installer",
- "/tmp/composer-install", "Composer"]]
+ "/var/lib/wo/tmp/composer-install",
+ "Composer"]]
# PHPREDISADMIN
if self.app.pargs.phpredisadmin:
Log.debug(self, "Setting packages variable for phpRedisAdmin")
packages = packages + [["https://github.com/ErikDubbelboer/"
"phpRedisAdmin/archive/master.tar.gz",
- "/tmp/pra.tar.gz", "phpRedisAdmin"],
+ "/var/lib/wo/tmp/pra.tar.gz",
+ "phpRedisAdmin"],
["https://github.com/nrk/predis/"
"archive/v1.1.1.tar.gz",
- "/tmp/predis.tar.gz", "Predis"]]
+ "/var/lib/wo/tmp/predis.tar.gz",
+ "Predis"]]
# ADMINER
if self.app.pargs.adminer:
Log.debug(self, "Setting packages variable for Adminer ")
@@ -1493,7 +1582,7 @@ class WOStackController(CementBaseController):
if not os.path.exists('/opt/netdata'):
packages = packages + [['https://my-netdata.io/'
'kickstart-static64.sh',
- '/tmp/kickstart.sh',
+ '/var/lib/wo/tmp/kickstart.sh',
'Netdata']]
# WordOps Dashboard
@@ -1503,11 +1592,11 @@ class WOStackController(CementBaseController):
[["https://github.com/WordOps/"
"wordops-dashboard/releases/"
"download/v1.0/wo-dashboard.tar.gz",
- "/tmp/wo-dashboard.tar.gz",
+ "/var/lib/wo/tmp/wo-dashboard.tar.gz",
"WordOps Dashboard"],
["https://github.com/soerennb/"
"extplorer/archive/v2.1.11.tar.gz",
- "/tmp/extplorer.tar.gz",
+ "/var/lib/wo/tmp/extplorer.tar.gz",
"eXtplorer"]]
# UTILS
@@ -1516,7 +1605,7 @@ class WOStackController(CementBaseController):
packages = packages + [["https://github.com/elijaa/"
"phpmemcachedadmin/archive/"
"1.3.0.tar.gz",
- '/tmp/memcached.tar.gz',
+ '/var/lib/wo/tmp/memcached.tar.gz',
'phpMemcachedAdmin'],
["https://raw.githubusercontent.com"
"/rtCamp/eeadmin/master/cache/nginx/"
@@ -1546,7 +1635,8 @@ class WOStackController(CementBaseController):
"OCP.php"],
["https://github.com/jokkedk/webgrind/"
"archive/master.tar.gz",
- '/tmp/webgrind.tar.gz', 'Webgrind'],
+ '/var/lib/wo/tmp/webgrind.tar.gz',
+ 'Webgrind'],
["http://bazaar.launchpad.net/~"
"percona-toolkit-dev/percona-toolkit/"
"2.1/download/head:/ptquerydigest-"
@@ -1556,21 +1646,22 @@ class WOStackController(CementBaseController):
"pt-query-advisor"],
["https://github.com/box/Anemometer/"
"archive/master.tar.gz",
- '/tmp/anemometer.tar.gz', 'Anemometer']
+ '/var/lib/wo/tmp/anemometer.tar.gz',
+ 'Anemometer']
]
except Exception as e:
pass
- if len(apt_packages) or len(packages):
+ if (apt_packages) or (packages):
Log.debug(self, "Calling pre_pref")
self.pre_pref(apt_packages)
- if len(apt_packages):
+ if (apt_packages):
WOSwap.add(self)
Log.info(self, "Updating apt-cache, please wait...")
WOAptGet.update(self)
Log.info(self, "Installing packages, please wait...")
WOAptGet.install(self, apt_packages)
- if len(packages):
+ if (packages):
Log.debug(self, "Downloading following: {0}".format(packages))
WODownload.download(self, packages)
Log.debug(self, "Calling post_pref")
@@ -1617,7 +1708,7 @@ class WOStackController(CementBaseController):
"/etc/redis/redis.conf")
WOService.restart_service(self, 'redis-server')
if disp_msg:
- if len(self.msg):
+ if (self.msg):
for msg in self.msg:
Log.info(self, Log.ENDC + msg)
Log.info(self, "Successfully installed packages")
@@ -1729,13 +1820,13 @@ class WOStackController(CementBaseController):
.format(WOVariables.wo_webroot),
'{0}22222/htdocs/cache/nginx/'
'clean.php'.format(WOVariables.wo_webroot),
- '{0}22222/htdocs/cache/memcached'
+ '{0}22222/htdocs/cache/memcache'
.format(WOVariables.wo_webroot),
'/usr/bin/pt-query-advisor',
'{0}22222/htdocs/db/anemometer'
.format(WOVariables.wo_webroot)]
- if len(packages) or len(apt_packages):
+ if (packages) or (apt_packages):
wo_prompt = input('Are you sure you to want to'
' remove from server.'
'\nPackage configuration will remain'
@@ -1749,11 +1840,11 @@ class WOStackController(CementBaseController):
if (set(["nginx-custom"]).issubset(set(apt_packages))):
WOService.stop_service(self, 'nginx')
- if len(packages):
+ if (packages):
WOFileUtils.remove(self, packages)
WOAptGet.auto_remove(self)
- if len(apt_packages):
+ if (apt_packages):
Log.debug(self, "Removing apt_packages")
Log.info(self, "Removing packages, please wait...")
WOAptGet.remove(self, apt_packages)
@@ -1861,14 +1952,14 @@ class WOStackController(CementBaseController):
.format(WOVariables.wo_webroot),
'{0}22222/htdocs/cache/nginx/'
'clean.php'.format(WOVariables.wo_webroot),
- '{0}22222/htdocs/cache/memcached'
+ '{0}22222/htdocs/cache/memcache'
.format(WOVariables.wo_webroot),
'/usr/bin/pt-query-advisor',
'{0}22222/htdocs/db/anemometer'
.format(WOVariables.wo_webroot)
]
- if len(packages) or len(apt_packages):
+ if (packages) or (apt_packages):
wo_prompt = input('Are you sure you to want to purge '
'from server '
'along with their configuration'
@@ -1881,12 +1972,12 @@ class WOStackController(CementBaseController):
if (set(["nginx-custom"]).issubset(set(apt_packages))):
WOService.stop_service(self, 'nginx')
- if len(apt_packages):
+ if (apt_packages):
Log.info(self, "Purging packages, please wait...")
WOAptGet.remove(self, apt_packages, purge=True)
WOAptGet.auto_remove(self)
- if len(packages):
+ if (packages):
WOFileUtils.remove(self, packages)
WOAptGet.auto_remove(self)
diff --git a/wo/cli/plugins/stack_migrate.py b/wo/cli/plugins/stack_migrate.py
index 058d076..38bb394 100644
--- a/wo/cli/plugins/stack_migrate.py
+++ b/wo/cli/plugins/stack_migrate.py
@@ -100,8 +100,8 @@ class WOStackMigrateController(CementBaseController):
Log.error(
self, "Remote MySQL server in use, skipping local install")
- if WOShellExec.cmd_exec(self, "mysqladmin ping") and (not
- WOAptGet.is_installed(self, 'mariadb-server')):
+ if (WOShellExec.cmd_exec(self, "mysqladmin ping") and
+ (not WOAptGet.is_installed(self, 'mariadb-server'))):
Log.info(self, "If your database size is big, "
"migration may take some time.")
diff --git a/wo/cli/plugins/stack_services.py b/wo/cli/plugins/stack_services.py
index 8b81b86..872e518 100644
--- a/wo/cli/plugins/stack_services.py
+++ b/wo/cli/plugins/stack_services.py
@@ -26,7 +26,9 @@ class WOStackStatusController(CementBaseController):
self.app.pargs.php73 or
self.app.pargs.mysql or
self.app.pargs.memcached or
- self.app.pargs.redis):
+ self.app.pargs.redis or
+ self.app.pargs.fail2ban or
+ self.app.pargs.netdata):
self.app.pargs.nginx = True
self.app.pargs.php = True
self.app.pargs.mysql = True
@@ -78,6 +80,12 @@ class WOStackStatusController(CementBaseController):
else:
Log.info(self, "Redis server is not installed")
+ if self.app.pargs.fail2ban:
+ if WOAptGet.is_installed(self, 'fail2ban'):
+ services = services + ['fail2ban-client']
+ else:
+ Log.info(self, "fail2ban is not installed")
+
for service in services:
Log.debug(self, "Starting service: {0}".format(service))
WOService.start_service(self, service)
@@ -143,6 +151,12 @@ class WOStackStatusController(CementBaseController):
else:
Log.info(self, "Redis server is not installed")
+ if self.app.pargs.fail2ban:
+ if WOAptGet.is_installed(self, 'fail2ban'):
+ services = services + ['fail2ban-client']
+ else:
+ Log.info(self, "fail2ban is not installed")
+
for service in services:
Log.debug(self, "Stopping service: {0}".format(service))
WOService.stop_service(self, service)
@@ -155,7 +169,8 @@ class WOStackStatusController(CementBaseController):
self.app.pargs.php73 or
self.app.pargs.mysql or
self.app.pargs.memcached or
- self.app.pargs.redis):
+ self.app.pargs.redis or
+ self.app.pargs.fail2ban):
self.app.pargs.nginx = True
self.app.pargs.php = True
self.app.pargs.mysql = True
@@ -209,6 +224,12 @@ class WOStackStatusController(CementBaseController):
else:
Log.info(self, "Redis server is not installed")
+ if self.app.pargs.fail2ban:
+ if WOAptGet.is_installed(self, 'fail2ban'):
+ services = services + ['fail2ban-client']
+ else:
+ Log.info(self, "fail2ban is not installed")
+
for service in services:
Log.debug(self, "Restarting service: {0}".format(service))
WOService.restart_service(self, service)
@@ -221,7 +242,8 @@ class WOStackStatusController(CementBaseController):
self.app.pargs.php73 or
self.app.pargs.mysql or
self.app.pargs.memcached or
- self.app.pargs.redis):
+ self.app.pargs.redis or
+ self.app.pargs.fail2ban):
self.app.pargs.nginx = True
self.app.pargs.php = True
self.app.pargs.mysql = True
@@ -274,6 +296,12 @@ class WOStackStatusController(CementBaseController):
else:
Log.info(self, "Redis server is not installed")
+ if self.app.pargs.fail2ban:
+ if WOAptGet.is_installed(self, 'fail2ban'):
+ services = services + ['fail2ban-client']
+ else:
+ Log.info(self, "fail2ban is not installed")
+
for service in services:
if WOService.get_service_status(self, service):
Log.info(self, "{0:10}: {1}".format(service, "Running"))
@@ -286,7 +314,8 @@ class WOStackStatusController(CementBaseController):
self.app.pargs.php73 or
self.app.pargs.mysql or
self.app.pargs.memcached or
- self.app.pargs.redis):
+ self.app.pargs.redis or
+ self.app.pargs.fail2ban):
self.app.pargs.nginx = True
self.app.pargs.php = True
self.app.pargs.mysql = True
@@ -340,6 +369,12 @@ class WOStackStatusController(CementBaseController):
else:
Log.info(self, "Redis server is not installed")
+ if self.app.pargs.fail2ban:
+ if WOAptGet.is_installed(self, 'fail2ban'):
+ services = services + ['fail2ban-client']
+ else:
+ Log.info(self, "fail2ban is not installed")
+
for service in services:
Log.debug(self, "Reloading service: {0}".format(service))
WOService.reload_service(self, service)
diff --git a/wo/cli/plugins/stack_upgrade.py b/wo/cli/plugins/stack_upgrade.py
index 0e844aa..43250f8 100644
--- a/wo/cli/plugins/stack_upgrade.py
+++ b/wo/cli/plugins/stack_upgrade.py
@@ -107,7 +107,8 @@ class WOStackUpgradeController(CementBaseController):
if self.app.pargs.php:
if WOAptGet.is_installed(self, 'php7.2-fpm'):
if not WOAptGet.is_installed(self, 'php7.3-fpm'):
- apt_packages = apt_packages + WOVariables.wo_php + WOVariables.wo_php_extra
+ apt_packages = apt_packages + WOVariables.wo_php + \
+ WOVariables.wo_php_extra
else:
apt_packages = apt_packages + WOVariables.wo_php
else:
diff --git a/wo/cli/plugins/update.py b/wo/cli/plugins/update.py
index 4f589de..fa955bc 100644
--- a/wo/cli/plugins/update.py
+++ b/wo/cli/plugins/update.py
@@ -1,9 +1,10 @@
-from cement.core.controller import CementBaseController, expose
+import os
+import time
+
from cement.core import handler, hook
+from cement.core.controller import CementBaseController, expose
from wo.core.download import WODownload
from wo.core.logging import Log
-import time
-import os
def wo_update_hook(app):
diff --git a/wo/cli/templates/fail2ban-forbidden.mustache b/wo/cli/templates/fail2ban-forbidden.mustache
new file mode 100644
index 0000000..6f708bf
--- /dev/null
+++ b/wo/cli/templates/fail2ban-forbidden.mustache
@@ -0,0 +1,4 @@
+[Definition]
+failregex = ^ \[error\] \d+#\d+: .* forbidden .*, client: , .*$
+
+ignoreregex =
diff --git a/wo/cli/templates/fail2ban.mustache b/wo/cli/templates/fail2ban.mustache
index 10937f6..f7eb611 100644
--- a/wo/cli/templates/fail2ban.mustache
+++ b/wo/cli/templates/fail2ban.mustache
@@ -21,4 +21,4 @@ port = http,https
logpath = /var/log/nginx/*error*.log
findtime = 60
bantime = 6000
-maxretry = 3
\ No newline at end of file
+maxretry = 5
\ No newline at end of file
diff --git a/wo/cli/templates/fastcgi.mustache b/wo/cli/templates/fastcgi.mustache
index 7300bb0..271e751 100644
--- a/wo/cli/templates/fastcgi.mustache
+++ b/wo/cli/templates/fastcgi.mustache
@@ -1,4 +1,4 @@
-# FastCGI cache settings - WO v3.9.5.1
+# FastCGI cache settings
fastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=WORDPRESS:50m inactive=60m max_size=256M;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header updating http_500 http_503;
@@ -17,3 +17,5 @@ fastcgi_buffer_size 32k;
fastcgi_param SERVER_NAME $http_host;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
fastcgi_keep_conn on;
+# only available with Nginx 1.15.6 and earlier
+fastcgi_socket_keepalive on;
diff --git a/wo/cli/templates/php-fpm.mustache b/wo/cli/templates/php-fpm.mustache
index eae67a4..17e5c69 100644
--- a/wo/cli/templates/php-fpm.mustache
+++ b/wo/cli/templates/php-fpm.mustache
@@ -2,4 +2,7 @@
pid = {{pid}}
error_log = {{error_log}}
log_level = notice
-include = {{include}}
\ No newline at end of file
+emergency_restart_threshold = 10
+emergency_restart_interval = 1m
+process_control_timeout = 10s
+include = {{include}}
diff --git a/wo/cli/templates/sysctl.mustache b/wo/cli/templates/sysctl.mustache
new file mode 100644
index 0000000..cc2c332
--- /dev/null
+++ b/wo/cli/templates/sysctl.mustache
@@ -0,0 +1,266 @@
+# Kernel sysctl configuration file for Linux
+#
+# Version 1.16 - 2019-10-25
+# Michiel Klaver - IT Professional
+# Modified by VirtuBox
+#
+# Instructions available on https://github.com/VirtuBox/ubuntu-nginx-web-server
+#
+# Sources :
+# https://klaver.it/linux/sysctl.conf
+# https://easyengine.io/tutorials/linux/sysctl-conf/
+#
+#
+# Credits:
+#
+# http://www.enigma.id.au/linux_tuning.txt
+# http://www.securityfocus.com/infocus/1729
+# http://fasterdata.es.net/TCP-tuning/linux.html
+# http://fedorahosted.org/ktune/browser/sysctl.ktune
+# http://www.cymru.com/Documents/ip-stack-tuning.html
+# http://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
+# http://www.frozentux.net/ipsysctl-tutorial/chunkyhtml/index.html
+# http://knol.google.com/k/linux-performance-tuning-and-measurement
+# http://www.cyberciti.biz/faq/linux-kernel-tuning-virtual-memory-subsystem/
+# http://www.redbooks.ibm.com/abstracts/REDP4285.html
+# http://www.speedguide.net/read_articles.php?id=121
+# http://lartc.org/howto/lartc.kernel.obscure.html
+# http://en.wikipedia.org/wiki/Sysctl
+#
+# Usage
+# wget -O /etc/sysctl.d/60-ubuntu-nginx-web-server.conf https://virtubox.github.io/ubuntu-nginx-web-server/files/etc/sysctl.d/60-ubuntu-nginx-web-server.conf
+#
+# sysctl -e -p /etc/sysctl.d/60-ubuntu-nginx-web-server.conf
+# For binary values, 0 is disabled, 1 is enabled. See sysctl(8) and sysctl.conf(5) for more details.
+#
+
+###
+### GENERAL SYSTEM SECURITY OPTIONS ###
+###
+
+# Controls the System Request debugging functionality of the kernel
+kernel.sysrq = 0
+
+# Controls whether core dumps will append the PID to the core filename.
+# Useful for debugging multi-threaded applications.
+kernel.core_uses_pid = 1
+
+#Allow for more PIDs
+kernel.pid_max = 65535
+
+# The contents of /proc//maps and smaps files are only visible to
+# readers that are allowed to ptrace() the process
+kernel.maps_protect = 1
+
+#Enable ExecShield protection
+kernel.exec-shield = 1
+kernel.randomize_va_space = 2
+
+# Controls the maximum size of a message, in bytes
+kernel.msgmnb = 65535
+
+# Controls the default maxmimum size of a mesage queue
+kernel.msgmax = 65535
+
+# Restrict core dumps
+fs.suid_dumpable = 0
+
+# Hide exposed kernel pointers
+kernel.kptr_restrict = 1
+
+###
+### IMPROVE SYSTEM MEMORY MANAGEMENT ###
+###
+
+# Increase size of file handles and inode cache
+fs.file-max = 209708
+
+# Do less swapping
+vm.swappiness = 10
+vm.dirty_ratio = 30
+vm.dirty_background_ratio = 5
+
+# specifies the minimum virtual address that a process is allowed to mmap
+vm.mmap_min_addr = 4096
+
+# 50% overcommitment of available memory
+vm.overcommit_ratio = 50
+
+# allow memory overcommit required for redis
+vm.overcommit_memory = 1
+
+# Set maximum amount of memory allocated to shm to 256MB
+kernel.shmmax = 268435456
+kernel.shmall = 268435456
+
+# Keep at least 64MB of free RAM space available
+vm.min_free_kbytes = 65535
+
+###
+### GENERAL NETWORK SECURITY OPTIONS ###
+###
+
+#Prevent SYN attack, enable SYNcookies (they will kick-in when the max_syn_backlog reached)
+net.ipv4.tcp_syncookies = 1
+net.ipv4.tcp_syn_retries = 2
+net.ipv4.tcp_synack_retries = 2
+net.ipv4.tcp_max_syn_backlog = 4096
+
+# Disables IP source routing
+net.ipv4.conf.all.send_redirects = 0
+net.ipv4.conf.default.send_redirects = 0
+net.ipv4.conf.all.accept_source_route = 0
+net.ipv4.conf.default.accept_source_route = 0
+net.ipv6.conf.all.accept_source_route = 0
+net.ipv6.conf.default.accept_source_route = 0
+
+# Enable IP spoofing protection, turn on source route verification
+net.ipv4.conf.all.rp_filter = 1
+net.ipv4.conf.default.rp_filter = 1
+
+# Disable ICMP Redirect Acceptance
+net.ipv4.conf.all.accept_redirects = 0
+net.ipv4.conf.default.accept_redirects = 0
+net.ipv4.conf.all.secure_redirects = 0
+net.ipv4.conf.default.secure_redirects = 0
+net.ipv6.conf.all.accept_redirects = 0
+net.ipv6.conf.default.accept_redirects = 0
+
+# Enable Log Spoofed Packets, Source Routed Packets, Redirect Packets
+net.ipv4.conf.all.log_martians = 1
+net.ipv4.conf.default.log_martians = 1
+
+# Decrease the time default value for tcp_fin_timeout connection
+net.ipv4.tcp_fin_timeout = 7
+
+# Decrease the time default value for connections to keep alive
+net.ipv4.tcp_keepalive_time = 300
+net.ipv4.tcp_keepalive_probes = 5
+net.ipv4.tcp_keepalive_intvl = 15
+
+# Don't relay bootp
+net.ipv4.conf.all.bootp_relay = 0
+
+# Don't proxy arp for anyone
+net.ipv4.conf.all.proxy_arp = 0
+
+# Turn on the tcp_timestamps, accurate timestamp make TCP congestion control algorithms work better
+net.ipv4.tcp_timestamps = 1
+
+# Don't ignore directed pings
+net.ipv4.icmp_echo_ignore_all = 0
+
+# Enable ignoring broadcasts request
+net.ipv4.icmp_echo_ignore_broadcasts = 1
+
+# Enable bad error message Protection
+net.ipv4.icmp_ignore_bogus_error_responses = 1
+
+# Allowed local port range
+net.ipv4.ip_local_port_range = 16384 65535
+
+# Enable a fix for RFC1337 - time-wait assassination hazards in TCP
+net.ipv4.tcp_rfc1337 = 1
+
+# Do not auto-configure IPv6
+net.ipv6.conf.all.autoconf=0
+net.ipv6.conf.all.accept_ra=0
+net.ipv6.conf.default.autoconf=0
+net.ipv6.conf.default.accept_ra=0
+net.ipv6.conf.all.accept_ra_defrtr = 0
+net.ipv6.conf.default.accept_ra_defrtr = 0
+net.ipv6.conf.all.accept_ra_pinfo = 0
+net.ipv6.conf.default.accept_ra_pinfo = 0
+
+###
+### TUNING NETWORK PERFORMANCE ###
+###
+
+# For servers with tcp-heavy workloads, enable 'fq' queue management scheduler (kernel > 3.12)
+net.core.default_qdisc = fq
+
+# Turn on the tcp_window_scaling
+net.ipv4.tcp_window_scaling = 1
+
+# Increase the read-buffer space allocatable
+net.ipv4.tcp_rmem = 8192 87380 16777216
+net.ipv4.udp_rmem_min = 16384
+net.core.rmem_default = 262144
+net.core.rmem_max = 16777216
+
+# Increase the write-buffer-space allocatable
+net.ipv4.tcp_wmem = 8192 65536 16777216
+net.ipv4.udp_wmem_min = 16384
+net.core.wmem_default = 262144
+net.core.wmem_max = 16777216
+
+# Increase number of incoming connections
+net.core.somaxconn = 32768
+
+# Increase number of incoming connections backlog
+net.core.netdev_max_backlog = 16384
+net.core.dev_weight = 64
+
+# Increase the maximum amount of option memory buffers
+net.core.optmem_max = 65535
+
+# Increase the tcp-time-wait buckets pool size to prevent simple DOS attacks
+net.ipv4.tcp_max_tw_buckets = 1440000
+
+# try to reuse time-wait connections, but don't recycle them (recycle can break clients behind NAT)
+net.ipv4.tcp_tw_recycle = 0
+net.ipv4.tcp_tw_reuse = 1
+
+# Limit number of orphans, each orphan can eat up to 16M (max wmem) of unswappable memory
+net.ipv4.tcp_max_orphans = 16384
+net.ipv4.tcp_orphan_retries = 0
+
+# Limit the maximum memory used to reassemble IP fragments (CVE-2018-5391)
+net.ipv4.ipfrag_low_thresh = 196608
+net.ipv6.ip6frag_low_thresh = 196608
+net.ipv4.ipfrag_high_thresh = 262144
+net.ipv6.ip6frag_high_thresh = 262144
+
+
+# don't cache ssthresh from previous connection
+net.ipv4.tcp_no_metrics_save = 1
+net.ipv4.tcp_moderate_rcvbuf = 1
+
+# Increase size of RPC datagram queue length
+net.unix.max_dgram_qlen = 50
+
+# Don't allow the arp table to become bigger than this
+net.ipv4.neigh.default.gc_thresh3 = 2048
+
+# Tell the gc when to become aggressive with arp table cleaning.
+# Adjust this based on size of the LAN. 1024 is suitable for most /24 networks
+net.ipv4.neigh.default.gc_thresh2 = 1024
+
+# Adjust where the gc will leave arp table alone - set to 32.
+net.ipv4.neigh.default.gc_thresh1 = 32
+
+# Adjust to arp table gc to clean-up more often
+net.ipv4.neigh.default.gc_interval = 30
+
+# Increase TCP queue length
+net.ipv4.neigh.default.proxy_qlen = 96
+net.ipv4.neigh.default.unres_qlen = 6
+
+# Enable Explicit Congestion Notification (RFC 3168), disable it if it doesn't work for you
+net.ipv4.tcp_ecn = 1
+net.ipv4.tcp_reordering = 3
+
+# How many times to retry killing an alive TCP connection
+net.ipv4.tcp_retries2 = 15
+net.ipv4.tcp_retries1 = 3
+
+# Avoid falling back to slow start after a connection goes idle
+# keeps our cwnd large with the keep alive connections (kernel > 3.6)
+net.ipv4.tcp_slow_start_after_idle = 0
+
+# Allow the TCP fastopen flag to be used, beware some firewalls do not like TFO! (kernel > 3.7)
+net.ipv4.tcp_fastopen = 3
+
+# This will enusre that immediatly subsequent connections use the new values
+net.ipv4.route.flush = 1
+net.ipv6.route.flush = 1
diff --git a/wo/core/apt_repo.py b/wo/core/apt_repo.py
index 4c89dae..cfd1f22 100644
--- a/wo/core/apt_repo.py
+++ b/wo/core/apt_repo.py
@@ -23,8 +23,8 @@ class WORepo():
"""
if repo_url is not None:
- repo_file_path = ("/etc/apt/sources.list.d/"
- + WOVariables().wo_repo_file)
+ repo_file_path = ("/etc/apt/sources.list.d/" +
+ WOVariables().wo_repo_file)
try:
if not os.path.isfile(repo_file_path):
with open(repo_file_path,
@@ -62,8 +62,8 @@ class WORepo():
"--remove '{ppa_name}'"
.format(ppa_name=ppa))
elif repo_url:
- repo_file_path = ("/etc/apt/sources.list.d/"
- + WOVariables().wo_repo_file)
+ repo_file_path = ("/etc/apt/sources.list.d/" +
+ WOVariables().wo_repo_file)
try:
repofile = open(repo_file_path, "w+")
@@ -84,8 +84,8 @@ class WORepo():
"""
WOShellExec.cmd_exec(self, "gpg --keyserver {serv}"
.format(serv=(keyserver or
- "hkp://keyserver.ubuntu.com"))
- + " --recv-keys {key}".format(key=keyids))
+ "hkp://keyserver.ubuntu.com")) +
+ " --recv-keys {key}".format(key=keyids))
WOShellExec.cmd_exec(self, "gpg -a --export --armor {0}"
- .format(keyids)
- + " | apt-key add - ")
+ .format(keyids) +
+ " | apt-key add - ")
diff --git a/wo/core/aptget.py b/wo/core/aptget.py
index 7c274cf..a480202 100644
--- a/wo/core/aptget.py
+++ b/wo/core/aptget.py
@@ -36,7 +36,8 @@ class WOAptGet():
for single_error in error_list:
if "NO_PUBKEY" in single_error:
key = single_error.rsplit(None, 1)[-1]
- WORepo.add_key(self, key, keyserver="hkp://pgp.mit.edu")
+ WORepo.add_key(
+ self, key, keyserver="hkp://pgp.mit.edu")
proc = subprocess.Popen('apt-get update',
shell=True,
@@ -47,9 +48,11 @@ class WOAptGet():
if proc.returncode == 0:
return True
else:
- Log.info(self, Log.FAIL + "Whoops, something went wrong...")
+ Log.info(self, Log.FAIL +
+ "Whoops, something went wrong...")
Log.error(self, "Check the WordOps log for more details "
- "`tail /var/log/wo/wordops.log` and please try again...")
+ "`tail /var/log/wo/wordops.log` "
+ "and please try again...")
except Exception as e:
Log.error(self, "apt-get update exited with error")
@@ -60,7 +63,7 @@ class WOAptGet():
"""
try:
check_update = subprocess.Popen(['apt-get upgrade -s | grep '
- '\"^Inst\" | wc -l'],
+ '\"^Inst\" | wc -l'],
stdout=subprocess.PIPE,
shell=True).communicate()[0]
if check_update == b'0\n':
@@ -96,7 +99,8 @@ class WOAptGet():
Log.info(self, Log.FAIL + "Oops Something went "
"wrong!!")
Log.error(self, "Check the WordOps log for more details "
- "`tail /var/log/wo/wordops.log` and please try again...")
+ "`tail /var/log/wo/wordops.log` "
+ "and please try again...")
except Exception as e:
Log.error(self, "Error while installing packages, "
"apt-get exited with error")
@@ -122,13 +126,15 @@ class WOAptGet():
Log.info(self, Log.FAIL + "Oops Something went "
"wrong!!")
Log.error(self, "Check the WordOps log for more details "
- "`tail /var/log/wo/wordops.log` and please try again...")
+ "`tail /var/log/wo/wordops.log` "
+ "and please try again...")
except Exception as e:
Log.info(self, Log.FAIL + "Oops Something went "
"wrong!!")
Log.error(self, "Check the WordOps log for more details "
- "`tail /var/log/wo/wordops.log` and please try again...")
+ "`tail /var/log/wo/wordops.log` "
+ "and please try again...")
def remove(self, packages, auto=False, purge=False):
all_packages = ' '.join(packages)
@@ -151,7 +157,8 @@ class WOAptGet():
Log.info(self, Log.FAIL + "Oops Something went "
"wrong!!")
Log.error(self, "Check the WordOps log for more details "
- "`tail /var/log/wo/wordops.log` and please try again...")
+ "`tail /var/log/wo/wordops.log` "
+ "and please try again...")
except Exception as e:
Log.error(self, "Error while installing packages, "
@@ -184,19 +191,20 @@ class WOAptGet():
def is_installed(self, package_name):
"""
- Checks if package is available in cache and is installed or not
+ Checks if package is available in cache "
+ "and is installed or not
returns True if installed otherwise returns False
"""
apt_cache = apt.cache.Cache()
apt_cache.open()
if (package_name.strip() in apt_cache and
- apt_cache[package_name.strip()].is_installed):
+ apt_cache[package_name.strip()].is_installed):
# apt_cache.close()
return True
# apt_cache.close()
return False
- def download_only(self,package_name,repo_url=None,repo_key=None):
+ def download_only(self, package_name, repo_url=None, repo_key=None):
"""
Similar to `apt-get install --download-only PACKAGE_NAME`
"""
@@ -207,7 +215,8 @@ class WOAptGet():
WORepo.add(self, repo_url=repo_url)
if repo_key is not None:
WORepo.add_key(self, repo_key)
- proc = subprocess.Popen("apt-get update && DEBIAN_FRONTEND=noninteractive "
+ proc = subprocess.Popen("apt-get update && "
+ "DEBIAN_FRONTEND=noninteractive "
"apt-get install -o "
"Dpkg::Options::=\"--force-confdef\""
" -o "
@@ -221,11 +230,12 @@ class WOAptGet():
if proc.returncode == 0:
return True
else:
- Log.error(self,"Error in fetching dpkg package.\nReverting changes ..",False)
+ Log.error(
+ self, "Error in fetching dpkg package.\n"
+ "Reverting changes ..", False)
if repo_url is not None:
WORepo.remove(self, repo_url=repo_url)
return False
except Exception as e:
Log.error(self, "Error while downloading packages, "
"apt-get exited with error")
-
diff --git a/wo/core/cron.py b/wo/core/cron.py
index 2392ad4..02e9e81 100644
--- a/wo/core/cron.py
+++ b/wo/core/cron.py
@@ -5,28 +5,30 @@ from wo.core.logging import Log
Set CRON on LINUX system.
"""
+
class WOCron():
- def setcron_weekly(self,cmd,comment='Cron set by WordOps',user='root',min=0,hour=12):
- if not WOShellExec.cmd_exec(self, "crontab -l | grep -q \'{0}\'".format(cmd)):
+ def setcron_weekly(self, cmd, comment='Cron set by WordOps', user='root',
+ min=0, hour=12):
+ if not WOShellExec.cmd_exec(self, "crontab -l "
+ "| grep -q \'{0}\'".format(cmd)):
WOShellExec.cmd_exec(self, "/bin/bash -c \"crontab -l "
- "2> /dev/null | {{ cat; echo -e"
- " \\\""
- "\\n0 0 * * 0 "
- "{0}".format(cmd) +
- " # {0}".format(comment)+
- "\\\"; } | crontab -\"")
+ "2> /dev/null | {{ cat; echo -e"
+ " \\\""
+ "\\n0 0 * * 0 "
+ "{0}".format(cmd) +
+ " # {0}".format(comment) +
+ "\\\"; } | crontab -\"")
Log.debug(self, "Cron set")
-
-
- def remove_cron(self,cmd):
- if WOShellExec.cmd_exec(self, "crontab -l | grep -q \'{0}\'".format(cmd)):
+ def remove_cron(self, cmd):
+ if WOShellExec.cmd_exec(self, "crontab -l "
+ "| grep -q \'{0}\'".format(cmd)):
if not WOShellExec.cmd_exec(self, "/bin/bash -c "
- "\"crontab "
- "-l | sed '/{0}/d'"
- "| crontab -\""
- .format(cmd)):
- Log.error(self, "Failed to remove crontab entry",False)
+ "\"crontab "
+ "-l | sed '/{0}/d'"
+ "| crontab -\""
+ .format(cmd)):
+ Log.error(self, "Failed to remove crontab entry", False)
else:
Log.debug(self, "Cron not found")
diff --git a/wo/core/download.py b/wo/core/download.py
index ef94d4f..36d3d7a 100644
--- a/wo/core/download.py
+++ b/wo/core/download.py
@@ -22,7 +22,8 @@ class WODownload():
if not os.path.exists(directory):
os.makedirs(directory)
Log.info(self, "Downloading {0:20}".format(pkg_name), end=' ')
- req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
+ req = urllib.request.Request(
+ url, headers={'User-Agent': 'Mozilla/5.0'})
with urllib.request.urlopen(req) as response, open(filename, 'wb') as out_file:
out_file.write(response.read())
Log.info(self, "{0}".format("[" + Log.ENDC + "Done"
diff --git a/wo/core/exc.py b/wo/core/exc.py
index 67e968f..02c03dc 100644
--- a/wo/core/exc.py
+++ b/wo/core/exc.py
@@ -3,6 +3,7 @@
class WOError(Exception):
"""Generic errors."""
+
def __init__(self, msg):
Exception.__init__(self)
self.msg = msg
diff --git a/wo/core/fileutils.py b/wo/core/fileutils.py
index a6032b8..a7a4c29 100644
--- a/wo/core/fileutils.py
+++ b/wo/core/fileutils.py
@@ -21,14 +21,14 @@ class WOFileUtils():
Log.info(self, "Removing {0:65}".format(file), end=' ')
os.remove(file)
Log.info(self, "{0}".format("[" + Log.ENDC + "Done" +
- Log.OKBLUE + "]"))
+ Log.OKBLUE + "]"))
Log.debug(self, 'file Removed')
if os.path.isdir(file):
try:
Log.info(self, "Removing {0:65}".format(file), end=' ')
shutil.rmtree(file)
Log.info(self, "{0}".format("[" + Log.ENDC + "Done" +
- Log.OKBLUE + "]"))
+ Log.OKBLUE + "]"))
except shutil.Error as e:
Log.debug(self, "{err}".format(err=str(e.reason)))
Log.error(self, 'Unable to Remove file ')
diff --git a/wo/core/mysql.py b/wo/core/mysql.py
index 92a1c5e..c289405 100644
--- a/wo/core/mysql.py
+++ b/wo/core/mysql.py
@@ -28,10 +28,12 @@ class WOMysql():
"""Method for MySQL connection"""
def connect(self):
- """Makes connection with MySQL server"""
+ # Makes connection with MySQL server
try:
if os.path.exists('/etc/mysql/conf.d/my.cnf'):
- connection = pymysql.connect(read_default_file='/etc/mysql/conf.d/my.cnf')
+ connection = \
+ pymysql.connect(read_default_file='/etc/mysql/'
+ 'conf.d/my.cnf')
else:
connection = pymysql.connect(read_default_file='~/.my.cnf')
return connection
@@ -45,9 +47,11 @@ class WOMysql():
def dbConnection(self, db_name):
try:
if os.path.exists('/etc/mysql/conf.d/my.cnf'):
- connection = pymysql.connect(db=db_name,read_default_file='/etc/mysql/conf.d/my.cnf')
+ connection = pymysql.connect(
+ db=db_name, read_default_file='/etc/mysql/conf.d/my.cnf')
else:
- connection = pymysql.connect(db=db_name,read_default_file='~/.my.cnf')
+ connection = pymysql.connect(
+ db=db_name, read_default_file='~/.my.cnf')
return connection
except DatabaseError as e:
@@ -58,12 +62,13 @@ class WOMysql():
except pymysql.err.InternalError as e:
Log.debug(self, str(e))
raise MySQLConnectionError
- except Exception as e :
+ except Exception as e:
Log.debug(self, "[Error]Setting up database: \'" + str(e) + "\'")
raise MySQLConnectionError
def execute(self, statement, errormsg='', log=True):
- """Get login details from /etc/mysql/conf.d/my.cnf & Execute MySQL query"""
+ # Get login details from /etc/mysql/conf.d/my.cnf
+ # & Execute MySQL query
connection = WOMysql.connect(self)
log and Log.debug(self, "Exceuting MySQL Statement : {0}"
.format(statement))
@@ -95,20 +100,22 @@ class WOMysql():
'/var/wo-mysqlbackup')
os.makedirs('/var/wo-mysqlbackup')
- db = subprocess.check_output(["mysql -Bse \'show databases\'"],
+ db = subprocess.check_output(["/usr/bin/mysql "
+ "-Bse \'show databases\'"],
universal_newlines=True,
shell=True).split('\n')
for dbs in db:
if dbs == "":
continue
Log.info(self, "Backing up {0} database".format(dbs))
- p1 = subprocess.Popen("mysqldump {0}"
+ p1 = subprocess.Popen("/usr/bin/mysqldump {0}"
" --max_allowed_packet=1024M"
" --single-transaction".format(dbs),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True)
- p2 = subprocess.Popen("gzip -c > /var/wo-mysqlbackup/{0}{1}.s"
- "ql.gz".format(dbs, WOVariables.wo_date),
+ p2 = subprocess.Popen("/usr/bin/pigz -c > "
+ "/var/wo-mysqlbackup/{0}{1}.sql.gz"
+ .format(dbs, WOVariables.wo_date),
stdin=p1.stdout,
shell=True)
diff --git a/wo/core/services.py b/wo/core/services.py
index c028880..a6e4c04 100644
--- a/wo/core/services.py
+++ b/wo/core/services.py
@@ -116,7 +116,8 @@ class WOService():
try:
is_exist = subprocess.getstatusoutput('which {0}'
.format(service_name))
- if is_exist[0] == 0 or service_name in ['php7.2-fpm', 'php7.3-fpm']:
+ if is_exist[0] == 0 or service_name in ['php7.2-fpm',
+ 'php7.3-fpm']:
retcode = subprocess.getstatusoutput('service {0} status'
.format(service_name))
if retcode[0] == 0:
diff --git a/wo/core/sslutils.py b/wo/core/sslutils.py
index 161a484..98f33d5 100644
--- a/wo/core/sslutils.py
+++ b/wo/core/sslutils.py
@@ -19,7 +19,8 @@ class SSL:
current_date = WOShellExec.cmd_exec_stdout(self, "date -d \"now\" +%s")
expiration_date = WOShellExec.cmd_exec_stdout(self, "date -d \""
- "`openssl x509 -in "
+ "`/usr/bin/openssl "
+ "x509 -in "
"/etc/letsencrypt/live/"
"{0}/cert.pem"
" -text -noout|grep "
@@ -45,7 +46,8 @@ class SSL:
"`tail /var/log/wo/wordops.log` and please try again...")
expiration_date = WOShellExec.cmd_exec_stdout(self, "date -d "
- "\"`openssl x509 -in "
+ "\"`/usr/bin/openssl "
+ "x509 -in "
"/etc/letsencrypt/live/"
"{0}/cert.pem"
" -text -noout|grep "
diff --git a/wo/core/variables.py b/wo/core/variables.py
index 74c4cec..d3af7bd 100644
--- a/wo/core/variables.py
+++ b/wo/core/variables.py
@@ -3,7 +3,6 @@ import platform
import socket
import configparser
import os
-import sys
import psutil
import datetime
@@ -12,9 +11,9 @@ class WOVariables():
"""Intialization of core variables"""
# WordOps version
- wo_version = "3.9.4"
+ wo_version = "3.9.5"
# WordOps packages versions
- wo_wp_cli = "2.1.0"
+ wo_wp_cli = "2.2.0"
wo_adminer = "4.7.1"
# Get WPCLI path
@@ -23,17 +22,18 @@ class WOVariables():
wo_wpcli_path = '/usr/local/bin/wp '
# get wan network interface name
- wo_wan_interface = os.popen("ip -4 route get 8.8.8.8 | "
- "grep -oP \"dev [^[:space:]]+ \" "
- "| cut -d ' ' -f 2").read()
- if wo_wan_interface == '':
- wo_wan_interface = 'eth0'
+ wo_wan = os.popen("/sbin/ip -4 route get 8.8.8.8 | "
+ "grep -oP \"dev [^[:space:]]+ \" "
+ "| cut -d ' ' -f 2").read()
+ if wo_wan == '':
+ wo_wan = 'eth0'
# Current date and time of System
wo_date = datetime.datetime.now().strftime('%d%b%Y%H%M%S')
# WordOps core variables
- wo_platform_distro = platform.linux_distribution()[0].lower()
+ wo_platform_distro = os.popen("lsb_release -si "
+ "| tr -d \'\\n\'").read().lower()
wo_platform_version = platform.linux_distribution()[1]
wo_platform_codename = os.popen("lsb_release -sc | tr -d \'\\n\'").read()
@@ -70,8 +70,9 @@ class WOVariables():
except Exception as e:
wo_user = input("Enter your name: ")
wo_email = input("Enter your email: ")
- os.system("git config --global user.name {0}".format(wo_user))
- os.system("git config --global user.email {0}".format(wo_email))
+ os.system("/usr/bin/git config --global user.name {0}".format(wo_user))
+ os.system(
+ "/usr/bin/git config --global user.email {0}".format(wo_email))
# Get System RAM and SWAP details
wo_ram = psutil.virtual_memory().total / (1024 * 1024)
@@ -94,21 +95,35 @@ class WOVariables():
# WordOps stack installation variables
# Nginx repo and packages
- if wo_platform_codename == 'trusty':
+ if wo_platform_distro == 'ubuntu':
+ if wo_platform_codename == 'trusty':
+ wo_nginx_repo = ("deb http://download.opensuse.org"
+ "/repositories/home:"
+ "/virtubox:/WordOps/xUbuntu_14.04/ /")
+ elif wo_platform_codename == 'xenial':
+ wo_nginx_repo = ("deb http://download.opensuse.org"
+ "/repositories/home:"
+ "/virtubox:/WordOps/xUbuntu_16.04/ /")
+ elif wo_platform_codename == 'bionic':
+ wo_nginx_repo = ("deb http://download.opensuse.org"
+ "/repositories/home:"
+ "/virtubox:/WordOps/xUbuntu_18.04/ /")
+ else:
+ wo_nginx_repo = ("deb http://download.opensuse.org"
+ "/repositories/home:"
+ "/virtubox:/WordOps/xUbuntu_19.04/ /")
+ elif wo_platform_distro == 'debian':
+ if wo_platform_codename == 'jessie':
+ wo_nginx_repo = ("deb http://download.opensuse.org"
+ "/repositories/home:"
+ "/virtubox:/WordOps/Debian_8.0/ /")
+ elif wo_platform_codename == 'stretch':
+ wo_nginx_repo = ("deb http://download.opensuse.org"
+ "/repositories/home:"
+ "/virtubox:/WordOps/Debian_9.0/ /")
+ else:
wo_nginx_repo = ("deb http://download.opensuse.org/repositories/home:"
- "/virtubox:/WordOps/xUbuntu_14.04/ /")
- elif wo_platform_codename == 'xenial':
- wo_nginx_repo = ("deb http://download.opensuse.org/repositories/home:"
- "/virtubox:/WordOps/xUbuntu_16.04/ /")
- elif wo_platform_codename == 'bionic':
- wo_nginx_repo = ("deb http://download.opensuse.org/repositories/home:"
- "/virtubox:/WordOps/xUbuntu_18.04/ /")
- elif wo_platform_codename == 'jessie':
- wo_nginx_repo = ("deb http://download.opensuse.org/repositories/home:"
- "/virtubox:/WordOps/Debian_8.0/ /")
- elif wo_platform_codename == 'stretch':
- wo_nginx_repo = ("deb http://download.opensuse.org/repositories/home:"
- "/virtubox:/WordOps/Debian_9.0/ /")
+ "/virtubox:/WordOps/Raspbian_9.0/ /")
wo_nginx = ["nginx-custom", "nginx-wo"]
wo_nginx_key = '188C9FB063F0247A'
@@ -129,7 +144,7 @@ class WOVariables():
wo_php_extra = ["php-memcached", "php-imagick", "memcached",
"graphviz", "php-xdebug", "php-msgpack", "php-redis"]
wo_php_key = ''
- elif wo_platform_distro == 'debian':
+ else:
wo_php_repo = (
"deb https://packages.sury.org/php/ {codename} main"
.format(codename=wo_platform_codename))
@@ -145,19 +160,21 @@ class WOVariables():
"php7.3-zip", "php7.3-xml", "php7.3-soap"]
wo_php_extra = ["php-memcached", "php-imagick", "memcached",
"graphviz", "php-xdebug", "php-msgpack", "php-redis"]
+
wo_php_key = 'AC0E47584A7A714D'
# MySQL repo and packages
if wo_platform_distro == 'ubuntu':
- wo_mysql_repo = ("deb [arch=amd64,ppc64el] http://sfo1.mirrors.digitalocean.com/mariadb/repo/"
+ wo_mysql_repo = ("deb [arch=amd64,ppc64el] "
+ "http://sfo1.mirrors.digitalocean.com/mariadb/repo/"
"10.3/ubuntu {codename} main"
.format(codename=wo_platform_codename))
- elif wo_platform_distro == 'debian':
- wo_mysql_repo = ("deb [arch=amd64,ppc64el] http://sfo1.mirrors.digitalocean.com/mariadb/repo/"
+ 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))
-
- wo_mysql = ["mariadb-server", "percona-toolkit"]
+ wo_mysql = ["mariadb-server", "percona-toolkit", "python3-mysqldb"]
wo_fail2ban = "fail2ban"