在 Amazon Linux 2023 中以 Cloud Init 自動安裝最新版的 NextClud


前陣子為了使用 WebDav 功能來備份書籤 (Floccus),一開始想手動設定 HTTPD 的 WebDav 功能,無奈怎麼弄都怪怪的,而且還要安全認證,所以就打算用 NextCloud 所提供的 WebDav 功能。除此之外,NextCloud 本身提供的安全性也更高一些,可以啟動二階段認證、掛載外部儲存空間等,省掉很多自行維護的人力。

在開始之前,你的網域託管在 Route 53:如果你的網域是在 CloudFlare 之類的東西的話,只要把 acme.sh 內的 --dns dns_aws 改成 --dns dns_cf 就可以了,也請記得加上 CloudFlare 的相關 Key,在 acme.sh 的 Github 有所有支援的 DNS Provider 的設定:How to use DNS API

目前有以下功能:

  • 申請、自動 renew Let’s Encrypt 憑證。
  • 安裝所有 NextCloud 的必要 PHP 模組
  • 建立資料庫、權限、密碼
  • 執行 mysql_secure_installation、更改 DB root 密碼
  • 依據官方文件調整 HTTPD 參數:
    • 關閉檔案清單:Options -Indexes
    • 啟用 rewriteAllowOverride All
    • 設定 Strict-Transport-Security
    • 設定 NextCloud 的 Maintenance Window 時段。

在開始執行之前,有幾個參數需要手動調整:

  • DOMAIN:要申請 SSL 憑證的域名。
  • MAIL:註冊 Let’s Encrypt 的信箱。
  • AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY:有 Route 53 的權限的 IAM User 的 AKSK,請參考:How to use Amazon Route53 API
  • ROOT_DB_PASSWORD:資料庫 root 密碼。
  • NC_DB_NAME:NextCloud 的資料庫名稱。
  • NC_DB_USER:資料庫使用者名稱。
  • NC_DB_PASSWORD:資料庫使用者的密碼。
  • NC_MAINTENANCE_WINDOW=16:NextCloud 的維護時段 (UTC+0)。
  • PHP_MEMORY_LIMIT:PHP 記憶體大小。
  • PHP_POST_MAX_SIZE:PHP 接受 POST 大小。
  • PHP_UPLOAD_MAX_FILESIZE:PHP 接受的檔案大小。

Userdata

#!/bin/sh
set -x
export DOMAIN=""
export MAIL=""
export AWS_ACCESS_KEY_ID=""
export AWS_SECRET_ACCESS_KEY=""

export ROOT_DB_PASSWORD=""
export NC_DB_NAME="nextcloud"
export NC_DB_USER="nc_user"
export NC_DB_PASSWORD=""
export NC_MAINTENANCE_WINDOW=16

export PHP_MEMORY_LIMIT="1G"
export PHP_POST_MAX_SIZE="10G"
export PHP_UPLOAD_MAX_FILESIZE="10G"

########

export HOME=/root

max_attempts=5
attempt_num=1
success=false
while [ $success = false ] && [ $attempt_num -le $max_attempts ]; do
    echo "Trying dnf install"
    dnf install cronie httpd mod_ssl socat mariadb105-server unzip php8.3-fpm php8.3-pdo php8.3-mysqlnd php8.3-xml php8.3-mbstring php8.3-gd php8.3-zip php8.3-opcache php8.3-modphp php8.3-process php8.3-bcmath php8.3-gmp php8.3-intl php8.3-sodium -y
    if [ $? -eq 0 ]; then
        echo "dnf install succeeded"
        success=true
    else
        echo "Attempt $attempt_num failed. Sleeping for 3 seconds and trying again..."
        sleep 3
        ((attempt_num++))
    fi
done

systemctl start httpd
systemctl enable httpd
systemctl start mariadb
systemctl enable mariadb

curl https://get.acme.sh | sh -s email=$MAIL
/root/.acme.sh/acme.sh --issue --force\
    --dns dns_aws \
    -d $DOMAIN \
    --cert-file /etc/pki/tls/certs/$DOMAIN.cert.pem \
    --key-file /etc/pki/tls/private/$DOMAIN.key.pem \
    --fullchain-file /etc/pki/tls/certs/$DOMAIN.fullchain.pem \
    --reloadcmd "systemctl reload httpd" --apache

# Replace the default SSL credential files.
sed -i 's/SSLCertificateFile\ \/etc\/pki\/tls\/certs\/localhost.crt/SSLCertificateFile\ \/etc\/pki\/tls\/certs\/'$DOMAIN'.cert.pem/g' /etc/httpd/conf.d/ssl.conf
sed -i 's/SSLCertificateKeyFile\ \/etc\/pki\/tls\/private\/localhost.key/SSLCertificateKeyFile\ \/etc\/pki\/tls\/private\/'$DOMAIN'.key.pem/g' /etc/httpd/conf.d/ssl.conf
sed -i 's/#SSLCertificateChainFile\ \/etc\/pki\/tls\/certs\/server-chain.crt/SSLCertificateChainFile\ \/etc\/pki\/tls\/certs\/'$DOMAIN'.fullchain.pem/g' /etc/httpd/conf.d/ssl.conf
sed -i 's/Options\ Indexes\ FollowSymLinks/Options\ -Indexes\ +FollowSymLinks/g' /etc/httpd/conf/httpd.conf
sed -i 's/AllowOverride\ None/AllowOverride\ All/g' /etc/httpd/conf/httpd.conf

# Update the php limits.
echo "php_value[memory_limit] = $PHP_MEMORY_LIMIT" >> /etc/php-fpm.d/www.conf
echo "php_value[post_max_size] = $PHP_POST_MAX_SIZE" >> /etc/php-fpm.d/www.conf
echo "php_value[upload_max_filesize] = $PHP_UPLOAD_MAX_FILESIZE" >> /etc/php-fpm.d/www.conf

# Set Strict-Transport-Security header
sed -i 's/<\/VirtualHost>/\ \ \ \ \<IfModule\ mod_headers.c\>\n\ \ \ \ \ \ \ \ Header\ always\ set\ Strict-Transport-Security\ \"max-age\=15552000\;\ includeSubDomains\"\n\ \ \ \ \<\/IfModule\>\n<\/VirtualHost>/g' /etc/httpd/conf.d/ssl.conf

# Update the opcache configuation.
sed -i "s/;opcache.interned_strings_buffer=8/opcache.interned_strings_buffer=64/g" /etc/php.d/10-opcache.ini

systemctl reload httpd
systemctl restart php-fpm

curl -O --output-dir /var/www/ https://download.nextcloud.com/server/releases/latest.zip
unzip /var/www/latest.zip -d /var/www/
rm -rf /var/www/html/
mv /var/www/nextcloud /var/www/html
chown -R apache:apache /var/www/html

### mysql_secure_installation
mysql -e "UPDATE mysql.user SET Password=PASSWORD('$ROOT_DB_PASSWORD') WHERE User='root';"
mysql -e "DELETE FROM mysql.user WHERE User='';"
mysql -e "DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');"
mysql -e "DROP DATABASE IF EXISTS test;"
mysql -e "DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';"
mysql -e "FLUSH PRIVILEGES;"

### Create a database for NextCloud
mysql -e "CREATE USER '$NC_DB_USER'@'localhost' IDENTIFIED BY '$NC_DB_PASSWORD';"
mysql -e "CREATE DATABASE IF NOT EXISTS $NC_DB_NAME CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;"
mysql -e "GRANT ALL PRIVILEGES on $NC_DB_NAME.* to '$NC_DB_USER'@'localhost';"

### Set root password form empty.
mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '$ROOT_DB_PASSWORD';"

### First access to generate config.php from config.sample.php
curl localhost >> /dev/null
sed -i "s/);/  'maintenance_window_start' => $NC_MAINTENANCE_WINDOW,\n);/g" /var/www/html/config/config.php

已知問題

  • Amazon Linux 2023 沒有 php-imagick 可用。

其他建議

  • 可依據需求連線 ElastiCache (Memcached、Reds) 快取(成本很高)。
  • 使用 S3 作為外部儲存空間 (External Storages),因為 S3 會依據檔案用量收費,但 EBS 會以硬碟的檔案大小來收費。使用 S3 還可以直接從 AWS Conosle 存取當案。

See also

comments powered by Disqus