From ce55d6acc762690d07e2320819419f89cb1f3322 Mon Sep 17 00:00:00 2001 From: j Date: Sun, 8 Mar 2026 09:44:40 +1300 Subject: [PATCH] Add setup-remote.sh script and publish it via getbin.xyz CI --- .gitea/workflows/build-publish.yaml | 14 +- README.md | 36 ++++- setup-remote.sh | 212 ++++++++++++++++++++++++++++ 3 files changed, 254 insertions(+), 8 deletions(-) create mode 100755 setup-remote.sh diff --git a/.gitea/workflows/build-publish.yaml b/.gitea/workflows/build-publish.yaml index af5ef95..7299b2a 100644 --- a/.gitea/workflows/build-publish.yaml +++ b/.gitea/workflows/build-publish.yaml @@ -96,7 +96,19 @@ jobs: fi - name: Publish template + env: + SOS_WRITE_TOKEN: ${{ secrets.SOS_WRITE_TOKEN }} run: | if [ "$GITHUB_REF" = "refs/heads/main" ]; then - SOS_WRITE_TOKEN=${{ secrets.SOS_WRITE_TOKEN }} ~/.local/bin/dropshell publish --all ./ + ~/.local/bin/dropshell publish --all ./ + fi + + - name: Publish setup script to getbin.xyz + env: + SOS_WRITE_TOKEN: ${{ secrets.SOS_WRITE_TOKEN }} + run: | + if [ "$GITHUB_REF" = "refs/heads/main" ]; then + curl -L -s -o /tmp/sos "https://getbin.xyz/sos:latest" + chmod +x /tmp/sos + /tmp/sos upload "getbin.xyz" "./setup-remote.sh" "infmap-setup:latest" fi diff --git a/README.md b/README.md index 4f90613..f0c52c2 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,10 @@ A Dropshell template that provides a web dashboard showing the status of your se - **Network**: all interfaces with IPv4/IPv6, MAC, state, speed, driver - **Routing**: default gateway and interface - **DNS**: configured nameservers +- **Temperatures**: CPU/chipset temps via lm-sensors or thermal zones - **Tailscale**: IP and hostname (if installed) -All information is gathered without root access using `/sys/class/dmi/id/`, `lscpu`, `/proc/meminfo`, `lspci`, `ip addr`, etc. +All information is gathered without root access using `/sys/class/dmi/id/`, `lscpu`, `/proc/meminfo`, `lspci`, `ip addr`, `sensors`, etc. ## Architecture @@ -50,13 +51,13 @@ Edit `infrastructure.conf` to define your servers: ``` Production - root@prod-web-01 https://web01.example.com - root@prod-db-01 - deploy@prod-app-01 https://app01.example.com:8080 + infmap@prod-web-01 https://web01.example.com + infmap@prod-db-01 + infmap@prod-app-01 https://app01.example.com:8080 Development - deploy@dev-01 - deploy@dev-02 + infmap@dev-01 + infmap@dev-02 ``` - Group names are freeform labels (no indentation) @@ -64,7 +65,28 @@ Development - An optional URL after the host adds a clickable link on the dashboard - Lines starting with `#` are comments -### 3. Install +### 3. Setup remote servers + +Run the setup script on each server you want to monitor. It creates a locked-down `infmap` user, installs your public key with SSH restrictions, and installs dependencies (lm-sensors, pciutils, etc.): + +```bash +curl -fsSL https://getbin.xyz/infmap-setup | bash +``` + +To use a custom public key URL instead of the default (`https://getbin.xyz/infmap-pub`): + +```bash +curl -fsSL https://getbin.xyz/infmap-setup | bash -s https://example.com/my-key.pub +``` + +The script: +- Creates an `infmap` user with no password (locked) +- Installs the SSH key restricted to `bash -s` only (no shell, no forwarding) +- Disables password auth for the user via sshd Match block +- Installs lm-sensors, pciutils, iproute2 for full data collection +- Supports Debian/Ubuntu, Alpine, and OpenWrt + +### 4. Install ```bash dropshell install diff --git a/setup-remote.sh b/setup-remote.sh new file mode 100755 index 0000000..7993a5a --- /dev/null +++ b/setup-remote.sh @@ -0,0 +1,212 @@ +#!/bin/bash +# Setup a remote server for infmap monitoring. +# Run as root: curl -fsSL /setup-remote.sh | bash -s +# +# What this does: +# 1. Creates a locked-down 'infmap' user (no password, no login shell abuse) +# 2. Downloads your public key and installs it with SSH restrictions +# 3. Disables password auth for the infmap user +# 4. Installs lm-sensors, pciutils, iproute2, util-linux for full gather coverage + +set -euo pipefail + +# --- Args --- + +KEY_URL="${1:-https://getbin.xyz/infmap-pub}" + +# --- Must be root --- + +if [ "$(id -u)" -ne 0 ]; then + echo "Error: must run as root" + exit 1 +fi + +# --- Detect OS --- + +detect_os() { + if [ -f /etc/os-release ]; then + . /etc/os-release + case "$ID" in + debian|ubuntu|raspbian) echo "debian" ;; + alpine) echo "alpine" ;; + openwrt) echo "openwrt" ;; + *) echo "unknown" ;; + esac + elif [ -f /etc/openwrt_release ]; then + echo "openwrt" + else + echo "unknown" + fi +} + +OS=$(detect_os) +echo "Detected OS: $OS" + +# --- Create user --- + +USERNAME="infmap" + +create_user() { + if id "$USERNAME" &>/dev/null; then + echo "User '$USERNAME' already exists" + else + case "$OS" in + debian) + adduser --system --group --shell /bin/bash --home "/home/$USERNAME" "$USERNAME" + ;; + alpine) + adduser -D -s /bin/bash -h "/home/$USERNAME" "$USERNAME" 2>/dev/null || \ + adduser -D -s /bin/sh -h "/home/$USERNAME" "$USERNAME" + ;; + openwrt) + # OpenWrt uses busybox adduser + grep -q "^$USERNAME:" /etc/passwd || \ + echo "$USERNAME:x:1000:1000:infmap:/home/$USERNAME:/bin/ash" >> /etc/passwd + mkdir -p "/home/$USERNAME" + chown "$USERNAME" "/home/$USERNAME" + ;; + *) + useradd --system --create-home --shell /bin/bash "$USERNAME" 2>/dev/null || \ + adduser --system --group --shell /bin/bash --home "/home/$USERNAME" "$USERNAME" + ;; + esac + echo "Created user '$USERNAME'" + fi + + # Lock password - prevents password login, key auth still works + case "$OS" in + openwrt) + sed -i "s|^$USERNAME:[^:]*:|$USERNAME:*:|" /etc/passwd + ;; + *) + passwd -l "$USERNAME" 2>/dev/null || usermod -L "$USERNAME" 2>/dev/null || true + ;; + esac +} + +create_user + +# --- Download and install public key --- + +HOMEDIR=$(eval echo "~$USERNAME") +SSH_DIR="$HOMEDIR/.ssh" +AUTH_KEYS="$SSH_DIR/authorized_keys" + +mkdir -p "$SSH_DIR" +chmod 700 "$SSH_DIR" + +echo "Downloading public key from $KEY_URL ..." +PUBKEY=$(curl -fsSL "$KEY_URL") || { echo "Error: failed to download key from $KEY_URL"; exit 1; } + +# Validate it looks like an SSH public key +if ! echo "$PUBKEY" | grep -qE '^(ssh-(ed25519|rsa)|ecdsa-sha2)'; then + echo "Error: downloaded content doesn't look like an SSH public key" + echo "Got: $(echo "$PUBKEY" | head -c 80)" + exit 1 +fi + +# Restrict: only allow bash -s (stdin script execution), no forwarding/pty +RESTRICTED_KEY="command=\"/bin/bash -s\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty $PUBKEY" + +# Replace any existing infmap key or append +if [ -f "$AUTH_KEYS" ]; then + # Remove old entries for this key (match on key data) + KEY_DATA=$(echo "$PUBKEY" | awk '{print $2}') + grep -v "$KEY_DATA" "$AUTH_KEYS" > "$AUTH_KEYS.tmp" 2>/dev/null || true + mv "$AUTH_KEYS.tmp" "$AUTH_KEYS" +fi + +echo "$RESTRICTED_KEY" >> "$AUTH_KEYS" +chmod 600 "$AUTH_KEYS" +chown -R "$USERNAME" "$SSH_DIR" + +# If the OS uses a group, fix group ownership +GID=$(id -g "$USERNAME" 2>/dev/null) && chgrp -R "$GID" "$SSH_DIR" 2>/dev/null || true + +echo "Public key installed with restrictions" + +# --- Ensure key-only SSH for this user --- + +SSHD_CONFIG="/etc/ssh/sshd_config" +if [ -f "$SSHD_CONFIG" ]; then + MATCH_BLOCK="Match User $USERNAME + PasswordAuthentication no + KbdInteractiveAuthentication no" + + if grep -q "Match User $USERNAME" "$SSHD_CONFIG"; then + echo "SSH Match block for '$USERNAME' already exists" + else + printf '\n%s\n' "$MATCH_BLOCK" >> "$SSHD_CONFIG" + echo "Added SSH Match block: key-only auth for '$USERNAME'" + fi + + # Reload sshd + if command -v systemctl &>/dev/null; then + systemctl reload sshd 2>/dev/null || systemctl reload ssh 2>/dev/null || true + elif command -v service &>/dev/null; then + service sshd reload 2>/dev/null || service ssh reload 2>/dev/null || true + elif [ -f /etc/init.d/sshd ]; then + /etc/init.d/sshd reload 2>/dev/null || true + fi + echo "sshd reloaded" +elif [ "$OS" = "openwrt" ] && [ -f /etc/config/dropbear ]; then + echo "Note: OpenWrt uses dropbear. Password auth is controlled via /etc/config/dropbear" + echo " Ensure 'option PasswordAuth off' or rely on the locked password." +fi + +# --- Install dependencies --- + +install_packages() { + echo "Installing dependencies..." + case "$OS" in + debian) + export DEBIAN_FRONTEND=noninteractive + apt-get update -qq + apt-get install -y -qq lm-sensors pciutils iproute2 util-linux >/dev/null + # Auto-detect sensor modules + sensors-detect --auto >/dev/null 2>&1 || true + ;; + alpine) + apk update --quiet + apk add --quiet lm-sensors pciutils iproute2 util-linux bash + # Auto-detect sensor modules + sensors-detect --auto >/dev/null 2>&1 || true + ;; + openwrt) + opkg update >/dev/null 2>&1 + # Best-effort - not all packages exist on all architectures + opkg install lm-sensors pciutils ip-full coreutils-stat 2>/dev/null || true + ;; + *) + echo "Warning: unknown OS, skipping package install" + echo "Manually install: lm-sensors pciutils iproute2 util-linux" + return + ;; + esac + echo "Dependencies installed" +} + +install_packages + +# --- Grant read access to hardware info --- + +# Some sensor/DMI files need group access +if [ "$OS" != "openwrt" ]; then + # Add infmap to common hardware-access groups if they exist + for group in i2c sensors; do + getent group "$group" &>/dev/null && usermod -aG "$group" "$USERNAME" 2>/dev/null || true + done +fi + +# --- Summary --- + +echo "" +echo "=== Setup complete ===" +echo " User: $USERNAME" +echo " Home: $HOMEDIR" +echo " Auth: key-only (password disabled)" +echo " SSH key: restricted to 'bash -s' (no shell, no forwarding)" +echo " Packages: lm-sensors, pciutils, iproute2" +echo "" +echo "Add to your infrastructure.conf:" +echo " ${USERNAME}@$(hostname)"