|
#!/bin/bash |
|
set -e |
|
|
|
trap ctrl_c SIGINT |
|
function ctrl_c { |
|
echo -e "\e[0m" |
|
exit 1; |
|
} |
|
|
|
function red { |
|
echo -e "\e[91m$1\e[0m" |
|
} |
|
|
|
function green { |
|
echo -e "\e[92m$1\e[0m" |
|
} |
|
|
|
function cyan { |
|
echo -e "\e[96m$1\e[0m" |
|
} |
|
|
|
function u { |
|
echo -e "\e[4;96m$1\e[0m" |
|
} |
|
|
|
OK=" [ `green ok` ] " |
|
KO="[ `red fatal` ] " |
|
NF=" [ `cyan info` ] " |
|
IN="[ `cyan input` ] " |
|
|
|
function separator { |
|
echo " [ ... ] ======================================================================" |
|
} |
|
|
|
function newline { |
|
echo " [ ... ]" |
|
} |
|
|
|
function ok { |
|
echo -e "${OK}$1" |
|
} |
|
|
|
function ko { |
|
echo -e "${KO}$1" |
|
} |
|
|
|
function info { |
|
echo -e "${NF}$1" |
|
} |
|
|
|
# Usage: ask <prompt> <varname> |
|
function ask { |
|
read -re -p "$(echo -e "${IN}$1 \e[0;96m")" -i "${!2}" $2 && printf "\e[0m" |
|
} |
|
|
|
# Persists/update environment variable to /home/ec2-user/.bash_profile |
|
# Usage: saveenv <varname> |
|
function saveenv { |
|
# Escape & and \1 through \9, which have special meaning in the sed replacement string |
|
SED_REPLACEMENT=`echo ${!1} | sed 's/[&\]/\\\\&/g'` |
|
grep -q "^export $1=" /home/ec2-user/.bash_profile \ |
|
&& sed -i "s/^export $1=.*/export $1=\'$SED_REPLACEMENT\'/g" /home/ec2-user/.bash_profile \ |
|
|| echo "export $1='${!1}'" >> /home/ec2-user/.bash_profile |
|
} |
|
|
|
function okdone { |
|
echo -e "\e[1A\r${OK}\r\e[74C`green ' done'`." |
|
} |
|
|
|
function koerror { |
|
echo -e "\e[1A\r${KO}\r\e[73C`red ' error'`." |
|
} |
|
|
|
trap 'catch $? $LINENO' ERR |
|
catch() { |
|
ko "Error $1 occurred on line $2" |
|
} |
|
|
|
################################################################################ |
|
# # |
|
# CONFIGURATION # |
|
# # |
|
################################################################################ |
|
|
|
# Ensure current user belongs to 'docker' group |
|
if ! id --groups --name --zero | grep --null-data --line-regexp --quiet docker |
|
then |
|
sudo groupadd --force docker |
|
sudo usermod -a -G docker ec2-user |
|
info "The UserData script was not properly set up for this machine." |
|
info "Please run this script once more." |
|
read -re -n 1 -p "$(echo -e "${IN}Press any key to continue...")" |
|
relog |
|
fi |
|
|
|
ask "WEBDEV domain name?" WD_HOST_NAME |
|
if [[ ! $WD_HOST_NAME =~ ^([a-z0-9-]+\.)+[a-z]{2,6}$ ]]; then |
|
koerror && ko 'Invalid domain name. Have you included the TLD?' |
|
exit 1 |
|
else |
|
okdone && saveenv WD_HOST_NAME; |
|
fi |
|
|
|
ask "WEBDEV site name?" WD_SITE_NAME |
|
if [[ ! $WD_SITE_NAME =~ ^[a-zA-Z0-9]+$ ]]; then |
|
koerror && ko 'Invalid WEBDEV site name.' |
|
exit 1 |
|
else |
|
okdone && saveenv WD_SITE_NAME; |
|
fi |
|
|
|
ask "WordPress domain name?" WP_HOST_NAME |
|
if [[ ! $WP_HOST_NAME =~ ^([a-z0-9-]+\.)+[a-z]{2,6}$ ]]; then |
|
koerror && ko 'Invalid domain name. Have you included the TLD?' |
|
exit 1 |
|
else |
|
okdone && saveenv WP_HOST_NAME; |
|
fi |
|
|
|
ask "WordPress database name?" WP_DB_NAME |
|
if [[ -z $WP_DB_NAME ]]; then |
|
koerror && ko 'Database name must not be empty.' |
|
exit 1 |
|
else |
|
okdone && saveenv WP_DB_NAME; |
|
fi |
|
|
|
ask "Database server root password?" DB_PWD |
|
if [[ -z $DB_PWD ]]; then |
|
koerror && ko 'Password must not be empty.' |
|
exit 1 |
|
else |
|
okdone && saveenv DB_PWD $DB_PWD; |
|
fi |
|
|
|
ask "Admin e-mail for SSL notices?" ADMIN_EMAIL |
|
if [[ -z $ADMIN_EMAIL ]]; then |
|
koerror && ko "The admin's e-mail must be communicated." |
|
exit 1 |
|
else |
|
okdone && saveenv ADMIN_EMAIL; |
|
fi |
|
|
|
ask "Access token for pCloud storage?" PCLOUD_TOKEN |
|
if [[ -z $PCLOUD_TOKEN ]]; then |
|
koerror && ko "The access token for the pCloud storage is required." |
|
exit 1 |
|
else |
|
okdone && saveenv PCLOUD_TOKEN; |
|
fi |
|
|
|
trap relog EXIT |
|
# Reload .bash_profile w/ persisted environment variables |
|
function relog { |
|
exec sudo su -l $USER |
|
} |
|
|
|
################################################################################ |
|
# # |
|
# SERVICES INSTALL # |
|
# # |
|
################################################################################ |
|
|
|
separator |
|
info 'Preparing installation...' |
|
sudo yum update -y -q > /dev/null 2>&1 |
|
okdone |
|
|
|
# Docker setup |
|
info 'Setting up Docker...' |
|
sudo amazon-linux-extras install docker -y -q > /dev/null 2>&1 |
|
sudo systemctl start docker.service |
|
sudo systemctl enable docker |
|
okdone |
|
|
|
# docker-compose setup |
|
info 'Installing docker-compose...' |
|
sudo curl -Ls "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose |
|
sudo chmod +x /usr/local/bin/docker-compose |
|
tee /home/ec2-user/zzz-custom.php.ini > /dev/null <<EOF |
|
post_max_size = 256M |
|
upload_max_filesize = 256M |
|
EOF |
|
tee /home/ec2-user/docker-compose.yml > /dev/null <<EOF |
|
version: "3" |
|
|
|
services: |
|
db: |
|
image: mariadb:latest |
|
container_name: mariadb |
|
restart: always |
|
volumes: |
|
- db-data:/var/lib/mysql |
|
ports: |
|
- 3306:3306 |
|
environment: |
|
# Escape dollar-signs in password w/ "$$" |
|
# See https://docs.docker.com/compose/compose-file/compose-file-v3/#variable-substitution |
|
MYSQL_ROOT_PASSWORD: `sed -re 's/\\$/$$/g' <<< ${DB_PWD}` # |
|
|
|
phpmyadmin: |
|
image: phpmyadmin/phpmyadmin:latest |
|
container_name: phpmyadmin |
|
restart: always |
|
ports: |
|
- 8000:80 |
|
environment: |
|
PMA_ABSOLUTE_URI: https://${WD_HOST_NAME}/phpmyadmin/ |
|
UPLOAD_LIMIT: 256M |
|
depends_on: |
|
- db |
|
|
|
webdev-server: |
|
image: ccjmne/webdev-mariadb |
|
container_name: webdev-server |
|
restart: always |
|
volumes: |
|
- webdev-data:/var/lib/WEBDEV/26.0/ |
|
ports: |
|
- 8001:80 |
|
depends_on: |
|
- db |
|
|
|
wordpress: |
|
image: wordpress:latest |
|
container_name: wordpress |
|
restart: always |
|
volumes: |
|
- wordpress-data:/var/www/html |
|
- /home/ec2-user/zzz-custom.php.ini:/usr/local/etc/php/conf.d/zzz-custom.php.ini |
|
- /home/ec2-user/pcloud:/mnt/pcloud |
|
ports: |
|
- 8002:80 |
|
environment: |
|
# Un-comment the next line to enable debugging |
|
# WORDPRESS_DEBUG: 1 |
|
WORDPRESS_DB_HOST: db |
|
WORDPRESS_DB_USER: root |
|
WORDPRESS_DB_NAME: ${WP_DB_NAME} |
|
# Escape dollar-signs in password w/ "$$" |
|
# See https://docs.docker.com/compose/compose-file/compose-file-v3/#variable-substitution |
|
WORDPRESS_DB_PASSWORD: `sed -re 's/\\$/$$/g' <<< ${DB_PWD}` |
|
depends_on: |
|
- db |
|
|
|
volumes: |
|
db-data: |
|
webdev-data: |
|
wordpress-data: |
|
EOF |
|
okdone |
|
|
|
# NGINX setup |
|
# TODO: Migrate to a docker image for NGINX w/ certbot |
|
# See https://hub.docker.com/r/staticfloat/nginx-certbot/ |
|
info 'Installing NGINX...' |
|
sudo amazon-linux-extras install nginx1 -y -q > /dev/null 2>&1 |
|
sudo tee /etc/nginx/conf.d/default.conf > /dev/null <<EOF |
|
server { |
|
# WEBDEV server |
|
server_name ${WD_HOST_NAME}; |
|
|
|
client_max_body_size 0; |
|
gzip on; |
|
gzip_types text/css text/plain application/javascript application/json image/png; |
|
|
|
include /etc/nginx/proxy_params.conf; |
|
|
|
location /wd-admin { |
|
return 301 https://\$host/WDAdminWeb; |
|
} |
|
|
|
location = / { |
|
return 301 https://\$host/${WD_SITE_NAME}; |
|
} |
|
|
|
location /phpmyadmin/ { |
|
proxy_pass http://127.0.0.1:8000/; |
|
} |
|
|
|
location / { |
|
proxy_pass http://127.0.0.1:8001; |
|
} |
|
} |
|
|
|
server { |
|
# WordPress server |
|
server_name ${WP_HOST_NAME}; |
|
server_name www.${WP_HOST_NAME}; |
|
|
|
client_max_body_size 0; |
|
gzip on; |
|
gzip_types text/css text/plain application/javascript application/json image/png; |
|
|
|
include /etc/nginx/proxy_params.conf; |
|
|
|
location /phpmyadmin/ { |
|
proxy_pass http://127.0.0.1:8000/; |
|
} |
|
|
|
location / { |
|
proxy_pass http://127.0.0.1:8002/; |
|
} |
|
} |
|
EOF |
|
sudo tee /etc/nginx/proxy_params.conf > /dev/null <<EOF |
|
proxy_set_header Host \$host; |
|
proxy_set_header X-Real-IP \$remote_addr; |
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; |
|
proxy_set_header X-Forwarded-Proto \$scheme; |
|
proxy_set_header X-Forwarded-Host \$host; |
|
proxy_set_header X-Forwarded-Port \$server_port; |
|
EOF |
|
sudo systemctl enable nginx > /dev/null 2>&1 |
|
sudo systemctl restart nginx |
|
okdone |
|
|
|
# Certbot setup |
|
info 'Installing Certbot...' |
|
# Install extra EPEL repositories for certbot |
|
sudo amazon-linux-extras install epel -y -q > /dev/null 2>&1 |
|
sudo yum install epel-release -y -q > /dev/null 2>&1 |
|
sudo yum install certbot python2-certbot-nginx -y -q > /dev/null 2>&1 |
|
okdone |
|
|
|
info 'Requesting TLS certificates...' |
|
sudo certbot --nginx --non-interactive --agree-tos --email $ADMIN_EMAIL --domains $WP_HOST_NAME --domains www.$WP_HOST_NAME --domains $WD_HOST_NAME --expand --quiet |
|
sudo systemctl restart nginx |
|
okdone |
|
|
|
info 'Setting up auto-renewal of TLS certificates...' |
|
# From https://certbot.eff.org/docs/using.html#setting-up-automated-renewal |
|
SLEEPTIME=$(awk 'BEGIN{srand(); print int(rand()*(3600+1))}'); echo "0 0,12 * * * root sleep $SLEEPTIME && certbot renew -q" | sudo tee -a /etc/crontab > /dev/null |
|
okdone |
|
|
|
info 'Setting up 1GiB of swap...' |
|
if ! free | awk '/^Swap:/ {exit !$2}'; then |
|
# create swap |
|
# https://www.digitalocean.com/community/tutorials/how-to-add-swap-space-on-ubuntu-16-04 |
|
sudo fallocate -l 1G /swapfile && sudo chmod 600 /swapfile |
|
sudo mkswap /swapfile && sudo swapon /swapfile |
|
# make the swap file permanent |
|
sudo cp /etc/fstab /etc/fstab.bak |
|
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab |
|
fi |
|
okdone |
|
|
|
# rclone setup |
|
info 'Setting up rclone...' |
|
sudo yum install rclone -y -q |
|
sudo tee /etc/fuse.conf > /dev/null <<EOF |
|
user_allow_other |
|
EOF |
|
|
|
sudo tee /home/ec2-user/.config/rclone/rclone.conf > /dev/null <<EOF |
|
[pcloud] |
|
type = pcloud |
|
hostname = eapi.pcloud.com |
|
token = {"access_token":"${PCLOUD_TOKEN}","token_type":"bearer","expiry":"0001-01-01T00:00:00Z"} |
|
EOF |
|
|
|
# create systemd service to mount on reboot |
|
# see https://github.com/rclone/rclone/wiki/Systemd-rclone-mount#systemd |
|
sudo tee /etc/systemd/system/rclone-pcloud.service > /dev/null <<EOF |
|
[Unit] |
|
Description=Mount pCloud storage with rclone |
|
After=network-online.target |
|
|
|
[Service] |
|
Type=notify |
|
KillMode=none |
|
|
|
ExecStart=/usr/bin/rclone mount pcloud: /home/ec2-user/pcloud \ |
|
--allow-other \ |
|
--vfs-cache-mode full |
|
|
|
ExecStop=/bin/fusermount -uz /home/ec2-user/pcloud |
|
|
|
User=ec2-user |
|
|
|
Restart=on-failure |
|
RestartSec=5 |
|
|
|
[Install] |
|
WantedBy=default.target |
|
EOF |
|
okdone |
|
|
|
info 'Mounting pCloud disk...' |
|
mkdir --parents /home/ec2-user/pcloud |
|
sudo systemctl start rclone-pcloud |
|
sudo systemctl enable rclone-pcloud |
|
okdone |
|
|
|
info 'Creating all services...' |
|
# only time we use docker as superuser, before ec2-user relogs |
|
docker-compose up --detach > /dev/null 2>&1 |
|
sudo tee /home/ec2-user/get-password.sh > /dev/null <<EOF |
|
#!/bin/bash |
|
|
|
docker logs webdev-server 2>&1 | sed -n 3p |
|
EOF |
|
sudo chmod +x /home/ec2-user/get-password.sh |
|
okdone |
|
|
|
################################################################################ |
|
# # |
|
# POST-INSTALL # |
|
# # |
|
################################################################################ |
|
|
|
sudo tee /etc/update-motd.d/30-banner > /dev/null <<EOF |
|
cat <<EOL |
|
$( |
|
separator |
|
# Credit: https://manytools.org/hacker-tools/ascii-banner/ |
|
# Font: Georgia11 |
|
echo -e "" |
|
echo -e " ,, ,, " |
|
echo -e " db \\\`7MM \\\`7MM " |
|
echo -e " ;MM: MM MM " |
|
echo -e " ,V^MM. \\\`7MMpMMMb.pMMMb. MMpMMMb.\\\`7M' \\\`MF' ,M\"\"bMM \\\`7Mb,od8 ,pW\"Wq. " |
|
echo -e " ,M \\\`MM MM MM MM MM MM VA ,V ,AP MM MM' \"'6W' \\\`Wb" |
|
echo -e " AbmmmqMA MM MM MM MM MM VA ,V 8MI MM MM 8M M8" |
|
echo -e " A' VML MM MM MM MM MM VVV \\\`Mb MM MM YA. ,A9" |
|
echo -e ".AMA. .AMMA..JMML JMML JMML..JMML JMML. ,V \\\`Wbmd\"MML..JMML. \\\`Ybmd9' " |
|
echo -e " ,V " |
|
echo -e " OOb\" " |
|
echo -e "" |
|
separator |
|
newline |
|
info "Site `cyan $WD_SITE_NAME` published at `u "https://${WD_HOST_NAME}"`" |
|
info "`cyan WordPress` site published at `u "https://${WP_HOST_NAME}"`" |
|
info "Start/restart services with: `cyan 'docker-compose restart'`" |
|
newline |
|
separator |
|
newline |
|
info "WEBDEV remote admin console" |
|
info " - Address : `u "https://${WD_HOST_NAME}/wd-admin"`" |
|
info " - Username : `cyan webdevuser`" |
|
info " - Password : \e[96m`/home/ec2-user/get-password.sh`\e[0m" |
|
newline |
|
info "WordPress remote admin console" |
|
info " - Address : `u "https://${WP_HOST_NAME}/wp-admin"`" |
|
info " - Database : `cyan $WP_DB_NAME`" |
|
info " - Username : `cyan ?`" |
|
info " - Password : `cyan ?`" |
|
newline |
|
info "phpMyAdmin console" |
|
info " - Address : `u "https://${WD_HOST_NAME}/phpmyadmin"`" |
|
info " - Username : `cyan root`" |
|
info " - Password : `cyan $(sed -re 's/\\$/\\\\$/g' <<< $DB_PWD)`" |
|
newline |
|
info "TLS Certificates" |
|
info " - Hostname #1 : `cyan $WD_HOST_NAME`" |
|
info " - Hostname #2 : `cyan $WP_HOST_NAME`" |
|
info " - Admin e-mail : `cyan $ADMIN_EMAIL`" |
|
) |
|
EOL |
|
EOF |
|
|
|
info 'Finishing up...' |
|
sudo update-motd |
|
okdone && ok 'All done!' |
|
cat /etc/motd |
|
|
|
newline |
|
separator |
|
newline |
|
ask 'The system will restart now. Press any key to continue...' |
|
okdone && sudo shutdown -r now |
|
|
|
exit 0 |