567 lines
15 KiB
Bash
567 lines
15 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
nginxConfigFile="/etc/nginx/nginx.conf"
|
|
nginxBackupFile="/etc/nginx/nginx.conf.configure-server.bak"
|
|
nginxSitesAvailable="/etc/nginx/sites-available"
|
|
nginxSitesEnabled="/etc/nginx/sites-enabled"
|
|
nginxDefaultSite="${nginxSitesAvailable}/default.conf"
|
|
nginxDefaultSiteLink="${nginxSitesEnabled}/default.conf"
|
|
clacksInfoUrl="https://xclacksoverhead.org/home/about"
|
|
nginxManagedInclude="include /etc/nginx/sites-enabled/*.conf;"
|
|
|
|
nginx_installed() {
|
|
pacman -Q nginx &> /dev/null
|
|
}
|
|
|
|
ufw_installed() {
|
|
pacman -Q ufw &> /dev/null
|
|
}
|
|
|
|
ensure_ufw() {
|
|
if ufw_installed; then
|
|
return 0
|
|
fi
|
|
|
|
if [[ "$(yesno "ufw is not installed. Install it now and continue?")" != "Yes" ]]; then
|
|
msgbox "Firewall action cancelled."
|
|
return 1
|
|
fi
|
|
|
|
if ! install_package ufw; then
|
|
msgbox "Failed to install ufw."
|
|
return 1
|
|
fi
|
|
|
|
msgbox "ufw installed."
|
|
return 0
|
|
}
|
|
|
|
enable_nginx_service() {
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" systemctl enable --now nginx; then
|
|
msgbox "nginx was configured, but the service failed to enable or start."
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
web_ports_open() {
|
|
local statusLine=""
|
|
local hasHttp="No"
|
|
local hasHttps="No"
|
|
|
|
if ! ufw_installed; then
|
|
return 1
|
|
fi
|
|
|
|
while IFS= read -r statusLine; do
|
|
if [[ "$statusLine" =~ ^[[:space:]]*80/tcp([[:space:]]+\(v6\))?[[:space:]]+ALLOW([[:space:]]+IN)?[[:space:]]+Anywhere([[:space:]]+\(v6\))?[[:space:]]*$ ]]; then
|
|
hasHttp="Yes"
|
|
elif [[ "$statusLine" =~ ^[[:space:]]*443/tcp([[:space:]]+\(v6\))?[[:space:]]+ALLOW([[:space:]]+IN)?[[:space:]]+Anywhere([[:space:]]+\(v6\))?[[:space:]]*$ ]]; then
|
|
hasHttps="Yes"
|
|
fi
|
|
done < <(
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
sudo "${sudoFlags[@]}" ufw status 2>&1
|
|
)
|
|
|
|
[[ "$hasHttp" == "Yes" && "$hasHttps" == "Yes" ]]
|
|
}
|
|
|
|
offer_open_web_ports() {
|
|
if ! ufw_installed; then
|
|
return 0
|
|
fi
|
|
|
|
if web_ports_open; then
|
|
return 0
|
|
fi
|
|
|
|
if [[ "$(yesno "ufw is installed. Open 80/tcp and 443/tcp for nginx now?")" != "Yes" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
open_web_ports
|
|
}
|
|
|
|
ensure_nginx() {
|
|
local clacksHeader=""
|
|
|
|
if nginx_installed; then
|
|
return 0
|
|
fi
|
|
|
|
if [[ "$(yesno "nginx is not installed. Install it now and continue?")" != "Yes" ]]; then
|
|
msgbox "nginx action cancelled."
|
|
return 1
|
|
fi
|
|
|
|
if ! install_package nginx; then
|
|
msgbox "Failed to install nginx."
|
|
return 1
|
|
fi
|
|
|
|
clacksHeader="$(prompt_clacks_header || true)"
|
|
setup_nginx_layout "$clacksHeader" || return 1
|
|
if ! test_nginx_config; then
|
|
return 1
|
|
fi
|
|
|
|
if ! enable_nginx_service; then
|
|
return 1
|
|
fi
|
|
|
|
offer_open_web_ports || return 1
|
|
msgbox "nginx is installed and running."
|
|
return 0
|
|
}
|
|
|
|
prompt_clacks_header() {
|
|
local rawNames=""
|
|
local formattedNames=()
|
|
local nameEntry=""
|
|
local clacksHeader=""
|
|
|
|
msgbox "X-Clacks-Overhead is an optional tribute header inspired by Terry Pratchett's Clacks. Websites use it to remember names with values such as GNU Terry Pratchett. For more information, please see: ${clacksInfoUrl}"
|
|
rawNames="$(inputbox "Enter a comma-separated list of names for the X-Clacks-Overhead header, or leave blank to skip.")" || return 1
|
|
if [[ -z "${rawNames//[[:space:]]/}" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
while IFS= read -r nameEntry; do
|
|
nameEntry="${nameEntry#"${nameEntry%%[![:space:]]*}"}"
|
|
nameEntry="${nameEntry%"${nameEntry##*[![:space:]]}"}"
|
|
if [[ -n "$nameEntry" ]]; then
|
|
formattedNames+=("GNU ${nameEntry}")
|
|
fi
|
|
done < <(printf '%s\n' "$rawNames" | tr ',' '\n')
|
|
|
|
if [[ "${#formattedNames[@]}" -eq 0 ]]; then
|
|
return 1
|
|
fi
|
|
|
|
clacksHeader="$(printf '%s, ' "${formattedNames[@]}")"
|
|
clacksHeader="${clacksHeader%, }"
|
|
printf '%s\n' "$clacksHeader"
|
|
return 0
|
|
}
|
|
|
|
setup_nginx_layout() {
|
|
local clacksHeader="$1"
|
|
local generatedConfig=""
|
|
local tempConfig=""
|
|
|
|
if ! sudo "${sudoFlags[@]}" test -r "$nginxConfigFile"; then
|
|
msgbox "Unable to read ${nginxConfigFile}. Install nginx first."
|
|
return 1
|
|
fi
|
|
|
|
generatedConfig="$(generate_nginx_config "$clacksHeader")" || {
|
|
msgbox "Failed to generate nginx.conf."
|
|
return 1
|
|
}
|
|
tempConfig="$(mktemp)"
|
|
printf '%s\n' "$generatedConfig" > "$tempConfig"
|
|
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" install -d -m 755 "$nginxSitesAvailable" "$nginxSitesEnabled"; then
|
|
rm -f "$tempConfig"
|
|
msgbox "Failed to create the nginx site directories."
|
|
return 1
|
|
fi
|
|
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" test -e "$nginxBackupFile"; then
|
|
# shellcheck disable=SC2154
|
|
sudo "${sudoFlags[@]}" cp "$nginxConfigFile" "$nginxBackupFile" 2> /dev/null || true
|
|
fi
|
|
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" install -m 644 "$tempConfig" "$nginxConfigFile"; then
|
|
rm -f "$tempConfig"
|
|
msgbox "Failed to write ${nginxConfigFile}."
|
|
return 1
|
|
fi
|
|
|
|
rm -f "$tempConfig"
|
|
ensure_default_site
|
|
}
|
|
|
|
generate_nginx_config() {
|
|
local clacksHeader="$1"
|
|
|
|
sudo "${sudoFlags[@]}" awk -v clacksHeader="$clacksHeader" -v managedInclude="$nginxManagedInclude" '
|
|
BEGIN {
|
|
skippingServer = 0
|
|
braceDepth = 0
|
|
insertedSitesInclude = 0
|
|
skipManagedComment = 0
|
|
}
|
|
{
|
|
if ($0 == "# Managed by configure-server") {
|
|
skipManagedComment = 1
|
|
next
|
|
}
|
|
|
|
if ($0 ~ /^[[:space:]]*add_header X-Clacks-Overhead /) {
|
|
next
|
|
}
|
|
|
|
if ($0 ~ /^[[:space:]]*include \/etc\/nginx\/sites-enabled\/\*\.conf;$/) {
|
|
next
|
|
}
|
|
|
|
if (skippingServer) {
|
|
opens = gsub(/\{/, "{")
|
|
closes = gsub(/\}/, "}")
|
|
braceDepth += opens - closes
|
|
if (braceDepth <= 0) {
|
|
skippingServer = 0
|
|
}
|
|
next
|
|
}
|
|
|
|
if ($0 ~ /^ server \{$/) {
|
|
skippingServer = 1
|
|
braceDepth = 1
|
|
next
|
|
}
|
|
|
|
if (skipManagedComment) {
|
|
skipManagedComment = 0
|
|
}
|
|
|
|
print
|
|
|
|
if (!insertedSitesInclude && $0 ~ /^[[:space:]]*#gzip[[:space:]]+on;/) {
|
|
if (clacksHeader != "") {
|
|
print ""
|
|
print " add_header X-Clacks-Overhead \"" clacksHeader "\" always;"
|
|
}
|
|
print ""
|
|
print " " managedInclude
|
|
insertedSitesInclude = 1
|
|
}
|
|
}
|
|
' "$nginxConfigFile"
|
|
}
|
|
|
|
ensure_default_site() {
|
|
local tempSiteFile=""
|
|
|
|
if [[ -e "$nginxDefaultSite" ]]; then
|
|
if [[ ! -L "$nginxDefaultSiteLink" ]]; then
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
sudo "${sudoFlags[@]}" ln -sf "$nginxDefaultSite" "$nginxDefaultSiteLink"
|
|
fi
|
|
return 0
|
|
fi
|
|
|
|
tempSiteFile="$(mktemp)"
|
|
cat > "$tempSiteFile" <<'EOF'
|
|
server {
|
|
listen 80;
|
|
server_name _;
|
|
|
|
root /usr/share/nginx/html;
|
|
index index.html index.htm;
|
|
|
|
location / {
|
|
try_files $uri $uri/ =404;
|
|
}
|
|
|
|
error_page 500 502 503 504 /50x.html;
|
|
location = /50x.html {
|
|
root /usr/share/nginx/html;
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" install -m 644 "$tempSiteFile" "$nginxDefaultSite"; then
|
|
rm -f "$tempSiteFile"
|
|
msgbox "Failed to create the default nginx site."
|
|
return 1
|
|
fi
|
|
|
|
rm -f "$tempSiteFile"
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" ln -sf "$nginxDefaultSite" "$nginxDefaultSiteLink"; then
|
|
msgbox "Failed to enable the default nginx site."
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
create_site() {
|
|
local siteName=""
|
|
local serverNames=""
|
|
local siteRoot=""
|
|
local tempSiteFile=""
|
|
local siteConfigFile=""
|
|
local defaultIndexFile=""
|
|
|
|
ensure_nginx || return 1
|
|
|
|
siteName="$(inputbox "Enter a short site name for the config file, for example example.com.")" || return 1
|
|
if [[ -z "$siteName" || ! "$siteName" =~ ^[A-Za-z0-9._-]+$ ]]; then
|
|
msgbox "Enter a valid site name using letters, numbers, dots, dashes, or underscores."
|
|
return 1
|
|
fi
|
|
|
|
serverNames="$(inputbox "Enter the server_name value, using spaces between names." "$siteName")" || return 1
|
|
siteRoot="$(inputbox "Enter the site document root." "/srv/http/${siteName}")" || return 1
|
|
if [[ -z "$siteRoot" ]]; then
|
|
msgbox "A document root is required."
|
|
return 1
|
|
fi
|
|
|
|
siteConfigFile="${nginxSitesAvailable}/${siteName}.conf"
|
|
tempSiteFile="$(mktemp)"
|
|
cat > "$tempSiteFile" <<EOF
|
|
server {
|
|
listen 80;
|
|
server_name ${serverNames};
|
|
|
|
root ${siteRoot};
|
|
index index.html index.htm;
|
|
|
|
location / {
|
|
try_files \$uri \$uri/ =404;
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" install -d -m 755 "$siteRoot"; then
|
|
rm -f "$tempSiteFile"
|
|
msgbox "Failed to create ${siteRoot}."
|
|
return 1
|
|
fi
|
|
|
|
defaultIndexFile="$(mktemp)"
|
|
cat > "$defaultIndexFile" <<EOF
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>${siteName}</title>
|
|
</head>
|
|
<body>
|
|
<h1>${siteName}</h1>
|
|
<p>nginx is serving ${siteName} from ${siteRoot}.</p>
|
|
</body>
|
|
</html>
|
|
EOF
|
|
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" install -m 644 "$tempSiteFile" "$siteConfigFile"; then
|
|
rm -f "$tempSiteFile" "$defaultIndexFile"
|
|
msgbox "Failed to write ${siteConfigFile}."
|
|
return 1
|
|
fi
|
|
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" test -e "${siteRoot}/index.html"; then
|
|
# shellcheck disable=SC2154
|
|
sudo "${sudoFlags[@]}" install -m 644 "$defaultIndexFile" "${siteRoot}/index.html" || true
|
|
fi
|
|
|
|
rm -f "$tempSiteFile" "$defaultIndexFile"
|
|
msgbox "Site ${siteName} was created in ${nginxSitesAvailable}. Enable it from the nginx menu when you are ready."
|
|
return 0
|
|
}
|
|
|
|
select_site_file() {
|
|
local searchPath="$1"
|
|
local filePattern="$2"
|
|
local selectedSite=""
|
|
local siteEntries=()
|
|
local sitePath=""
|
|
|
|
while IFS= read -r sitePath; do
|
|
[[ -n "$sitePath" ]] && siteEntries+=("$(basename "$sitePath")")
|
|
done < <(find "$searchPath" -maxdepth 1 -type "$filePattern" -name '*.conf' | sort)
|
|
|
|
if [[ "${#siteEntries[@]}" -eq 0 ]]; then
|
|
return 1
|
|
fi
|
|
|
|
selectedSite="$(menulist "${siteEntries[@]}")" || return 1
|
|
printf '%s\n' "$selectedSite"
|
|
}
|
|
|
|
enable_site() {
|
|
local siteName=""
|
|
|
|
ensure_nginx || return 1
|
|
|
|
siteName="$(select_site_file "$nginxSitesAvailable" f)" || {
|
|
msgbox "No available site configs were found."
|
|
return 1
|
|
}
|
|
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" ln -sf "${nginxSitesAvailable}/${siteName}" "${nginxSitesEnabled}/${siteName}"; then
|
|
msgbox "Failed to enable ${siteName}."
|
|
return 1
|
|
fi
|
|
|
|
msgbox "${siteName} is enabled. Test and reload nginx when you are ready."
|
|
return 0
|
|
}
|
|
|
|
disable_site() {
|
|
local siteName=""
|
|
|
|
ensure_nginx || return 1
|
|
|
|
siteName="$(select_site_file "$nginxSitesEnabled" l)" || {
|
|
msgbox "No enabled sites were found."
|
|
return 1
|
|
}
|
|
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" rm -f "${nginxSitesEnabled}/${siteName}"; then
|
|
msgbox "Failed to disable ${siteName}."
|
|
return 1
|
|
fi
|
|
|
|
msgbox "${siteName} is disabled. Test and reload nginx when you are ready."
|
|
return 0
|
|
}
|
|
|
|
test_nginx_config() {
|
|
local tempFile=""
|
|
local status=0
|
|
local statusText=""
|
|
|
|
ensure_nginx || return 1
|
|
|
|
tempFile="$(mktemp)"
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
statusText="$(sudo "${sudoFlags[@]}" nginx -t 2>&1)"
|
|
status=$?
|
|
printf '%s\n' "$statusText" > "$tempFile"
|
|
textbox "$tempFile"
|
|
rm -f "$tempFile"
|
|
return "$status"
|
|
}
|
|
|
|
reload_nginx() {
|
|
ensure_nginx || return 1
|
|
|
|
if ! test_nginx_config; then
|
|
msgbox "nginx was not reloaded because the config test failed."
|
|
return 1
|
|
fi
|
|
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" systemctl reload nginx; then
|
|
msgbox "Failed to reload nginx."
|
|
return 1
|
|
fi
|
|
|
|
msgbox "nginx reloaded successfully."
|
|
return 0
|
|
}
|
|
|
|
open_web_ports() {
|
|
ensure_ufw || return 1
|
|
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" ufw allow 80/tcp; then
|
|
msgbox "Failed to open 80/tcp."
|
|
return 1
|
|
fi
|
|
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" ufw allow 443/tcp; then
|
|
msgbox "Failed to open 443/tcp."
|
|
return 1
|
|
fi
|
|
|
|
msgbox "Web ports 80/tcp and 443/tcp are allowed."
|
|
return 0
|
|
}
|
|
|
|
close_web_ports() {
|
|
ensure_ufw || return 1
|
|
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" ufw delete allow 80/tcp; then
|
|
msgbox "Failed to close 80/tcp."
|
|
return 1
|
|
fi
|
|
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" ufw delete allow 443/tcp; then
|
|
msgbox "Failed to close 443/tcp."
|
|
return 1
|
|
fi
|
|
|
|
msgbox "Web ports 80/tcp and 443/tcp were removed."
|
|
return 0
|
|
}
|
|
|
|
web_ports_menu_label() {
|
|
if web_ports_open; then
|
|
printf '%s\n' "Close web ports"
|
|
else
|
|
printf '%s\n' "Open web ports"
|
|
fi
|
|
}
|
|
|
|
while true; do
|
|
webPortsChoice="$(web_ports_menu_label)"
|
|
nginxChoice="$(menulist \
|
|
"Create site" \
|
|
"Enable site" \
|
|
"Disable site" \
|
|
"Test config" \
|
|
"Reload nginx" \
|
|
"$webPortsChoice" \
|
|
"Back")" || break
|
|
|
|
case "$nginxChoice" in
|
|
"Create site")
|
|
create_site
|
|
;;
|
|
"Enable site")
|
|
enable_site
|
|
;;
|
|
"Disable site")
|
|
disable_site
|
|
;;
|
|
"Test config")
|
|
test_nginx_config
|
|
;;
|
|
"Reload nginx")
|
|
reload_nginx
|
|
;;
|
|
"Open web ports")
|
|
open_web_ports
|
|
;;
|
|
"Close web ports")
|
|
close_web_ports
|
|
;;
|
|
"Back")
|
|
break
|
|
;;
|
|
esac
|
|
done
|