Skip to content

Instantly share code, notes, and snippets.

@nginx-gists
Last active May 29, 2025 05:42
Show Gist options
  • Save nginx-gists/bdc7da70b124c4f3e472970c7826cccc to your computer and use it in GitHub Desktop.
Save nginx-gists/bdc7da70b124c4f3e472970c7826cccc to your computer and use it in GitHub Desktop.

Revisions

  1. nginx-gists revised this gist Sep 28, 2021. 1 changed file with 19 additions and 5 deletions.
    24 changes: 19 additions & 5 deletions ubuntu_install.sh
    Original file line number Diff line number Diff line change
    @@ -588,8 +588,22 @@ echo "▶ Starting NGINX to use new configuration and enabling NGINX Unit servic
    service nginx start
    systemctl enable unit.service

    # Write crontab for periodic Let's Encrypt cert renewal
    if [ "$(crontab -l | grep -m1 'certbot renew')" == "" ]; then
    echo "▶ Adding certbot to crontab for automatic Let's Encrypt renewal"
    (crontab -l 2>/dev/null; echo "24 3 * * * certbot renew --nginx --post-hook 'service nginx reload'") | crontab -
    fi
    # Periodic Let's Encrypt cert renewal
    if [ ! -f "/etc/systemd/system/certbot-renewal.timer" ]; then
    echo "▶ Adding systemd Certbot renewal service timer"
    cat > /etc/systemd/system/certbot-renewal.timer << EOM
    [Unit]
    Description=Timer for Certbot Renewal
    [Timer]
    OnBootSec=300
    OnUnitActiveSec=1w
    [Install]
    WantedBy=multi-user.target
    EOM
    fi

    echo "▶ Enabling Let's Encrypt auto-renewal"
    systemctl start certbot-renewal.timer
    systemctl enable certbot-renewal.timer
  2. nginx-gists revised this gist Nov 23, 2020. 1 changed file with 3 additions and 2 deletions.
    5 changes: 3 additions & 2 deletions ubuntu_install.sh
    Original file line number Diff line number Diff line change
    @@ -223,7 +223,7 @@ else
    NAMESPACES='"namespaces": {}'
    fi

    PHP_MEM_LIMIT="$(grep 'memory_limit' /etc/php/7.4/embed/php.ini | tr -d ' ' | cut -f2 -d= | numfmt --from=iec)"
    PHP_MEM_LIMIT="$(grep 'memory_limit' /etc/php/${PHP_MAJOR_MINOR_VERSION}/embed/php.ini | tr -d ' ' | cut -f2 -d= | numfmt --from=iec)"
    AVAIL_MEM="$(grep MemAvailable /proc/meminfo | tr -d ' kB' | cut -f2 -d: | numfmt --from-unit=K)"
    MAX_PHP_PROCESSES="$(echo "${AVAIL_MEM}/${PHP_MEM_LIMIT}+5" | bc)"
    echo "▶ Calculated the maximum number of PHP processes as ${MAX_PHP_PROCESSES}. You may want to tune this value due to variations in your configuration. It is not unusual to see values between 10-100 in production configurations."
    @@ -584,8 +584,9 @@ if [ ! -f "${CERT_DIR}/fullchain.pem" ]; then
    -d "${TLS_HOSTNAME}"
    fi

    echo "▶ Starting NGINX in order to use new configuration"
    echo "▶ Starting NGINX to use new configuration and enabling NGINX Unit service"
    service nginx start
    systemctl enable unit.service

    # Write crontab for periodic Let's Encrypt cert renewal
    if [ "$(crontab -l | grep -m1 'certbot renew')" == "" ]; then
  3. nginx-gists revised this gist Oct 19, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion ubuntu_install.sh
    Original file line number Diff line number Diff line change
    @@ -590,5 +590,5 @@ service nginx start
    # Write crontab for periodic Let's Encrypt cert renewal
    if [ "$(crontab -l | grep -m1 'certbot renew')" == "" ]; then
    echo "▶ Adding certbot to crontab for automatic Let's Encrypt renewal"
    (crontab -l 2>/dev/null; echo "24 3 * * * certbot renew --dry-run --nginx --post-hook 'service nginx reload'") | crontab -
    (crontab -l 2>/dev/null; echo "24 3 * * * certbot renew --nginx --post-hook 'service nginx reload'") | crontab -
    fi
  4. nginx-gists revised this gist Oct 5, 2020. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions ubuntu_install.sh
    Original file line number Diff line number Diff line change
    @@ -109,7 +109,7 @@ apt-get -qq update
    # The php-common package contains the following extensions: exif

    # Install PHP with dependencies and NGINX Unit
    echo "▶ Installing PHP, Unit, NGINX, Certbot, and MariaDB"
    echo "▶ Installing PHP, NGINX Unit, NGINX, Certbot, and MariaDB"
    apt-get -qq install -y --no-install-recommends \
    certbot \
    python3-certbot-nginx \
    @@ -146,7 +146,7 @@ error_log=/dev/stderr
    EOM
    fi

    # Restart Unit because we have reconfigured PHP
    # Restart NGINX Unit because we have reconfigured PHP
    echo "▶ Restarting NGINX Unit"
    service unit restart

  5. nginx-gists revised this gist Oct 5, 2020. 1 changed file with 5 additions and 5 deletions.
    10 changes: 5 additions & 5 deletions ubuntu_install.sh
    Original file line number Diff line number Diff line change
    @@ -85,7 +85,7 @@ apt-get -qq install -y \

    # Install the NGINX Unit repository
    if [ ! -f /etc/apt/sources.list.d/unit.list ]; then
    echo "▶ Installing NGINX UNIT repository"
    echo "▶ Installing NGINX Unit repository"
    curl -fsSL https://nginx.org/keys/nginx_signing.key | apt-key add -
    echo "deb https://packages.nginx.org/unit/ubuntu/ $(lsb_release -cs) unit" > /etc/apt/sources.list.d/unit.list
    fi
    @@ -134,20 +134,20 @@ apt-get -qq install -y --no-install-recommends \
    PHP_MAJOR_MINOR_VERSION="$(php -v | head -n1 | cut -d' ' -f2 | cut -d'.' -f1,2)"

    if [ ! -f "/etc/php/${PHP_MAJOR_MINOR_VERSION}/embed/conf.d/30-wordpress-overrides.ini" ]; then
    echo "▶ Configuring PHP for use with UNIT and WordPress"
    echo "▶ Configuring PHP for use with NGINX Unit and WordPress"
    # Add PHP configuration overrides
    cat > "/etc/php/${PHP_MAJOR_MINOR_VERSION}/embed/conf.d/30-wordpress-overrides.ini" << EOM
    ; Set a larger maximum upload size so that WordPress can handle
    ; bigger media files.
    upload_max_filesize=${UPLOAD_MAX_FILESIZE}
    post_max_size=${UPLOAD_MAX_FILESIZE}
    ; Write error log to STDERR so that error messages show up in UNIT
    ; Write error log to STDERR so that error messages show up in the NGINX Unit log
    error_log=/dev/stderr
    EOM
    fi

    # Restart Unit because we have reconfigured PHP
    echo "▶ Restarting NGINX UNIT"
    echo "▶ Restarting NGINX Unit"
    service unit restart

    # Set up the WordPress database
    @@ -228,7 +228,7 @@ AVAIL_MEM="$(grep MemAvailable /proc/meminfo | tr -d ' kB' | cut -f2 -d: | numfm
    MAX_PHP_PROCESSES="$(echo "${AVAIL_MEM}/${PHP_MEM_LIMIT}+5" | bc)"
    echo "▶ Calculated the maximum number of PHP processes as ${MAX_PHP_PROCESSES}. You may want to tune this value due to variations in your configuration. It is not unusual to see values between 10-100 in production configurations."

    echo "▶ Configuring NGINX UNIT to use PHP and WordPress"
    echo "▶ Configuring NGINX Unit to use PHP and WordPress"
    cat > /tmp/wordpress.json << EOM
    {
    "settings": {
  6. nginx-gists revised this gist Oct 5, 2020. No changes.
  7. nginx-gists revised this gist Oct 3, 2020. 1 changed file with 28 additions and 25 deletions.
    53 changes: 28 additions & 25 deletions ubuntu_install.sh
    Original file line number Diff line number Diff line change
    @@ -43,7 +43,7 @@ fi

    # Best practice settings for running bash scripts:

    # Exit the script and an error is encountered
    # Exit the script when an error is encountered
    set -o errexit
    # Exit the script when a pipe operation fails
    set -o pipefail
    @@ -83,14 +83,14 @@ apt-get -qq install -y \
    gnupg2 \
    lsb-release

    # Install NGINX UNIT repository
    # Install the NGINX Unit repository
    if [ ! -f /etc/apt/sources.list.d/unit.list ]; then
    echo "▶ Installing NGINX UNIT repository"
    curl -fsSL https://nginx.org/keys/nginx_signing.key | apt-key add -
    echo "deb https://packages.nginx.org/unit/ubuntu/ $(lsb_release -cs) unit" > /etc/apt/sources.list.d/unit.list
    fi

    # Install NGINX repository
    # Install the NGINX repository
    if [ ! -f /etc/apt/sources.list.d/nginx.list ]; then
    echo "▶ Installing NGINX repository"
    curl -fsSL https://nginx.org/keys/nginx_signing.key | apt-key add -
    @@ -102,14 +102,14 @@ apt-get -qq update

    # WordPress module recommendation: https://make.wordpress.org/hosting/handbook/handbook/server-environment/#php-extensions

    # Ubuntu bundles the following php extensions with its package distribution:
    # Ubuntu bundles the following PHP extensions with its package distribution:
    # date filter hash libxml openssl pcntl pcre Reflection session sodium SPL
    # standard zlib

    # The php-common package contains the following extensions: exif

    # Install php with dependencies and NGINX UNIT
    echo "▶ Installing PHP, UNIT, NGINX CertBot and MariaDB"
    # Install PHP with dependencies and NGINX Unit
    echo "▶ Installing PHP, Unit, NGINX, Certbot, and MariaDB"
    apt-get -qq install -y --no-install-recommends \
    certbot \
    python3-certbot-nginx \
    @@ -146,11 +146,11 @@ error_log=/dev/stderr
    EOM
    fi

    # Restart UNIT because we have reconfigured PHP
    # Restart Unit because we have reconfigured PHP
    echo "▶ Restarting NGINX UNIT"
    service unit restart

    # Setup WordPress database
    # Set up the WordPress database
    echo "▶ Configuring MariaDB for WordPress"
    mysqladmin create wordpress || echo "Ignoring above error because database may already exist"
    mysql -e "GRANT ALL PRIVILEGES ON wordpress.* TO \"wordpress\"@\"localhost\" IDENTIFIED BY \"$WORDPRESS_DB_PASSWORD\"; FLUSH PRIVILEGES;"
    @@ -174,9 +174,9 @@ if [ ! -d /var/www/wordpress ]; then

    WP_CONFIG_CREATE_CMD="wp --path=/var/www/wordpress config create --extra-php --dbname=wordpress --dbuser=wordpress --dbhost=\"localhost:/var/run/mysqld/mysqld.sock\" --dbpass=\"${WORDPRESS_DB_PASSWORD}\""

    # This snippet is injected into the wp-config.php file when it is created.
    # It informs WordPress that we are behind a reverse proxy and as such
    # allow it to generate links using https.
    # This snippet is injected into the wp-config.php file when it is created;
    # it informs WordPress that we are behind a reverse proxy and as such
    # allows it to generate links using HTTPS
    cat > /tmp/wp_forwarded_for.php << 'EOM'
    /* Turn HTTPS 'on' if HTTP_X_FORWARDED_PROTO matches 'https' */
    if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false) {
    @@ -203,7 +203,7 @@ EOM
    # Remove sample file because it is cruft and could be a security problem
    rm /var/www/wordpress/wp-config-sample.php

    # Ensure that Wordpres permissions are correct
    # Ensure that WordPress permissions are correct
    find /var/www/wordpress -type d -exec chmod g+s {} \;
    chmod g+w /var/www/wordpress/wp-content
    chmod -R g+w /var/www/wordpress/wp-content/themes
    @@ -325,7 +325,7 @@ http {
    client_max_body_size ${UPLOAD_MAX_FILESIZE};
    keepalive_timeout 65;
    # GZIP settings
    # gzip settings
    include ${NGINX_CONF_DIR}/gzip_compression.conf;
    # Cache settings
    @@ -425,7 +425,7 @@ server {
    listen 80;
    listen [::]:80;
    # ACME-challenge used by CertBot for Let's Encrypt
    # ACME-challenge used by Certbot for Let's Encrypt
    location ^~ /.well-known/acme-challenge/ {
    root /var/www/certbot;
    }
    @@ -449,11 +449,11 @@ server {
    include ${NGINX_CONF_DIR}/options-ssl-nginx.conf;
    ssl_dhparam ${NGINX_CONF_DIR}/ssl-dhparams.pem;
    # OCSP Stapling
    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    # Proxy Caching
    # Proxy caching
    proxy_cache wp_cache;
    proxy_cache_valid 200 302 1h;
    proxy_cache_valid 404 1m;
    @@ -472,21 +472,24 @@ server {
    log_not_found off;
    access_log off;
    }
    # Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
    # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
    # Deny all attempts to access hidden files such as .htaccess, .htpasswd,
    # .DS_Store (Mac)
    # Keep logging the requests to parse later (or to pass to firewall utilities
    # such as fail2ban)
    location ~ /\. {
    deny all;
    }
    # Deny access to any files with a .php extension in the uploads directory
    # Works in sub-directory installs and also in multisite network
    # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
    # Deny access to any files with a .php extension in the uploads directory;
    # works in subdirectory installs and also in multi-site network.
    # Keep logging the requests to parse later (or to pass to firewall utilities
    # such as fail2ban).
    location ~* /(?:uploads|files)/.*\.php\$ {
    deny all;
    }
    # WordPress: deny wp-content, wp-includes php files
    # WordPress: deny access to wp-content, wp-includes PHP files
    location ~* ^/(?:wp-content|wp-includes)/.*\.php\$ {
    deny all;
    }
    @@ -496,7 +499,7 @@ server {
    deny all;
    }
    # assets, media
    # Do not log access for static assets, media
    location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ {
    access_log off;
    }
    @@ -560,7 +563,7 @@ if [ ! -f ${NGINX_CONF_DIR}/ssl-dhparams.pem ]; then
    || echo "Couldn't download latest ssl-dhparams.pem"
    fi

    # If tls_certs_init.sh hasn't been run before, let's remove the self-signed certs
    # If tls_certs_init.sh hasn't been run before, remove the self-signed certs
    if [ ! -d "/etc/letsencrypt/accounts" ]; then
    echo "▶ Removing self-signed certificates"
    rm -rf "${CERT_DIR}"
  8. nginx-gists revised this gist Oct 3, 2020. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions ubuntu_install.sh
    Original file line number Diff line number Diff line change
    @@ -302,8 +302,8 @@ mkdir -p /var/cache/nginx/proxy

    echo "▶ Configuring NGINX"
    cat > ${NGINX_CONF_DIR}/nginx.conf << EOM
    user nginx;
    worker_processes auto;
    user nginx;
    worker_processes auto;
    error_log /var/log/nginx/error.log warn;
    pid /var/run/nginx.pid;
  9. nginx-gists revised this gist Oct 2, 2020. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion ubuntu_install.sh
    Original file line number Diff line number Diff line change
    @@ -457,7 +457,6 @@ server {
    proxy_cache wp_cache;
    proxy_cache_valid 200 302 1h;
    proxy_cache_valid 404 1m;
    proxy_cache_revalidate on;
    proxy_cache_background_update on;
    proxy_cache_lock on;
  10. nginx-gists revised this gist Oct 2, 2020. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion ubuntu_install.sh
    Original file line number Diff line number Diff line change
    @@ -308,7 +308,6 @@ worker_processes auto;
    error_log /var/log/nginx/error.log warn;
    pid /var/run/nginx.pid;
    events {
    worker_connections 1024;
    }
  11. nginx-gists revised this gist Oct 2, 2020. 1 changed file with 18 additions and 10 deletions.
    28 changes: 18 additions & 10 deletions ubuntu_install.sh
    Original file line number Diff line number Diff line change
    @@ -150,7 +150,7 @@ fi
    echo "▶ Restarting NGINX UNIT"
    service unit restart

    # Set up WordPress database
    # Setup WordPress database
    echo "▶ Configuring MariaDB for WordPress"
    mysqladmin create wordpress || echo "Ignoring above error because database may already exist"
    mysql -e "GRANT ALL PRIVILEGES ON wordpress.* TO \"wordpress\"@\"localhost\" IDENTIFIED BY \"$WORDPRESS_DB_PASSWORD\"; FLUSH PRIVILEGES;"
    @@ -203,7 +203,7 @@ EOM
    # Remove sample file because it is cruft and could be a security problem
    rm /var/www/wordpress/wp-config-sample.php

    # Ensure that WordPress permissions are correct
    # Ensure that Wordpres permissions are correct
    find /var/www/wordpress -type d -exec chmod g+s {} \;
    chmod g+w /var/www/wordpress/wp-content
    chmod -R g+w /var/www/wordpress/wp-content/themes
    @@ -223,6 +223,11 @@ else
    NAMESPACES='"namespaces": {}'
    fi

    PHP_MEM_LIMIT="$(grep 'memory_limit' /etc/php/7.4/embed/php.ini | tr -d ' ' | cut -f2 -d= | numfmt --from=iec)"
    AVAIL_MEM="$(grep MemAvailable /proc/meminfo | tr -d ' kB' | cut -f2 -d: | numfmt --from-unit=K)"
    MAX_PHP_PROCESSES="$(echo "${AVAIL_MEM}/${PHP_MEM_LIMIT}+5" | bc)"
    echo "▶ Calculated the maximum number of PHP processes as ${MAX_PHP_PROCESSES}. You may want to tune this value due to variations in your configuration. It is not unusual to see values between 10-100 in production configurations."

    echo "▶ Configuring NGINX UNIT to use PHP and WordPress"
    cat > /tmp/wordpress.json << EOM
    {
    @@ -270,7 +275,7 @@ cat > /tmp/wordpress.json << EOM
    "user": "www-data",
    "group": "www-data",
    "processes": {
    "max": $(echo `nproc`+1 | bc),
    "max": ${MAX_PHP_PROCESSES},
    "spare": 1
    },
    "isolation": {
    @@ -303,6 +308,7 @@ worker_processes auto;
    error_log /var/log/nginx/error.log warn;
    pid /var/run/nginx.pid;
    events {
    worker_connections 1024;
    }
    @@ -412,13 +418,15 @@ EOM
    cat > ${NGINX_CONF_DIR}/conf.d/default.conf << EOM
    upstream unit_php_upstream {
    server 127.0.0.1:8080;
    keepalive 32;
    }
    server {
    listen 80;
    listen [::]:80;
    # ACME-challenge used by Certbot for Let's Encrypt
    # ACME-challenge used by CertBot for Let's Encrypt
    location ^~ /.well-known/acme-challenge/ {
    root /var/www/certbot;
    }
    @@ -442,11 +450,11 @@ server {
    include ${NGINX_CONF_DIR}/options-ssl-nginx.conf;
    ssl_dhparam ${NGINX_CONF_DIR}/ssl-dhparams.pem;
    # OCSP stapling
    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    # Proxy caching
    # Proxy Caching
    proxy_cache wp_cache;
    proxy_cache_valid 200 302 1h;
    proxy_cache_valid 404 1m;
    @@ -468,14 +476,14 @@ server {
    }
    # Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
    # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban).
    # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
    location ~ /\. {
    deny all;
    }
    # Deny access to any files with a .php extension in the uploads directory.
    # Works in sub-directory installs and also in multisite network.
    # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban).
    # Deny access to any files with a .php extension in the uploads directory
    # Works in sub-directory installs and also in multisite network
    # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
    location ~* /(?:uploads|files)/.*\.php\$ {
    deny all;
    }
  12. nginx-gists revised this gist Sep 24, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion ubuntu_install.sh
    Original file line number Diff line number Diff line change
    @@ -150,7 +150,7 @@ fi
    echo "▶ Restarting NGINX UNIT"
    service unit restart

    # Setup WordPress database
    # Set up WordPress database
    echo "▶ Configuring MariaDB for WordPress"
    mysqladmin create wordpress || echo "Ignoring above error because database may already exist"
    mysql -e "GRANT ALL PRIVILEGES ON wordpress.* TO \"wordpress\"@\"localhost\" IDENTIFIED BY \"$WORDPRESS_DB_PASSWORD\"; FLUSH PRIVILEGES;"
  13. nginx-gists revised this gist Sep 23, 2020. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion ubuntu_install.sh
    Original file line number Diff line number Diff line change
    @@ -303,7 +303,6 @@ worker_processes auto;
    error_log /var/log/nginx/error.log warn;
    pid /var/run/nginx.pid;
    events {
    worker_connections 1024;
    }
  14. nginx-gists revised this gist Sep 23, 2020. No changes.
  15. nginx-gists revised this gist Sep 23, 2020. 1 changed file with 7 additions and 7 deletions.
    14 changes: 7 additions & 7 deletions ubuntu_install.sh
    Original file line number Diff line number Diff line change
    @@ -419,7 +419,7 @@ server {
    listen 80;
    listen [::]:80;
    # ACME-challenge used by CertBot for Let's Encrypt
    # ACME-challenge used by Certbot for Let's Encrypt
    location ^~ /.well-known/acme-challenge/ {
    root /var/www/certbot;
    }
    @@ -443,11 +443,11 @@ server {
    include ${NGINX_CONF_DIR}/options-ssl-nginx.conf;
    ssl_dhparam ${NGINX_CONF_DIR}/ssl-dhparams.pem;
    # OCSP Stapling
    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    # Proxy Caching
    # Proxy caching
    proxy_cache wp_cache;
    proxy_cache_valid 200 302 1h;
    proxy_cache_valid 404 1m;
    @@ -469,14 +469,14 @@ server {
    }
    # Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
    # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
    # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban).
    location ~ /\. {
    deny all;
    }
    # Deny access to any files with a .php extension in the uploads directory
    # Works in sub-directory installs and also in multisite network
    # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
    # Deny access to any files with a .php extension in the uploads directory.
    # Works in sub-directory installs and also in multisite network.
    # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban).
    location ~* /(?:uploads|files)/.*\.php\$ {
    deny all;
    }
  16. nginx-gists revised this gist Sep 23, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion ubuntu_install.sh
    Original file line number Diff line number Diff line change
    @@ -203,7 +203,7 @@ EOM
    # Remove sample file because it is cruft and could be a security problem
    rm /var/www/wordpress/wp-config-sample.php

    # Ensure that Wordpres permissions are correct
    # Ensure that WordPress permissions are correct
    find /var/www/wordpress -type d -exec chmod g+s {} \;
    chmod g+w /var/www/wordpress/wp-content
    chmod -R g+w /var/www/wordpress/wp-content/themes
  17. nginx-gists created this gist Sep 22, 2020.
    586 changes: 586 additions & 0 deletions ubuntu_install.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,586 @@
    #!/usr/bin/env bash

    if [ "$EUID" -ne 0 ];then
    >&2 echo "This script requires root level access to run"
    exit 1
    fi

    if [ -z "${WORDPRESS_DB_PASSWORD}" ]; then
    >&2 echo "WORDPRESS_DB_PASSWORD must be set"
    >&2 echo "Here is a random one that you can paste:"
    >&2 echo "export WORDPRESS_DB_PASSWORD=\"$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)\""
    exit 1
    fi

    if [ -z "${WORDPRESS_ADMIN_USER}" ]; then
    >&2 echo "WORDPRESS_ADMIN_USER must be set"
    >&2 echo "Here is a sensible default that you can paste:"
    >&2 echo "export WORDPRESS_ADMIN_USER=\"moderator\""
    exit 1
    fi

    if [ -z "${WORDPRESS_ADMIN_PASSWORD}" ]; then
    >&2 echo "WORDPRESS_ADMIN_PASSWORD must be set"
    >&2 echo "Here is a random one that you can paste:"
    >&2 echo "export WORDPRESS_ADMIN_PASSWORD=\"$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)\""
    exit 1
    fi

    if [ -z "${WORDPRESS_ADMIN_EMAIL}" ]; then
    >&2 echo "WORDPRESS_ADMIN_EMAIL must be set"
    exit 1
    fi

    if [ -z "${WORDPRESS_URL}" ]; then
    >&2 echo "WORDPRESS_URL must be set"
    exit 1
    fi

    if [ -z "${WORDPRESS_SITE_TITLE}" ]; then
    >&2 echo "WORDPRESS_SITE_TITLE must be set"
    exit 1
    fi

    # Best practice settings for running bash scripts:

    # Exit the script and an error is encountered
    set -o errexit
    # Exit the script when a pipe operation fails
    set -o pipefail
    # Exit the script when there are undeclared variables
    set -o nounset
    # Uncomment this to see a log to the screen of each command run in the script
    # set -o xtrace

    export DEBIAN_FRONTEND="noninteractive"
    export WORDPRESS_CLI_VERSION="2.4.0"
    export WORDPRESS_CLI_MD5="dedd5a662b80cda66e9e25d44c23b25c"
    export UPLOAD_MAX_FILESIZE="16M"
    export TLS_HOSTNAME="$(echo ${WORDPRESS_URL} | cut -d'/' -f3)"
    export NGINX_CONF_DIR="/etc/nginx"
    export CERT_DIR="/etc/letsencrypt/live/${TLS_HOSTNAME}"

    # Change the hostname to be the same as the WordPress hostname
    if [ ! "$(hostname)" == "${TLS_HOSTNAME}" ]; then
    echo "▶ Changing hostname to ${TLS_HOSTNAME}"
    hostnamectl set-hostname "${TLS_HOSTNAME}"
    fi

    # Add the hostname to /etc/hosts
    if [ "$(grep -m1 "${TLS_HOSTNAME}" /etc/hosts)" = "" ]; then
    echo "▶ Adding hostname ${TLS_HOSTNAME} to /etc/hosts so that WordPress can ping itself"
    printf "::1 %s\n127.0.0.1 %s\n" "${TLS_HOSTNAME}" "${TLS_HOSTNAME}" >> /etc/hosts
    fi

    # Make sure tools needed for install are present
    echo "▶ Installing prerequisite tools"
    apt-get -qq update
    apt-get -qq install -y \
    bc \
    ca-certificates \
    coreutils \
    curl \
    gnupg2 \
    lsb-release

    # Install NGINX UNIT repository
    if [ ! -f /etc/apt/sources.list.d/unit.list ]; then
    echo "▶ Installing NGINX UNIT repository"
    curl -fsSL https://nginx.org/keys/nginx_signing.key | apt-key add -
    echo "deb https://packages.nginx.org/unit/ubuntu/ $(lsb_release -cs) unit" > /etc/apt/sources.list.d/unit.list
    fi

    # Install NGINX repository
    if [ ! -f /etc/apt/sources.list.d/nginx.list ]; then
    echo "▶ Installing NGINX repository"
    curl -fsSL https://nginx.org/keys/nginx_signing.key | apt-key add -
    echo "deb https://nginx.org/packages/mainline/ubuntu $(lsb_release -cs) nginx" > /etc/apt/sources.list.d/nginx.list
    fi

    echo "▶ Updating repository metadata"
    apt-get -qq update

    # WordPress module recommendation: https://make.wordpress.org/hosting/handbook/handbook/server-environment/#php-extensions

    # Ubuntu bundles the following php extensions with its package distribution:
    # date filter hash libxml openssl pcntl pcre Reflection session sodium SPL
    # standard zlib

    # The php-common package contains the following extensions: exif

    # Install php with dependencies and NGINX UNIT
    echo "▶ Installing PHP, UNIT, NGINX CertBot and MariaDB"
    apt-get -qq install -y --no-install-recommends \
    certbot \
    python3-certbot-nginx \
    php-cli \
    php-common \
    php-bcmath \
    php-curl \
    php-gd \
    php-imagick \
    php-mbstring \
    php-mysql \
    php-opcache \
    php-xml \
    php-zip \
    ghostscript \
    nginx \
    unit \
    unit-php \
    mariadb-server

    # Find the major and minor PHP version so that we can write to its conf.d directory
    PHP_MAJOR_MINOR_VERSION="$(php -v | head -n1 | cut -d' ' -f2 | cut -d'.' -f1,2)"

    if [ ! -f "/etc/php/${PHP_MAJOR_MINOR_VERSION}/embed/conf.d/30-wordpress-overrides.ini" ]; then
    echo "▶ Configuring PHP for use with UNIT and WordPress"
    # Add PHP configuration overrides
    cat > "/etc/php/${PHP_MAJOR_MINOR_VERSION}/embed/conf.d/30-wordpress-overrides.ini" << EOM
    ; Set a larger maximum upload size so that WordPress can handle
    ; bigger media files.
    upload_max_filesize=${UPLOAD_MAX_FILESIZE}
    post_max_size=${UPLOAD_MAX_FILESIZE}
    ; Write error log to STDERR so that error messages show up in UNIT
    error_log=/dev/stderr
    EOM
    fi

    # Restart UNIT because we have reconfigured PHP
    echo "▶ Restarting NGINX UNIT"
    service unit restart

    # Setup WordPress database
    echo "▶ Configuring MariaDB for WordPress"
    mysqladmin create wordpress || echo "Ignoring above error because database may already exist"
    mysql -e "GRANT ALL PRIVILEGES ON wordpress.* TO \"wordpress\"@\"localhost\" IDENTIFIED BY \"$WORDPRESS_DB_PASSWORD\"; FLUSH PRIVILEGES;"

    if [ ! -f /usr/local/bin/wp ]; then
    # Install the WordPress CLI
    echo "▶ Installing the WordPress CLI tool"
    curl --retry 6 -Ls "https://github.com/wp-cli/wp-cli/releases/download/v${WORDPRESS_CLI_VERSION}/wp-cli-${WORDPRESS_CLI_VERSION}.phar" > /usr/local/bin/wp
    echo "$WORDPRESS_CLI_MD5 /usr/local/bin/wp" | md5sum -c -
    chmod +x /usr/local/bin/wp
    fi

    if [ ! -d /var/www/wordpress ]; then
    # Create WordPress directories
    mkdir -p /var/www/wordpress
    chown -R www-data:www-data /var/www

    # Download WordPress using the WordPress CLI
    echo "▶ Installing WordPress"
    su -s /bin/sh -c 'wp --path=/var/www/wordpress core download' www-data

    WP_CONFIG_CREATE_CMD="wp --path=/var/www/wordpress config create --extra-php --dbname=wordpress --dbuser=wordpress --dbhost=\"localhost:/var/run/mysqld/mysqld.sock\" --dbpass=\"${WORDPRESS_DB_PASSWORD}\""

    # This snippet is injected into the wp-config.php file when it is created.
    # It informs WordPress that we are behind a reverse proxy and as such
    # allow it to generate links using https.
    cat > /tmp/wp_forwarded_for.php << 'EOM'
    /* Turn HTTPS 'on' if HTTP_X_FORWARDED_PROTO matches 'https' */
    if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false) {
    $_SERVER['HTTPS'] = 'on';
    }
    if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
    $_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST'];
    }
    EOM

    # Create WordPress configuration
    su -s /bin/sh -p -c "cat /tmp/wp_forwarded_for.php | ${WP_CONFIG_CREATE_CMD}" www-data
    rm /tmp/wp_forwarded_for.php
    su -s /bin/sh -p -c "wp --path=/var/www/wordpress config set 'FORCE_SSL_ADMIN' 'true'" www-data

    # Install WordPress
    WP_SITE_INSTALL_CMD="wp --path=/var/www/wordpress core install --url=\"${WORDPRESS_URL}\" --title=\"${WORDPRESS_SITE_TITLE}\" --admin_user=\"${WORDPRESS_ADMIN_USER}\" --admin_password=\"${WORDPRESS_ADMIN_PASSWORD}\" --admin_email=\"${WORDPRESS_ADMIN_EMAIL}\" --skip-email"
    su -s /bin/sh -p -c "${WP_SITE_INSTALL_CMD}" www-data

    # Set permalink structure to a sensible default that isn't in the UI
    su -s /bin/sh -p -c "wp --path=/var/www/wordpress option update permalink_structure '/%year%/%monthnum%/%postname%/'" www-data

    # Remove sample file because it is cruft and could be a security problem
    rm /var/www/wordpress/wp-config-sample.php

    # Ensure that Wordpres permissions are correct
    find /var/www/wordpress -type d -exec chmod g+s {} \;
    chmod g+w /var/www/wordpress/wp-content
    chmod -R g+w /var/www/wordpress/wp-content/themes
    chmod -R g+w /var/www/wordpress/wp-content/plugins
    fi

    if [ "${container:-unknown}" != "lxc" ] && [ "$(grep -m1 -a container=lxc /proc/1/environ | tr -d '\0')" == "" ]; then
    NAMESPACES='"namespaces": {
    "cgroup": true,
    "credential": true,
    "mount": true,
    "network": false,
    "pid": true,
    "uname": true
    }'
    else
    NAMESPACES='"namespaces": {}'
    fi

    echo "▶ Configuring NGINX UNIT to use PHP and WordPress"
    cat > /tmp/wordpress.json << EOM
    {
    "settings": {
    "http": {
    "header_read_timeout": 30,
    "body_read_timeout": 30,
    "send_timeout": 30,
    "idle_timeout": 180,
    "max_body_size": $(numfmt --from=iec ${UPLOAD_MAX_FILESIZE})
    }
    },
    "listeners": {
    "127.0.0.1:8080": {
    "pass": "routes/wordpress"
    }
    },
    "routes": {
    "wordpress": [
    {
    "match": {
    "uri": [
    "*.php",
    "*.php/*",
    "/wp-admin/"
    ]
    },
    "action": {
    "pass": "applications/wordpress/direct"
    }
    },
    {
    "action": {
    "share": "/var/www/wordpress",
    "fallback": {
    "pass": "applications/wordpress/index"
    }
    }
    }
    ]
    },
    "applications": {
    "wordpress": {
    "type": "php",
    "user": "www-data",
    "group": "www-data",
    "processes": {
    "max": $(echo `nproc`+1 | bc),
    "spare": 1
    },
    "isolation": {
    ${NAMESPACES}
    },
    "targets": {
    "direct": {
    "root": "/var/www/wordpress/"
    },
    "index": {
    "root": "/var/www/wordpress/",
    "script": "index.php"
    }
    }
    }
    }
    }
    EOM

    curl -X PUT --data-binary @/tmp/wordpress.json --unix-socket /run/control.unit.sock http://localhost/config

    # Make directory for NGINX cache
    mkdir -p /var/cache/nginx/proxy

    echo "▶ Configuring NGINX"
    cat > ${NGINX_CONF_DIR}/nginx.conf << EOM
    user nginx;
    worker_processes auto;
    error_log /var/log/nginx/error.log warn;
    pid /var/run/nginx.pid;
    events {
    worker_connections 1024;
    }
    http {
    include ${NGINX_CONF_DIR}/mime.types;
    default_type application/octet-stream;
    log_format main '\$remote_addr - \$remote_user [\$time_local] "\$request" '
    '\$status \$body_bytes_sent "\$http_referer" '
    '"\$http_user_agent" "\$http_x_forwarded_for"';
    access_log /var/log/nginx/access.log main;
    sendfile on;
    client_max_body_size ${UPLOAD_MAX_FILESIZE};
    keepalive_timeout 65;
    # GZIP settings
    include ${NGINX_CONF_DIR}/gzip_compression.conf;
    # Cache settings
    proxy_cache_path /var/cache/nginx/proxy
    levels=1:2
    keys_zone=wp_cache:10m
    max_size=10g
    inactive=60m
    use_temp_path=off;
    include ${NGINX_CONF_DIR}/conf.d/*.conf;
    }
    EOM

    cat > ${NGINX_CONF_DIR}/gzip_compression.conf << 'EOM'
    # Credit: https://github.com/h5bp/server-configs-nginx/
    # ----------------------------------------------------------------------
    # | Compression |
    # ----------------------------------------------------------------------
    # https://nginx.org/en/docs/http/ngx_http_gzip_module.html
    # Enable gzip compression.
    # Default: off
    gzip on;
    # Compression level (1-9).
    # 5 is a perfect compromise between size and CPU usage, offering about 75%
    # reduction for most ASCII files (almost identical to level 9).
    # Default: 1
    gzip_comp_level 6;
    # Don't compress anything that's already small and unlikely to shrink much if at
    # all (the default is 20 bytes, which is bad as that usually leads to larger
    # files after gzipping).
    # Default: 20
    gzip_min_length 256;
    # Compress data even for clients that are connecting to us via proxies,
    # identified by the "Via" header (required for CloudFront).
    # Default: off
    gzip_proxied any;
    # Tell proxies to cache both the gzipped and regular version of a resource
    # whenever the client's Accept-Encoding capabilities header varies;
    # Avoids the issue where a non-gzip capable client (which is extremely rare
    # today) would display gibberish if their proxy gave them the gzipped version.
    # Default: off
    gzip_vary on;
    # Compress all output labeled with one of the following MIME-types.
    # `text/html` is always compressed by gzip module.
    # Default: text/html
    gzip_types
    application/atom+xml
    application/geo+json
    application/javascript
    application/x-javascript
    application/json
    application/ld+json
    application/manifest+json
    application/rdf+xml
    application/rss+xml
    application/vnd.ms-fontobject
    application/wasm
    application/x-web-app-manifest+json
    application/xhtml+xml
    application/xml
    font/eot
    font/otf
    font/ttf
    image/bmp
    image/svg+xml
    text/cache-manifest
    text/calendar
    text/css
    text/javascript
    text/markdown
    text/plain
    text/xml
    text/vcard
    text/vnd.rim.location.xloc
    text/vtt
    text/x-component
    text/x-cross-domain-policy;
    EOM

    cat > ${NGINX_CONF_DIR}/conf.d/default.conf << EOM
    upstream unit_php_upstream {
    server 127.0.0.1:8080;
    }
    server {
    listen 80;
    listen [::]:80;
    # ACME-challenge used by CertBot for Let's Encrypt
    location ^~ /.well-known/acme-challenge/ {
    root /var/www/certbot;
    }
    location / {
    return 301 https://${TLS_HOSTNAME}\$request_uri;
    }
    }
    server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name ${TLS_HOSTNAME};
    root /var/www/wordpress/;
    # Let's Encrypt configuration
    ssl_certificate ${CERT_DIR}/fullchain.pem;
    ssl_certificate_key ${CERT_DIR}/privkey.pem;
    ssl_trusted_certificate ${CERT_DIR}/chain.pem;
    include ${NGINX_CONF_DIR}/options-ssl-nginx.conf;
    ssl_dhparam ${NGINX_CONF_DIR}/ssl-dhparams.pem;
    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    # Proxy Caching
    proxy_cache wp_cache;
    proxy_cache_valid 200 302 1h;
    proxy_cache_valid 404 1m;
    proxy_cache_revalidate on;
    proxy_cache_background_update on;
    proxy_cache_lock on;
    proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
    location = /favicon.ico {
    log_not_found off;
    access_log off;
    }
    location = /robots.txt {
    allow all;
    log_not_found off;
    access_log off;
    }
    # Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
    # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
    location ~ /\. {
    deny all;
    }
    # Deny access to any files with a .php extension in the uploads directory
    # Works in sub-directory installs and also in multisite network
    # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
    location ~* /(?:uploads|files)/.*\.php\$ {
    deny all;
    }
    # WordPress: deny wp-content, wp-includes php files
    location ~* ^/(?:wp-content|wp-includes)/.*\.php\$ {
    deny all;
    }
    # Deny public access to wp-config.php
    location ~* wp-config.php {
    deny all;
    }
    # assets, media
    location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ {
    access_log off;
    }
    location ~* \.(?:svgz?|ttf|ttc|otf|eot|woff2?)$ {
    add_header Access-Control-Allow-Origin "*";
    access_log off;
    }
    location / {
    try_files \$uri @index_php;
    }
    location @index_php {
    proxy_socket_keepalive on;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    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 Host \$host;
    proxy_pass http://unit_php_upstream;
    }
    location ~* \.php\$ {
    proxy_socket_keepalive on;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    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 Host \$host;
    try_files \$uri =404;
    proxy_pass http://unit_php_upstream;
    }
    }
    EOM

    echo "▶ Stopping NGINX in order to set up Let's Encrypt"
    service nginx stop

    mkdir -p /var/www/certbot
    chown www-data:www-data /var/www/certbot
    chmod g+s /var/www/certbot

    if [ ! -f ${NGINX_CONF_DIR}/options-ssl-nginx.conf ]; then
    echo "▶ Downloading recommended TLS parameters"
    curl --retry 6 -Ls -z "Tue, 14 Apr 2020 16:36:07 GMT" \
    -o "${NGINX_CONF_DIR}/options-ssl-nginx.conf" \
    "https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf" \
    || echo "Couldn't download latest options-ssl-nginx.conf"
    fi

    if [ ! -f ${NGINX_CONF_DIR}/ssl-dhparams.pem ]; then
    echo "▶ Downloading recommended TLS DH parameters"
    curl --retry 6 -Ls -z "Tue, 14 Apr 2020 16:49:18 GMT" \
    -o "${NGINX_CONF_DIR}/ssl-dhparams.pem" \
    "https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem" \
    || echo "Couldn't download latest ssl-dhparams.pem"
    fi

    # If tls_certs_init.sh hasn't been run before, let's remove the self-signed certs
    if [ ! -d "/etc/letsencrypt/accounts" ]; then
    echo "▶ Removing self-signed certificates"
    rm -rf "${CERT_DIR}"
    fi

    if [ "" = "${LETS_ENCRYPT_STAGING:-}" ] || [ "0" = "${LETS_ENCRYPT_STAGING}" ]; then
    CERTBOT_STAGING_FLAG=""
    else
    CERTBOT_STAGING_FLAG="--staging"
    fi

    if [ ! -f "${CERT_DIR}/fullchain.pem" ]; then
    echo "▶ Generating certificates with Let's Encrypt"
    certbot certonly --standalone \
    -m "${WORDPRESS_ADMIN_EMAIL}" \
    ${CERTBOT_STAGING_FLAG} \
    --agree-tos --force-renewal --non-interactive \
    -d "${TLS_HOSTNAME}"
    fi

    echo "▶ Starting NGINX in order to use new configuration"
    service nginx start

    # Write crontab for periodic Let's Encrypt cert renewal
    if [ "$(crontab -l | grep -m1 'certbot renew')" == "" ]; then
    echo "▶ Adding certbot to crontab for automatic Let's Encrypt renewal"
    (crontab -l 2>/dev/null; echo "24 3 * * * certbot renew --dry-run --nginx --post-hook 'service nginx reload'") | crontab -
    fi