Created
December 9, 2019 19:17
-
-
Save JamesTheHacker/10d07d8fa473fc1c83423253f5deb2dd to your computer and use it in GitHub Desktop.
WIP provision script for magento
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# Developer: [email protected] | |
set -o errexit | |
set -o pipefail | |
set -o nounset | |
export DEBIAN_FRONTEND=noninteractive | |
mode="developer" | |
domain="ucp.local" | |
protocol="http" | |
# Public key that will be added to authorized_keys for SSH login by the unprivilaged user | |
public_key="" | |
# Github personal access token | |
github_pa_token="" | |
# Megento | |
magento_public_key="" | |
magento_private_key="" | |
magento_language="en_GB" | |
magento_currency="GBP" | |
magento_timezone="Europe/London" | |
magento_backend="admin_ucp_654678" | |
# Database | |
db_username="magento" | |
db_password="" | |
db_root_password="" | |
db_name="magento_shop" | |
db_port="3306" | |
db_host="localhost:${db_port}" | |
# Webmaster | |
webmaster_email="" | |
webmaster_username="jamie" | |
webmaster_password="" | |
# Unprivileged user's username | |
user="web" | |
# Unprivileged user's home | |
home="/home/${user}" | |
# Webroot directory | |
webroot="/var/www/shop" | |
# PHP version | |
phpv="7.2" | |
# php.ini location | |
phpini="/etc/php/${phpv}/fpm/php.ini" | |
# php.ini settings | |
php_mem_limit="2G" | |
php_date_timezone="Europe\/London" # \ required to escape for sed | |
########################################################################################## | |
# WARNING: DO NOT MESS WITH ANYTHING BEYOND THIS POINT UNLESS YOU KNOW WHAT YOU'RE DOING # | |
########################################################################################## | |
apt -y update | |
apt -y upgrade | |
apt install -y \ | |
curl \ | |
nginx \ | |
unzip \ | |
git \ | |
openssl \ | |
libxml2 \ | |
mysql-server \ | |
mysql-client \ | |
"php${phpv}-fpm" \ | |
php-dev \ | |
php-bcmath \ | |
php-ctype \ | |
php-curl \ | |
php-dom \ | |
php-gd \ | |
php-iconv \ | |
php-intl \ | |
php-mbstring \ | |
php-mysql \ | |
php-simplexml \ | |
php-soap \ | |
php-xsl \ | |
php-zip \ | |
wget \ | |
software-properties-common | |
# DEV ONLY: For some reason the box has it installed by default :S | |
apt remove -y apache2 | |
# Configure/Secure MySQL | |
mysql -u root <<ILOVEBASH | |
UPDATE mysql.user SET authentication_string=PASSWORD('${db_root_password}') WHERE User='root'; | |
DELETE FROM mysql.user WHERE User=''; | |
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1'); | |
DROP DATABASE IF EXISTS test; | |
DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%'; | |
CREATE DATABASE ${db_name}; | |
CREATE USER '${db_username}'@'localhost' IDENTIFIED WITH mysql_native_password BY '${db_password}'; | |
GRANT ALL ON ${db_name}.* TO ${db_username}@localhost; | |
FLUSH PRIVILEGES; | |
ILOVEBASH | |
# Install composer globally | |
curl -sS https://getcomposer.org/installer -o /tmp/composer-setup.php | |
php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer | |
######################################################################### | |
# Secure PHP7.x # | |
######################################################################### | |
# Compile snuffleupagus for PHP7 hardening | |
#git clone https://github.com/nbs-system/snuffleupagus /tmp/snuffleupagus | |
#cd /tmp/snuffleupagus/src | |
#phpize | |
#./configure --enable-snuffleupagus | |
#make | |
#make install | |
#make test | |
#rm -rf /tmp/snuffleupagus | |
# Add snuffleupagus rules | |
tee /etc/php/"${phpv}"/fpm/conf.d/snuffleupagus.rules > /dev/null <<'ILOVEBASH' | |
# Harden the PRNG | |
sp.harden_random.enable(); | |
# Disabled XXE | |
sp.disable_xxe.enable(); | |
# Globally activate strict mode | |
sp.global_strict.enable(); | |
# Prevent unserialize-related exploits | |
sp.unserialize_hmac.enable(); | |
# Only allow execution of read-only files. This is a low-hanging fruit that you should enable. | |
sp.readonly_exec.enable(); | |
# Php has a lot of wrappers, most of them aren't usually useful, you should only enable the ones you're using. | |
sp.wrappers_whitelist.list("file,php,phar"); | |
# Prevent sloppy comparisons. | |
sp.sloppy_comparison.enable(); | |
# use SameSite on session cookie | |
# https://snuffleupagus.readthedocs.io/features.html#protection-against-cross-site-request-forgery | |
sp.cookie.name("PHPSESSID").samesite("lax"); | |
# Harden the `chmod` function | |
sp.disable_function.function("chmod").param("mode").value_r("^[0-9]{2}[67]$").drop(); | |
# Prevent various `mail`-related vulnerabilities | |
sp.disable_function.function("mail").param("additional_parameters").value_r("\\-").drop(); | |
# Since it's now burned, me might as well mitigate it publicly | |
sp.disable_function.function("putenv").param("setting").value_r("LD_").drop() | |
# This is also burned: | |
# ini_set('open_basedir','..');chdir('..');…;chdir('..');ini_set('open_basedir','/');echo(file_get_contents('/etc/passwd')); | |
# Since we have no way of matching on two parameters at the same time, we're | |
# blocking calls to open_basedir altogether: nobody is using it via ini_set anyway. | |
# Moreover, there are non-public bypasses that are also using this vector ;) | |
sp.disable_function.function("ini_set").param("varname").value_r("open_basedir").drop() | |
# Prevent various `include`-related vulnerabilities | |
sp.disable_function.function("require_once").value_r("\.(inc|phtml|php)$").allow(); | |
sp.disable_function.function("include_once").value_r("\.(inc|phtml|php)$").allow(); | |
sp.disable_function.function("require").value_r("\.(inc|phtml|php)$").allow(); | |
sp.disable_function.function("include").value_r("\.(inc|phtml|php)$").allow(); | |
sp.disable_function.function("require_once").drop() | |
sp.disable_function.function("include_once").drop() | |
sp.disable_function.function("require").drop() | |
sp.disable_function.function("include").drop() | |
# Prevent `system`-related injections | |
sp.disable_function.function("system").param("command").value_r("[$|;&`\\n\\(\\)\\\\]").drop(); | |
sp.disable_function.function("shell_exec").param("command").value_r("[$|;&`\\n\\(\\)\\\\]").drop(); | |
sp.disable_function.function("exec").param("command").value_r("[$|;&`\\n\\(\\)\\\\]").drop(); | |
sp.disable_function.function("proc_open").param("command").value_r("[$|;&`\\n\\(\\)\\\\]").drop(); | |
# Prevent runtime modification of interesting things | |
sp.disable_function.function("ini_set").param("varname").value("assert.active").drop(); | |
sp.disable_function.function("ini_set").param("varname").value("zend.assertions").drop(); | |
sp.disable_function.function("ini_set").param("varname").value("memory_limit").drop(); | |
sp.disable_function.function("ini_set").param("varname").value("include_path").drop(); | |
sp.disable_function.function("ini_set").param("varname").value("open_basedir").drop(); | |
# Detect some backdoors via environnement recon | |
sp.disable_function.function("ini_get").param("varname").value("allow_url_fopen").drop(); | |
sp.disable_function.function("ini_get").param("varname").value("open_basedir").drop(); | |
sp.disable_function.function("ini_get").param("varname").value_r("suhosin").drop(); | |
sp.disable_function.function("function_exists").param("function_name").value("eval").drop(); | |
sp.disable_function.function("function_exists").param("function_name").value("exec").drop(); | |
sp.disable_function.function("function_exists").param("function_name").value("system").drop(); | |
sp.disable_function.function("function_exists").param("function_name").value("shell_exec").drop(); | |
sp.disable_function.function("function_exists").param("function_name").value("proc_open").drop(); | |
sp.disable_function.function("function_exists").param("function_name").value("passthru").drop(); | |
sp.disable_function.function("is_callable").param("var").value("eval").drop(); | |
sp.disable_function.function("is_callable").param("var").value("exec").drop(); | |
sp.disable_function.function("is_callable").param("var").value("system").drop(); | |
sp.disable_function.function("is_callable").param("var").value("shell_exec").drop(); | |
sp.disable_function.function("is_callable").param("var").value("proc_open").drop(); | |
sp.disable_function.function("is_callable").param("var").value("passthru").drop(); | |
# Ensure that certificates are properly verified | |
sp.disable_function.function("curl_setopt").param("value").value("1").allow(); | |
sp.disable_function.function("curl_setopt").param("value").value("2").allow(); | |
# `81` is SSL_VERIFYHOST and `64` SSL_VERIFYPEER | |
sp.disable_function.function("curl_setopt").param("option").value("64").drop().alias("Please don't turn CURLOPT_SSL_VERIFYCLIENT off."); | |
sp.disable_function.function("curl_setopt").param("option").value("81").drop().alias("Please don't turn CURLOPT_SSL_VERIFYHOST off."); | |
#File upload | |
sp.disable_function.function("move_uploaded_file").param("destination").value_r("\\.ph").drop(); | |
sp.disable_function.function("move_uploaded_file").param("destination").value_r("\\.ht").drop(); | |
ILOVEBASH | |
# TODO: Fix the rules for Magento 2 | |
# Add snuffleupagus extension to PHP config | |
sudo tee /etc/php/"${phpv}"/fpm/conf.d/20-snuffleupagus.ini > /dev/null <<ILOVEBASH | |
;extension=snuffleupagus.so | |
;sp.configuration_file=/etc/php/${phpv}/fpm/conf.d/snuffleupagus.rules | |
ILOVEBASH | |
# Create a new user, add to www-data group | |
useradd -m "${user}" -G www-data | |
# SSH doesn't allow password-less users by default and will report | |
# the account is locked. As I use pubkey auth I am ok with that. | |
usermod -p "*" "${user}" | |
# Secure SSH | |
tee /etc/ssh/sshd_config > /dev/null <<ILOVEBASH | |
X11Forwarding no | |
PubkeyAuthentication yes | |
PasswordAuthentication no | |
PermitRootLogin no | |
AllowUsers ${user} | |
Port 22 | |
AuthorizedKeysFile .ssh/authorized_keys | |
ILOVEBASH | |
# Add public key for unprivilaged user | |
sudo -u "${user}" bash -s <<ILOVEBASH | |
mkdir $home/.ssh | |
printf "${public_key}" | tee --append ${home}/.ssh/authorized_keys | |
chmod 700 ${home}/.ssh | |
chmod 600 ${home}/.ssh/authorized_keys | |
ILOVEBASH | |
# Modify PHP configuration | |
sed -r -i 's/^memory_limit ?=.*/memory_limit = '${php_mem_limit}'/' /etc/php/7.2/fpm/php.ini | |
sed -r -i 's/^;date.timezone ?=.*$/date.timezone = "'${php_date_timezone}'"/' /etc/php/7.2/fpm/php.ini | |
sed -r -i 's/^;opcache.save_comments ?=.*$/opcache.save_comments = 1/' /etc/php/7.2/fpm/php.ini | |
sed -r -i 's/^max_file_uploads ?=.*$/max_file_uploads = 5/' /etc/php/7.2/fpm/php.ini | |
# Configure nginx for magento | |
tee /etc/nginx/sites-available/default > /dev/null <<ILOVEBASH | |
upstream fastcgi_backend { | |
server unix:/var/run/php/php${phpv}-fpm.sock; | |
} | |
server { | |
listen 80; | |
server_name ${domain}; | |
set \$MAGE_ROOT ${webroot}; | |
set \$MAGE_DEBUG_SHOW_ARGS 1; | |
include ${webroot}/nginx.conf.sample; | |
} | |
ILOVEBASH | |
# Permissions for php-fpm and nginx logs | |
chown :www-data "/var/log/php${phpv}-fpm.log" | |
chmod 640 "/var/log/php${phpv}-fpm.log" | |
chown :www-data /var/log/nginx/access.log | |
chown :www-data /var/log/nginx/error.log | |
############################### | |
# Magento # | |
############################### | |
rm -rf "${webroot}" | |
mkdir "${webroot}" | |
# Change permissions for webroot directory | |
chown "${user}":www-data "${webroot}" | |
# Create auth.json required for magento installation | |
sudo -u "${user}" bash -c "mkdir ${home}/.composer" | |
sudo -u "${user}" bash -c "tee ${home}/.composer/auth.json" > /dev/null <<ILOVEBASH | |
{ | |
"github-oauth": { | |
"github.com": "${github_pa_token}" | |
}, | |
"http-basic": { | |
"repo.magento.com": { | |
"username": "${magento_public_key}", | |
"password": "${magento_private_key}" | |
} | |
} | |
} | |
ILOVEBASH | |
cd "${webroot}" | |
# Setup magento | |
sudo -H -u "${user}" bash -c "composer create-project --repository=https://repo.magento.com/ magento/project-community-edition ." | |
# Magento permissions | |
find var generated vendor pub/static pub/media app/etc -type f -exec chmod g+w {} + | |
find var generated vendor pub/static pub/media app/etc -type d -exec chmod g+ws {} + | |
chown -R :www-data . | |
chmod u+x bin/magento | |
# Install Magento | |
sudo -u "${user}" bash -s <<ILOVEBASH | |
bin/magento setup:install \ | |
--base-url="${protocol}://${domain}" \ | |
--db-host="${db_host}" \ | |
--db-name="${db_name}" \ | |
--db-user="${db_username}" \ | |
--db-password="${db_password}" \ | |
--admin-firstname=admin \ | |
--admin-lastname=admin \ | |
--admin-email="${webmaster_email}" \ | |
--admin-user="${webmaster_username}" \ | |
--admin-password="${webmaster_password}" \ | |
--language="${magento_language}" \ | |
--currency="${magento_currency}" \ | |
--timezone="${magento_timezone}" \ | |
--use-rewrites=1 \ | |
--cleanup-database \ | |
--backend-frontname "${magento_backend}" | |
bin/magento deploy:mode:set ${mode} | |
ln -s ${home}/.composer/auth.json ${webroot}/auth.json | |
bin/magento cron:install | |
bin/magento setup:upgrade | |
ILOVEBASH | |
# If developer mode add some sample data | |
if [[ "${mode}" == "developer" ]]; then | |
sudo -u "${user}" bash -s <<ILOVEBASH | |
bin/magento cache:disable | |
bin/magento sampledata:deploy | |
ln -s /vagrant/jr2 ${webroot}/app/design/frontend/jr2 | |
bin/magento setup:upgrade | |
ILOVEBASH | |
fi | |
# Create SSL certificate for domain | |
if [[ "${mode}" != "developer" ]]; then | |
echo "Setting up SSL certificate" | |
wget https://dl.eff.org/certbot-auto | |
mv certbot-auto /usr/local/bin/certbot-auto | |
chown root /usr/local/bin/certbot-auto | |
chmod 0755 /usr/local/bin/certbot-autos | |
/usr/local/bin/certbot-auto --non-interactive --agree-tos --email ${webmaster_email} --nginx --domains ${domain} | |
fi | |
# Reload everything | |
nginx -t | |
systemctl reload nginx | |
/etc/init.d/mysql restart | |
systemctl reload "php${phpv}-fpm" | |
systemctl reload sshd | |
# Configure firewall | |
ufw allow ssh | |
ufw allow http | |
ufw allow https | |
ufw enable | |
echo "Magento server successfully configured :)" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment