\documentclass[a4paper,12pt]{article} \usepackage[utf8]{inputenc} \usepackage[T1]{fontenc} \usepackage{lmodern} \usepackage{geometry} \geometry{margin=1in} \usepackage{listings} \usepackage{xcolor} \usepackage{parskip} \lstset{ basicstyle=\ttfamily\small, breaklines=true, frame=single, numbers=left, numberstyle=\tiny, keywordstyle=\color{blue}, commentstyle=\color{gray}, stringstyle=\color{red} } % Define YAML language for listings \lstdefinelanguage{yaml}{ keywords={true,false,null,yaml,network,version,ethernets,dhcp4,addresses,routes,to,via,nameservers}, keywordstyle=\color{blue}\bfseries, basicstyle=\ttfamily\small, sensitive=false, comment=[l]{\#}, commentstyle=\color{gray}\itshape, stringstyle=\color{red}, morestring=[b]{"}, morestring=[b]{'} } % Define Python language for listings \lstdefinelanguage{python}{ keywords={def,class,import,from,as,try,except,with,return,raise,if,elif,else,for,in,while,break,continue}, keywordstyle=\color{blue}\bfseries, basicstyle=\ttfamily\small, sensitive=true, comment=[l]{\#}, commentstyle=\color{gray}\itshape, stringstyle=\color{red}, morestring=[b]{"}, morestring=[b]{'}, identifierstyle=\color{black} } \begin{document} \title{Basic Configuration of dnsmasq in an Incus Container on Debian with Netplan} \author{} \date{} \maketitle \section{Introduction} This guide provides step-by-step instructions for setting up \texttt{dnsmasq} as a DNS and DHCP server in an Incus container running Debian. The network configuration is managed using Netplan and a custom Python script to create virtual Ethernet (veth) pairs and bridges, ensuring proper network integration. \section{Prerequisites} Before proceeding, ensure the following: \begin{itemize} \item Incus is installed on the host system (\texttt{sudo apt install incus}). \item A Debian-based container is created in Incus. \item Python 3 and the \texttt{pyroute2} package are installed on the host (\texttt{sudo apt install python3 python3-pyroute2}). \item Basic knowledge of Linux networking and container management. \item Root or sudo access to the host and container. \end{itemize} \section{Step-by-Step Configuration} \subsection{Creating and Setting Up the Incus Container} Create a Debian container named \texttt{dnsmasq-container} using the following commands on the host: \begin{lstlisting}[language=bash] incus create images:debian/12 dnsmasq-container incus config set dnsmasq-container security.syscalls.intercept.mount true incus config set dnsmasq-container security.nesting true incus config set dnsmasq-container security.privileged true incus start dnsmasq-container \end{lstlisting} The \texttt{security.syscalls.intercept.mount}, \texttt{security.nesting}, and \texttt{security.privileged} settings are required for \texttt{dnsmasq} and Docker to function correctly in the container. \subsection{Installing Additional Packages} Install the necessary packages inside the container: \begin{lstlisting}[language=bash] incus exec dnsmasq-container -- apt update incus exec dnsmasq-container -- apt install -y \ netplan.io \ sudo vim nano git tmux mc zip unzip curl wget htop lynx \ iproute2 termshark bridge-utils \ python3 python3-ipython python3-pyroute2 python3-scapy \ docker.io docker-compose \end{lstlisting} \subsection{Configuring Users and Permissions} Configure user access and permissions within the container. \subsubsection{Changing the Root Password} Set the root password to "passroot": \begin{lstlisting}[language=bash] incus exec dnsmasq-container -- bash -c 'echo "root:passroot" | chpasswd' \end{lstlisting} \subsubsection{Adding a New User} Add a new user named "user" with the password "pass" and add them to the "sudo" and "docker" groups: \begin{lstlisting}[language=bash] incus exec dnsmasq-container -- useradd -m -s /bin/bash user incus exec dnsmasq-container -- bash -c 'echo "user:pass" | chpasswd' incus exec dnsmasq-container -- usermod -aG sudo user incus exec dnsmasq-container -- usermod -aG docker user \end{lstlisting} \subsection{Accessing the Container} Access the container's shell: \begin{lstlisting}[language=bash] incus exec dnsmasq-container -- bash \end{lstlisting} \subsection{Installing dnsmasq} Update the package list and install \texttt{dnsmasq}: \begin{lstlisting}[language=bash] incus exec dnsmasq-container -- apt update incus exec dnsmasq-container -- apt install dnsmasq -y \end{lstlisting} \subsection{Configuring Network with Veth Pairs and Netplan} To enable advanced networking, use the provided Python script (\texttt{link.py}) to create a virtual Ethernet (veth) pair connecting the container to the host's network namespace, with an optional bridge for network integration. Save the following script as \texttt{link.py} on the host: \begin{lstlisting}[language=python] import argparse import os import subprocess import sys from pyroute2 import IPRoute, NetNS # ... (rest of the link.py script as provided) ... \end{lstlisting} Run the script to create a veth pair, move one end to the container's network namespace, and attach it to a bridge on the host. First, identify the container's name or ID: \begin{lstlisting}[language=bash] incus list \end{lstlisting} Assuming the container name is \texttt{dnsmasq-container}, execute the script with sudo privileges: \begin{lstlisting}[language=bash] sudo python3 link.py --namespace1 dnsmasq-container --namespace2 1 \ --name1 veth-container --name2 veth-host \ --bridge2 br0 --type1 incus \end{lstlisting} \textbf{Explanation:} \begin{itemize} \item \texttt{--namespace1 dnsmasq-container}: Specifies the container's network namespace (Incus container). \item \texttt{--namespace2 1}: Specifies the default (host) namespace. \item \texttt{--name1 veth-container}: Names the veth interface inside the container. \item \texttt{--name2 veth-host}: Names the veth interface on the host. \item \texttt{--bridge2 br0}: Attaches the host's veth interface to a bridge named \texttt{br0}. \item \texttt{--type1 incus}: Indicates that \texttt{namespace1} is an Incus container. \end{itemize} Before running the script, ensure the bridge \texttt{br0} exists on the host. Create it if necessary: \begin{lstlisting}[language=bash] sudo ip link add name br0 type bridge sudo ip link set br0 up \end{lstlisting} The script exposes the container's network namespace, creates the veth pair, moves \texttt{veth-container} to the container's namespace, attaches \texttt{veth-host} to \texttt{br0}, and brings both interfaces up. \subsection{Configuring the Network with Netplan} Configure the container's network using Netplan to assign a static IP address to the \texttt{veth-container} interface (aliased as \texttt{eth0} for simplicity). Create or edit the Netplan configuration file at \texttt{/etc/netplan/01-netcfg.yaml} inside the container: \begin{lstlisting}[language=bash] incus exec dnsmasq-container -- nano /etc/netplan/01-netcfg.yaml \end{lstlisting} Add the following configuration: \begin{lstlisting}[language=yaml] network: version: 2 ethernets: eth0: match: name: veth-container dhcp4: no addresses: - 192.168.1.10/24 routes: - to: default via: 192.168.1.1 nameservers: addresses: [8.8.8.8, 8.8.4.4] \end{lstlisting} \textbf{Explanation:} \begin{itemize} \item \texttt{match: name: veth-container}: Matches the \texttt{veth-container} interface created by the script, aliased as \texttt{eth0}. \item \texttt{dhcp4: no}: Disables DHCP to use a static IP. \item \texttt{addresses}: Assigns the static IP \texttt{192.168.1.10/24}. \item \texttt{routes}: Sets the default gateway to \texttt{192.168.1.1}. \item \texttt{nameservers}: Specifies Google's DNS servers. \end{itemize} Apply the configuration: \begin{lstlisting}[language=bash] incus exec dnsmasq-container -- netplan apply \end{lstlisting} Verify the network configuration: \begin{lstlisting}[language=bash] incus exec dnsmasq-container -- ip a show eth0 incus exec dnsmasq-container -- ping 8.8.8.8 \end{lstlisting} \subsection{Configuring dnsmasq} Edit the \texttt{dnsmasq} configuration file at \texttt{/etc/dnsmasq.conf}: \begin{lstlisting}[language=bash] incus exec dnsmasq-container -- nano /etc/dnsmasq.conf \end{lstlisting} Add or modify the following settings to enable DNS and DHCP: \begin{lstlisting} # DNS settings domain-needed bogus-priv no-resolv server=8.8.8.8 server=8.8.4.4 local=/example.local/ domain=example.local # DHCP settings dhcp-range=192.168.1.100,192.168.1.200,12h dhcp-option=3,192.168.1.1 dhcp-option=6,8.8.8.8,8.8.4.4 \end{lstlisting} \textbf{Explanation:} \begin{itemize} \item \texttt{domain-needed}: Prevents incomplete domain names from being sent to upstream DNS. \item \texttt{bogus-priv}: Blocks reverse DNS lookups for private IP ranges. \item \texttt{no-resolv}: Disables reading \texttt{/etc/resolv.conf}. \item \texttt{server}: Specifies upstream DNS servers (Google DNS in this case). \item \texttt{local} and \texttt{domain}: Configures a local domain. \item \texttt{dhcp-range}: Defines the IP range for DHCP clients (from 192.168.1.100 to 192.168.1.200, lease time 12 hours). \item \texttt{dhcp-option}: Sets the default gateway (option 3) and DNS servers (option 6). \end{itemize} \subsection{Starting and Enabling dnsmasq} Restart and enable the \texttt{dnsmasq} service: \begin{lstlisting}[language=bash] incus exec dnsmasq-container -- systemctl restart dnsmasq incus exec dnsmasq-container -- systemctl enable dnsmasq \end{lstlisting} Verify that \texttt{dnsmasq} is running: \begin{lstlisting}[language=bash] incus exec dnsmasq-container -- systemctl status dnsmasq \end{lstlisting} \subsection{Testing the Configuration} Test DNS resolution from within the container: \begin{lstlisting}[language=bash] incus exec dnsmasq-container -- nslookup example.local 192.168.1.10 \end{lstlisting} To test DHCP, connect a client device to the same network (via the \texttt{br0} bridge) and verify that it receives an IP address in the range \texttt{192.168.1.100--192.168.1.200}. \section{Troubleshooting} If \texttt{dnsmasq} fails to start: \begin{itemize} \item Check the logs: \texttt{incus exec dnsmasq-container -- journalctl -u dnsmasq}. \item Ensure no other service is using port 53 (DNS) or 67 (DHCP). \item Verify the network configuration with \texttt{incus exec dnsmasq-container -- ip a} and \texttt{incus exec dnsmasq-container -- ping 8.8.8.8}. \item Confirm the veth pair and bridge setup: \texttt{ip link show} on the host and \texttt{incus exec dnsmasq-container -- ip link show}. \end{itemize} \section{Conclusion} This guide configures \texttt{dnsmasq} as a DNS and DHCP server in an Incus container on Debian. The \texttt{link.py} script and Netplan configuration ensure a robust network setup with veth pairs and static IP addressing. For advanced configurations, refer to the \texttt{dnsmasq} documentation (\texttt{man dnsmasq}) and \texttt{pyroute2} documentation. \end{document}