Skip to content

Instantly share code, notes, and snippets.

@HackingGate
Last active December 1, 2025 15:30
Show Gist options
  • Select an option

  • Save HackingGate/1508e7a1d7eeb1145b2a32c15606f774 to your computer and use it in GitHub Desktop.

Select an option

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 "i@hackinggate.com"
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."
@HackingGate
Copy link
Author

HackingGate commented Oct 5, 2025

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 300

Enable 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 route

@HackingGate
Copy link
Author

HackingGate commented Oct 11, 2025

Router 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 (device br0) 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 1

2. 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-proxy

In most cases, it listens on 127.0.2.1:53.

Verify the service is running:

sudo systemctl status dnscrypt-proxy

3. 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
EOF

4. 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 --now

5. 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.conf

6. 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.conf

7. 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/64

8. 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 dnsmasq

You 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/ufw

10. 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.rules

Add 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.rules

Add 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 verbose

13. 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 dnsmasq

From 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.com or ipv6.google.com to 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:

  1. IP Forwarding: /etc/sysctl.d/99-custom-forward.conf
  2. NetworkManager DNS Config: /etc/NetworkManager/conf.d/00-use-dnsmasq.conf
  3. NetworkManager systemd-resolved: /etc/NetworkManager/conf.d/no-systemd-resolved.conf
  4. Dnsmasq Config: /etc/NetworkManager/dnsmasq-shared.d/router.conf (includes IPv6)
  5. Router DNS: /etc/resolv.conf (immutable, points to 127.0.0.1)
  6. UFW Default Config: /etc/default/ufw
  7. UFW IPv4 NAT Rules: /etc/ufw/before.rules
  8. 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.com

Notes

  • 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 +i makes /etc/resolv.conf immutable 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment