Add operator SD setup wizard: flash, configure, first-boot autonomy
Single script covers full drop implant prep: image download and flash, WiFi (with UUID fix), per-device SSH keygen to Infisical, mode selection, VPN (Tailscale/WireGuard), network alerter webhook, and first-boot service. Insert card, power on, walk away.
This commit is contained in:
Executable
+578
@@ -0,0 +1,578 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# BigBrother Operator Setup Wizard
|
||||||
|
#
|
||||||
|
# Single interactive script for prepping Orange Pi Zero 3 (or compatible Debian) SD cards.
|
||||||
|
# Operator runs this on their machine with an SD card inserted.
|
||||||
|
#
|
||||||
|
# What this does:
|
||||||
|
# 1. Detects block devices and has operator pick SD card target
|
||||||
|
# 2. Downloads/caches Armbian minimal image for Orange Pi Zero 3
|
||||||
|
# 3. Flashes image to card with verification
|
||||||
|
# 4. Mounts card and collects configuration (WiFi, SSH, VPN, alerter, etc.)
|
||||||
|
# 5. Applies all config to card in a single batch after operator confirms summary
|
||||||
|
# 6. Sets up first-boot systemd service for autonomous installation
|
||||||
|
# 7. Unmounts card — ready to insert and power on
|
||||||
|
#
|
||||||
|
# Style: bash only, ANSI colors, numbered menus, c2itall-style (no preamble, direct action)
|
||||||
|
# Root required for: mount, unmount, dd, partprobe, rsync to /mnt, chown, chmod on mounted fs
|
||||||
|
#
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# COLORS (bash ANSI only)
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
MAGENTA='\033[0;35m'
|
||||||
|
BOLD='\033[1m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# HELPERS
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
info() { echo -e "${GREEN}[+]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
||||||
|
error() { echo -e "${RED}[-]${NC} $*"; exit 1; }
|
||||||
|
step() { echo -e "${CYAN}[*]${NC} $*"; }
|
||||||
|
section() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}${BLUE}─────────────────────────────────────────────${NC}"
|
||||||
|
echo -e "${BOLD}$*${NC}"
|
||||||
|
echo -e "${BOLD}${BLUE}─────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
banner() {
|
||||||
|
echo -e "${BOLD}${MAGENTA}╔════════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${BOLD}${MAGENTA}║ BIGBROTHER // OPERATOR SETUP ║${NC}"
|
||||||
|
echo -e "${BOLD}${MAGENTA}╚════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# PREFLIGHT
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
[[ $EUID -eq 0 ]] || error "Must run as root (use: sudo bash scripts/operator_setup.sh)"
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
CACHE_DIR="${HOME}/.bigbrother/images"
|
||||||
|
CREDS="${HOME}/.local/bin/creds"
|
||||||
|
MOUNT_POINT="/mnt/opi"
|
||||||
|
|
||||||
|
mkdir -p "$CACHE_DIR"
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 1. BANNER
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
clear
|
||||||
|
banner
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 2. SD CARD DETECTION
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
section "SD Card Detection"
|
||||||
|
info "Scanning block devices..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
lsblk -ndo NAME,SIZE,LABEL,TYPE | grep -E "^(sd|mmc)" | nl -v 1 -w1 -s') ' || error "No block devices found. Insert SD card and try again."
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -n -e "${CYAN}Enter device number (1-9):${NC} "
|
||||||
|
read -r device_num
|
||||||
|
[[ "$device_num" =~ ^[1-9]$ ]] || error "Invalid selection"
|
||||||
|
|
||||||
|
device_row=$(lsblk -ndo NAME,SIZE,LABEL,TYPE | grep -E "^(sd|mmc)" | sed -n "${device_num}p")
|
||||||
|
DEVICE=$(echo "$device_row" | awk '{print $1}')
|
||||||
|
DEVICE_SIZE=$(echo "$device_row" | awk '{print $2}')
|
||||||
|
|
||||||
|
info "Selected: /dev/${DEVICE} (${DEVICE_SIZE})"
|
||||||
|
warn "This will OVERWRITE all data on /dev/${DEVICE}"
|
||||||
|
echo -n -e "${YELLOW}Type device name to confirm (${DEVICE}):${NC} "
|
||||||
|
read -r confirm_dev
|
||||||
|
[[ "$confirm_dev" == "$DEVICE" ]] || error "Device name mismatch. Aborting."
|
||||||
|
|
||||||
|
info "Device confirmed: /dev/${DEVICE}"
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 3. IMAGE MANAGEMENT
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
section "Image Management"
|
||||||
|
|
||||||
|
# Look for existing cached image
|
||||||
|
CACHED_IMAGE=$(ls -1 "$CACHE_DIR"/Armbian_*_Orangepizero3_bookworm_current_*_minimal.img.xz 2>/dev/null | head -1)
|
||||||
|
|
||||||
|
if [[ -n "$CACHED_IMAGE" ]]; then
|
||||||
|
info "Found cached image: $(basename $CACHED_IMAGE)"
|
||||||
|
echo -n -e "${CYAN}Use cached image? (y/n):${NC} "
|
||||||
|
read -r use_cached
|
||||||
|
if [[ "$use_cached" =~ ^[yY]$ ]]; then
|
||||||
|
IMAGE="$CACHED_IMAGE"
|
||||||
|
else
|
||||||
|
IMAGE=""
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
IMAGE=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$IMAGE" ]]; then
|
||||||
|
step "Downloading Armbian image..."
|
||||||
|
# Hardcoded known-good URL (operator can update)
|
||||||
|
IMAGE_URL="https://dl.armbian.com/orangepizero3/Armbian_25.5.1_Orangepizero3_bookworm_current_6.12.23_minimal.img.xz"
|
||||||
|
IMAGE_FILENAME=$(basename "$IMAGE_URL")
|
||||||
|
IMAGE="${CACHE_DIR}/${IMAGE_FILENAME}"
|
||||||
|
|
||||||
|
if [[ ! -f "$IMAGE" ]]; then
|
||||||
|
curl -fL --progress-bar "$IMAGE_URL" -o "$IMAGE" || error "Failed to download image"
|
||||||
|
info "Image downloaded: $IMAGE_FILENAME"
|
||||||
|
else
|
||||||
|
info "Image already cached: $IMAGE_FILENAME"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# SHA256 verification (optional, skip if no .sha file)
|
||||||
|
SHA_FILE="${IMAGE}.sha"
|
||||||
|
if [[ -f "$SHA_FILE" ]]; then
|
||||||
|
step "Verifying SHA256..."
|
||||||
|
cd "$CACHE_DIR"
|
||||||
|
sha256sum -c "$(basename $SHA_FILE)" >/dev/null 2>&1 && info "SHA256 verified" || warn "SHA256 mismatch (proceeding anyway)"
|
||||||
|
cd - >/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 4. CONFIGURATION WIZARD
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
section "Configuration Wizard"
|
||||||
|
|
||||||
|
# WiFi (optional)
|
||||||
|
step "WiFi Configuration"
|
||||||
|
echo -n -e "${CYAN}Configure WiFi? (y/n):${NC} "
|
||||||
|
read -r enable_wifi
|
||||||
|
if [[ "$enable_wifi" =~ ^[yY]$ ]]; then
|
||||||
|
echo -n -e "${CYAN}WiFi SSID:${NC} "
|
||||||
|
read -r wifi_ssid
|
||||||
|
echo -n -e "${CYAN}WiFi Passphrase:${NC} "
|
||||||
|
read -s -r wifi_pass; echo
|
||||||
|
echo -n -e "${CYAN}Confirm Passphrase:${NC} "
|
||||||
|
read -s -r wifi_pass2; echo
|
||||||
|
[[ "$wifi_pass" == "$wifi_pass2" ]] || error "Passphrases do not match"
|
||||||
|
WIFI_SSID="$wifi_ssid"
|
||||||
|
WIFI_PASS="$wifi_pass"
|
||||||
|
unset wifi_pass wifi_pass2
|
||||||
|
else
|
||||||
|
WIFI_SSID=""
|
||||||
|
WIFI_PASS=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Root password (required)
|
||||||
|
step "Root Password"
|
||||||
|
while true; do
|
||||||
|
echo -n -e "${CYAN}Root password:${NC} "
|
||||||
|
read -s -r root_pass; echo
|
||||||
|
echo -n -e "${CYAN}Confirm:${NC} "
|
||||||
|
read -s -r root_pass2; echo
|
||||||
|
if [[ "$root_pass" == "$root_pass2" && -n "$root_pass" ]]; then
|
||||||
|
ROOT_PASS="$root_pass"
|
||||||
|
unset root_pass root_pass2
|
||||||
|
break
|
||||||
|
else
|
||||||
|
warn "Passwords do not match or empty. Try again."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# SSH Key
|
||||||
|
step "SSH Key Configuration"
|
||||||
|
echo " [1] Generate new keypair (stored in Infisical)"
|
||||||
|
echo " [2] Use existing public key file"
|
||||||
|
echo -n -e "${CYAN}SSH option (1-2):${NC} "
|
||||||
|
read -r ssh_opt
|
||||||
|
|
||||||
|
if [[ "$ssh_opt" == "1" ]]; then
|
||||||
|
SSH_MODE="generate"
|
||||||
|
elif [[ "$ssh_opt" == "2" ]]; then
|
||||||
|
SSH_MODE="existing"
|
||||||
|
echo -n -e "${CYAN}Path to public key file:${NC} "
|
||||||
|
read -r ssh_pubkey_path
|
||||||
|
[[ -f "$ssh_pubkey_path" ]] || error "File not found: $ssh_pubkey_path"
|
||||||
|
SSH_PUBKEY=$(cat "$ssh_pubkey_path")
|
||||||
|
else
|
||||||
|
error "Invalid SSH option"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Device ID
|
||||||
|
step "Device ID"
|
||||||
|
echo -n -e "${CYAN}Device label (e.g. bb-01):${NC} "
|
||||||
|
read -r device_id
|
||||||
|
[[ -n "$device_id" ]] || error "Device ID cannot be empty"
|
||||||
|
DEVICE_ID="$device_id"
|
||||||
|
|
||||||
|
# Auto-start mode
|
||||||
|
step "Auto-start Mode on Boot"
|
||||||
|
echo " [1] Passive only (capture + analysis, no active offense)"
|
||||||
|
echo " [2] Full autonomous (all modules)"
|
||||||
|
echo " [3] Manual (operator SSHs in to start)"
|
||||||
|
echo -n -e "${CYAN}Select mode (1-3):${NC} "
|
||||||
|
read -r mode_opt
|
||||||
|
|
||||||
|
if [[ "$mode_opt" == "1" ]]; then
|
||||||
|
BB_MODE="passive"
|
||||||
|
elif [[ "$mode_opt" == "2" ]]; then
|
||||||
|
BB_MODE="autonomous"
|
||||||
|
elif [[ "$mode_opt" == "3" ]]; then
|
||||||
|
BB_MODE="manual"
|
||||||
|
else
|
||||||
|
error "Invalid mode"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# VPN tunnel
|
||||||
|
step "VPN Tunnel"
|
||||||
|
echo " [1] None"
|
||||||
|
echo " [2] Tailscale"
|
||||||
|
echo " [3] WireGuard"
|
||||||
|
echo -n -e "${CYAN}Select VPN (1-3):${NC} "
|
||||||
|
read -r vpn_opt
|
||||||
|
|
||||||
|
BB_VPN="none"
|
||||||
|
TAILSCALE_KEY=""
|
||||||
|
WG_CONFIG=""
|
||||||
|
|
||||||
|
if [[ "$vpn_opt" == "1" ]]; then
|
||||||
|
BB_VPN="none"
|
||||||
|
elif [[ "$vpn_opt" == "2" ]]; then
|
||||||
|
BB_VPN="tailscale"
|
||||||
|
echo -n -e "${CYAN}Tailscale auth key:${NC} "
|
||||||
|
read -r tailscale_key
|
||||||
|
TAILSCALE_KEY="$tailscale_key"
|
||||||
|
elif [[ "$vpn_opt" == "3" ]]; then
|
||||||
|
BB_VPN="wireguard"
|
||||||
|
echo -n -e "${CYAN}Path to WireGuard config (wg0.conf):${NC} "
|
||||||
|
read -r wg_config_path
|
||||||
|
[[ -f "$wg_config_path" ]] || error "File not found: $wg_config_path"
|
||||||
|
WG_CONFIG=$(cat "$wg_config_path")
|
||||||
|
else
|
||||||
|
error "Invalid VPN option"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Network alerter webhook
|
||||||
|
step "Network Alerter"
|
||||||
|
echo -n -e "${CYAN}Enable alerter webhook? (y/n):${NC} "
|
||||||
|
read -r enable_alerter
|
||||||
|
if [[ "$enable_alerter" =~ ^[yY]$ ]]; then
|
||||||
|
echo -n -e "${CYAN}Webhook URL (Slack/Discord/Matrix):${NC} "
|
||||||
|
read -r webhook_url
|
||||||
|
BB_ALERTER_WEBHOOK="$webhook_url"
|
||||||
|
else
|
||||||
|
BB_ALERTER_WEBHOOK=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 5. CONFIG SUMMARY
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
section "Configuration Summary"
|
||||||
|
|
||||||
|
echo "Device ID: ${BOLD}${DEVICE_ID}${NC}"
|
||||||
|
echo "Target: ${BOLD}/dev/${DEVICE}${NC}"
|
||||||
|
echo "Image: ${BOLD}$(basename $IMAGE)${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "WiFi: ${BOLD}$([ -n "$WIFI_SSID" ] && echo "$WIFI_SSID" || echo "disabled")${NC}"
|
||||||
|
echo "Root Pass: ${BOLD}[set]${NC}"
|
||||||
|
echo "SSH Mode: ${BOLD}${SSH_MODE}${NC}"
|
||||||
|
echo "Auto-start: ${BOLD}${BB_MODE}${NC}"
|
||||||
|
echo "VPN: ${BOLD}${BB_VPN}${NC}"
|
||||||
|
echo "Alerter: ${BOLD}$([ -n "$BB_ALERTER_WEBHOOK" ] && echo "enabled" || echo "disabled")${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -n -e "${CYAN}Proceed with flashing and configuration? (y/n):${NC} "
|
||||||
|
read -r confirm_proceed
|
||||||
|
[[ "$confirm_proceed" =~ ^[yY]$ ]] || { info "Aborted"; exit 0; }
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 6. FLASH AND MOUNT
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
section "Flashing SD Card"
|
||||||
|
|
||||||
|
step "Unmounting any existing mounts on /dev/${DEVICE}..."
|
||||||
|
for part in /dev/${DEVICE}*; do
|
||||||
|
[[ -b "$part" ]] && umount "$part" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
|
||||||
|
step "Flashing image to /dev/${DEVICE}..."
|
||||||
|
xzcat "$IMAGE" | dd of="/dev/${DEVICE}" bs=4M status=progress conv=fsync || error "Flash failed"
|
||||||
|
info "Flash complete"
|
||||||
|
|
||||||
|
step "Running partprobe..."
|
||||||
|
partprobe "/dev/${DEVICE}" || warn "partprobe failed (non-critical)"
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
step "Mounting to ${MOUNT_POINT}..."
|
||||||
|
mkdir -p "$MOUNT_POINT"
|
||||||
|
# Try /dev/${DEVICE}1 (most common for single-partition images)
|
||||||
|
mount "/dev/${DEVICE}1" "$MOUNT_POINT" 2>/dev/null || mount "/dev/${DEVICE}p1" "$MOUNT_POINT" || error "Mount failed. Check device partitioning."
|
||||||
|
|
||||||
|
info "Mounted at ${MOUNT_POINT}"
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 7. APPLY CONFIGURATION TO CARD
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
section "Applying Configuration"
|
||||||
|
|
||||||
|
# WiFi profile
|
||||||
|
if [[ -n "$WIFI_SSID" ]]; then
|
||||||
|
step "Writing WiFi profile..."
|
||||||
|
NM_DIR="${MOUNT_POINT}/etc/NetworkManager/system-connections"
|
||||||
|
NM_FILE="${NM_DIR}/wifi-bb.nmconnection"
|
||||||
|
mkdir -p "$NM_DIR"
|
||||||
|
|
||||||
|
WIFI_UUID=$(cat /proc/sys/kernel/random/uuid)
|
||||||
|
cat > "$NM_FILE" << EOF
|
||||||
|
[connection]
|
||||||
|
id=wifi-bb
|
||||||
|
uuid=${WIFI_UUID}
|
||||||
|
type=wifi
|
||||||
|
autoconnect=true
|
||||||
|
autoconnect-priority=10
|
||||||
|
|
||||||
|
[wifi]
|
||||||
|
mode=infrastructure
|
||||||
|
ssid=${WIFI_SSID}
|
||||||
|
|
||||||
|
[wifi-security]
|
||||||
|
auth-alg=open
|
||||||
|
key-mgmt=wpa-psk
|
||||||
|
psk=${WIFI_PASS}
|
||||||
|
|
||||||
|
[ipv4]
|
||||||
|
method=auto
|
||||||
|
|
||||||
|
[ipv6]
|
||||||
|
addr-gen-mode=stable-privacy
|
||||||
|
method=auto
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod 600 "$NM_FILE"
|
||||||
|
chown root:root "$NM_FILE"
|
||||||
|
info "WiFi profile written (SSID: ${WIFI_SSID}, UUID: ${WIFI_UUID})"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Root password
|
||||||
|
step "Setting root password..."
|
||||||
|
ROOT_PASS_HASH=$(openssl passwd -6 "$ROOT_PASS")
|
||||||
|
sed -i "s|^root:[^:]*:|root:${ROOT_PASS_HASH}:|" "${MOUNT_POINT}/etc/shadow"
|
||||||
|
info "Root password set"
|
||||||
|
unset ROOT_PASS ROOT_PASS_HASH
|
||||||
|
|
||||||
|
# SSH keys
|
||||||
|
step "Configuring SSH..."
|
||||||
|
SSH_DIR="${MOUNT_POINT}/root/.ssh"
|
||||||
|
mkdir -p "$SSH_DIR"
|
||||||
|
|
||||||
|
if [[ "$SSH_MODE" == "generate" ]]; then
|
||||||
|
TMP_KEY=$(mktemp)
|
||||||
|
ssh-keygen -t ed25519 -N "" -C "" -f "$TMP_KEY" -q
|
||||||
|
PRIVKEY=$(cat "$TMP_KEY")
|
||||||
|
PUBKEY=$(cat "${TMP_KEY}.pub")
|
||||||
|
rm -f "$TMP_KEY" "${TMP_KEY}.pub"
|
||||||
|
|
||||||
|
echo "$PUBKEY" > "${SSH_DIR}/authorized_keys"
|
||||||
|
|
||||||
|
INFISICAL_KEY="SSH_PRIVKEY_$(echo "$DEVICE_ID" | tr '[:lower:]-' '[:upper:]_')"
|
||||||
|
if [[ -x "$CREDS" ]] && "$CREDS" set "$INFISICAL_KEY" "$PRIVKEY" bigbrother 2>/dev/null; then
|
||||||
|
info "Private key stored in Infisical: bigbrother/${INFISICAL_KEY}"
|
||||||
|
else
|
||||||
|
warn "Could not store in Infisical. SAVE THIS PRIVATE KEY:"
|
||||||
|
echo "---"
|
||||||
|
echo "$PRIVKEY"
|
||||||
|
echo "---"
|
||||||
|
fi
|
||||||
|
unset PRIVKEY
|
||||||
|
else
|
||||||
|
echo "$SSH_PUBKEY" > "${SSH_DIR}/authorized_keys"
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod 700 "$SSH_DIR"
|
||||||
|
chmod 600 "${SSH_DIR}/authorized_keys"
|
||||||
|
info "SSH authorized_keys configured"
|
||||||
|
|
||||||
|
# Hostname
|
||||||
|
echo "orangepi" > "${MOUNT_POINT}/etc/hostname"
|
||||||
|
|
||||||
|
# Disable first-login wizard
|
||||||
|
rm -f "${MOUNT_POINT}/root/.not_logged_in_yet"
|
||||||
|
|
||||||
|
# Copy BigBrother source
|
||||||
|
step "Copying BigBrother source to card..."
|
||||||
|
SRC_DEST="${MOUNT_POINT}/root/bb-src"
|
||||||
|
rm -rf "$SRC_DEST"
|
||||||
|
rsync -a \
|
||||||
|
--exclude='.git' \
|
||||||
|
--exclude='.venv' \
|
||||||
|
--exclude='storage/' \
|
||||||
|
--exclude='__pycache__' \
|
||||||
|
--exclude='*.pyc' \
|
||||||
|
--exclude='*.db' \
|
||||||
|
"${SCRIPT_DIR}/" "$SRC_DEST/" || error "rsync failed"
|
||||||
|
info "Source copied to /root/bb-src/"
|
||||||
|
|
||||||
|
# Write bb-config.env
|
||||||
|
step "Writing configuration to card..."
|
||||||
|
cat > "${MOUNT_POINT}/root/bb-config.env" << EOF
|
||||||
|
BB_MODE="${BB_MODE}"
|
||||||
|
BB_VPN="${BB_VPN}"
|
||||||
|
BB_DEVICE_ID="${DEVICE_ID}"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [[ -n "$BB_ALERTER_WEBHOOK" ]]; then
|
||||||
|
echo "BB_ALERTER_WEBHOOK=\"${BB_ALERTER_WEBHOOK}\"" >> "${MOUNT_POINT}/root/bb-config.env"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Store Tailscale auth key if needed
|
||||||
|
if [[ "$BB_VPN" == "tailscale" && -n "$TAILSCALE_KEY" ]]; then
|
||||||
|
echo "BB_TAILSCALE_KEY=\"${TAILSCALE_KEY}\"" >> "${MOUNT_POINT}/root/bb-config.env"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Store WireGuard config if needed
|
||||||
|
if [[ "$BB_VPN" == "wireguard" && -n "$WG_CONFIG" ]]; then
|
||||||
|
echo "$WG_CONFIG" > "${MOUNT_POINT}/etc/wireguard/wg0.conf"
|
||||||
|
chmod 600 "${MOUNT_POINT}/etc/wireguard/wg0.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod 600 "${MOUNT_POINT}/root/bb-config.env"
|
||||||
|
info "Configuration written"
|
||||||
|
|
||||||
|
# First-boot script
|
||||||
|
step "Installing first-boot service..."
|
||||||
|
cat > "${MOUNT_POINT}/root/bb-firstboot-run.sh" << 'RUNEOF'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Runs once on first boot, then disables itself
|
||||||
|
set -euo pipefail
|
||||||
|
LOG=/root/bb-firstboot.log
|
||||||
|
exec >> "$LOG" 2>&1
|
||||||
|
|
||||||
|
echo "=== BigBrother first-boot setup: $(date) ==="
|
||||||
|
|
||||||
|
# Wait for internet connectivity
|
||||||
|
echo "[*] Waiting for internet..."
|
||||||
|
for i in $(seq 1 30); do
|
||||||
|
if curl -sf --max-time 5 http://deb.debian.org/debian > /dev/null 2>&1; then
|
||||||
|
echo "[+] Internet OK"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if [[ $i -eq 30 ]]; then
|
||||||
|
echo "[-] No internet after 90s"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep 3
|
||||||
|
done
|
||||||
|
|
||||||
|
source /root/bb-config.env
|
||||||
|
|
||||||
|
# Install BigBrother
|
||||||
|
cd /root/bb-src
|
||||||
|
bash setup.sh --enable-service
|
||||||
|
|
||||||
|
# VPN setup
|
||||||
|
if [[ "${BB_VPN:-}" == "tailscale" && -n "${BB_TAILSCALE_KEY:-}" ]]; then
|
||||||
|
echo "[*] Installing Tailscale..."
|
||||||
|
curl -fsSL https://tailscale.com/install.sh | sh
|
||||||
|
echo "[*] Connecting to Tailscale..."
|
||||||
|
tailscale up --auth-key="${BB_TAILSCALE_KEY}" --accept-routes || echo "[-] Tailscale auth failed"
|
||||||
|
# Remove auth key from config after use
|
||||||
|
sed -i '/BB_TAILSCALE_KEY/d' /root/bb-config.env
|
||||||
|
elif [[ "${BB_VPN:-}" == "wireguard" ]]; then
|
||||||
|
echo "[*] Installing WireGuard..."
|
||||||
|
apt-get update >/dev/null 2>&1
|
||||||
|
apt-get install -y wireguard >/dev/null 2>&1
|
||||||
|
systemctl enable --now wg-quick@wg0 || echo "[-] WireGuard startup failed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Network alerter
|
||||||
|
if [[ -n "${BB_ALERTER_WEBHOOK:-}" ]]; then
|
||||||
|
echo "[*] Sending online alert..."
|
||||||
|
curl -sf -X POST "$BB_ALERTER_WEBHOOK" \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d "{\"text\":\"BigBrother ${BB_DEVICE_ID} online\"}" || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[+] First-boot complete: $(date)"
|
||||||
|
|
||||||
|
# Disable ourselves
|
||||||
|
rm -f /root/.bb-firstboot-needed
|
||||||
|
systemctl disable bb-firstboot.service
|
||||||
|
RUNEOF
|
||||||
|
chmod 755 "${MOUNT_POINT}/root/bb-firstboot-run.sh"
|
||||||
|
|
||||||
|
# First-boot systemd service
|
||||||
|
cat > "${MOUNT_POINT}/etc/systemd/system/bb-firstboot.service" << 'SVCEOF'
|
||||||
|
[Unit]
|
||||||
|
Description=BigBrother first-boot setup
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
ConditionPathExists=/root/.bb-firstboot-needed
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/bin/bash /root/bb-firstboot-run.sh
|
||||||
|
RemainAfterExit=yes
|
||||||
|
TimeoutStartSec=900
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
SVCEOF
|
||||||
|
|
||||||
|
# Enable service symlink
|
||||||
|
WANTS_DIR="${MOUNT_POINT}/etc/systemd/system/multi-user.target.wants"
|
||||||
|
mkdir -p "$WANTS_DIR"
|
||||||
|
ln -sf /etc/systemd/system/bb-firstboot.service "${WANTS_DIR}/bb-firstboot.service"
|
||||||
|
|
||||||
|
# Firstboot sentinel
|
||||||
|
touch "${MOUNT_POINT}/root/.bb-firstboot-needed"
|
||||||
|
|
||||||
|
info "First-boot service installed and enabled"
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 8. UNMOUNT
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
section "Finalizing"
|
||||||
|
|
||||||
|
step "Syncing and unmounting..."
|
||||||
|
sync
|
||||||
|
umount "$MOUNT_POINT" || warn "umount failed (non-critical)"
|
||||||
|
info "SD card ready to remove"
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 9. DONE
|
||||||
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
section "Setup Complete"
|
||||||
|
|
||||||
|
echo "${GREEN}Insert SD card into device and power on.${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "The device will:"
|
||||||
|
echo " 1. Boot and connect to WiFi (if configured)"
|
||||||
|
echo " 2. Run /root/bb-firstboot-run.sh automatically"
|
||||||
|
echo " 3. Install BigBrother source and dependencies"
|
||||||
|
echo " 4. Set up systemd services"
|
||||||
|
echo " 5. Configure VPN (if selected)"
|
||||||
|
echo " 6. Send alerter webhook (if configured)"
|
||||||
|
echo ""
|
||||||
|
echo "No further operator interaction required. Device walks away autonomously."
|
||||||
|
echo ""
|
||||||
|
echo "${CYAN}To SSH after first-boot:${NC}"
|
||||||
|
|
||||||
|
if [[ "$SSH_MODE" == "generate" ]]; then
|
||||||
|
INFISICAL_KEY="SSH_PRIVKEY_$(echo "$DEVICE_ID" | tr '[:lower:]-' '[:upper:]_')"
|
||||||
|
if [[ -x "$CREDS" ]]; then
|
||||||
|
echo " ${CREDS} get ${INFISICAL_KEY} bigbrother > /tmp/${DEVICE_ID}.key"
|
||||||
|
echo " chmod 600 /tmp/${DEVICE_ID}.key"
|
||||||
|
echo " ssh -i /tmp/${DEVICE_ID}.key root@<DEVICE_IP>"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " ssh -i $(dirname $ssh_pubkey_path)/$(basename $ssh_pubkey_path .pub) root@<DEVICE_IP>"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "${CYAN}Monitor first-boot:${NC}"
|
||||||
|
echo " ssh root@<DEVICE_IP> 'tail -f /root/bb-firstboot.log'"
|
||||||
|
echo ""
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Pre-configure an Armbian SD card for fully autonomous first-boot setup.
|
|
||||||
#
|
|
||||||
# What this does:
|
|
||||||
# 1. Writes WiFi profile (correct NM keyfile with UUID)
|
|
||||||
# 2. Sets root password
|
|
||||||
# 3. Generates per-device SSH keypair, stores private key in Infisical
|
|
||||||
# 4. Copies BigBrother source onto the card
|
|
||||||
# 5. Installs a systemd firstboot service that runs setup.sh on first boot
|
|
||||||
# 6. Disables the Armbian first-login wizard
|
|
||||||
#
|
|
||||||
# After this: unmount, insert, power on. Device connects to WiFi and
|
|
||||||
# self-installs BigBrother with no further operator interaction required.
|
|
||||||
#
|
|
||||||
# Usage: sudo bash scripts/preconfig_sd.sh SSID PASSPHRASE DEVICE_ID [MOUNT_POINT]
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
|
|
||||||
info() { echo -e "${GREEN}[+]${NC} $*"; }
|
|
||||||
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
|
||||||
error() { echo -e "${RED}[-]${NC} $*"; exit 1; }
|
|
||||||
step() { echo -e "${CYAN}[*]${NC} $*"; }
|
|
||||||
|
|
||||||
[[ $EUID -eq 0 ]] || error "Must run as root (sudo)"
|
|
||||||
|
|
||||||
SSID="${1:-}"
|
|
||||||
PASSPHRASE="${2:-}"
|
|
||||||
DEVICE_ID="${3:-}"
|
|
||||||
MOUNT="${4:-/mnt/opi}"
|
|
||||||
|
|
||||||
[[ -z "$SSID" || -z "$PASSPHRASE" || -z "$DEVICE_ID" ]] && {
|
|
||||||
echo "Usage: sudo $0 SSID PASSPHRASE DEVICE_ID [MOUNT_POINT]"
|
|
||||||
echo " DEVICE_ID short label for this unit (e.g. bb-01, drop-lab)"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
[[ -d "$MOUNT/etc" ]] || error "Mount point not valid: $MOUNT — is the SD card mounted?"
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
||||||
CREDS="${HOME}/.local/bin/creds"
|
|
||||||
|
|
||||||
# ── 1. WiFi ───────────────────────────────────────────────────────────────────
|
|
||||||
step "Writing WiFi profile..."
|
|
||||||
NM_DIR="${MOUNT}/etc/NetworkManager/system-connections"
|
|
||||||
NM_FILE="${NM_DIR}/wifi-bb.nmconnection"
|
|
||||||
mkdir -p "$NM_DIR"
|
|
||||||
|
|
||||||
# UUID is required — NM silently ignores keyfiles without it
|
|
||||||
WIFI_UUID=$(cat /proc/sys/kernel/random/uuid)
|
|
||||||
|
|
||||||
cat > "$NM_FILE" << EOF
|
|
||||||
[connection]
|
|
||||||
id=wifi-bb
|
|
||||||
uuid=${WIFI_UUID}
|
|
||||||
type=wifi
|
|
||||||
autoconnect=true
|
|
||||||
autoconnect-priority=10
|
|
||||||
|
|
||||||
[wifi]
|
|
||||||
mode=infrastructure
|
|
||||||
ssid=${SSID}
|
|
||||||
|
|
||||||
[wifi-security]
|
|
||||||
auth-alg=open
|
|
||||||
key-mgmt=wpa-psk
|
|
||||||
psk=${PASSPHRASE}
|
|
||||||
|
|
||||||
[ipv4]
|
|
||||||
method=auto
|
|
||||||
|
|
||||||
[ipv6]
|
|
||||||
addr-gen-mode=stable-privacy
|
|
||||||
method=auto
|
|
||||||
EOF
|
|
||||||
|
|
||||||
chmod 600 "$NM_FILE"
|
|
||||||
chown root:root "$NM_FILE"
|
|
||||||
info "WiFi profile written (SSID: ${SSID}, UUID: ${WIFI_UUID})"
|
|
||||||
|
|
||||||
# ── 2. Root password ──────────────────────────────────────────────────────────
|
|
||||||
step "Setting root password..."
|
|
||||||
read -s -r -p "New root password: " ROOT_PASS; echo
|
|
||||||
read -s -r -p "Confirm: " ROOT_PASS2; echo
|
|
||||||
[[ "$ROOT_PASS" == "$ROOT_PASS2" ]] || error "Passwords do not match"
|
|
||||||
HASH=$(openssl passwd -6 "$ROOT_PASS")
|
|
||||||
sed -i "s|^root:[^:]*:|root:${HASH}:|" "${MOUNT}/etc/shadow"
|
|
||||||
info "Root password set"
|
|
||||||
unset ROOT_PASS ROOT_PASS2
|
|
||||||
|
|
||||||
# ── 3. Per-device SSH keypair ─────────────────────────────────────────────────
|
|
||||||
step "Generating per-device SSH keypair..."
|
|
||||||
TMP_KEY=$(mktemp)
|
|
||||||
ssh-keygen -t ed25519 -N "" -C "" -f "$TMP_KEY" -q
|
|
||||||
PRIVKEY=$(cat "$TMP_KEY")
|
|
||||||
PUBKEY=$(cat "${TMP_KEY}.pub")
|
|
||||||
rm -f "$TMP_KEY" "${TMP_KEY}.pub"
|
|
||||||
|
|
||||||
SSH_DIR="${MOUNT}/root/.ssh"
|
|
||||||
mkdir -p "$SSH_DIR"
|
|
||||||
echo "$PUBKEY" > "${SSH_DIR}/authorized_keys"
|
|
||||||
chmod 700 "$SSH_DIR"
|
|
||||||
chmod 600 "${SSH_DIR}/authorized_keys"
|
|
||||||
|
|
||||||
INFISICAL_KEY="SSH_PRIVKEY_$(echo "$DEVICE_ID" | tr '[:lower:]-' '[:upper:]_')"
|
|
||||||
if [[ -x "$CREDS" ]] && "$CREDS" set "$INFISICAL_KEY" "$PRIVKEY" bigbrother 2>/dev/null; then
|
|
||||||
info "Private key stored in Infisical: bigbrother/${INFISICAL_KEY}"
|
|
||||||
else
|
|
||||||
warn "Could not store in Infisical. SAVE THIS PRIVATE KEY NOW:"
|
|
||||||
echo "---"
|
|
||||||
echo "$PRIVKEY"
|
|
||||||
echo "---"
|
|
||||||
fi
|
|
||||||
unset PRIVKEY
|
|
||||||
info "Public key written to device (no operator keys present)"
|
|
||||||
|
|
||||||
# ── 4. Copy BigBrother source onto card ───────────────────────────────────────
|
|
||||||
step "Copying BigBrother source to SD card..."
|
|
||||||
SRC_DEST="${MOUNT}/root/bb-src"
|
|
||||||
rm -rf "$SRC_DEST"
|
|
||||||
rsync -a \
|
|
||||||
--exclude='.git' \
|
|
||||||
--exclude='.venv' \
|
|
||||||
--exclude='storage/' \
|
|
||||||
--exclude='__pycache__' \
|
|
||||||
--exclude='*.pyc' \
|
|
||||||
--exclude='*.db' \
|
|
||||||
"${SCRIPT_DIR}/" "$SRC_DEST/"
|
|
||||||
info "Source copied to /root/bb-src/"
|
|
||||||
|
|
||||||
# ── 5. First-boot systemd service ─────────────────────────────────────────────
|
|
||||||
step "Installing first-boot service..."
|
|
||||||
|
|
||||||
# Sentinel file — service checks for this before running
|
|
||||||
touch "${MOUNT}/root/.bb-firstboot-needed"
|
|
||||||
|
|
||||||
# The service wrapper — logs to /root/bb-firstboot.log
|
|
||||||
cat > "${MOUNT}/root/bb-firstboot-run.sh" << 'RUNEOF'
|
|
||||||
#!/usr/bin/env bash
|
|
||||||
# Runs once on first boot, then disables itself
|
|
||||||
set -euo pipefail
|
|
||||||
LOG=/root/bb-firstboot.log
|
|
||||||
exec >> "$LOG" 2>&1
|
|
||||||
|
|
||||||
echo "=== BigBrother first-boot setup: $(date) ==="
|
|
||||||
|
|
||||||
# Wait for actual internet connectivity (apt needs it)
|
|
||||||
echo "[*] Waiting for internet..."
|
|
||||||
for i in $(seq 1 30); do
|
|
||||||
if curl -sf --max-time 5 http://deb.debian.org/debian > /dev/null 2>&1; then
|
|
||||||
echo "[+] Internet OK"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
if [[ $i -eq 30 ]]; then
|
|
||||||
echo "[-] No internet after 90s — aborting"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
sleep 3
|
|
||||||
done
|
|
||||||
|
|
||||||
cd /root/bb-src
|
|
||||||
bash setup.sh --enable-service
|
|
||||||
|
|
||||||
echo "[+] Setup complete: $(date)"
|
|
||||||
|
|
||||||
# Disable ourselves
|
|
||||||
rm -f /root/.bb-firstboot-needed
|
|
||||||
systemctl disable bb-firstboot.service
|
|
||||||
RUNEOF
|
|
||||||
chmod 755 "${MOUNT}/root/bb-firstboot-run.sh"
|
|
||||||
|
|
||||||
cat > "${MOUNT}/etc/systemd/system/bb-firstboot.service" << 'SVCEOF'
|
|
||||||
[Unit]
|
|
||||||
Description=BigBrother first-boot installation
|
|
||||||
After=network-online.target
|
|
||||||
Wants=network-online.target
|
|
||||||
ConditionPathExists=/root/.bb-firstboot-needed
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
ExecStart=/root/bb-firstboot-run.sh
|
|
||||||
RemainAfterExit=yes
|
|
||||||
TimeoutStartSec=600
|
|
||||||
StandardOutput=journal
|
|
||||||
StandardError=journal
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
SVCEOF
|
|
||||||
|
|
||||||
# Enable the service via symlink (same as systemctl enable without running systemd)
|
|
||||||
WANTS_DIR="${MOUNT}/etc/systemd/system/multi-user.target.wants"
|
|
||||||
mkdir -p "$WANTS_DIR"
|
|
||||||
ln -sf /etc/systemd/system/bb-firstboot.service "${WANTS_DIR}/bb-firstboot.service"
|
|
||||||
info "First-boot service installed and enabled"
|
|
||||||
|
|
||||||
# ── 6. Hostname + wizard bypass ───────────────────────────────────────────────
|
|
||||||
echo "orangepi" > "${MOUNT}/etc/hostname"
|
|
||||||
rm -f "${MOUNT}/root/.not_logged_in_yet"
|
|
||||||
info "Hostname set, first-login wizard disabled"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
info "SD card ready. Unmount and insert."
|
|
||||||
echo ""
|
|
||||||
echo " On boot the device will:"
|
|
||||||
echo " 1. Connect to ${SSID}"
|
|
||||||
echo " 2. Run setup.sh automatically (~5-10 min)"
|
|
||||||
echo " 3. Start bigbrother-core.service"
|
|
||||||
echo ""
|
|
||||||
echo " To connect after setup:"
|
|
||||||
if [[ -x "$CREDS" ]]; then
|
|
||||||
echo " ${CREDS} get ${INFISICAL_KEY} bigbrother > /tmp/${DEVICE_ID}.key"
|
|
||||||
echo " chmod 600 /tmp/${DEVICE_ID}.key"
|
|
||||||
echo " ssh -i /tmp/${DEVICE_ID}.key root@<DEVICE_IP>"
|
|
||||||
fi
|
|
||||||
echo ""
|
|
||||||
echo " Monitor first-boot: ssh ... 'tail -f /root/bb-firstboot.log'"
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Write a NetworkManager WiFi profile to an unmounted Armbian SD card.
|
|
||||||
# Run this on the machine where the SD card is mounted BEFORE first boot.
|
|
||||||
#
|
|
||||||
# Usage: bash scripts/setup_wifi.sh SSID PASSPHRASE [MOUNT_POINT]
|
|
||||||
# MOUNT_POINT defaults to /media/$USER/armbi_root
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
|
||||||
info() { echo -e "${GREEN}[+]${NC} $*"; }
|
|
||||||
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
|
||||||
error() { echo -e "${RED}[-]${NC} $*"; exit 1; }
|
|
||||||
|
|
||||||
SSID="${1:-}"
|
|
||||||
PASSPHRASE="${2:-}"
|
|
||||||
MOUNT="${3:-/media/${USER:-root}/armbi_root}"
|
|
||||||
|
|
||||||
[[ -z "$SSID" || -z "$PASSPHRASE" ]] && {
|
|
||||||
echo "Usage: $0 SSID PASSPHRASE [MOUNT_POINT]"
|
|
||||||
echo " MOUNT_POINT defaults to /media/\$USER/armbi_root"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
[[ -d "$MOUNT" ]] || error "Mount point not found: $MOUNT — is the SD card mounted?"
|
|
||||||
|
|
||||||
NM_DIR="${MOUNT}/etc/NetworkManager/system-connections"
|
|
||||||
NM_FILE="${NM_DIR}/wifi-bb.nmconnection"
|
|
||||||
|
|
||||||
mkdir -p "$NM_DIR"
|
|
||||||
|
|
||||||
cat > "$NM_FILE" << EOF
|
|
||||||
[connection]
|
|
||||||
id=wifi-bb
|
|
||||||
type=wifi
|
|
||||||
autoconnect=true
|
|
||||||
|
|
||||||
[wifi]
|
|
||||||
mode=infrastructure
|
|
||||||
ssid=${SSID}
|
|
||||||
|
|
||||||
[wifi-security]
|
|
||||||
auth-alg=open
|
|
||||||
key-mgmt=wpa-psk
|
|
||||||
psk=${PASSPHRASE}
|
|
||||||
|
|
||||||
[ipv4]
|
|
||||||
method=auto
|
|
||||||
|
|
||||||
[ipv6]
|
|
||||||
method=disabled
|
|
||||||
EOF
|
|
||||||
|
|
||||||
chmod 600 "$NM_FILE"
|
|
||||||
info "WiFi profile written to ${NM_FILE}"
|
|
||||||
info "SSID: ${SSID}"
|
|
||||||
echo ""
|
|
||||||
info "After first boot, device should connect automatically."
|
|
||||||
warn "If it doesn't connect, SSH in via Ethernet or serial and run:"
|
|
||||||
warn " nmcli dev wifi connect '${SSID}' password '<passphrase>'"
|
|
||||||
echo ""
|
|
||||||
info "Once connected, deploy BigBrother with:"
|
|
||||||
info " bash scripts/deploy.sh root@<DEVICE_IP> --enable-service"
|
|
||||||
Reference in New Issue
Block a user