Push Tracker
ria-ran--sim-drop/scripts/setup_wifi_ap.sh
A ashkan@beigi.net 035bc59460 Harden deployment: DB ownership and config secrecy
- Restrict the deployed /etc/esim-provisioning/config.yaml to 600 owned by the
  esim service user; it holds the OP key and was previously world-readable.
- Chown data/ AFTER init_db runs (as root) so the SQLite file is owned by the
  esim service user; the service runs with ReadWritePaths=.../data and could
  not write a root-owned database before.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 21:59:27 +00:00

179 lines
5.2 KiB
Bash
Executable File

#!/bin/bash
# setup_wifi_ap.sh — Configure a Raspberry Pi as a WiFi AP + captive portal
# for the eSIM provisioning system.
# Run as root: sudo bash scripts/setup_wifi_ap.sh
set -euo pipefail
[ "$(id -u)" = "0" ] || { echo "ERROR: Must run as root (sudo)."; exit 1; }
SSID="${ESIM_SSID:-Emergency-5G}"
CHANNEL="${ESIM_CHANNEL:-6}"
AP_IP="${ESIM_AP_IP:-192.168.4.1}"
DHCP_RANGE="${ESIM_DHCP_RANGE:-192.168.4.10,192.168.4.250,24h}"
INSTALL_DIR="${ESIM_INSTALL_DIR:-/opt/esim-provisioning}"
IFACE="${ESIM_IFACE:-wlan0}"
FLASK_PORT="${ESIM_FLASK_PORT:-5000}"
REPO_DIR="$(cd "$(dirname "$0")/.." && pwd)"
echo "=== eSIM Provisioning — WiFi AP Setup ==="
echo " SSID: $SSID"
echo " Channel: $CHANNEL"
echo " AP IP: $AP_IP"
echo " Interface: $IFACE"
echo " Install dir: $INSTALL_DIR"
echo ""
# ---- 1. Install packages ----
echo "[1/8] Installing packages..."
apt-get update -qq
apt-get install -y hostapd dnsmasq nginx python3 python3-pip python3-venv
# ---- 2. Static IP on wlan0 ----
echo "[2/8] Configuring static IP on $IFACE..."
DHCPCD_CONF=/etc/dhcpcd.conf
if ! grep -q "interface $IFACE" "$DHCPCD_CONF" 2>/dev/null; then
cat >> "$DHCPCD_CONF" << EOF
interface $IFACE
static ip_address=$AP_IP/24
nohook wpa_supplicant
EOF
fi
# ---- 3. hostapd ----
echo "[3/8] Configuring hostapd..."
cat > /etc/hostapd/hostapd.conf << EOF
interface=$IFACE
driver=nl80211
ssid=$SSID
utf8_ssid=1
hw_mode=g
channel=$CHANNEL
ieee80211n=1
wmm_enabled=1
auth_algs=1
ignore_broadcast_ssid=0
max_num_sta=50
EOF
sed -i 's|#DAEMON_CONF=.*|DAEMON_CONF="/etc/hostapd/hostapd.conf"|' /etc/default/hostapd
# ---- 4. dnsmasq ----
echo "[4/8] Configuring dnsmasq..."
# Back up original if present
[ -f /etc/dnsmasq.conf ] && mv /etc/dnsmasq.conf /etc/dnsmasq.conf.bak
cat > /etc/dnsmasq.conf << EOF
interface=$IFACE
bind-interfaces
dhcp-range=$DHCP_RANGE
address=/#/$AP_IP
no-resolv
dhcp-leasefile=/var/lib/misc/dnsmasq.leases
log-queries
log-dhcp
EOF
# ---- 5. nginx ----
echo "[5/8] Configuring nginx..."
cat > /etc/nginx/sites-available/esim-provisioning << EOF
server {
listen 80 default_server;
server_name _;
location = / {
return 302 http://$AP_IP/activate;
}
location /activate {
alias $INSTALL_DIR/portal/;
index index.html;
try_files \$uri \$uri/ /index.html;
}
location /css/ { alias $INSTALL_DIR/portal/css/; }
location /js/ { alias $INSTALL_DIR/portal/js/; }
location /api/ {
proxy_pass http://127.0.0.1:$FLASK_PORT;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
}
location / {
return 302 http://$AP_IP/activate;
}
}
EOF
ln -sf /etc/nginx/sites-available/esim-provisioning /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t
# ---- 6. IP forwarding ----
echo "[6/8] Enabling IP forwarding..."
sed -i 's|#net.ipv4.ip_forward=1|net.ipv4.ip_forward=1|' /etc/sysctl.conf
sysctl -w net.ipv4.ip_forward=1
# ---- 7. iptables — captive portal redirect ----
echo "[7/8] Configuring iptables..."
# Redirect all HTTP traffic from wlan0 clients to nginx (captive portal)
iptables -t nat -A PREROUTING -i "$IFACE" -p tcp --dport 80 -j DNAT \
--to-destination "$AP_IP:80" 2>/dev/null || true
# Persist iptables rules
apt-get install -y iptables-persistent
netfilter-persistent save
# ---- 8. Deploy server and services ----
echo "[8/8] Deploying server..."
mkdir -p "$INSTALL_DIR" /etc/esim-provisioning
cp -r "$REPO_DIR/server" "$INSTALL_DIR/"
cp -r "$REPO_DIR/portal" "$INSTALL_DIR/"
cp -r "$REPO_DIR/scripts" "$INSTALL_DIR/"
cp -r "$REPO_DIR/requirements.txt" "$INSTALL_DIR/"
[ -f /etc/esim-provisioning/config.yaml ] || \
cp "$REPO_DIR/config.yaml.example" /etc/esim-provisioning/config.yaml
# Create venv and install deps
python3 -m venv "$INSTALL_DIR/venv"
"$INSTALL_DIR/venv/bin/pip" install -q -r "$INSTALL_DIR/requirements.txt"
# Create system user
id esim &>/dev/null || useradd --system --no-create-home --shell /usr/sbin/nologin esim
# Restrict config — it holds the OP key and other secrets. Readable only by
# the service user, not world-readable.
chown esim:esim /etc/esim-provisioning/config.yaml
chmod 600 /etc/esim-provisioning/config.yaml
mkdir -p "$INSTALL_DIR/data"
# Install systemd services
cp "$REPO_DIR/systemd/esim-provisioning.service" /etc/systemd/system/
cp "$REPO_DIR/systemd/captive-portal.service" /etc/systemd/system/
systemctl daemon-reload
# Initialise DB
CONFIG_PATH=/etc/esim-provisioning/config.yaml \
"$INSTALL_DIR/venv/bin/python" "$INSTALL_DIR/scripts/init_db.py"
# Own data (incl. the DB file just created by root) as the service user, which
# runs with ReadWritePaths=.../data — otherwise the service cannot write it.
chown -R esim:esim "$INSTALL_DIR/data"
# Enable everything
systemctl unmask hostapd
systemctl enable hostapd dnsmasq nginx esim-provisioning captive-portal
systemctl restart hostapd dnsmasq nginx esim-provisioning
echo ""
echo "=== Setup Complete ==="
echo ""
echo "Edit /etc/esim-provisioning/config.yaml to set your MCC/MNC/OP key."
echo "Then: systemctl restart esim-provisioning"
echo ""
echo "Clients can connect to WiFi '$SSID' and will be redirected to the"
echo "eSIM activation portal at http://$AP_IP/activate"