# How to use: # # rex commons nsd_master nsd_slaves # # Why use Rex to automate my servers? Because Rex is KISS, Puppet, SALT and Chef # are not. So, why not use Ansible then? To use Ansible correctly you should also # install Python on the target machines (not mandatory, though. But better). # Rex is programmed in Perl and there is already Perl in the base system of OpenBSD. # Also, I find Perl > Python (my personal opinion). use Rex -feature => ['1.4']; use Rex::Logger; use File::Slurp; # REX CONFIG SECTION group frontends => 'blowfish.buetow.org', 'fishfinger.buetow.org'; group dnsmaster => 'blowfish.buetow.org'; group dnsslaves => 'fishfinger.buetow.org'; user 'rex'; sudo TRUE; parallelism 5; # CUSTOM (PERL-ish) CONFIG SECTION (what Rex can't do by itself) # Note we using anonymous subs here. This is so we can pass the subs as # Rex template variables too. # Gather IPv6 addresses based on hostname. our $ipv6address = sub { my $hostname = shift; return '2a01:4f8:c17:20f1::42' if $hostname eq 'blowfish'; return '2a03:6000:6f67:624::99' if $hostname eq 'fishfinger'; Rex::Logger::info("Unable to determine IPv6 address for $hostname", 'error'); return '::1'; }; # Bootstrapping the FQDN based on the server IP as the hostname and domain # facts aren't set yet due to the myname file in the first place. our $fqdns = sub { my $ipv4 = shift; return 'blowfish.buetow.org' if $ipv4 eq '23.88.35.144'; return 'fishfinger.buetow.org' if $ipv4 eq '46.23.94.99'; Rex::Logger::info("Unable to determine hostname for $ipv4", 'error'); return 'HOSTNAME-UNKNOWN.buetow.org'; }; # To determine whether te server is te primary or the secondary. our $is_primary = sub { my $ipv4 = shift; $fqdns->($ipv4) eq 'blowfish.buetow.org'; }; our $filewalk; our $filewalk = sub { my $dir = shift; my @files; opendir my $dh, $dir or die $!; while (my $entry = readdir $dh) { next if $entry eq '.' or $entry eq '..'; if (-d "$dir/$entry") { push @files, $_ for $filewalk->("$dir/$entry"); } elsif (-f "$dir/$entry") { push @files, "$dir/$entry"; } else { Rex::Logger::info("Unsupported file type for $dir/$entry", 'error'); } } closedir $dh; return @files; }; # The secret store. Note to myself: "geheim cat rexfilesecrets.txt" our $secrets = sub { read_file './secrets/' . shift }; our @dns_zones = qw/buetow.org dtail.dev foo.surf foo.zone irregular.ninja sidewalk.ninja snonux.de snonux.me snonux.land/; our @acme_hosts = qw/buetow.org paul.buetow.org tmp.buetow.org dory.buetow.org footos.buetow.org dtail.dev foo.zone irregular.ninja snonux.land/; # UTILITY TASKS task 'id', group => 'frontends', sub { say run 'id' }; task 'dump_info', group => 'frontends', sub { dump_system_information }; # OPENBSD TASKS SECTION desc 'Install base stuff'; task 'base', group => 'frontends', sub { pkg 'tig', ensure => present; pkg 'vger', ensure => present; pkg 'zsh', ensure => present; append_if_no_such_line '/etc/rc.conf.local', 'pkg_scripts="uptimed httpd failunderd dserver"'; file '/etc/myname', content => template('./etc/myname.tpl', fqdns => $fqdns), owner => 'root', group => 'wheel', mode => '644'; }; desc 'Setup uptimed'; task 'uptimed', group => 'frontends', sub { pkg 'uptimed', ensure => present; service 'uptimed', ensure => 'started'; }; desc 'Setup rsync'; task 'rsync', group => 'frontends', sub { pkg 'rsync', ensure => present; file '/etc/rsyncd.conf', content => template('./etc/rsyncd.conf.tpl'), owner => 'root', group => 'wheel', mode => '644'; file '/usr/local/bin/rsync.sh', content => template('./scripts/rsync.sh.tpl', is_primary => $is_primary), owner => 'root', group => 'wheel', mode => '755'; append_if_no_such_line '/etc/daily.local', '/usr/local/bin/rsync.sh'; }; desc 'Configure the gemtexter sites'; task 'gemtexter', group => 'frontends', sub { file '/usr/local/bin/gemtexter.sh', content => template('./scripts/gemtexter.sh.tpl', is_primary => $is_primary), owner => 'root', group => 'wheel', mode => '744'; file '/etc/daily.local', ensure => 'present', owner => 'root', group => 'wheel', mode => '644'; append_if_no_such_line '/etc/daily.local', '/usr/local/bin/gemtexter.sh'; }; desc 'Configure ACME client'; task 'acme', group => 'frontends', sub { file '/etc/acme-client.conf', content => template('./etc/acme-client.conf.tpl', acme_hosts => \@acme_hosts, is_primary => $is_primary), owner => 'root', group => 'wheel', mode => '644'; file '/usr/local/bin/acme.sh', content => template('./scripts/acme.sh.tpl', acme_hosts => \@acme_hosts, is_primary => $is_primary), owner => 'root', group => 'wheel', mode => '744'; file '/etc/daily.local', ensure => 'present', owner => 'root', group => 'wheel', mode => '644'; append_if_no_such_line '/etc/daily.local', '/usr/local/bin/acme.sh'; }; desc 'Invoke ACME client'; task 'acme_invoke', group => 'frontends', sub { say run '/usr/local/bin/acme.sh'; }; desc 'Setup httpd'; task 'httpd', group => 'frontends', sub { append_if_no_such_line '/etc/rc.conf.local', 'httpd_flags='; #delete_lines_according_to qr{httpd_flags}, '/etc/rc.conf.local'; file '/etc/httpd.conf', content => template('./etc/httpd.conf.tpl', acme_hosts => \@acme_hosts, is_primary => $is_primary), owner => 'root', group => 'wheel', mode => '644', on_change => sub { service 'httpd' => 'restart' }; service 'httpd', ensure => 'started'; }; desc 'Setup inetd'; task 'inetd', group => 'frontends', sub { append_if_no_such_line '/etc/rc.conf.local', 'inetd_flags='; file '/etc/inetd.conf', source => './etc/inetd.conf', owner => 'root', group => 'wheel', mode => '644', on_change => sub { service 'inetd' => 'restart' }; service 'inetd', ensure => 'started'; }; desc 'Setup relayd'; task 'relayd', group => 'frontends', sub { append_if_no_such_line '/etc/rc.conf.local', 'relayd_flags='; file '/etc/relayd.conf', content => template('./etc/relayd.conf.tpl', ipv6address => $ipv6address, is_primary => $is_primary), owner => 'root', group => 'wheel', mode => '600', on_change => sub { service 'relayd' => 'restart' }; service 'relayd', ensure => 'started'; }; desc 'Setup OpenSMTPD'; task 'smtpd', group => 'frontends', sub { Rex::Logger::info('Dealing with mail aliases'); file '/etc/mail/aliases', source => './etc/mail/aliases', owner => 'root', group => 'wheel', mode => '644', on_change => sub { say run 'newaliases' }; Rex::Logger::info('Dealing with mail virtual domains'); file '/etc/mail/virtualdomains', source => './etc/mail/virtualdomains', owner => 'root', group => 'wheel', mode => '644', on_change => sub { service 'smtpd' => 'restart' }; Rex::Logger::info('Dealing with mail virtual users'); file '/etc/mail/virtualusers', source => './etc/mail/virtualusers', owner => 'root', group => 'wheel', mode => '644', on_change => sub { service 'smtpd' => 'restart' }; Rex::Logger::info('Dealing with smtpd.conf'); file '/etc/mail/smtpd.conf', content => template('./etc/mail/smtpd.conf.tpl'), owner => 'root', group => 'wheel', mode => '644', on_change => sub { service 'smtpd' => 'restart' }; service 'smtpd', ensure => 'started'; }; desc 'Setup DNS server'; task 'nsd_master', group => 'dnsmaster', sub { my $restart = FALSE; append_if_no_such_line '/etc/rc.conf.local', 'nsd_flags='; Rex::Logger::info('Dealing with master DNS key'); file '/var/nsd/etc/key.conf', content => template('./var/nsd/etc/key.conf.tpl', nsd_key => $secrets->('/var/nsd/etc/nsd_key.txt')), owner => 'root', group => '_nsd', mode => '640', on_change => sub { $restart = TRUE }; Rex::Logger::info('Dealing with master DNS config'); file '/var/nsd/etc/nsd.conf', content => template('./var/nsd/etc/nsd.conf.master.tpl', dns_zones => \@dns_zones), owner => 'root', group => '_nsd', mode => '640', on_change => sub { $restart = TRUE }; for my $zone (@dns_zones) { Rex::Logger::info("Dealing with DNS zone $zone"); file "/var/nsd/zones/master/$zone.zone", content => template("./var/nsd/zones/master/$zone.zone.tpl"), owner => 'root', group => 'wheel', mode => '644', on_change => sub { $restart = TRUE }; } service 'nsd' => 'restart' if $restart; service 'nsd', ensure => 'started'; }; desc 'Setup DNS slaves'; task 'nsd_slaves', group => 'dnsslaves', sub { my $restart = FALSE; Rex::Logger::info('Dealing with slave DNS key'); file '/var/nsd/etc/key.conf', content => template('./var/nsd/etc/key.conf.tpl', nsd_key => $secrets->('/var/nsd/etc/nsd_key.txt')), owner => 'root', group => '_nsd', mode => '640', on_change => sub { $restart = TRUE }; Rex::Logger::info('Dealing with slave DNS config'); file '/var/nsd/etc/nsd.conf', content => template('./var/nsd/etc/nsd.conf.slave.tpl', dns_zones => \@dns_zones), owner => 'root', group => '_nsd', mode => '640', on_change => sub { $restart = TRUE }; service 'nsd' => 'restart' if $restart; service 'nsd', ensure => 'started'; }; desc 'Setup DTail'; task 'dtail', group => 'frontends', sub { my $restart = FALSE; file '/etc/rc.d/dserver', content => template('./etc/rc.d/dserver.tpl'), owner => 'root', group => 'wheel', mode => '755', on_change => sub { $restart = TRUE }; file '/etc/dserver', ensure => 'directory', owner => 'root', group => 'wheel', mode => '755'; file '/etc/dserver/dtail.json', content => template('./etc/dserver/dtail.json.tpl'), owner => 'root', group => 'wheel', mode => '755', on_change => sub { $restart = TRUE }; file '/usr/local/bin/dserver-update-key-cache.sh', content => template('./scripts/dserver-update-key-cache.sh.tpl'), owner => 'root', group => 'wheel', mode => '500'; append_if_no_such_line '/etc/daily.local', '/usr/local/bin/dserver-update-key-cache.sh'; service 'dserver' => 'restart' if $restart; service 'dserver', ensure => 'started'; }; desc 'Setup failunderd'; task 'failunderd', group => 'frontends', sub { }; # COMBINED TASKS SECTION desc 'Common configs of all hosts'; task 'commons', group => 'frontends', sub { base(); uptimed(); httpd(); gemtexter(); acme(); acme_invoke(); inetd(); relayd(); smtpd(); rsync(); dtail(); failunderd(); }; 1; # vim: syntax=perl