Title: (R)?ex automation for deploying Matrix synapse on OpenBSD | |
Author: Solène | |
Date: 31 May 2021 | |
Tags: rex matrix openbsd | |
Description: | |
# Introduction | |
Today I will introduce you to Rex, an automation tool written in Perl | |
and using SSH, it's an alternative to Salt, Ansible or drist. | |
(R)?ex project website | |
# Setup | |
You need to install Rex on the management system, this can be done | |
using cpan or your package manager, on OpenBSD you can use "pkg_add | |
p5-Rex" to install it. You will get an executable script named "rex". | |
To make things easier, we will use ssh from the management machine | |
(your own computer) and a remote server, using your ssh key to access | |
the root account (escalation with sudo is possible but will complicate | |
things). | |
Get Rex | |
# Simple steps | |
Create a text file named "Rexfile" in a directory, this will contain | |
all the instructions and tasks available. | |
We will write in it that we want the features up to the syntax version | |
1.4 (latest at this time, doesn't change often), the default user to | |
connect to remote host will be root and our servers group has only one | |
address. | |
```Rexfile example | |
use Rex -feature => ['1.4']; | |
user "root"; | |
group servers => "myremoteserver.com"; | |
``` | |
We can go further now. | |
# Rex commands cheat sheet | |
Here are some commands, you don't need much to use Rex. | |
- rex -T : display the list of tasks defined in Rexfile | |
- rex -h : display help | |
- rex -d : when you need some debug | |
- rex -g <group> <task> : run a task on group | |
# Installing Munin-master | |
An example I like is deploying Munin on a computer, it requires a cron | |
and a package. | |
The following task will install a package and add a crontab entry for | |
root. | |
```Rexfile content | |
desc "Munin-cron installation"; | |
task "install_munin_cron", sub { | |
pkg "munin-server", ensure => "present"; | |
cron add => "root", { | |
ensure => "present", | |
command = > "su -s /bin/sh _munin /usr/local/bin/munin-cron", | |
on_change => sub { | |
say "Munin cron modified"; | |
} | |
}; | |
}; | |
``` | |
Now, let's say we want to configure this munin cron by providing it a | |
/etc/munin/munin.conf file that we have locally. This can be done by | |
adding the following code: | |
``` | |
file "/etc/munin/munin.conf", | |
source => "local_munin.conf", | |
owner => "root", | |
group => "wheel", | |
mode => 644, | |
on_change => sub { | |
say "munin.conf has been modified"; | |
}; | |
``` | |
This will install the local file "local_munin.conf" into | |
"/etc/munin/munin.conf" on the remote host, owned by root:wheel with a | |
chmod 644. | |
Now you can try "rex -g servers install_munin_cron" to deploy. | |
# Real world tasks | |
## Configuring PF | |
This task deploys a local pf.conf file into /etc/pf.conf and reload the | |
configuration on changes. | |
```Rexfile code | |
desc "Configuration PF"; | |
task "prepare_pf", sub { | |
file "/etc/pf.conf", | |
source => "pf.conf", | |
owner => "root", | |
group => "wheel", | |
mode => 400, | |
on_change => sub { | |
say "pf.conf modified"; | |
run "Restart pf", command => "pfctl -f /etc/pf.conf"; | |
}; | |
}; | |
``` | |
## Deploying Matrix Synapse | |
A task can call multiples tasks for bigger deployments. In this one, | |
we have a "synapse_deploy" task that will run synapse_install() and | |
then synapse_configure() and synapse_service() and finally prepare_pf() | |
to ensure the rules are correct. | |
As synapse will generate a working config file, there are no reason to | |
push one from the local system. | |
``` | |
desc "Deploy synapse"; | |
task "synapse_deploy", sub { | |
synapse_install(); | |
synapse_configure(); | |
synapse_service(); | |
prepare_pf(); | |
}; | |
desc "Install synapse"; | |
task "synapse_install", sub { | |
pkg "synapse", ensure => "present"; | |
run "Init synapse", | |
command => 'su -s /bin/sh _synapse -c "/usr/local/bin/python3 -m sy… | |
cwd => "/tmp/", | |
only_if => is_file("/var/synapse/homeserver.yaml"); | |
}; | |
desc "Configure synapse"; | |
task "synapse_configure", sub { | |
file "/etc/nginx/sites-enabled/synapse.conf", | |
source => "nginx_synapse.conf", | |
owner => "root", | |
group => "wheel", | |
mode => "444", | |
on_change => sub { | |
service nginx => "reload"; | |
}; | |
}; | |
desc "Service for synapse"; | |
task "synapse_service", sub { | |
service synapse => "ensure", "started"; | |
}; | |
``` | |
# Going further | |
Rex offers many feature because the configuration is real Perl code, | |
you can make loops, conditions and extend Rex by writing local modules. | |
Instead of pushing configuration file from an hard coded local one, I | |
could write a template of the configuration file and then use Rex to | |
generate the configuration file on the fly by giving it the needed | |
variables. | |
Rex has many functions to directly alter text files like | |
"append-if_no_such_line" to add a line if it doesn't exist or | |
replace/add/update a line matching a regex (can be handy to uncomment | |
some lines). | |
Full list of Rex commands | |
Rex guides | |
Rex FAQ | |
# Conclusion | |
Rex is a fantastic tool if you want to programmaticaly configure a | |
system, it can even be used for your local machine to allow | |
reproducible configuration or for keeping track of all the changes in | |
one place. | |
I really like it because it's simple to work with, it's Perl code doing | |
real things, it's easy to hack on it (I contributed to some changes and | |
the process was easy) and it only requires a working ssh toward a | |
server (and Perl on the remote host). While Salt stack also works | |
"agent less", it's painfully slow compared to Rex. |