-
-
Save HackingGate/1508e7a1d7eeb1145b2a32c15606f774 to your computer and use it in GitHub Desktop.
| #!/bin/bash | |
| set -e | |
| set -o pipefail | |
| # --- System and Time Configuration --- | |
| # Set system timezone to Tokyo, Japan | |
| sudo timedatectl set-timezone Asia/Tokyo | |
| # Configure hardware clock to use UTC (recommended for Linux) | |
| sudo timedatectl set-local-rtc 0 | |
| # Display current time and date settings | |
| timedatectl | |
| # Reference for dual-booting with Windows 11 (requires Windows to use UTC) | |
| # https://gist.github.com/HackingGate/180aafbc6342ad4b1cb31309fa83c91a | |
| # --- Core Package Installation --- | |
| # Update package lists and upgrade the system | |
| sudo apt update | |
| sudo apt upgrade -y | |
| # Install essential development tools, utilities, and Nvidia dependencies | |
| sudo apt install emacs-nox vim-nox neovim curl wget gh git build-essential zsh efibootmgr jq fastfetch htop dkms linux-headers-$(uname -r) firmware-misc-nonfree -y | |
| # --- Nvidia Driver Installation (Debian 13 "Trixie" Method) --- | |
| # Support for GeForce 700 series and newer GPUs (Version 550.163.01) | |
| # For older devices, consider Version 535.216.03 or nouveau | |
| # Configure APT sources to include contrib, non-free, and non-free-firmware for drivers | |
| echo "Adding contrib, non-free, and non-free-firmware components to sources..." | |
| sudo tee /etc/apt/sources.list.d/debian.sources > /dev/null <<'EOF' | |
| Types: deb deb-src | |
| URIs: http://deb.debian.org/debian | |
| Suites: trixie | |
| Components: main contrib non-free non-free-firmware | |
| Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg | |
| Types: deb deb-src | |
| URIs: http://security.debian.org/debian-security | |
| Suites: trixie-security | |
| Components: main contrib non-free non-free-firmware | |
| Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg | |
| EOF | |
| # Check if system uses dracut and configure it for nvidia | |
| if command -v dracut &> /dev/null; then | |
| echo "System uses dracut - configuring for NVIDIA..." | |
| sudo mkdir -p /etc/dracut.conf.d | |
| sudo tee /etc/dracut.conf.d/10-nvidia.conf > /dev/null <<'EOF' | |
| install_items+=" /etc/modprobe.d/nvidia-blacklists-nouveau.conf /etc/modprobe.d/nvidia.conf /etc/modprobe.d/nvidia-options.conf " | |
| EOF | |
| echo "Dracut configuration for NVIDIA created." | |
| fi | |
| # Update package lists | |
| echo "Updating package lists..." | |
| sudo apt update | |
| # Install linux-headers for current kernel (required for DKMS) | |
| echo "Installing linux headers for kernel $(uname -r)..." | |
| sudo apt install linux-headers-$(uname -r) -y | |
| # Install Nvidia proprietary drivers and DKMS | |
| sudo apt install nvidia-kernel-dkms nvidia-driver firmware-misc-nonfree -y | |
| # Verify DKMS build status | |
| echo "Checking DKMS build status..." | |
| sudo dkms status | |
| # Configure NVIDIA options for Wayland support and suspend/hibernate (if applicable) | |
| echo "Configuring NVIDIA options for Wayland and power management..." | |
| # Enable kernel modesetting for NVIDIA Wayland support | |
| echo "Enabling NVIDIA kernel modesetting for Wayland..." | |
| NVIDIA_GRUB_CONFIG="/etc/default/grub.d/nvidia-modeset.cfg" | |
| NVIDIA_CMDLINE='GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX nvidia-drm.modeset=1 nvidia-drm.fbdev=1"' | |
| if [ ! -f "$NVIDIA_GRUB_CONFIG" ]; then | |
| sudo mkdir -p "$(dirname "$NVIDIA_GRUB_CONFIG")" | |
| echo "$NVIDIA_CMDLINE" | sudo tee "$NVIDIA_GRUB_CONFIG" > /dev/null | |
| echo "Created NVIDIA modeset configuration in GRUB" | |
| elif ! grep -q "nvidia-drm.modeset=1" "$NVIDIA_GRUB_CONFIG"; then | |
| echo "$NVIDIA_CMDLINE" | sudo tee "$NVIDIA_GRUB_CONFIG" > /dev/null | |
| echo "Updated NVIDIA modeset configuration in GRUB" | |
| else | |
| echo "NVIDIA modeset configuration already exists in GRUB" | |
| fi | |
| # Configure NVIDIA power management for suspend/hibernate support | |
| echo "Configuring NVIDIA power management..." | |
| NVIDIA_PM_CONFIG="/etc/modprobe.d/nvidia-power-management.conf" | |
| NVIDIA_PM_OPTION="options nvidia NVreg_PreserveVideoMemoryAllocations=1" | |
| if [ ! -f "$NVIDIA_PM_CONFIG" ]; then | |
| echo "$NVIDIA_PM_OPTION" | sudo tee "$NVIDIA_PM_CONFIG" > /dev/null | |
| echo "Created NVIDIA power management configuration" | |
| elif ! grep -q "NVreg_PreserveVideoMemoryAllocations=1" "$NVIDIA_PM_CONFIG"; then | |
| # Remove any existing conflicting line and add the correct one | |
| sudo sed -i '/NVreg_PreserveVideoMemoryAllocations=/d' "$NVIDIA_PM_CONFIG" | |
| echo "$NVIDIA_PM_OPTION" | sudo tee -a "$NVIDIA_PM_CONFIG" > /dev/null | |
| echo "Updated NVIDIA power management configuration" | |
| else | |
| echo "NVIDIA power management configuration already exists" | |
| fi | |
| # Install and enable NVIDIA suspend/hibernate services | |
| echo "Installing NVIDIA suspend/hibernate support..." | |
| sudo apt install nvidia-suspend-common -y | |
| # Enable NVIDIA power management services | |
| echo "Enabling NVIDIA power management services..." | |
| sudo systemctl enable nvidia-suspend.service 2>/dev/null || echo "nvidia-suspend.service already enabled or not available" | |
| sudo systemctl enable nvidia-hibernate.service 2>/dev/null || echo "nvidia-hibernate.service already enabled or not available" | |
| sudo systemctl enable nvidia-resume.service 2>/dev/null || echo "nvidia-resume.service already enabled or not available" | |
| # Update GRUB configuration to apply kernel modesetting changes | |
| sudo update-grub | |
| # Update the initial ramdisk to include the new drivers | |
| if command -v dracut &> /dev/null; then | |
| echo "Updating dracut initrd..." | |
| sudo dracut --regenerate-all --force | |
| else | |
| echo "Updating initramfs..." | |
| sudo update-initramfs -u | |
| fi | |
| # Clean up any old, uninstalled Nvidia packages | |
| if dpkg -l | grep -q '^rc.*nvidia'; then | |
| echo "Purging old Nvidia package configurations..." | |
| dpkg -l | awk '/^rc/ && /nvidia/ { print $2 }' | xargs sudo apt purge -y | |
| fi | |
| echo "NVIDIA driver installation complete." | |
| # --- Shell and Package Manager Setup --- | |
| # Install Oh My Zsh for a better terminal experience | |
| sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" | |
| # Install Homebrew package manager for Linux | |
| /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" | |
| echo >> ~/.zshrc | |
| echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >> ~/.zshrc | |
| eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" | |
| brew install gcc | |
| # Configure Flatpak for application management | |
| sudo apt install flatpak gnome-software-plugin-flatpak -y | |
| sudo flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo | |
| sudo flatpak update | |
| # --- Desktop Application Installation --- | |
| # Install GNOME Extensions utility and Extension Manager | |
| sudo flatpak install flathub org.gnome.Extensions -y | |
| sudo flatpak install flathub com.mattjakeman.ExtensionManager -y | |
| # Replace Firefox ESR with the latest Flatpak version | |
| # Note: Snap is not installed by default on Debian, so 'snap remove' may be unnecessary. | |
| if command -v snap &> /dev/null; then sudo snap remove firefox; fi | |
| sudo apt purge firefox-esr -y | |
| sudo flatpak install flathub org.mozilla.firefox | |
| # Install Brave browser | |
| sudo apt install curl -y | |
| sudo curl -fsSLo /usr/share/keyrings/brave-browser-archive-keyring.gpg https://brave-browser-apt-release.s3.brave.com/brave-browser-archive-keyring.gpg | |
| echo "deb [signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg] https://brave-browser-apt-release.s3.brave.com/ stable main"|sudo tee /etc/apt/sources.list.d/brave-browser-release.list | |
| sudo apt update | |
| sudo apt install brave-browser -y | |
| # Debloat Brave browser by disabling certain features via policy | |
| sudo mkdir -p /etc/brave/policies/managed/ && sudo chmod 755 /etc/brave/policies/managed/ | |
| sudo tee /etc/brave/policies/managed/00_debloat.json > /dev/null << 'EOF' | |
| { | |
| "TorDisabled": true, | |
| "BraveRewardsDisabled": true, | |
| "BraveWalletDisabled": true, | |
| "BraveVPNDisabled": true, | |
| "BraveAIChatEnabled": false | |
| } | |
| EOF | |
| # Install Thunderbird email client | |
| sudo flatpak install flathub org.mozilla.Thunderbird | |
| # Set Flatpak Firefox as the default web browser | |
| xdg-settings set default-web-browser org.mozilla.firefox.desktop | |
| # Refresh snap packages if snapd is installed | |
| if command -v snap &> /dev/null; then sudo snap refresh; fi | |
| # --- User Environment and Tool Configuration --- | |
| # Configure Emacs as the default text editor | |
| sudo update-alternatives --set editor /usr/bin/emacs | |
| echo ' | |
| # Set default editor to Emacs | |
| export EDITOR="/usr/bin/emacs" | |
| export VISUAL="/usr/bin/emacs" | |
| ' >> ~/.zshrc | |
| # Install 1Password password manager and CLI | |
| # https://support.1password.com/install-linux/#debian-or-ubuntu | |
| curl -sS https://downloads.1password.com/linux/keys/1password.asc | sudo gpg --dearmor --output /usr/share/keyrings/1password-archive-keyring.gpg | |
| echo 'deb [arch=amd64 signed-by=/usr/share/keyrings/1password-archive-keyring.gpg] https://downloads.1password.com/linux/debian/amd64 stable main' | sudo tee /etc/apt/sources.list.d/1password.list | |
| sudo mkdir -p /etc/debsig/policies/AC2D62742012EA22/ | |
| curl -sS https://downloads.1password.com/linux/debian/debsig/1password.pol | sudo tee /etc/debsig/policies/AC2D62742012EA22/1password.pol | |
| sudo mkdir -p /usr/share/debsig/keyrings/AC2D62742012EA22 | |
| curl -sS https://downloads.1password.com/linux/keys/1password.asc | sudo gpg --dearmor --output /usr/share/debsig/keyrings/AC2D62742012EA22/debsig.gpg | |
| sudo apt update && sudo apt install 1password-cli 1password -y | |
| op --version | |
| # Setup SSH key from 1Password | |
| mkdir -p ~/.ssh | |
| # Sign in to 1Password CLI | |
| eval $(op signin) | |
| # Retrieve and install SSH key with specific fingerprint | |
| echo "Retrieving SSH key with fingerprint SHA256:dsPhhaQhifJccmUhI2ZZIoSnEOUIWYRbSe1TWZs2JuA" | |
| ITEM_ID="mijcwmynssrwh33ad3mknt77fy" | |
| op item get "$ITEM_ID" --format json | jq -r '.fields[] | select(.label == "private key") | .value' > ~/.ssh/id_ed25519 | |
| op item get "$ITEM_ID" --format json | jq -r '.fields[] | select(.label == "private key") | .ssh_formats.openssh.value' > ~/.ssh/id_ed25519 | |
| chmod 600 ~/.ssh/id_ed25519.pub | |
| # Set proper security permissions | |
| chmod 600 ~/.ssh/id_ed25519 | |
| echo "SSH private and public keys saved to ~/.ssh/" | |
| eval "$(ssh-agent -s)" | |
| ssh-add ~/.ssh/id_ed25519 | |
| # Setup git global configuration | |
| git config --global user.name "HackingGate" | |
| git config --global user.email "[email protected]" | |
| git config --global core.editor "emacs" | |
| git config --global init.defaultBranch main | |
| git config --global gpg.format ssh | |
| git config --global commit.gpgSign true | |
| git config --global user.signingkey ~/.ssh/id_ed25519.pub | |
| git config --global submodule.recurse true | |
| # Setup 1Password browser integration for Flatpak | |
| # https://gist.github.com/LinuxSBC/7c39374130d2d443871ddde64cba18a3 1password-flatpak-browser-integration.sh | |
| curl -L https://gist.githubusercontent.com/LinuxSBC/7c39374130d2d443871ddde64cba18a3/raw/1password-flatpak-browser-integration.sh -o 1password-flatpak-browser-integration.sh | |
| chmod +x 1password-flatpak-browser-integration.sh | |
| ./1password-flatpak-browser-integration.sh | |
| # Auto start 1Password for GNOME Shell | |
| mkdir -p ~/.config/autostart | |
| cat > ~/.config/autostart/1password.desktop << 'EOF' | |
| [Desktop Entry] | |
| Name=1Password | |
| Exec=/usr/bin/1password --silent %U | |
| Terminal=false | |
| Type=Application | |
| Icon=1password | |
| StartupWMClass=1Password | |
| Comment=Password manager and secure wallet | |
| MimeType=x-scheme-handler/onepassword; | |
| Categories=Office; | |
| EOF | |
| chmod +x ~/.config/autostart/1password.desktop | |
| echo "1Password autostart configured" | |
| # --- Networking and System Customization --- | |
| # Install Tailscale for secure networking | |
| curl -fsSL https://tailscale.com/install.sh | sh | |
| sudo tailscale up | |
| # Install and configure Starship prompt | |
| brew install starship | |
| echo 'eval "$(starship init zsh)"' >> ~/.zshrc | |
| # Install essential fonts including CJK support | |
| sudo apt install -y fonts-firacode fonts-noto fonts-noto-cjk-extra fonts-noto-extra fonts-noto-ui-core fonts-noto-ui-extra fonts-noto-unhinted | |
| # NOTE: The 'mainline' PPA for kernel management is specific to Ubuntu and has been removed. | |
| # For newer kernels on Debian, consider using the 'backports' repository or manual installation. | |
| # Update firmware | |
| sudo fwupdmgr refresh --force | |
| sudo fwupdmgr update -y | |
| # Install rEFInd boot manager | |
| sudo apt install refind -y | |
| # Configure rEFInd boot timeout | |
| echo "Configuring rEFInd timeout to 5 seconds..." | |
| if [ -f /boot/efi/EFI/refind/refind.conf ]; then | |
| CURRENT_TIMEOUT=$(grep -oP 'timeout \K[0-9]+' /boot/efi/EFI/refind/refind.conf || echo "not set") | |
| sudo sed -i 's/timeout [0-9]\+/timeout 5/' /boot/efi/EFI/refind/refind.conf | |
| echo "rEFInd timeout successfully changed from $CURRENT_TIMEOUT to 5 seconds" | |
| else | |
| echo "Warning: rEFInd configuration file not found at /boot/efi/EFI/refind/refind.conf" | |
| fi | |
| # Configure GRUB boot timeout | |
| echo "Configuring GRUB timeout to 5 seconds..." | |
| if [ -f /etc/default/grub ]; then | |
| sudo sed -i 's/GRUB_TIMEOUT=[0-9]*/GRUB_TIMEOUT=5/' /etc/default/grub | |
| sudo update-grub | |
| echo "GRUB timeout successfully set to 5 seconds" | |
| else | |
| echo "Warning: GRUB configuration file not found at /etc/default/grub" | |
| fi | |
| # --- GNOME Desktop Tweaks --- | |
| # Enable Emacs keybindings across GTK applications | |
| gsettings set org.gnome.desktop.interface gtk-key-theme "Emacs" | |
| # Enable Emacs daemon for better performance for the current user | |
| systemctl --user enable --now emacs | |
| # Configure Caps Lock as an additional Ctrl key | |
| echo "Setting Caps Lock to function as Ctrl..." | |
| current_options=$(gsettings get org.gnome.desktop.input-sources xkb-options) | |
| if [[ $current_options == "@as []" ]]; then | |
| gsettings set org.gnome.desktop.input-sources xkb-options "['ctrl:nocaps']" | |
| else | |
| # Avoid adding if already present | |
| if [[ $current_options != *"ctrl:nocaps"* ]]; then | |
| current_options=${current_options:5:-1} | |
| gsettings set org.gnome.desktop.input-sources xkb-options "[$current_options, 'ctrl:nocaps']" | |
| fi | |
| fi | |
| echo "--- Debian setup script finished ---" | |
| echo "Please reboot your system to apply all changes, especially for the new drivers and kernel modules." |
Setup DNS on Debian 13 Trixie using dnscrypt-proxy
https://github.com/DNSCrypt/dnscrypt-proxy
sudo apt install dnsutils dnscrypt-proxy -yGo to https://dnscrypt.info/stamps/ to calculate sdns.
- Choose
DNS-over-HTTPS (DoH)protocol. - Fill
dnsaptokyo1.hackinggate.cominHost name (vhost+SNI) and optional port number. - Fill
/dns-queryinPath. - Copy generated Stamp.
Modify /etc/dnscrypt-proxy/dnscrypt-proxy.toml
# Empty listen_addresses to use systemd socket activation
listen_addresses = []
server_names = ['hackinggate-tokyo']
http3 = true
[static.'hackinggate-tokyo']
stamp = 'sdns://AgUAAAAAAAAAAAAbZG5zYXB0b2t5bzEuaGFja2luZ2dhdGUuY29tCi9kbnMtcXVlcnk'Enable & start
sudo systemctl enable dnscrypt-proxy --nowSee port
sudo ss -lnup | grep ':53'5G Celluar Debugging
Enter AT command interface
sudo socat - /dev/ttyUSB2,crnlAT+CGDCONT=1,"IPV4V6","Your_APN"Debugging help
| AT Command | Purpose/Function | Expected Success Response |
|---|---|---|
| ATE0 | Disable command echo for scripting. | OK |
| AT+QMBNCFG="AutoSel",0 | Disable automatic MBN profile selection. | OK |
| AT+CFUN=1,1 | Perform a full module reset and reboot. | OK (Wait 30-60s after response) |
| AT+CPIN? | Check SIM card status. | +CPIN: READY |
| AT+CSQ | Check signal strength. | +CSQ: , (where is ideally ≥10) |
| AT+CEREG? / AT+C5GREG? | Check network registration status. | +CEREG:...,1 or ...,5 / +C5GREG:...,1 or ...,5 |
| AT+CGDCONT=1,"IPV4V6","Your_APN" | Define the PDP context for 'Your_APN' dual-stack. | OK |
| AT+CGDCONT? | Verify the PDP context was set correctly. | +CGDCONT: 1,"IPV4V6","povo.jp",... |
| AT+CGACT=1,1 | Activate the data session for CID 1. | OK |
| AT+CGPADDR=1 | Query for assigned IP addresses. | +CGPADDR: 1,"<ipv4_addr>","<ipv6_addr>" |
| AT+QPING=1,"8.8.8.8" | Test IPv4 connectivity from the module. | +QPING: 0, "8.8.8.8",... |
| AT+QPING=1,"2001:4860:4860::8888" | Test IPv6 connectivity from the module. | +QPING: 0, "2001:4860:4860::8888",... |
Photonicat 2 fan control (supported by photonicat-pm driver)
# Check current fan state:
sudo cat /sys/class/thermal/cooling_device0/cur_state
# Set fan to maximum speed:
sudo sh -c 'echo 9 > /sys/class/thermal/cooling_device0/cur_state'
# Set fan to medium speed:
sudo sh -c 'echo 5 > /sys/class/thermal/cooling_device0/cur_state'
# Set fan to quiet (can't hear fan noise at all when put it on desk in a very quiet room):
sudo sh -c 'echo 1 > /sys/class/thermal/cooling_device0/cur_state'
# Turn fan off:
sudo sh -c 'echo 0 > /sys/class/thermal/cooling_device0/cur_state'
Router Settings
On Debian 13, Photonicat 2
# --- Delete the old, duplicated bridge and port profiles ---
# You might need to run these commands multiple times if you have many duplicates.
sudo nmcli connection
sudo nmcli connection delete "Your_Old_Connection"# --- Create the Bridge Interface (br0) for your LAN ---
# This bridge will have a static IP and act as the gateway for your LAN devices.
sudo nmcli connection add type bridge con-name "br-lan" ifname br0 \
ipv4.method shared ipv4.addresses 172.16.0.1/24 \
ipv6.method shared
# --- Add a physical Ethernet port (end1) to the bridge ---
sudo nmcli connection add type ethernet con-name "lan0" ifname end1 master br0
# --- Configure the external Wi-Fi card (wlxdcec4f7e2d67) as an Access Point and add it to the bridge ---
# Note: For WPA3, key-mgmt should be 'sae'. For WPA2/WPA3 mixed-mode, use 'wpa-psk'.
sudo nmcli connection add type wifi con-name "ap" ifname wlxdcec4f7e2d67 master br0 \
ssid "Your_Hotspot_Name" \
802-11-wireless.mode ap \
802-11-wireless.band bg \
802-11-wireless.channel 1 \
802-11-wireless-security.key-mgmt wpa-psk \
802-11-wireless-security.psk "Your_WPA_Password"
# --- Configure the external Wi-Fi card (wlp1s0) as an Access Point and add it to the bridge ---
# Note: For WPA3, key-mgmt should be 'sae'. For WPA2/WPA3 mixed-mode, use 'wpa-psk'.
sudo nmcli connection add type wifi con-name "ap" ifname wlp1s0 master br0 \
ssid "Your_Hotspot_Name" \
802-11-wireless.mode ap \
802-11-wireless.band bg \
802-11-wireless.channel 1 \
802-11-wireless-security.key-mgmt sae \
802-11-wireless-security.psk "Your_WPA_Password"
# Change it to WPA3-Personal if need (may unstable depend on your device, kernel, driver)
# sudo nmcli connection modify ap 802-11-wireless-security.key-mgmt sae# First, let's rename your existing "Wired connection 1" for clarity.
sudo nmcli connection modify "Wired connection 1" con-name wan0
# Create a connection for your GSM modem. Replace "your_apn" with your carrier's APN.
sudo nmcli connection add type gsm con-name cellular ifname cdc-wdm0 apn "your_apn"
# This connects your second Wi-Fi card to another network for internet access.
# With MAC address randomized and hostname hidden, like how your phone connects to a public Wi-Fi.
sudo nmcli connection add type wifi con-name "YourConnectionName" ifname wlp1s0 ssid "Your-SSID" -- \
wifi-sec.key-mgmt wpa-psk \
wifi-sec.psk "Your-Password" \
802-11-wireless.mac-address-randomization 2 \
ipv4.dhcp-send-hostname no \
ipv6.dhcp-send-hostname no
sudo nmcli connection up "YourConnectionName"
# List Wi-Fi with rescan 2-in-1 on on-board Wi-Fi card
sudo nmcli dev wifi list ifname wlp1s0 --rescan yes
# Or coonect directly (doesn't support options on my version yet)
sudo nmcli device wifi connect "Your-SSID" password "YourWifiPassword" ifname wlp1s0
# Or coonect directly with interactive password
sudo nmcli device wifi connect "Your-SSID" --ask ifname wlp1s0
# Now, set its metric.
sudo nmcli connection modify wan0 ipv4.route-metric 100 ipv6.route-metric 100
sudo nmcli connection modify cellular ipv4.route-metric 200 ipv6.route-metric 200
# By default, NetworkManager gives Wi-Fi a much higher priority than a GSM (cellular) connection unless you specifically sets it.
# sudo nmcli connection modify YourConnectionName ipv4.route-metric 300 ipv6.route-metric 300Enable IPv4/IPv6 forwarding
sudo -S tee /etc/sysctl.d/99-custom-forward.conf > /dev/null << 'EOF'
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
EOF
cat /etc/sysctl.d/99-custom-forward.conf
sudo sysctl -p /etc/sysctl.d/99-custom-forward.conf# --- Activate your new LAN bridge ---
sudo nmcli connection up br-lan
# --- Verify everything is working ---
# Check device states and connections
nmcli device status
# Check IP addresses. You should see br0 with 172.16.0.1.
ip a
# Check the routing table. You should see default routes with their metrics.
ip routeSetup Dnsmasq
Debian 13 Trixie by default runs avahi-daemon and NetworkManager.
I'm going to replace it with a standalone dnsmasq (not the integrated dnsmasq with NetworkManager)
sudo -S tee /etc/NetworkManager/conf.d/00-use-dnsmasq.conf > /dev/null << 'EOF'
[main]
dns=dnsmasq
EOF
cat /etc/NetworkManager/conf.d/00-use-dnsmasq.confsudo tee /etc/NetworkManager/conf.d/no-systemd-resolved.conf> /dev/null <<EOF
[main]
systemd-resolved=false
EOFsudo ss -lnup | grep ':53'avahi-daemon is using port 53
sudo apt install dnsmasq -y
sudo systemctl disable dnsmasq --now
sudo systemctl restart NetworkManagerUse DNS from dnscrypt-proxy
Check which address and port is dnscrypt-proxy listen on
sudo ss -lnp | grep dnscrypt-proxyIn my case it's 127.0.2.1:53
# Create the configuration file for standalone dnsmasq
sudo tee /etc/NetworkManager/dnsmasq.d/dnscrypt-proxy-dns.conf> /dev/null <<EOF
# Prevents dnsmasq from reading /etc/resolv.conf for upstream servers.
no-resolv
# Set the upstream DNS server to 127.0.2.1
server=127.0.2.1
# Listen for DNS queries on these addresses
listen-address=127.0.0.1,172.16.0.1
# Advertise br0 address as the DNS server for LAN clients.
# DHCP Option 6 is the standard for specifying DNS servers.
dhcp-option=6,172.16.0.1
EOF
sudo systemctl restart NetworkManagersudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved
sudo systemctl mask systemd-resolved
# sudo rm /etc/resolv.conf
sudo systemctl restart NetworkManagerHarden security of SSH
ssh-copy-id 172.16.0.1
ssh 172.16.0.1
sudo sed -i.bak -E 's/^#? *(PasswordAuthentication|PermitRootLogin).*/\1 no/' /etc/ssh/sshd_config
sudo sshd -t
sudo systemctl restart sshd
sudo passwd -l rootSet up a simple firewall
A device that connects directly to the public internet or public Wi-Fi ap is highly recommended to set up a firewall to deny incoming traffic by rules.
For Debian system, it's very easy to use ufw (Uncomplicated Firewall)
sudo apt install ufw -y
sudo ufw allow ssh
sudo ufw enable
sudo ufw status verboseFirewall rules for NAT
br0 firewall
# Broad "Allow All" Rule
sudo ufw allow in on br0
# Or more secured (like it's not a personal Wi-Fi station but public)
# Allow DHCP (if the router is your DHCP server)
# sudo ufw allow in on br0 from any to any port 67,68 proto udp
# Allow DNS (if the router is your DNS server)
# sudo ufw allow in on br0 from any to any port 53
# Allow SSH from your LAN subnet only
# sudo ufw allow in on br0 from 172.16.0.0/24 to any port 22 proto tcpUpdate NAT Rules for All WANs
You need to tell the firewall to apply Network Address Translation (NAT) when traffic is leaving through any of your WAN interfaces.
- Edit the
/etc/ufw/before.rulesfile:sudo nano /etc/ufw/before.rules
- Add the following lines to the very top of the file, before the *filter section:
# NAT table rules *nat :POSTROUTING ACCEPT [0:0] # Forward traffic from LAN (br0) to all possible WANs -A POSTROUTING -s 172.16.0.0/24 -o end0 -j MASQUERADE -A POSTROUTING -s 172.16.0.0/24 -o wlp1s0 -j MASQUERADE -A POSTROUTING -s 172.16.0.0/24 -o cdc-wdm0 -j MASQUERADE COMMIT
Update Forwarding Rules for All WANs
Next, explicitly allow traffic to be routed from your LAN to each of your WAN interfaces.
- Run these commands to add a rule for each WAN interface. If you ran a similar command before,
ufwis smart enough to skip adding a duplicate.sudo ufw route allow in on br0 out on end0 sudo ufw route allow in on br0 out on wlp1s0 sudo ufw route allow in on br0 out on cdc-wdm0
- Finally, reload the firewall to apply all your new rules:
sudo ufw disable && sudo ufw enable
Your router will now correctly forward traffic from the br0 LAN to whichever WAN interface is active.
sudo ufw status verboseSet up Cockpit web accessible interactive admin interface
Red Hat sponsored, if you are a Debian user who likes Systemd and NetworkManager, you must like Cockpit too.
sudo apt install cockpit -y
sudo systemctl enable cockpit.socket --now
sudo ufw allow 9090/tcpSet up Cloudflare tunnel
# Add cloudflare gpg key
sudo mkdir -p --mode=0755 /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
# Add this repo to your apt repositories
echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
# install cloudflared
sudo apt-get update && sudo apt-get install cloudflaredReplace TOKEN with actual token.
sudo cloudflared service install TOKENExpose Cockpit via Cloudflare tunnel
Create a self-hosted application, configure access policies, expose cockpit.
Now access to Cockpit via Cloudflare Zero Trust made it safer, available everywhere.
Delete previously allowed 9090/tcp for Cockpit for incoming traffic.
sudo ufw status numbered
# replace NUM with 9090/tcp
sudo ufw delete NUM
sudo ufw status verbose
Fix Cockpit TLS issue
If you have your Cloudflare requires Full SSL and Cockpit's TLS setting is invalid. You can't access it through Cloudflare.
To fix it, you need to figure out reverse proxy setting, issue a real SSL cert for your server, or get the "special SSL" cert from Cloudflare that specifically used between Cloudflare and your server.
Or simply allow unencrypted.
Since I deny 9090/tcp incoming and I have no intent to set up SSL or use Cloudflare CDN with the device.
The only way to access Cockpit from the outside is through Cloudflare tunnel, so it's already encrypted from the beginning, should be safe to set this.
sudo tee /etc/cockpit/cockpit.conf > /dev/null <<'EOF'
[WebService]
AllowUnencrypted=true
EOF
sudo systemctl restart cockpit.socketResize partition
lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
mmcblk0 179:0 0 58.3G 0 disk
├─mmcblk0p1 179:1 0 256M 0 part
└─mmcblk0p2 179:2 0 12G 0 part /
mmcblk0boot0 179:32 0 4M 1 disk
mmcblk0boot1 179:64 0 4M 1 disk
zram0 253:0 0 0B 0 disk I have free space on mmcblk0 but mmcblk0p1 mmcblk0p2 are not using them all.
I want to create a SWAP at the end of the disk, and grow mmcblk0p2 to take the rest of the disk space.
sudo apt install gdisk cloud-guest-utils -y
# Create 8GB partition 3 from the end of the disk for swap
sudo gdisk /dev/mmcblk0
n
Enter
-8GB
Enter
8200
w
y
# Enable swap
sudo partprobe /dev/mmcblk0
sudo mkswap /dev/mmcblk0p3
sudo swapon /dev/mmcblk0p3
# Grow partition 2 to use the rest space
sudo growpart /dev/mmcblk0 2
sudo resize2fs /dev/mmcblk0p2
# Check result
lsblk
free -h $ free -h
total used free shared buff/cache available
Mem: 3.8Gi 1.0Gi 2.5Gi 244Mi 706Mi 2.8Gi
Swap: 8.0Gi 0B 8.0Gi
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
mmcblk0 179:0 0 58.3G 0 disk
├─mmcblk0p1 179:1 0 256M 0 part /boot
├─mmcblk0p2 179:2 0 50G 0 part /
└─mmcblk0p3 179:3 0 8G 0 part [SWAP]
mmcblk0boot0 179:32 0 4M 1 disk
mmcblk0boot1 179:64 0 4M 1 disk
zram0 253:0 0 0B 0 disk Router Settings
On Armbian edge, Photonicat 2
# --- Delete the old, duplicated bridge and port profiles ---
# You might need to run these commands multiple times if you have many duplicates.
sudo nmcli connection
sudo nmcli connection delete "Your_Old_Connection"# --- Create the Bridge Interface (br0) for your LAN ---
# This bridge will have a static IP and act as the gateway for your LAN devices.
sudo nmcli connection add type bridge con-name "br-lan" ifname br0 \
ipv4.method shared ipv4.addresses 172.16.0.1/24 \
ipv6.method shared
# --- Add a physical Ethernet port (end1) to the bridge ---
sudo nmcli connection add type ethernet con-name "lan0" ifname end1 master br0
# --- Configure the external Wi-Fi card (wlp1s0) as an Access Point and add it to the bridge ---
# Note: For WPA3, key-mgmt should be 'sae'. For WPA2/WPA3 mixed-mode, use 'wpa-psk'.
sudo nmcli connection add type wifi con-name "ap" ifname wlp1s0 master br0 \
ssid "Your_Hotspot_Name" \
802-11-wireless.mode ap \
802-11-wireless.band bg \
802-11-wireless.channel 1 \
802-11-wireless-security.key-mgmt sae \
802-11-wireless-security.psk "Your_WPA_Password"
# Change it to WPA3-Personal if need (may unstable depend on your device, kernel, driver)
# sudo nmcli connection modify ap 802-11-wireless-security.key-mgmt sae# First, let's rename your existing "Wired connection 1" for clarity.
sudo nmcli connection modify "Wired connection 1" con-name wan0
# Create a connection for your GSM modem. Replace "your_apn" with your carrier's APN.
sudo nmcli connection add type gsm con-name cellular ifname cdc-wdm0 apn "your_apn"
# This connects your second Wi-Fi card to another network for internet access.
# With MAC address randomized and hostname hidden, like how your phone connects to a public Wi-Fi.
sudo nmcli connection add type wifi con-name "YourConnectionName" ifname wlan0 ssid "Your-SSID" -- \
wifi-sec.key-mgmt wpa-psk \
wifi-sec.psk "Your-Password" \
802-11-wireless.mac-address-randomization 2 \
ipv4.dhcp-send-hostname no \
ipv6.dhcp-send-hostname no
sudo nmcli connection up "YourConnectionName"
# List Wi-Fi with rescan 2-in-1 on on-board Wi-Fi card
sudo nmcli dev wifi list ifname wlan0 --rescan yes
# Or coonect directly (doesn't support options on my version yet)
sudo nmcli device wifi connect "Your-SSID" password "YourWifiPassword" ifname wlan0
# Or coonect directly with interactive password
sudo nmcli device wifi connect "Your-SSID" --ask ifname wlan0
# Now, set its metric.
sudo nmcli connection modify wan0 ipv4.route-metric 100 ipv6.route-metric 100
sudo nmcli connection modify cellular ipv4.route-metric 200 ipv6.route-metric 200
# By default, NetworkManager gives Wi-Fi a much higher priority than a GSM (cellular) connection unless you specifically sets it.
# sudo nmcli connection modify YourConnectionName ipv4.route-metric 300 ipv6.route-metric 300Enable IPv4 forwarding
echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-custom-forward.conf
sudo sysctl -p /etc/sysctl.d/99-custom-forward.conf# --- Activate your new LAN bridge ---
sudo nmcli connection up br-lan
# --- Verify everything is working ---
# Check device states and connections
nmcli device status
# Check IP addresses. You should see br0 with 172.16.0.1.
ip a
# Check the routing table. You should see default routes with their metrics.
ip routeRouter Setup with Dnsmasq and DNSCrypt-Proxy
Debian 13 Trixie router configuration using NetworkManager, dnsmasq for DHCP/DNS, dnscrypt-proxy for encrypted upstream DNS, and UFW for firewall management. Supports both IPv4 and IPv6 with full internet access.
Prerequisites
- Debian 13 Trixie
- NetworkManager installed
- Bridge interface
br-lan(devicebr0) configured at 172.16.0.1/24 - Internet connection via
end0(wan0)
1. Enable IP Forwarding
IP forwarding is required for routing traffic between interfaces for both IPv4 and IPv6.
# Check if already enabled
cat /etc/sysctl.d/99-custom-forward.conf
# If not present, create it:
sudo tee /etc/sysctl.d/99-custom-forward.conf > /dev/null << 'EOF'
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
EOF
# Apply immediately
sudo sysctl -p /etc/sysctl.d/99-custom-forward.conf
# Verify
cat /proc/sys/net/ipv4/ip_forward # Should return 1
cat /proc/sys/net/ipv6/conf/all/forwarding # Should return 12. Install DNSCrypt-Proxy
DNSCrypt-proxy provides encrypted DNS queries (DNS-over-HTTPS or DNSCrypt).
# Install dnscrypt-proxy
sudo apt install dnscrypt-proxy -y
# Check which address dnscrypt-proxy is listening on
sudo ss -lnp | grep dnscrypt-proxyIn most cases, it listens on 127.0.2.1:53.
Verify the service is running:
sudo systemctl status dnscrypt-proxy3. Configure NetworkManager to Use Dnsmasq
NetworkManager can run dnsmasq as an integrated service.
# Configure NetworkManager to use dnsmasq
sudo tee /etc/NetworkManager/conf.d/00-use-dnsmasq.conf > /dev/null << 'EOF'
[main]
dns=dnsmasq
EOF
# Disable systemd-resolved if running
sudo tee /etc/NetworkManager/conf.d/no-systemd-resolved.conf > /dev/null << 'EOF'
[main]
systemd-resolved=false
EOF4. Install and Configure Dnsmasq
# Check for port conflicts
sudo ss -lnup | grep ':53'
# Install dnsmasq
sudo apt install dnsmasq -y
# Disable standalone dnsmasq service (NetworkManager will manage it)
sudo systemctl disable dnsmasq --now5. Configure Dnsmasq for DHCP and DNS
IMPORTANT: NetworkManager's integrated dnsmasq uses the /etc/NetworkManager/dnsmasq-shared.d/ directory for configuration files, NOT /etc/NetworkManager/dnsmasq.d/.
Create dnsmasq configuration to:
- Serve DHCP on br0 (172.16.0.100-200 for IPv4, fd00:172:16::100-200 for IPv6)
- Use dnscrypt-proxy as upstream DNS
- Provide DNS and gateway to clients
- Enable IPv6 Router Advertisements
sudo tee /etc/NetworkManager/dnsmasq-shared.d/router.conf > /dev/null << 'EOF'
# DHCP server for br0 (172.16.0.0/24)
interface=br0
dhcp-range=172.16.0.100,172.16.0.200,12h
# Use dnscrypt-proxy as upstream DNS
server=127.0.2.1
# Never forward plain names (without a dot or domain part)
domain-needed
# Never forward addresses in the non-routed address spaces
bogus-priv
# Don't read /etc/resolv.conf for upstream servers
no-resolv
# Cache size
cache-size=1000
# Listen for DNS queries on these addresses
listen-address=127.0.0.1,172.16.0.1
# DHCP options
dhcp-option=option:router,172.16.0.1
dhcp-option=option:dns-server,172.16.0.1
# IPv6 DHCP range using ULA (Unique Local Address)
dhcp-range=fd00:172:16::100,fd00:172:16::200,constructor:br0,ra-names,64,12h
# Enable IPv6 Router Advertisements
enable-ra
EOF
cat /etc/NetworkManager/dnsmasq-shared.d/router.conf6. Configure Router's Own DNS
The router itself should use its local dnsmasq instance for DNS resolution.
# Remove the symlink to systemd-resolved's resolv.conf
sudo rm /etc/resolv.conf
# Create a static resolv.conf pointing to local dnsmasq
echo "nameserver 127.0.0.1" | sudo tee /etc/resolv.conf
# Make it immutable so NetworkManager doesn't overwrite it
sudo chattr +i /etc/resolv.conf
# Verify
cat /etc/resolv.conf7. Configure IPv6 on br0 Interface
Configure NetworkManager to assign an IPv6 ULA address to br0 for LAN clients:
# Add IPv6 address to br-lan connection
sudo nmcli connection modify br-lan ipv6.addresses "fd00:172:16::1/64"
sudo nmcli connection modify br-lan ipv6.method shared
# Reload the connection
sudo nmcli connection up br-lan
# Verify
ip -6 addr show br0
# Should show: inet6 fd00:172:16::1/648. Restart NetworkManager
# Stop systemd-resolved if it's running
sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved
sudo systemctl mask systemd-resolved
# Restart NetworkManager to apply changes
sudo systemctl restart NetworkManager
# Verify dnsmasq is running under NetworkManager
sudo ss -lnup | grep ':53'
ps aux | grep dnsmasqYou should see dnsmasq listening on both 127.0.0.1:53 and 172.16.0.1:53, and dnscrypt-proxy on 127.0.2.1:53.
9. Install and Configure UFW Firewall
UFW (Uncomplicated Firewall) provides an easy-to-use interface for managing iptables/ip6tables rules. It supports both IPv4 and IPv6 by default.
# Install UFW
sudo apt install ufw -y
# Enable IP forwarding in UFW configuration
sudo sed -i 's/^DEFAULT_FORWARD_POLICY=.*/DEFAULT_FORWARD_POLICY="ACCEPT"/' /etc/default/ufw
# Verify IPv6 is enabled in UFW (should be yes by default)
grep "^IPV6=" /etc/default/ufw10. Configure IPv4 NAT Rules in UFW
UFW needs NAT (masquerading) rules added to /etc/ufw/before.rules for IPv4 routing.
# Edit /etc/ufw/before.rules and add NAT table BEFORE the *filter table
# The NAT section should be added after the header comments and before the "Don't delete these required lines" section
sudo nano /etc/ufw/before.rulesAdd these lines after the header comments and BEFORE the # Don't delete these required lines line:
# NAT table rules for IPv4 masquerading
*nat
:POSTROUTING ACCEPT [0:0]
# Forward traffic from br0 to WAN interfaces with masquerading
-A POSTROUTING -s 172.16.0.0/24 -o end0 -j MASQUERADE
-A POSTROUTING -s 172.16.0.0/24 -o wlp1s0 -j MASQUERADE
-A POSTROUTING -s 172.16.0.0/24 -o wlan0 -j MASQUERADE
-A POSTROUTING -s 172.16.0.0/24 -o cdc-wdm0 -j MASQUERADE
# don't delete the 'COMMIT' line or these rules won't be processed
COMMIT
The file should look like this:
#
# rules.before
#
# Rules that should be run before the ufw command line added rules. Custom
# rules should be added to one of these chains:
# ufw-before-input
# ufw-before-output
# ufw-before-forward
#
# NAT table rules for IPv4 masquerading
*nat
:POSTROUTING ACCEPT [0:0]
# Forward traffic from br0 to WAN interfaces with masquerading
-A POSTROUTING -s 172.16.0.0/24 -o end0 -j MASQUERADE
-A POSTROUTING -s 172.16.0.0/24 -o wlp1s0 -j MASQUERADE
-A POSTROUTING -s 172.16.0.0/24 -o wlan0 -j MASQUERADE
-A POSTROUTING -s 172.16.0.0/24 -o cdc-wdm0 -j MASQUERADE
# don't delete the 'COMMIT' line or these rules won't be processed
COMMIT
# Don't delete these required lines, otherwise there will be errors
*filter
:ufw-before-input - [0:0]
... (rest of file continues)
11. Configure IPv6 NAT66 Rules in UFW
For IPv6 internet access using ULA addresses, we need NAT66 (IPv6 masquerading).
# Edit /etc/ufw/before6.rules and add NAT table BEFORE the *filter table
sudo nano /etc/ufw/before6.rulesAdd these lines after the header comments and BEFORE the # Don't delete these required lines line:
# NAT66 table for IPv6 masquerading
*nat
:POSTROUTING ACCEPT [0:0]
# Masquerade IPv6 traffic from LAN (ULA) to WAN interfaces
-A POSTROUTING -s fd00:172:16::/64 -o end0 -j MASQUERADE
-A POSTROUTING -s fd00:172:16::/64 -o wlp1s0 -j MASQUERADE
-A POSTROUTING -s fd00:172:16::/64 -o wlan0 -j MASQUERADE
-A POSTROUTING -s fd00:172:16::/64 -o cdc-wdm0 -j MASQUERADE
# don't delete the 'COMMIT' line or these rules won't be processed
COMMIT
12. Configure UFW Rules
Configure UFW to allow LAN traffic and routing. These rules apply to both IPv4 and IPv6.
# Set default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw default allow forward
# Allow all traffic coming in on br0 (LAN interface)
sudo ufw allow in on br0
# Allow SSH access
sudo ufw allow ssh
# Allow routing from br0 (LAN) to WAN interfaces (applies to both IPv4 and IPv6)
sudo ufw route allow in on br0 out on end0
sudo ufw route allow in on br0 out on wlp1s0
sudo ufw route allow in on br0 out on wlan0
sudo ufw route allow in on br0 out on cdc-wdm0
# Enable UFW
sudo ufw enable
# Check status
sudo ufw status verbose13. Verification
Test the setup for both IPv4 and IPv6:
# Check IP forwarding
cat /proc/sys/net/ipv4/ip_forward # Should return 1
cat /proc/sys/net/ipv6/conf/all/forwarding # Should return 1
# Check DNS resolution on the router (IPv4)
dig @127.0.0.1 google.com
dig @172.16.0.1 google.com
# Check that dnsmasq is using dnscrypt-proxy as upstream
dig @127.0.2.1 google.com
# Check dnsmasq is listening on correct addresses
sudo ss -lnup | grep ':53'
# Should show:
# - dnsmasq on 127.0.0.1:53 and 172.16.0.1:53
# - dnscrypt-proxy on 127.0.2.1:53
# Check IPv6 address on br0
ip -6 addr show br0
# Should show fd00:172:16::1/64
# Check UFW status (shows both IPv4 and IPv6 rules)
sudo ufw status numbered
# Check IPv4 NAT rules
sudo iptables -t nat -L POSTROUTING -n -v
# Check IPv6 NAT66 rules
sudo ip6tables -t nat -L POSTROUTING -n -v
# Test IPv4 connectivity
ping -c 3 8.8.8.8
# Test IPv6 connectivity
ping6 -c 3 google.com
# Check dnsmasq leases (after a client connects)
cat /var/lib/NetworkManager/dnsmasq-br0.leases
# Monitor dnsmasq logs
sudo journalctl -u NetworkManager -f | grep dnsmasqFrom a LAN client:
- Connect to the br0 network
- Verify you receive an IPv4 IP in the range 172.16.0.100-200
- Verify you receive an IPv6 IP in the range fd00:172:16::100-200
- Gateway should be 172.16.0.1
- DNS server should be 172.16.0.1
- Test internet connectivity (both IPv4 and IPv6)
- Visit
test-ipv6.comoripv6.google.comto verify IPv6 internet access
Summary
Your router is now configured with:
- DHCP Server: Assigns IPv4 IPs 172.16.0.100-200 and IPv6 IPs fd00:172:16::100-200 to LAN clients on br0
- DNS Server: Dnsmasq on 172.16.0.1, forwarding to dnscrypt-proxy (127.0.2.1)
- Encrypted DNS: DNSCrypt-proxy provides DNS-over-HTTPS/DNSCrypt
- Firewall: UFW managing iptables/ip6tables rules with support for both IPv4 and IPv6
- IPv4 NAT/Routing: Traffic from LAN (br0) is routed to WAN (end0, wlp1s0, wlan0, cdc-wdm0) with masquerading
- IPv6 ULA + NAT66: LAN clients use ULA addresses (fd00:172:16::/64) with NAT66 for internet access
- IPv6 Router Advertisements: Enabled for automatic IPv6 configuration on clients
- Gateway: LAN clients use 172.16.0.1 as their default gateway (both IPv4 and IPv6)
Configuration Files Reference
Key Files Created/Modified:
- IP Forwarding:
/etc/sysctl.d/99-custom-forward.conf - NetworkManager DNS Config:
/etc/NetworkManager/conf.d/00-use-dnsmasq.conf - NetworkManager systemd-resolved:
/etc/NetworkManager/conf.d/no-systemd-resolved.conf - Dnsmasq Config:
/etc/NetworkManager/dnsmasq-shared.d/router.conf(includes IPv6) - Router DNS:
/etc/resolv.conf(immutable, points to 127.0.0.1) - UFW Default Config:
/etc/default/ufw - UFW IPv4 NAT Rules:
/etc/ufw/before.rules - UFW IPv6 NAT Rules:
/etc/ufw/before6.rules(NAT66 for ULA)
Quick Verification Commands
# Check all services are running
sudo systemctl status NetworkManager dnscrypt-proxy ufw
# Check DNS chain is working (both IPv4 and IPv6)
dig @172.16.0.1 google.com +short
dig @172.16.0.1 AAAA google.com +short
# Check firewall rules count
sudo ufw status numbered | wc -l
# Check IPv4 NAT is active
sudo iptables -t nat -L POSTROUTING -n | grep MASQUERADE
# Check IPv6 NAT66 is active
sudo ip6tables -t nat -L POSTROUTING -n | grep MASQUERADE
# Check IPv6 forwarding
cat /proc/sys/net/ipv6/conf/all/forwarding
# Test connectivity (both protocols)
ping -c 2 8.8.8.8 && ping6 -c 2 google.comNotes
- IPv6 with NAT66: This router uses ULA (Unique Local Addresses) for IPv6 with NAT66 enabled for internet access. ULA addresses (fd00::/8) are similar to private IPv4 addresses.
- DNS: The router uses its own dnsmasq (127.0.0.1) which forwards to dnscrypt-proxy (127.0.2.1) for encrypted DNS.
- Immutable resolv.conf: The
chattr +imakes/etc/resolv.confimmutable to prevent NetworkManager from overwriting it. - UFW Configuration: UFW automatically creates both IPv4 and IPv6 rules when you add firewall rules.
- Alternative to NAT66: If your ISP provides IPv6 prefix delegation, you can configure it to get globally routable IPv6 addresses instead of using ULA+NAT66.
Replace debian mirror