diff options
| -rw-r--r-- | .build.yml | 2 | ||||
| -rw-r--r-- | Makefile | 7 | ||||
| -rwxr-xr-x | deployment/install.sh | 201 | ||||
| -rwxr-xr-x | deployment/uninstall.sh | 62 | ||||
| -rw-r--r-- | deployment/wg-portal.service | 39 |
5 files changed, 309 insertions, 2 deletions
@@ -3,7 +3,7 @@ image: alpine/edge secrets: - 5077f4a0-6a32-46e8-aa7c-5be8778d0524 sources: - - [email protected]/~a14m/wireguard-gateway-portal + - [email protected]/~a14m/wg-portal environment: GIT_SSH_COMMAND: ssh -o StrictHostKeyChecking=no packages: @@ -29,6 +29,11 @@ lint-js: @echo "Linting JavaScript files..." npx eslint static/js/*.js +lint-shell: + @echo "Linting shell files..." + shellcheck deployment/install.sh + shellcheck deployment/uninstall.sh + deps: @echo "Downloading Go dependencies..." go mod download @@ -60,5 +65,5 @@ clean: rm -rf dist/ @echo "Clean complete!" -lint: lint-go lint-js lint-html lint-css +lint: lint-go lint-js lint-html lint-css lint-shell @echo "All linting complete!" diff --git a/deployment/install.sh b/deployment/install.sh new file mode 100755 index 0000000..18652b9 --- /dev/null +++ b/deployment/install.sh @@ -0,0 +1,201 @@ +#!/bin/bash +set -euo pipefail + +abort() { + printf "\033[31m%s\033[0m\n" "$@" >&2 + exit 1 +} + +log() { + printf "%s\n" "$@" +} + +ensure_visudo() { + if ! command -v visudo >/dev/null 2>&1; then + log "visudo was not found" + log "Please install visudo for your distro" + abort "sudo must be installed" + fi +} + +ensure_curl() { + if ! command -v curl >/dev/null 2>&1; then + log "curl was not found" + log "Please install curl for your distro" + abort "curl must be installed" + fi +} + +ensure_acl() { + if ! command -v setfacl >/dev/null 2>&1; then + log "ACL tools not found" + log "Please install acl for your distro" + abort "setfacl must be installed" + fi +} + +ensure_wg() { + if ! command -v wg >/dev/null 2>&1; then + log "Wireguard tools not found" + log "Please install wireguard for your distro" + abort "wg/wg-quick must be installed" + fi + WIREGUARD_PATH="$(which wg)" + WIREGUARD_QUICK_PATH="$(which wg-quick)" +} + +ensure_systemd() { + if ! systemctl --version >/dev/null 2>&1; then + abort "systemd is required but not available" + fi +} + +ensure_sudo() { + if [ "$EUID" -ne 0 ]; then + abort "Must be run as sudo" + fi +} + +get_arch() { + ARCH=$(uname -m) + case $ARCH in + x86_64) ARCH="amd64" ;; + aarch64) ARCH="arm64" ;; + armv7l) ARCH="arm" ;; + *) abort "Unsupported architecture: $ARCH" ;; + esac + log "Architecture: $ARCH" +} + +get_version() { + if [ "$VERSION" = "latest" ]; then + RELEASE_URL="https://api.github.com/repos/a14m/wg-portal/releases/latest" + VERSION=$(curl -s "$RELEASE_URL" | grep '"tag_name":' | cut -d '"' -f 4) + log "Latest version: $VERSION" + fi +} + +validate_version() { + if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] && [ "$VERSION" != "latest" ]; then + abort "Invalid version format: $VERSION (valid format example v1.0.0)" + fi +} + +create_tmp() { + TMP_DIR=$(mktemp -d) + trap 'rm -rf $TMP_DIR' EXIT +} + +download_binary() { + log "Downloading wg-portal binary" + DOWNLOAD_URL="https://github.com/a14m/wg-portal/releases/download/$VERSION/wg-portal-linux-${ARCH}" + if ! timeout 120 curl -s -L -o "$TMP_DIR/wg-portal" "$DOWNLOAD_URL"; then + log "Failed to download $DOWNLOAD_URL" + abort "Failed to download wg-portal binary" + fi + log "Downloaded $TMP_DIR/wg-portal" +} + +download_systemd_service() { + log "Downloading wg-portal.service" + DOWNLOAD_SERVICE_URL="https://git.sr.ht/~a14m/wg-portal/blob/$VERSION/deployment/wg-portal.service" + if ! timeout 30 curl -s -L -o "$TMP_DIR/wg-portal.service" "$DOWNLOAD_SERVICE_URL"; then + log "Failed to download $DOWNLOAD_SERVICE_URL" + abort "Failed to download wg-portal.service" + fi + log "Downloaded $TMP_DIR/wg-portal.service" +} + +create_system_user() { + if ! id -u wg-portal >/dev/null 2>&1; then + log "Creating wg-portal system user and group..." + useradd --system --no-create-home --shell /usr/sbin/nologin wg-portal + else + log "User wg-portal already exists" + fi +} + +create_etc_directory() { + log "Creating /etc/wg-portal directory..." + mkdir -p /etc/wg-portal + chown root:wg-portal /etc/wg-portal + chmod 750 /etc/wg-portal +} + +configure_access_control() { + log "Configure ACL for /etc/wireguard" + setfacl -R -m g:wg-portal:r /etc/wireguard + + log "Setting up wg-portal user/group sudo permissions" + cat > "$TMP_DIR/wg-portal-sudoers" << EOF +%wg-portal ALL=(ALL) NOPASSWD: ${WIREGUARD_QUICK_PATH} up *, ${WIREGUARD_QUICK_PATH} down * +%wg-portal ALL=(ALL) NOPASSWD: ${WIREGUARD_PATH} show +EOF + # Validate before installing + if visudo -c -f "$TMP_DIR/wg-portal-sudoers"; then + mv "$TMP_DIR/wg-portal-sudoers" /etc/sudoers.d/wg-portal + chmod 440 /etc/sudoers.d/wg-portal + else + abort "Invalid sudoers configuration" + fi +} + +install_binary() { + log "Installing wg-portal binary to /etc/wg-portal" + chmod +x "$TMP_DIR/wg-portal" + mv "$TMP_DIR/wg-portal" /etc/wg-portal +} + +install_systemd_service() { + log "Installing wg-portal systemd service..." + mv "$TMP_DIR/wg-portal.service" /etc/systemd/system/wg-portal.service + systemctl daemon-reload + systemctl enable wg-portal + log "Service installed and enabled" +} + +show_completion_message() { + log "" + log "wg-portal installed successfully!" + log "" + log "Configuration:" + log " - Configuration file: /etc/wg-portal/config" + log "" + log "Service Information:" + log " - Binary installed: /etc/wg-portal/wg-portal" + log " - Service: wg-portal.service (enabled)" + log " - User: wg-portal (system user)" + log "" + log "Useful Commands:" + log " - Check status: systemctl status wg-portal" + log " - View logs: journalctl -u wg-portal -f" + log " - Restart: systemctl restart wg-portal" + log " - Stop: systemctl stop wg-portal" + log "" + log "Next steps:" + log " - Configure: /etc/wg-portal/config" + log " - Start Service: systemctl start wg-portal" +} + + +# Configuration +VERSION=${1:-latest} +# Main +ensure_visudo +ensure_curl +ensure_acl +ensure_wg +ensure_systemd +ensure_sudo +get_arch +get_version +validate_version +create_tmp +download_binary +download_systemd_service +create_system_user +create_etc_directory +configure_access_control +install_binary +install_systemd_service +show_completion_message diff --git a/deployment/uninstall.sh b/deployment/uninstall.sh new file mode 100755 index 0000000..a3a1b0e --- /dev/null +++ b/deployment/uninstall.sh @@ -0,0 +1,62 @@ +#!/bin/bash +set -euo pipefail + +abort() { + printf "\033[31m%s\033[0m\n" "$@" >&2 + exit 1 +} + +log() { + printf "%s\n" "$@" +} + +ensure_sudo() { + if [ "$EUID" -ne 0 ]; then + abort "Must be run as sudo" + fi +} + +uninstall_systemd_configurations() { + log "Removing wg-portal systemd configurations" + systemctl stop wg-portal 2>/dev/null || true + systemctl disable wg-portal 2>/dev/null || true + rm -f /etc/systemd/system/wg-portal.service + systemctl daemon-reload +} + +remove_directories() { + log "Removing wg-portal directories..." + rm -rf /etc/wg-portal +} + +remove_sudo_configurations() { + log "Removing wg-portal sudo permissions..." + rm -f /etc/sudoers.d/wg-portal +} + +remove_acl_permissions() { + if [ -d /etc/wireguard ]; then + log "Removing wg-portal ACL permissions from /etc/wireguard..." + setfacl -R -x g:wg-portal /etc/wireguard 2>/dev/null || true + fi +} + +remove_system_user() { + log "Removing wg-portal system user and group..." + userdel wg-portal 2>/dev/null || true + groupdel wg-portal 2>/dev/null || true +} + +show_completion_message() { + log "" + log "wg-portal uninstalled successfully!" +} + +# Main +ensure_sudo +uninstall_systemd_configurations +remove_directories +remove_sudo_configurations +remove_acl_permissions +remove_system_user +show_completion_message diff --git a/deployment/wg-portal.service b/deployment/wg-portal.service new file mode 100644 index 0000000..23d9dad --- /dev/null +++ b/deployment/wg-portal.service @@ -0,0 +1,39 @@ +[Unit] +Description=WireGuard Portal Web Interface +Documentation=https://git.sr.ht/~a14m/wg-portal +After=network-online.target +Wants=network-online.target +Requires=network.target + +[Service] +Type=simple +User=wg-portal +Group=wg-portal +ExecStart=/etc/wg-portal/wg-portal +WorkingDirectory=/etc/wg-portal +Restart=always +RestartSec=5 +TimeoutStartSec=30 +TimeoutStopSec=30 + +# Security hardening +NoNewPrivileges=true +ProtectSystem=strict +ProtectHome=true +ReadWritePaths=/etc/wireguard +PrivateTmp=true +PrivateDevices=true +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectControlGroups=true +RestrictSUIDSGID=true +RestrictRealtime=true +LockPersonality=true + +# Logging +StandardOutput=journal +StandardError=journal +SyslogIdentifier=wg-portal + +[Install] +WantedBy=multi-user.target |
