From 1f6f3b00b06a2cb3c829c5dbbc12a9aa2b9e56a6 Mon Sep 17 00:00:00 2001 From: Patrick Spek Date: Wed, 4 May 2022 11:05:05 +0200 Subject: Add draft post for bashtard --- .../posts/2022/2022-04-25-bashtard-introduction.md | 251 +++++++++++++++++++++ 1 file changed, 251 insertions(+) create mode 100644 content/posts/2022/2022-04-25-bashtard-introduction.md diff --git a/content/posts/2022/2022-04-25-bashtard-introduction.md b/content/posts/2022/2022-04-25-bashtard-introduction.md new file mode 100644 index 0000000..bdc0010 --- /dev/null +++ b/content/posts/2022/2022-04-25-bashtard-introduction.md @@ -0,0 +1,251 @@ +--- +draft: true +title: Configuring my Machines with Bashtard +date: 2022-04-25 +tags: +- Bash +- FreeBSD +- GNU+Linux +- Programming +--- + +Over the past couple weeks I've been spending some time here and there to work +on my own application for configuring my machines. Before this I've tried +Ansible, but found it to be very convoluted to use, and requires a lot of +conditionals if your machines aren't all running the same base system. + +So I made something in Bash, with a few abstractions to make certain +interactions less annoying to do manually every time. This used to be called +`tyilnet`, but I've discussed the setup with a few people on IRC, and decided it +would be a fun project to make it a bit more agnostic, so other people could +also easily start using it. This resulted in the creation of [Bashtard](). + +It works by simply writing Bash scripts to do the configuration, and provides +abstractions for using the system's package manager, service manager, and some +utilities such as logging and dealing with configured values. Configuration +values can be set on a per-host or per-OS basis. Since I run a varied base of +OSs, including Gentoo, Debian, and FreeBSD, the per-OS configuration comes in +very handy to me. + +As for the reason to use Bash, I chose it because most of the systems I run +already have this installed, so it doesn't add a dependency _most of the time_. +I would've liked to do it in POSIX sh, but I feel that when you're reaching a +certain level of complexity, Bash offers some very nice features which can make +your code cleaner, or less likely to contain bugs. Features such as `[[ ]]`, +`local`, and arrays come to mind. + +I've been kindly asked to guide potential new users to writing their first +Bashtard script, known as a _playbook_, so if you want to know about how it +works in practice, keep on reading. If you're satisfied with your current +configuration management system, this might not be quite as interesting to you, +so be warned. + +The first steps for a new user would obviously to install Bashtard, as it's not +in any OSs package repositories yet. A `Makefile` is supplied in the repository, +which should make this easy enough. + +```txt +git clone https://git.tyil.nl/bashtard +cd bashtard +sudo make install +hash -r +``` + +Once installed, it needs some initialization. + +```txt +bashtard init +``` + +This will create the basic structure in `/etc/bashtard`, including a +`playbooks.d`. Inside this `playbooks.d` directory, any directory is considered +to be a playbook, which requires a `description.txt` and a `playbook.bash`. + +```txt +cd /etc/bashtard/playbooks.d +mkdir ssh +cd ssh +echo "OpenSSH configuration" > description.txt +$EDITOR playbook.bash +``` + +The `playbook.bash` needs to contain 3 functions which are used by `bashtard`, a +`playbook_add()`, `playbook_sync()`, and `playbook_del()`. These will be called +by the `bashtard` subcommand `add`, `sync`, and `del` respectively. + +I generally start with the `playbook_sync()` function first, since this is the +function that'll ensure all the configurations are kept in sync with my desires. +I want to have my own `sshd_config`, which needs some templating for the +`Subsystem sftp` line. There's a `template` function provided by bashtard, +which does some very simple templating. I'll pass it the `sftp` variable to use. + +```bash +playbook_sync() { + template sshd_config \ + "sftp=$(config "ssh.sftp")" \ + > /etc/ssh/sshd_config +} +``` + +Now to create the actual template. The `template` function looks for templates +in the `share` directory inside the playbook directory. + +```txt +mkdir share +$EDITOR share/sshd_config +``` + +Since I already know what I want my `sshd_config` to look like from previous +installed systems, I'll just use that, but with a variable for the `Subsystem +sftp` value. + +```cfg +# Connectivity +Port 22 +AddressFamily any +ListenAddress 0.0.0.0 +ListenAddress :: + +# Fluff +PrintMotd yes + +# SFTP +Subsystem sftp ${sftp} + +# Authentication +AuthorizedKeysFile /etc/ssh/authorized_keys .ssh/authorized_keys +PermitRootLogin no +PasswordAuthentication no +ChallengeResponseAuthentication no +PubkeyAuthentication no + +# Allow tyil +Match User tyil + PubkeyAuthentication yes + +# Allow public key authentication over VPN +Match Address 10.57.0.0/16 + PubkeyAuthentication yes + PermitRootLogin prohibit-password +``` + +The `${sftp}` placeholder will be filled with whatever value is returned by +`config "ssh.sftp"`. And for this to work properly, we will need to define the +variable somewhere. These are written to the `etc` directory inside a playbook. +You can specify defaults in a file called `defaults`, and this can be +overwritten by OS-specific values, which in turn can be overwritten by +host-specific values. + +```txt +mkdir etc +$EDITOR etc/defaults +``` + +The format for these files is a very simple `key=value`. It splits on the first +`=` to determine what the key and value are. This means you can use `=` in your +values, but not your keys. + +```txt +ssh.sftp=/usr/lib/openssh/sftp-server +``` + +This value is correct for Debian and derivatives, but not for my Gentoo or +FreeBSD systems, so I've created OS-specific configuration files for these. + +```txt +mkdir etc/os.d +cat /etc/os.d/linux-gentoo +ssh.sftp=/usr/lib64/misc/sftp-server +``` + +```txt +cat /etc/os.d/freebsd +ssh.sftp=/usr/lib64/misc/sftp-server +``` + +My `sshd_config` template also specifies the use of a `Motd`, so that needs to +be created as well. This can again be done using the `template` function. + +```bash +template "motd" \ + "fqdn=${BASHTARD_PLATFORM[fqdn]}" \ + "time=$(date -u "+%FT%T")" \ + > /etc/motd +``` + +The `motd` template gets saved at `share/motd`. + +```txt + ████████╗██╗ ██╗██╗██╗ ███╗ ██╗███████╗████████╗ + ╚══██╔══╝╚██╗ ██╔╝██║██║ ████╗ ██║██╔════╝╚══██╔══╝ + ██║ ╚████╔╝ ██║██║ ██╔██╗ ██║█████╗ ██║ + ██║ ╚██╔╝ ██║██║ ██║╚██╗██║██╔══╝ ██║ + ██║ ██║ ██║███████╗██╗██║ ╚████║███████╗ ██║ + ╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═══╝╚══════╝ ╚═╝ + +Welcome to ${fqdn}, last updated on ${time}. +``` + +Lastly, we want to ensure the SSH daemon gets reloaded after every sync, so +let's add that to the `playbook_sync()` function as well. + +```bash +svc reload "sshd" +``` + +The `svc` utility looks for a configuration value that stars with `svc.`, +followed by the service you're trying to act upon, so in this case that would be +`svc.sshd`. We can add this to our configuration files in `etc`. Across all my +machines, `sshd` seems to work as the value, so I only need to add one line to +`etc/defaults`. + +```txt +svc.sshd=sshd +``` + +This should take care of all the things I want automatically synced. The +`playbook_add()` function is intended for all one-time setup required for any +playbook. In this case that means the SSH daemon's service needs to be +activated, since it is not active by default on all my setups. + +```bash +playbook_add() { + svc enable "sshd" + svc start "sshd" +} +``` + +However, `add` does not call a `sync`, and I don't want my SSH service to run +with default configuration until a `sync` is initialized. So before enabling and +starting the service, I will call `sync` manually, by running a `playbook_sync` +first. This in turn, however, poses another problem, as `playbook_sync()` wants +to reload the service, which it can't do unless it is already running. To fix +this, I'll add an `if` statement to skip reloading if `bashtard` is running the +`add` command. + +```bash +playbook_sync() { + ... + + [[ $BASHTARD_COMMAND == "add" ]] && return + + svc reload "sshd" +} +``` + +Now, `bashtard add sshd` will run the `playbook_add()` function, which call the +`playbook_sync()` function before enabling and starting the `sshd` service. All +that is left is the `playbook_del()` function, which only really needs to stop +and disable the service. The templated files can be removed here as well if +desired, of course. + +```bash +playbook_del() { + svc stop "sshd" + svc disable "sshd" +} +``` + +Lastly, I configured my `crond` to run `bashtard sync` every 20 minutes, so +whenever I update my configurations, it can take up to 20 minutes to propagate +to all my machines. -- cgit v1.1