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