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.
       
 (HTM) (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).
       
 (HTM) 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 synapse.app.homeserver -c /var/synapse/
                   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).
       
 (HTM) Full list of Rex commands
 (HTM) Rex guides
 (HTM) 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.