unofficial mirror of help-guix@gnu.org 
 help / color / mirror / Atom feed
* Guix Network Router?
@ 2022-08-24 15:27 Peter Polidoro
  2022-08-29 20:12 ` Niklas Schmidt
  0 siblings, 1 reply; 4+ messages in thread
From: Peter Polidoro @ 2022-08-24 15:27 UTC (permalink / raw)
  To: help-guix

Does anyone have any examples of using Guix System as a network 
router?

I am hoping to replace some Ubuntu server routers with Guix.

I found this post:

https://timmydouglas.com/2021/02/07/guix-router.html

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

Thanks!


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Guix Network Router?
  2022-08-24 15:27 Guix Network Router? Peter Polidoro
@ 2022-08-29 20:12 ` Niklas Schmidt
  2022-08-30 17:27   ` Peter Polidoro
  0 siblings, 1 reply; 4+ messages in thread
From: Niklas Schmidt @ 2022-08-29 20:12 UTC (permalink / raw)
  To: Peter Polidoro; +Cc: help-guix

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)))



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Guix Network Router?
  2022-08-29 20:12 ` Niklas Schmidt
@ 2022-08-30 17:27   ` Peter Polidoro
  2022-09-01 19:00     ` Niklas Schmidt
  0 siblings, 1 reply; 4+ messages in thread
From: Peter Polidoro @ 2022-08-30 17:27 UTC (permalink / raw)
  To: Niklas Schmidt; +Cc: help-guix

This looks great, thank you!

I will test it out as soon as I get a chance.

> - Set up static IPv4-only networking.

For my particular case I would like a dynamic IP address on the 
wan interface and static IP addresses on the lan interfaces if 
that is possible.

> To run without error, the code requires
> nftables to be installed as it is used to check the rules.

Does nftables-service-type automatically install nftables?



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Guix Network Router?
  2022-08-30 17:27   ` Peter Polidoro
@ 2022-09-01 19:00     ` Niklas Schmidt
  0 siblings, 0 replies; 4+ messages in thread
From: Niklas Schmidt @ 2022-09-01 19:00 UTC (permalink / raw)
  To: Peter Polidoro; +Cc: help-guix

On Tue, Aug 30, 2022 at 13:27:45 -0400, Peter Polidoro wrote:
>This looks great, thank you!
>
>I will test it out as soon as I get a chance.

Great!

>>- Set up static IPv4-only networking.
>
>For my particular case I would like a dynamic IP address on the wan 
>interface and static IP addresses on the lan interfaces if that is 
>possible.

I haven't used DHCP. If you find out that dhcp-client-service-type is 
not flexible enough to listen only on the wan interface, you can always 
write your own service definition as I did for Dnsmasq.

>>To run without error, the code requires
>>nftables to be installed as it is used to check the rules.
>
>Does nftables-service-type automatically install nftables?
>

Good you ask! My wording was a bit sloppy.

By specifying nftables-service-type, Guix does all necessary steps to 
configure the Linux kernel's Netfilter. So you don't have to think about 
that.

But have a look at these lines from my last mail:

(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")))

For the first test, you can just remove these four lines of code and 
don't worry about it.

I wrote this code, because I wanted to ensure that the rules are at 
least syntactically correct before Guix activates the new operating 
system definition. The main reason is that my router machine is quite 
slow and reconfiguration takes, say, a minute or so.
If you configure your system with syntactically incorrect rules, 'herd 
status' (perhaps with 'sudo' in front of it) will report the service 
failing and there might be log messages at /var/log/messages. Make sure 
to have console access to the machine, as SSH (and networking in 
general) is likely to not work.

The above code invokes the 'nft' binary in check mode (-c) and pipes to 
it's standard input the rules, which are to be included in the operating 
system definition.
If you don't have nft in a directory listed in your PATH environment 
variable, trying to invoke nft will lead to some error (a non-zero exit 
code of the shell). At this point, my code will just fail and you get no 
operating system definition at all.

There are more elegant ways to perform such a check. I am convinced it 
is possible to let the build daemon execute the check, but I haven't 
looked into that.


Greetings
Niklas


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2022-09-01 19:27 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-24 15:27 Guix Network Router? Peter Polidoro
2022-08-29 20:12 ` Niklas Schmidt
2022-08-30 17:27   ` Peter Polidoro
2022-09-01 19:00     ` Niklas Schmidt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).