diff --git a/docs/analysis/BOSE-LAB-RUNBOOK.md b/docs/analysis/BOSE-LAB-RUNBOOK.md
index d5a0c97..36c4cba 100644
--- a/docs/analysis/BOSE-LAB-RUNBOOK.md
+++ b/docs/analysis/BOSE-LAB-RUNBOOK.md
@@ -6,13 +6,13 @@
## Prerequisites
-| Component | Details |
-|--------------------|------------------------------------------------------------|
-| Raspberry Pi | Pi 3 or newer, Raspberry Pi OS (Bullseye or newer) |
-| Network interfaces | `eth0` → LAN cable to FritzBox, `wlan0` → own Access Point |
-| FritzBox | Unchanged, assigns an IP to the Pi via DHCP on eth0 |
-| Custom DNS Server | Already present, incl. custom CA certificate |
-| Phone | Android, connects to the Pi's Wi-Fi |
+| Component | Details |
+|--------------------|------------------------------------------------------------------|
+| Raspberry Pi | Pi 3 or newer, Raspberry Pi OS (Bullseye, Bookworm, Trixie) |
+| Network interfaces | `eth0` → LAN cable to FritzBox, `wlan0` → own Access Point |
+| FritzBox | Unchanged, assigns an IP to the Pi via DHCP on eth0 |
+| Custom DNS Server | Already present (or see Appendix A), incl. custom CA certificate |
+| Phone | Android, connects to the Pi's Wi-Fi |
### Network Architecture
@@ -41,8 +41,7 @@ Android Phone
sudo apt update && sudo apt install -y \
hostapd \ # Wi-Fi Access Point daemon
dnsmasq \ # DHCP + DNS forwarding
- iptables \ # NAT / firewall / forwarding
- iptables-persistent \ # Save rules across reboots
+ nftables \ # Modern NAT / firewall / forwarding
tcpdump \ # Packet capture at all levels
wireshark-common # tshark CLI (optional, for live analysis)
```
@@ -58,31 +57,53 @@ The Pi must forward packets between `wlan0` (phone) and `eth0` (FritzBox).
sudo sysctl -w net.ipv4.ip_forward=1
# Permanent (survives reboots)
-echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
+# On modern Debian, using a dedicated file in sysctl.d/ is more reliable:
+echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-ip-forward.conf
+
+# Apply changes immediately
+sudo sysctl --system
+```
+
+**Verify:**
+```bash
+# After a reboot, ensure it is still '1'
+cat /proc/sys/net/ipv4/ip_forward
```
---
-## Step 3 – Static IP on wlan0
+## Step 3 – Static IP on wlan0 (systemd-networkd)
-`wlan0` gets a fixed IP – this is the gateway for the phone.
+On modern Debian (Bookworm/Trixie), `dhcpcd` is replaced by `systemd-networkd`.
```bash
-# Append to /etc/dhcpcd.conf
-sudo tee -a /etc/dhcpcd.conf << 'EOF'
+# Create network configuration
+sudo tee /etc/systemd/network/08-wlan0.network << 'EOF'
+[Match]
+Name=wlan0
-interface wlan0
- static ip_address=192.168.10.1/24
- nohook wpa_supplicant # wlan0 becomes AP, not Wi-Fi client
+[Network]
+Address=192.168.10.1/24
+IPForward=yes
+ConfigureWithoutCarrier=yes
+DHCP=no
+IPv6AcceptRA=no
EOF
-sudo systemctl restart dhcpcd
+# Restart service
+sudo systemctl enable systemd-networkd
+sudo systemctl restart systemd-networkd
+
+# Ensure wpa_supplicant and NetworkManager don't interfere
+sudo nmcli device set wlan0 managed no
+sudo systemctl stop wpa_supplicant@wlan0
+sudo systemctl mask wpa_supplicant@wlan0
```
**Verify:**
```bash
ip addr show wlan0
-# Expected: inet 192.168.10.1/24
+# Expected: ONLY inet 192.168.10.1/24 (NO second DHCP IP)
```
---
@@ -93,22 +114,20 @@ ip addr show wlan0
sudo tee /etc/hostapd/hostapd.conf << 'EOF'
interface=wlan0
driver=nl80211
-ssid=Bose-Lab # Wi-Fi name – phone connects here
-hw_mode=g
-channel=6
+ssid=Bose-Lab
+hw_mode=b
+#hw_mode=g
+channel=1
+#channel=6
wmm_enabled=0
auth_algs=1
wpa=2
-wpa_passphrase=secret123 # Adjust password
+wpa_passphrase=secret123
wpa_key_mgmt=WPA-PSK
wpa_pairwise=CCMP
EOF
-# Enter config path
-sudo sed -i \
- 's|#DAEMON_CONF=""|DAEMON_CONF="/etc/hostapd/hostapd.conf"|' \
- /etc/default/hostapd
-
+# The modern way is to just use hostapd.service which defaults to /etc/hostapd/hostapd.conf
sudo systemctl unmask hostapd
sudo systemctl enable --now hostapd
```
@@ -130,10 +149,10 @@ dnsmasq gives the phone an IP and forwards DNS queries to the custom DNS server.
sudo mv /etc/dnsmasq.conf /etc/dnsmasq.conf.bak
sudo tee /etc/dnsmasq.conf << 'EOF'
-interface=wlan0 # Only listen on AP interface
-dhcp-range=192.168.10.100,192.168.10.200,24h # IP pool for clients
-dhcp-option=3,192.168.10.1 # Gateway = Pi
-dhcp-option=6,192.168.10.1 # DNS = Pi (custom DNS server)
+interface=wlan0
+dhcp-range=192.168.10.100,192.168.10.200,24h
+dhcp-option=3,192.168.10.1
+dhcp-option=6,192.168.10.1
# DNS Upstream: custom server on localhost (adjust port if necessary)
server=127.0.0.1#5353 # Example: custom server on port 5353
@@ -154,31 +173,76 @@ sudo tail -f /var/log/dnsmasq.log
---
-## Step 6 – NAT and Forwarding (iptables)
+## Step 6 – NAT and Forwarding (nftables)
-The Pi routes the phone's traffic to the FritzBox and back.
+On modern Debian (Bookworm/Trixie), `nftables` is the default and recommended way to manage NAT and traffic forwarding.
```bash
-# NAT: outgoing packets get the Pi's IP (eth0)
-sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
+# Define the NAT and Forwarding rules
+sudo tee /etc/nftables.conf << 'EOF'
+#!/usr/sbin/nft -f
-# Forwarding: Phone → Internet
-sudo iptables -A FORWARD -i wlan0 -o eth0 -j ACCEPT
+flush ruleset
-# Forwarding: Responses back to the phone
-sudo iptables -A FORWARD -i eth0 -o wlan0 \
- -m state --state RELATED,ESTABLISHED -j ACCEPT
+table inet filter {
+ chain forward {
+ type filter hook forward priority 0; policy drop;
-# Save rules permanently (iptables-persistent)
-sudo netfilter-persistent save
+ # Allow traffic from phone (wlan0) to internet (eth0)
+ iifname "wlan0" oifname "eth0" accept
+
+ # Allow established/related traffic back to the phone
+ iifname "eth0" oifname "wlan0" ct state established,related accept
+ }
+}
+
+table ip nat {
+ chain posterouting {
+ type nat hook postrouting priority 100; policy accept;
+
+ # MASQUERADE outgoing packets on eth0
+ oifname "eth0" masquerade
+ }
+}
+EOF
+
+# Enable and start nftables
+sudo systemctl enable nftables
+sudo systemctl restart nftables
```
**Verify:**
```bash
-sudo iptables -t nat -L -n -v
-# Expected: MASQUERADE rule on POSTROUTING for eth0
+sudo nft list ruleset
+# Expected: ruleset showing the forward and nat chains
```
+### WiFi "Bose-Lab" not visible?
+
+If you cannot see the `Bose-Lab` SSID on your phone:
+
+1. **Check hostapd status:** `sudo systemctl status hostapd`. If it failed with "nl80211: Driver does not support configured mode", try changing `hw_mode=g` to `hw_mode=b`.
+2. **Interface blocking:** Ensure `rfkill` hasn't blocked WiFi: `sudo rfkill unblock wlan`.
+3. **Country Code:** Some systems require a country code in `hostapd.conf` to enable the radio. Add `country_code=DE` (or your country) to the top of `/etc/hostapd/hostapd.conf` and restart hostapd: `sudo systemctl restart hostapd`.
+4. **Local Radio Check:** You can verify that the radio is actually configured as an AP: `iw dev wlan0 info`. Look for `type AP` and your SSID.
+ > **Note:** Do NOT rely on `iw dev wlan0 scan` for your own SSID; many WiFi drivers cannot "scan" and "broadcast" simultaneously.
+5. **Debug Mode:** If the scan still returns nothing, stop the service and run hostapd in the foreground to see real-time errors:
+ ```bash
+ sudo systemctl stop hostapd
+ sudo hostapd -dd /etc/hostapd/hostapd.conf
+ ```
+ Look for messages like `nl80211: Failed to set interface wlan0 into AP mode`. This usually means the hardware is busy or doesn't support the current `hw_mode` / `channel` combination.
+6. **Conflicting Services:** Ensure nothing else is managing `wlan0`. NetworkManager is common on modern Debian:
+ ```bash
+ sudo nmcli device set wlan0 managed no
+ ```
+7. **Ghost IP Conflict:** If `ip addr show wlan0` shows both `192.168.10.1` and another IP (like `192.168.178.x`), `hostapd` will fail. This is usually caused by NetworkManager managing the interface. Ensure you've run:
+ ```bash
+ sudo nmcli device set wlan0 managed no
+ # If the ghost IP is still there, remove it manually:
+ sudo ip addr del 192.168.178.X/24 dev wlan0
+ ```
+
---
## Step 7 – Install Custom CA Certificate on the Phone
@@ -187,6 +251,8 @@ Since a custom DNS server with a custom CA certificate is used, it must be trust
### Copy CA Certificate to the Pi (if not already there)
+If you haven't created a CA yet, follow **Appendix A** first.
+
```bash
# Certificate is located e.g. at /etc/my-dns-ca/ca.crt
# Temporarily make reachable via HTTP for easy download:
@@ -318,41 +384,246 @@ websocket.payload
## Step 10 – mitmproxy (optional, for HTTPS content)
-Only useful if the CA certificate on the phone is trusted and no certificate pinning is active.
+Only useful if the CA certificate on the phone is trusted and no certificate pinning is active. `mitmproxy` acts as a Man-in-the-Middle by generating fake, on-the-fly certificates for any domain (e.g., `global.api.bose.io`) using your custom CA.
+
+### 1. Configure mitmproxy to use your Custom CA
+
+By default, `mitmproxy` creates its own CA in `~/.mitmproxy/`. To ensure the phone (which already trusts your `ca.crt`) accepts the traffic, you must tell `mitmproxy` to use your existing CA:
```bash
-sudo apt install -y mitmproxy
+# mitmproxy expects the CA in a specific PEM format (cert + key in one file)
+sudo mkdir -p ~/.mitmproxy
+sudo cat /etc/my-dns-ca/ca.crt /etc/my-dns-ca/ca.key | sudo tee ~/.mitmproxy/mitmproxy-ca.pem > /dev/null
+```
+
+### 2. Install and Start mitmproxy
+
+```bash
+# Install mitmproxy binary (stable version for aarch64)
+cd /tmp
+wget https://downloads.mitmproxy.org/12.2.1/mitmproxy-12.2.1-linux-aarch64.tar.gz
+tar -xzf mitmproxy-12.2.1-linux-aarch64.tar.gz
+sudo mv mitmproxy mitmdump mitmweb /usr/local/bin/
+rm mitmproxy-12.2.1-linux-aarch64.tar.gz
+
+mitmproxy --version
# Transparent proxy on port 8080
+# It will now use the CA from ~/.mitmproxy/mitmproxy-ca.pem
mitmproxy --mode transparent --listen-port 8080
# Alternatively: mitmdump for automatic logging to file
-mitmdump --mode transparent --listen-port 8080 \
- -w /tmp/bose-https.mitm
+# mitmdump --mode transparent --listen-port 8080 -w /tmp/bose-https.mitm
```
-**iptables rule: redirect HTTPS traffic to mitmproxy**
+### 3. Troubleshooting: TLS Handshake Failures
+
+If you see `Client TLS handshake failed. The client does not trust the proxy's certificate for www.google.com` (or other domains) in the `mitmproxy` logs:
+
+1. **HSTS and Pre-installed Pinning:** High-security sites like `www.google.com` use **HSTS (HTTP Strict Transport Security)** and have their certificates hardcoded (pinned) into browsers like Chrome and the Android system. **These will always fail with a User-installed CA.**
+2. **User vs. System CA Store:** On Android 7.0+, apps **do not trust User-installed CAs by default**. They only trust the "System" store.
+ * **The Bose app:** If it fails, it's because it only trusts the System store or uses its own certificate pinning.
+ * **The Fix (Rooted Phone):** Use a Magisk module like `AlwaysTrustUserCerts` or manually move your `ca.crt` to `/system/etc/security/cacerts/` (see Step 7).
+3. **The "Golden Rule" - Verify the Proxy is Working:**
+ To confirm your CA and `mitmproxy` are correctly configured, test with a non-HSTS site on the phone's browser (e.g., `http://neverssl.com`). Once redirected to HTTPS, **inspect the certificate**. It should say it was issued by your "Bose-Lab Root CA" (or "SoundTouch Root CA").
+
+ * **If this works:** Your "factory" (mitmproxy + CA) is 100% correct. Any failure in the Bose app is due to its own security policy (ignore User Store or Pinning).
+ * **If this fails:** Your CA is not trusted by the browser or `mitmproxy` is not using your PEM file.
+
+ Alternatively, use `curl` from a terminal emulator on the phone:
+ ```bash
+ # This should work if the CA is in the user store and curl is told to use it
+ curl -v --cacert /path/to/ca.crt https://example.com
+ ```
+4. **Check mitmproxy CA:** Ensure `mitmproxy` is actually using your CA. When it starts, it should NOT generate a new CA in `~/.mitmproxy/mitmproxy-ca.pem` if you've already placed yours there.
+
+---
+
+**nftables rule: redirect HTTPS traffic to mitmproxy**
```bash
-# Only for wlan0 traffic (phone) → Port 443 → mitmproxy on 8080
-sudo iptables -t nat -A PREROUTING \
- -i wlan0 -p tcp --dport 443 \
- -j REDIRECT --to-port 8080
+# Create a temporary file for the redirection rule
+sudo nft add table ip mitm
+sudo nft add chain ip mitm prerouting { type nat hook prerouting priority -100 \; }
+sudo nft add rule ip mitm prerouting iifname "wlan0" tcp dport 443 redirect to :8080
```
**Remove rule when no longer needed:**
```bash
-sudo iptables -t nat -D PREROUTING \
- -i wlan0 -p tcp --dport 443 \
- -j REDIRECT --to-port 8080
+sudo nft delete table ip mitm
```
> **Detecting Certificate Pinning:** If the app immediately disconnects after mitmproxy redirection (connection reset directly after TLS ClientHello), pinning is active. In this case, Frida + root is needed to patch the pinning.
---
-## Helper Commands / Troubleshooting
+## Step 11 – Bypassing Android Trust Restrictions
+
+If `neverssl.com` works in the browser but the Bose app shows `TLS handshake failed` in `mitmproxy`, the app is either ignoring the **User CA store** (common on Android 7+) or using **Certificate Pinning**.
+
+### Option A: Move CA to System Store (Requires Root/Magisk)
+
+This is the most reliable way to make apps trust your CA without modifying the app itself.
+
+1. **Using Magisk (Recommended):**
+ Install the **"AlwaysTrustUserCerts"** or **"Move Certificates"** module in Magisk. It automatically mirrors all certificates from the User store to the System store on every boot.
+
+2. **Manual Move (via ADB):**
+ Android system certificates are stored in `/system/etc/security/cacerts/` and must be named using the hash of the certificate.
+
+ ```bash
+ # 1. Get the hash of your certificate
+ hash=$(openssl x509 -inform PEM -subject_hash_old -in ca.crt | head -1)
+
+ # 2. Rename the certificate locally
+ cp ca.crt ${hash}.0
+
+ # 3. Push to the phone (requires remounting /system as read-write)
+ adb push ${hash}.0 /sdcard/
+ adb shell
+ su
+ mount -o rw,remount /
+ cp /sdcard/${hash}.0 /system/etc/security/cacerts/
+ chmod 644 /system/etc/security/cacerts/${hash}.0
+ chown root:root /system/etc/security/cacerts/${hash}.0
+ reboot
+ ```
+
+### Option B: Patching the App (No Root Required)
+
+If you cannot root your phone, you can modify the app's APK to trust user-installed certificates. This involves obtaining the APK, decompiling it, adding a network security configuration, and then repackaging and signing it.
+
+#### 0. How to get the .apk file?
+
+You have two main ways to get the official Bose SoundTouch APK:
+
+**Method 1: Extract from your phone (Safest)**
+If the app is already installed on your phone, you can pull it using `adb`:
+```bash
+# 1. Find the package name (usually com.bose.soundtouch)
+adb shell pm list packages | grep bose
+
+# 2. Get the full path to the APK on the phone
+adb shell pm path com.bose.soundtouch
+# Output: package:/data/app/~~...==/com.bose.soundtouch-.../base.apk
+
+# 3. Pull the file to your computer
+adb pull /data/app/~~...==/com.bose.soundtouch-.../base.apk Bose-SoundTouch.apk
+```
+
+**Method 2: Download from a Mirror (Easiest)**
+You can download the APK from reputable third-party sites.
+> **Warning:** Always verify the site's reputation.
+* [APKMirror](https://www.apkmirror.com/apk/bose-corporation/bose-soundtouch/)
+* [APKPure](https://apkpure.com/bose-soundtouch/com.bose.soundtouch)
+
+#### 1. Automated Method: apk-mitm (Recommended)
+The easiest way is to use `apk-mitm`, which automates the entire process including fixing common certificate pinning libraries.
+
+```bash
+# Requires Node.js installed on your PC
+npx apk-mitm Bose-SoundTouch.apk
+```
+This will produce a `Bose-SoundTouch-patched.apk` which you can install on your phone.
+
+#### 2. Manual Method: Network Security Config
+If you prefer to do it manually:
+
+1. **Decompile the APK:**
+ ```bash
+ apktool d Bose-SoundTouch.apk
+ ```
+2. **Create/Modify `res/xml/network_security_config.xml`:**
+ ```xml
+
+
+
+
+
+
+
+
+
+ ```
+3. **Update `AndroidManifest.xml`:**
+ Ensure the `` tag includes: `android:networkSecurityConfig="@xml/network_security_config"`.
+4. **Repackage and Sign:**
+ ```bash
+ apktool b Bose-SoundTouch -o Bose-SoundTouch-patched.apk
+ # Sign with your own key
+ # 1. Generate a keystore (if you don't have one)
+ # Note: You can use ANY name/values here. The phone does not need to "know" or "trust" this key beforehand.
+ # It only needs the APK to be digitally signed so the Android installer accepts it.
+ keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000
+
+ # 2. Sign the APK
+ apksigner sign --ks my-release-key.keystore --out Bose-SoundTouch-patched-signed.apk Bose-SoundTouch-patched.apk
+
+ # Alternatively, use uber-apk-signer (recommended for simplicity)
+ # It handles zipalign and signing automatically.
+ java -jar uber-apk-signer.jar --apk Bose-SoundTouch-patched.apk
+ ```
+
+#### 3. Install the Patched APK
+
+Once you have your `Bose-SoundTouch-patched.apk` (and it is signed), you need to install it on your phone.
+
+**Important:** You must **uninstall the original Bose app first**. Android will not allow you to "update" the official app with your patched version because the digital signatures won't match.
+
+**Method 1: via ADB (Recommended)**
+```bash
+# 1. Uninstall the original app
+adb uninstall com.bose.soundtouch
+
+# 2. Install your patched version
+adb install Bose-SoundTouch-patched.apk
+```
+
+**Method 2: Manual Transfer**
+1. Copy the `Bose-SoundTouch-patched.apk` to your phone's storage (via USB, Google Drive, or the Pi's HTTP server).
+2. On the phone, use a File Manager to open the APK.
+3. If prompted, allow "Install from Unknown Sources" for your File Manager.
+
+### Option C: Patching the App with Frida (Requires Root)
+
+If the app uses **Certificate Pinning** (hardcoded hashes), even moving the CA to the System store won't work. You must disable the pinning check in the app's code.
+
+1. **Install Frida** on your PC and `frida-server` on the rooted phone.
+2. **Use a universal bypass script:**
+ ```bash
+ frida -U -f com.bose.soundtouch -l https://codeshare.frida.re/@pcipolloni/universal-android-ssl-pinning-bypass-with-frida/ --no-pause
+ ```
+ *(Replace `com.bose.soundtouch` with the actual package name if different).*
+
+## Step 12 – Alternative: Regular HTTP Proxy Mode
+
+If the **Transparent AP** setup (Steps 1–6) is too complex or you are experiencing routing issues, you can use `mitmproxy` as a **Regular HTTP Proxy**.
+
+### 1. How it works
+In this mode, the Pi acts as a simple server on port 8080. You tell your phone's Wi-Fi settings to send all traffic to `192.168.10.1:8080`.
+
+* **Pros:** No complex `nftables` or NAT rules required.
+* **Cons:** Many Android apps (and background processes) ignore system-wide proxy settings. **HTTPS still requires a trusted CA for decryption.**
+
+### 2. Start mitmproxy in Regular Mode
+```bash
+# Stop transparent mode first if it's running
+# No special flags needed for regular mode
+mitmproxy --listen-port 8080
+```
+
+### 3. Configure the Phone
+1. Go to **Settings → Wi-Fi → Bose-Lab**.
+2. Select **Modify Network** (or the "i" icon).
+3. Set **Proxy** to **Manual**.
+4. **Proxy hostname:** `192.168.10.1`
+5. **Proxy port:** `8080`
+6. Save and try to browse a site.
+
+---
+
+## Step 13 – Extracting for soundtouch-service
```bash
# Which IPs did the phone receive?
@@ -370,9 +641,8 @@ ip addr show
# Check routing table
ip route show
-# Show active iptables rules
-sudo iptables -L -n -v
-sudo iptables -t nat -L -n -v
+# Show active nftables rules
+sudo nft list ruleset
# All running tcpdump processes
pgrep -a tcpdump
@@ -391,10 +661,14 @@ ping 192.168.10.101 # Phone IP from dnsmasq.leases
After a Pi reboot, everything should come up automatically. If not:
```bash
-sudo systemctl start dhcpcd
-sudo systemctl start hostapd
-sudo systemctl start dnsmasq
-sudo netfilter-persistent reload
+# Restart and enable all core services
+sudo systemctl restart systemd-networkd
+sudo systemctl enable --now hostapd
+sudo systemctl enable --now dnsmasq
+sudo systemctl restart nftables
+
+# Verify the unmanaged state of wlan0 (nmcli)
+sudo nmcli device set wlan0 managed no
```
---
@@ -421,3 +695,83 @@ sudo netfilter-persistent reload
3. Document auth flow (OAuth2? Proprietary? Token format?)
4. Build a minimal mock server simulating the critical endpoints
5. Testing: App against mock server → does pairing work offline?
+
+---
+
+## Appendix A – Generating a Custom CA Certificate
+
+If you don't have a custom DNS server with a CA yet, you can create one directly on the Pi. Alternatively, if you are already using the `soundtouch-service` from this repository, you can reuse its CA certificate located in the `data/certs/` directory.
+
+### 0. (Optional) Copy an Existing CA from another host
+
+If you are already using the `soundtouch-service` on another machine (e.g., your notebook), you can copy the existing CA to the Pi instead of generating a new one:
+
+```bash
+# On your Pi:
+sudo mkdir -p /etc/my-dns-ca
+sudo chown $USER:$USER /etc/my-dns-ca
+
+# Run this on your notebook (replace hostnames and paths):
+# Note: This is easiest if your SSH key is added to the Pi and soundtouch-service host.
+# If you run into permission issues with sudo, ensure the source user has passwordless sudo for 'cat'.
+
+# Step A: Download from source to your notebook
+ssh soundtouch-service "sudo cat /var/lib/soundtouch-service/certs/ca.crt" > ca.crt
+ssh soundtouch-service "sudo cat /var/lib/soundtouch-service/certs/ca.key" > ca.key
+
+# Step B: Upload from notebook to the Pi
+scp ca.crt ca.key soundtouch-access-point:/tmp/
+ssh soundtouch-access-point "sudo mv /tmp/ca.crt /tmp/ca.key /etc/my-dns-ca/ && sudo chown root:root /etc/my-dns-ca/ca.*"
+rm ca.crt ca.key
+```
+
+### 1. Create CA Key and Certificate
+
+```bash
+sudo mkdir -p /etc/my-dns-ca
+cd /etc/my-dns-ca
+
+# Generate CA private key
+sudo openssl genrsa -out ca.key 4096
+
+# Generate Root CA certificate
+# Note: we explicitly add basicConstraints=CA:TRUE for modern TLS clients
+sudo openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
+ -out ca.crt \
+ -subj "/C=DE/O=Bose-Lab/CN=Bose-Lab Root CA" \
+ -addext "basicConstraints=critical,CA:TRUE" \
+ -addext "keyUsage=critical,keyCertSign,cRLSign"
+```
+
+### 2. Generate a Certificate for Interception (Example)
+
+To intercept `global.api.bose.io`, you need a certificate for it, signed by your CA:
+
+```bash
+# Generate server key
+sudo openssl genrsa -out bose.key 2048
+
+# Create CSR (Certificate Signing Request) configuration
+sudo tee bose.ext << 'EOF'
+authorityKeyIdentifier=keyid,issuer
+basicConstraints=CA:FALSE
+keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
+subjectAltName = @alt_names
+
+[alt_names]
+DNS.1 = global.api.bose.io
+DNS.2 = *.bose.io
+EOF
+
+# Generate CSR
+sudo openssl req -new -key bose.key -out bose.csr \
+ -subj "/C=DE/O=Bose-Lab/CN=global.api.bose.io"
+
+# Sign the certificate with your CA
+sudo openssl x509 -req -in bose.csr -CA ca.crt -CAkey ca.key \
+ -CAcreateserial -out bose.crt -days 365 -sha256 -extfile bose.ext
+```
+
+### 3. Usage in your DNS/HTTPS Server
+
+Your custom server (e.g., a small Go or Python script) would then use `bose.crt` and `bose.key` to serve HTTPS traffic for those domains.