help-guix
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Guix Network Router?


From: Niklas Schmidt
Subject: Re: Guix Network Router?
Date: Mon, 29 Aug 2022 20:12:08 +0000

Hello Peter.

On Wed, Aug 24, 2022 at 11:27:33 -0400, Peter Polidoro wrote:
Does anyone have any examples of using Guix System as a network router?

Below I attach a system configuration that works for me. It doesn't necessarily adhere to Guix's best practices in every regard.

I would prefer, though, not to need custom packages or channels, if possible.

The question is what exactly the router is supposed to do, i.e. which network services it shall provide.

The below example does the following:

- Set custom meaningful names to the three network interfaces based on their MAC addresses.

- Set up static IPv4-only networking.

- Route traffic between the different subnets, restricted by Nftables rules.

- Provide DNS and DHCP to some subnets, using Dnsmasq.

- Use the serial console for bootloader and kernel consoles. (I run this router on a QEMU-KVM virtual machine with no graphics.)

I found Dnsmasq to be sufficient as a combined DNS/DHCP server for a small network. For larger deployments one might use separate software packages for DNS and DHCP.

I removed some unrelated services from the configuration. The code is thus not tested and might need adjustment. To run without error, the code requires nftables to be installed as it is used to check the rules.
Feel free to ask if questions arise.

'Hope that provides some inspiration.


Greetings
Niklas


(use-modules (gnu))
(use-service-modules networking shepherd ssh sysctl)
(use-package-modules admin bash dns linux)
(use-modules (ice-9 popen))
(use-modules (guix records))

(define-record-type* <if-rename> if-rename make-if-rename
  if-rename?
  this-if-rename
  (name if-rename-name)
  (mac if-rename-mac))

(define (if-renames->udev-rule if-renames)
  (udev-rule
   "70-netifnames.rules"
   (string-join
    (map
     (lambda (iface)
       (format #f
               "SUBSYSTEM==\"net\", ACTION==\"add\", ATTR{address}==~s, NAME=~s"
               (if-rename-mac iface)
               (if-rename-name iface)))
     if-renames)
    "\n" 'suffix)))

(define (acpi-daemon-service)
  (simple-service
   'acpi-daemon shepherd-root-service-type
   (list
    (shepherd-service
     (provision '(acpi-daemon))
     (start #~(make-forkexec-constructor
               (list #$(file-append acpid "/sbin/acpid")
                     "-f" "-l" "--confdir"
                     #$(file-union "etc-acpi-events"
                                   `(("power"
                                      ,(plain-file "power"
                                                   
"event=button/power\naction=/run/current-system/profile/sbin/shutdown"))
                                     ("reboot" ,(plain-file "reboot"
                                                            
"event=button/reboot\naction=/run/current-system/profile/sbin/reboot")))))))
     (stop #~(make-kill-destructor))))))


(define %my-nftables-ruleset
  (plain-file "nftables.conf"
              "
flush ruleset

define NET_LAB = 192.168.10.0/24
define NET_LAN = 192.168.3.0/24

table ip global {

  chain inbound_wan {
    # accept ping
    icmp type echo-request accept

    # accept ICMP Fragmentation Needed packets, for Path MTU Discovery
    icmp code frag-needed accept
    icmp type destination-unreachable accept
  }

  chain inbound_private {
    # accept ping
    icmp type echo-request limit rate 5/second accept

    # accept SSH, DNS, DHCP
    ip protocol . th dport vmap { \\
      tcp . 22 : accept, \\
      udp . 53 : accept, \\
      tcp . 53 : accept, \\
      udp . 67 : accept \\
    }
  }

  chain inbound {
    type filter hook input priority 0; policy drop;

    ct state established,related accept

    iifname vmap { \\
      lo : accept, \\
      wan : jump inbound_wan, \\
      lab : jump inbound_private, \\
      lan : jump inbound_private \\
    }
  }

  chain forward {
    type filter hook forward priority 0; policy drop;

    ct state established,related accept

    # allow from lab, lan to wan
    iifname { lab, lan } oifname wan accept
  }

  chain postrouting {
    type nat hook postrouting priority 100; policy accept;

    # masquerade private IP addresses
    ip saddr { $NET_LAB, $NET_LAN } oifname wan masquerade
  }
}
"))

(let ((port (open-output-pipe "nft -c -f -")))
  (display (plain-file-content %my-nftables-ruleset) port)
  (if (not (eqv? 0 (status:exit-val (close-pipe port))))
      (error "Nftables rules don't pass check")))


(define %my-dnsmasq-config
  (plain-file "dnsmasq.conf"
              "
dhcp-authoritative
domain-needed
localise-queries
expand-hosts

domain=lan

server=/lan/
server=/lab/

# RFC6761: domains that should not be forwarded to Internet name servers
server=/bind/
server=/invalid/
server=/local/
server=/localhost/
server=/onion/
server=/test/

dhcp-leasefile=/tmp/dhcp.leases
stop-dns-rebind
rebind-localhost-ok
dhcp-broadcast=tag:needs-broadcast

user=nobody
group=nogroup

cache-size=1500

#bind-dynamic
#interface=lan,lab
#listen-address=192.168.3.3,192.168.10.3


bogus-priv
#conf-file=/usr/share/dnsmasq/rfc6761.conf
dhcp-range=set:lan,192.168.3.100,192.168.3.249,255.255.255.0,12h
no-dhcp-interface=wan
dhcp-range=set:lab,192.168.10.100,192.168.10.249,255.255.255.0,12h


# in response to CERT VU#598349
# An attacker may add a device with the name 'wpad` to the network,
# which may produce a collision with a standardized WPAD DNS name.

dhcp-ignore-names=tag:dhcp_bogus_hostname

dhcp-name-match=set:dhcp_bogus_hostname,localhost
dhcp-name-match=set:dhcp_bogus_hostname,wpad
"))

(define (dnsmasq-service)
  (simple-service
   'mydnsmasq shepherd-root-service-type
   (list
    (shepherd-service
     (provision '(mydnsmasq))
     (start #~(make-forkexec-constructor
               (list #$(file-append dnsmasq "/sbin/dnsmasq")
                     "-k"
                     "-C" #$%my-dnsmasq-config)))
     (stop #~(make-kill-destructor))
     (respawn? #f)))))



(operating-system
 (locale "en_GB.utf8")
 (timezone "Europe/Berlin")
 (keyboard-layout (keyboard-layout "de"))
 (host-name "gw")
 (users (cons* (user-account
                (name "nschmidt")
                (comment "Nschmidt")
                (group "users")
                (home-directory "/home/nschmidt")
                (supplementary-groups
                 '("wheel" "netdev")))
               %base-user-accounts))
 (packages
  (cons*
   (list (specification->package "bind") "utils")
   (append (map specification->package
                '("emacs-no-x" "htop" "net-tools" "nss-certs"
                  "tmux" "wireguard-tools"))
           %base-packages)))
 (hosts-file
  (plain-file "hosts"
              (string-append (local-host-aliases host-name)
                             "
192.168.3.2     server.lan
192.168.10.3    gw.lab
")))
 (services
  (append
   (list
    ;; Handle the power and restart ACPI buttons.
    ;; Alternatively use Elogind.
    (acpi-daemon-service)
    (dnsmasq-service)
    (service openssh-service-type
             (openssh-configuration
              (password-authentication? #f)))
    (service static-networking-service-type
             (list (static-networking
                    (addresses
                     (list (network-address
                            (device "wan")
                            (value "192.168.1.4/24"))
                           (network-address
                            (device "lab")
                            (value "192.168.10.3/24"))
                           (network-address
                            (device "lan")
                            (value "192.168.3.3/24"))))
                    (routes
                     (list (network-route
                            (destination "default")
                            (gateway "192.168.1.1"))))
                    (name-servers '("192.168.1.1")))))
    (service nftables-service-type
             (nftables-configuration
              (ruleset %my-nftables-ruleset))))
   (modify-services
    %base-services
    (udev-service-type config =>
                       (udev-configuration
                        (inherit config)
                        (rules
                         (cons* (if-renames->udev-rule
                                 (list (if-rename
                                        (mac "52:54:00:7e:7c:41")
                                        (name "lab"))
                                       (if-rename
                                        (mac "52:54:00:77:38:d2")
                                        (name "wan"))
                                       (if-rename
                                        (mac "52:54:00:5e:20:94")
                                        (name "lan"))))
                          (udev-configuration-rules config)))))
    (sysctl-service-type config =>
                         (sysctl-configuration
                          (settings
                           (append '(("net.ipv4.ip_forward" . "1"))
                                   %default-sysctl-settings))))
    ;; The machine has no graphics card, so VTs are not used
    (delete console-font-service-type)
    (delete mingetty-service-type)
    (delete virtual-terminal-service-type))))
 (kernel-arguments
  '("quiet"
    "ipv6.disable=1"
    ;; Use serial console
    ;; Linux uses the last console= argument as /dev/console
    "console=tty0"
    "console=ttyS0,115200"))
 (bootloader
  (bootloader-configuration
   (bootloader grub-bootloader)
   (targets '("/dev/sda"))
   (terminal-inputs '(console serial))
   (terminal-outputs '(gfxterm serial))
   (serial-unit 0)
   (serial-speed 115200)
   (keyboard-layout keyboard-layout)))
 (swap-devices
  (list (swap-space
         (target (uuid "7472d01e-0d42-4a04-8f90-8b8e0f08a957")))))
 (file-systems
  (cons* (file-system
          (mount-point "/")
          (device
           (uuid "b9dc4762-73b0-46d6-b4e9-33a530522a16"
                 'ext4))
          (type "ext4"))
         %base-file-systems)))




reply via email to

[Prev in Thread] Current Thread [Next in Thread]