# Unit 08 — System Configuration Files ## Goal Production-ready nginx, dnsmasq, hostapd configs and systemd service units. ## Files - `configs/nginx.conf` - `configs/dnsmasq.conf` - `configs/hostapd.conf` - `systemd/esim-provisioning.service` - `systemd/captive-portal.service` --- ## `configs/nginx.conf` ```nginx # Captive portal + reverse proxy for eSIM provisioning server server { listen 80 default_server; server_name _; # Redirect any request not matching known paths to the activation portal location = / { return 302 http://192.168.4.1/activate; } # Serve the captive portal static files location /activate { alias /opt/esim-provisioning/portal/; index index.html; try_files $uri $uri/ /index.html; } location /css/ { alias /opt/esim-provisioning/portal/css/; } location /js/ { alias /opt/esim-provisioning/portal/js/; } # Proxy API calls to Flask backend location /api/ { proxy_pass http://127.0.0.1:5000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_connect_timeout 10s; proxy_read_timeout 30s; } # Catch-all: redirect everything else to activate location / { return 302 http://192.168.4.1/activate; } } ``` --- ## `configs/dnsmasq.conf` ```conf # eSIM Provisioning - DNS/DHCP configuration # Only listen on WiFi AP interface interface=wlan0 bind-interfaces # DHCP range dhcp-range=192.168.4.10,192.168.4.250,24h # DNS: return WiFi AP IP for ALL domains (captive portal hijacking) address=/#/192.168.4.1 # Prevent upstream DNS resolution (keep it local) no-resolv # DHCP lease file dhcp-leasefile=/var/lib/misc/dnsmasq.leases # Log queries (useful for debugging) log-queries log-dhcp ``` --- ## `configs/hostapd.conf` ```conf # WiFi AP configuration for eSIM provisioning drop node interface=wlan0 driver=nl80211 # Network identity ssid=Emergency-5G utf8_ssid=1 # Radio settings hw_mode=g channel=6 ieee80211n=1 wmm_enabled=1 # Open network (no password) — intentional for emergency access auth_algs=1 ignore_broadcast_ssid=0 # Country code — adjust per deployment region country_code=CA # Max clients max_num_sta=50 ``` --- ## `systemd/esim-provisioning.service` ```ini [Unit] Description=eSIM Provisioning Server After=network.target Wants=network.target [Service] Type=simple User=esim Group=esim WorkingDirectory=/opt/esim-provisioning Environment=CONFIG_PATH=/etc/esim-provisioning/config.yaml ExecStart=/opt/esim-provisioning/venv/bin/python -m server.app Restart=always RestartSec=5 StandardOutput=journal StandardError=journal SyslogIdentifier=esim-provisioning # Security hardening NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ReadWritePaths=/opt/esim-provisioning/data [Install] WantedBy=multi-user.target ``` --- ## `systemd/captive-portal.service` ```ini [Unit] Description=Captive Portal (nginx + dnsmasq) After=network.target hostapd.service Wants=nginx.service dnsmasq.service [Service] Type=oneshot RemainAfterExit=yes ExecStart=/bin/systemctl start nginx dnsmasq ExecStop=/bin/systemctl stop nginx dnsmasq [Install] WantedBy=multi-user.target ``` --- ## Deployment Notes Install configs: ```bash # nginx sudo cp configs/nginx.conf /etc/nginx/sites-available/esim-provisioning sudo ln -sf /etc/nginx/sites-available/esim-provisioning /etc/nginx/sites-enabled/ sudo rm -f /etc/nginx/sites-enabled/default sudo nginx -t && sudo systemctl reload nginx # dnsmasq sudo cp configs/dnsmasq.conf /etc/dnsmasq.d/esim-provisioning.conf sudo systemctl restart dnsmasq # hostapd sudo cp configs/hostapd.conf /etc/hostapd/hostapd.conf sudo systemctl unmask hostapd sudo systemctl enable --now hostapd # systemd services sudo cp systemd/esim-provisioning.service /etc/systemd/system/ sudo cp systemd/captive-portal.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable --now esim-provisioning captive-portal ``` ## Verification ```bash sudo nginx -t # Config syntax check sudo systemctl status esim-provisioning sudo systemctl status captive-portal nslookup google.com 192.168.4.1 # Should return 192.168.4.1 curl http://192.168.4.1/api/status # Should return JSON status ```