diff --git a/README.md b/README.md index e494008..f1e3ecc 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ wget -qO- https://gitea.young.computer/david/MiniServer/raw/branch/main/install. update ``` -This pulls the latest MiniServer into `~/.local/share/MiniServer`, then runs each step in `updates.d/` in order: the Arch keyring and system packages (plus any packages newly added to `packages.conf`), AUR packages, pm2, sibling projects, cache cleanup, and a reboot check. If a step fails, the others still run and the failed ones are listed at the end. +This pulls the latest MiniServer into `~/.local/share/MiniServer`, then runs each step in `updates.d/` in order: the Arch keyring and system packages (plus any packages newly added to `packages.conf`), AUR packages, pm2, Cockpit, the firewall (services and ports from `firewall.conf`), sibling projects, cache cleanup, and a reboot check. If a step fails, the others still run and the failed ones are listed at the end. ## Refresh diff --git a/firewall.conf b/firewall.conf new file mode 100644 index 0000000..3cbb5d8 --- /dev/null +++ b/firewall.conf @@ -0,0 +1,15 @@ +# Allowed through firewalld (default zone) by install and update. +# Removing an entry here does not close it; use firewall-cmd for that. + +# firewalld service names (see `firewall-cmd --get-services`) +FIREWALL_SERVICES=( + ssh # remote login + dhcpv6-client # IPv6 address from the router + mdns # Avahi: .local + cockpit # Cockpit web console, port 9090 +) + +# Extra ports, e.g. for pm2 apps: "3000/tcp" +# Docker containers' published ports don't need to be listed here. +FIREWALL_PORTS=( +) diff --git a/install.sh b/install.sh index 3acabba..7c21708 100755 --- a/install.sh +++ b/install.sh @@ -132,15 +132,12 @@ cd ~/.local/share/MiniServer . fix-networking.sh -# Install Cockpit -if ! pacman -Qi cockpit &> /dev/null; then - echo "Installing Cockpit..." - sudo pacman -S --noconfirm cockpit - sudo systemctl enable --now cockpit.socket - echo "Cockpit installed. Access it at http://localhost:9090" -else - echo "Cockpit is already installed, skipping." -fi +# Set up Cockpit (installed with the utilities above) +setup_cockpit +echo "Cockpit is at https://$(uname -n).local:9090" + +# Set up firewalld (installed with the utilities above) +setup_firewall # Install Sway config files if [ ! -d "$HOME/.config/sway" ]; then diff --git a/updates.d/40-cockpit.sh b/updates.d/40-cockpit.sh new file mode 100644 index 0000000..86c6111 --- /dev/null +++ b/updates.d/40-cockpit.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +source "$HOME/.local/share/MiniServer/utils.sh" + +# Defined after sourcing utils.sh, which has its own is_installed +is_installed() { + pacman -Qi cockpit &> /dev/null +} + +run_update() { + setup_cockpit +} diff --git a/updates.d/45-firewall.sh b/updates.d/45-firewall.sh new file mode 100644 index 0000000..04be382 --- /dev/null +++ b/updates.d/45-firewall.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +source "$HOME/.local/share/MiniServer/utils.sh" + +# Defined after sourcing utils.sh, which has its own is_installed +is_installed() { + pacman -Qi firewalld &> /dev/null +} + +run_update() { + setup_firewall +} diff --git a/utils.sh b/utils.sh index a364ce5..5855b97 100755 --- a/utils.sh +++ b/utils.sh @@ -73,3 +73,66 @@ install_script() { local tmp tmp="$(mktemp "$2.XXXXXX")" && cp "$1" "$tmp" && chmod 755 "$tmp" && mv -f "$tmp" "$2" } + +# Start Cockpit. Safe to re-run: does nothing once set up. The cockpit package +# itself comes from packages.conf; its firewall port is in firewall.conf. +setup_cockpit() { + if ! systemctl is-enabled --quiet cockpit.socket || ! systemctl is-active --quiet cockpit.socket; then + echo "Enabling Cockpit..." + sudo systemctl enable --now cockpit.socket + fi +} + +# Allow the services and ports in firewall.conf through firewalld's default +# zone, then make sure firewalld is running. Only adds rules, never removes +# them, so rules added by hand or by sibling projects stay. Safe to re-run. +setup_firewall() { + command -v firewall-offline-cmd &> /dev/null || return 0 + source "$HOME/.local/share/MiniServer/firewall.conf" + + # firewall-cmd only works while firewalld is running; before that, write the + # permanent config directly so SSH is allowed before the firewall comes up + local fw running=0 changed=0 svc port + if systemctl is-active --quiet firewalld; then + running=1 + fw=(sudo firewall-cmd --permanent) + else + fw=(sudo firewall-offline-cmd) + fi + + # Keep SSH reachable if sshd listens on a port other than 22 + local ports=("${FIREWALL_PORTS[@]}") + if command -v sshd &> /dev/null; then + for port in $(sudo sshd -T 2> /dev/null | awk '$1 == "port" && $2 != 22 { print $2 }'); do + ports+=("$port/tcp") + done + fi + + for svc in "${FIREWALL_SERVICES[@]}"; do + if ! "${fw[@]}" --query-service="$svc" &> /dev/null; then + echo "Firewall: allowing $svc" + "${fw[@]}" --add-service="$svc" + changed=1 + fi + done + for port in "${ports[@]}"; do + if ! "${fw[@]}" --query-port="$port" &> /dev/null; then + echo "Firewall: allowing $port" + "${fw[@]}" --add-port="$port" + changed=1 + fi + done + + if (( running )); then + if (( changed )); then + sudo firewall-cmd --reload + fi + else + echo "Starting firewalld..." + sudo systemctl enable --now firewalld + # Docker sets up its firewalld zone only when it starts + if systemctl is-active --quiet docker; then + echo "Docker started before firewalld. Restart it (sudo systemctl restart docker) or reboot so its containers' ports work through the firewall." + fi + fi +}