rdist(1) Posted by .jh on 2019-02-10T18:41:00Z

Recently in a conversation a question came up if anyone used rdist(1), as I long time user I said I did. The inevitable question then became what I used it for, which then turned into “you should write a blog post about this”. So here we are. To quote the manual for rdist it’s “a program to maintain identical copies of files over multiple hosts”, which is exactly what it does. My primary use for it is that I have a firewall cluster using carp(4) for fail over. Now that works great, I can pull the power on the primary firewall and it gracefully fails over to the backup host without so much as a lost ping. Users are never the wiser. However I do need to ensure that all services run on both hosts and they have identical config files so things don’t start going wonky when the backup host becomes the primary. This is where rdist comes in.

On the primary node (I have a well defined carp master so the primary node will be the same unless it’s demoted manually or fails) I have a file /etc/Distfile which contains all files that should exist in identical fashion on the other node. If you name your distfile exactly that (with a capital D), it will be run automatically by the daily(8) script by cron(8), if the file in question has been updated. You do have to make sure that root can log in remotely to the other host, so setup ssh keys between the hosts. Now letting root log in remotely is generally a bad idea, and I’m sure you could do some fancy footwork with a restricted user and doas to accomplish this, but that is left as an exercise to the reader. I modified my sshd_config to only allow root logins on the internal carp interface not connected to the internet and only from the other host. So you’d already have had to have gotten in and become root to then be able to access the partner firewall, at which point I probably have bigger issues. This could look something like this:

In the main section of sshd_config you make sure you disallow root logins:

PermitRootLogin no

At the end of the file you then create a section:

Match Address 10.0.0.1/32 PermitRootLogin prohibit-password AllowUsers root

this will only allow root logins from 10.0.0.1 and only with keys (no passwords). There are probably better and more secure solutions, once again left as an exercise for the reader.

With the prerequisites out of the way it’s time to look at rdist itself. As noted above the configuration file is /etc/Distfile, and it consists of blocks within parentheses where you define various variables which you can then reference. A simple example:

HOSTS = ( myhost )

you’ve now created a group for your hosts. If you need to distribute to more than one host it’s a space separated list.

HOSTS = ( myhost1 myhost2 myhost3 )

Now you would define some groups of files you want to transfer.

FILES = ( /etc/dhcpd.conf /etc/someOtherFile )

To then make something happen you have to tell rdist what to do, so you create a stanza of commands:

$\{FILES\} -> $\{HOSTS\} install ; cmdspecial "/usr/sbin/rcctl restart dhcpd" ; notify someone@example.com ;

This will install the file, and then restart dhcpd (as we installed a new dhcpd.conf), and send a notification to someone@example.com that this has been done. Now if you only run this through daily and you read your daily emails it contains the same information so having a notify rule is not necessary. If you have files you simply want to copy over without executing any commands or notifications you simply leave those off.

You can of course define multiple stanzas for various files and various commands. An example could be that you run unbound and nsd and want to ensure those are up to date, but changes to those and dhcpd don’t necessarily happen at the same time, so you only want to restart that which has changed. You’d simply create different file and command stanzas.

DNSFILES = ( /var/nsd/etc/nsd.conf /var/nsd/zones/master /var/unbound/etc/unbound.conf ) $\{DNSFILES\} -> $\{HOSTS\} install ; cmdspecial "/usr/sbin/rcctl reload nsd unbound" ;

Create stanzas for each file and associated program you want action to be taken on. If the command you need to be run needs action to be taken on the file you distributed you can tell rdist to use $REMFILE. Example:

/etc/pf.conf -> $\{HOSTS\} install ; special "/sbin/pfctl -f $REMFILE" ;

So putting all this together you could have a Distfile that looks something like this:

HOSTS = ( myhost ) FILES = ( /etc/dhcpd.conf /etc/someOtherFile ) DNSFILES = ( /var/nsd/etc/nsd.conf /var/nsd/zones/master /var/unbound/etc/unbound.conf ) /etc/pf.conf -> $\{HOSTS\} install ; special "/sbin/pfctl -f $REMFILE" ; $\{DNSFILES\} -> $\{HOSTS\} install ; cmdspecial "/usr/sbin/rcctl reload nsd unbound" ; $\{FILES\} -> $\{HOSTS\} install ; cmdspecial "/usr/sbin/rcctl restart dhcpd" ; notify someone@example.com ;

Well that’s it for a quick crash course in rdist, hopefully it’s proven useful to someone.