# infrastructure A shell-based configuration management framework for unix-like systems. ## boxconf [boxconf](./boxconf) is an extremely simple config management system written in shell. As long as you can SSH to the remote system (or "box") as root, the only requirement is a POSIX-compliant sh(1) and coreutils. It was inspired by many years of frustration with Ansible. ### Running boxconf To execute boxconf on a target host, just run the following: ./boxconf $HOSTNAME A deployment tarball will be generated and SCP'd to the remote box, where `boxconf` will re-exec itself. After gathering some information about the target system (such as the operating system, IP address, etc), `boxconf` will source your scripts in the following order: vars/common site/vars/common vars/os/${os} site/vars/os/${os} vars/distro/${distro} site/vars/distro/${distro} vars/hostclass/${hostclass} site/vars/hostclass/${hostclass} vars/hostname/${hostname} site/vars/hostname/${hostname} scripts/common site/scripts/common scripts/os/${os} site/scripts/os/${os} scripts/distro/${distro} site/scripts/distro/${distro} scripts/hostclass/${hostclass} site/scripts/hostclass/${hostclass} scripts/hostname/${hostname} site/scripts/hostname/${hostname} If any of those paths point to a directory, boxconf will source all files in that directory in glob order. The `site/` directory does not exist in this repo. Its purpose is to hold personal site-specific variables and scripts that you would rather not share in a public git repo. Ideally, you would use git submodules for this. If the hostname does not exist in DNS, you can manually specify the SSH target by passing the `-s $IP_ADDRESS` option to `boxconf`. The `hostclass` value is matched based on the regular expressions listed in the [hostclasses](./hostclasses) file. ### Encrypting source files `boxconf` supports encrypting any script or file using OpenSSL's [pbkdf2](https://www.openssl.org/docs/man3.0/man1/openssl-enc.html). The encrypted file will be automatically decrypted when generating the deployment tarball. The encryption password is read from the `BOXCONF_VAULT_PASSWORD` environment variable or the `.vault_password` file. If nether is set, you will be prompted for the password interactively. The [vault](./vault) script in the root of this directory can be used to manage encrypted files. ### Copying files to the remote host From your `boxconf` scripts, you can copy files in the `files/` (or `site/files/`) directory to the target system using the `install_file` function. The source file should have the same path as the remote path, and it can be tailored to the remote system by adding a custom suffix. For example, if you ran the following code: install_file -m 0644 /etc/passwd Then the following paths would be searched to find a suitable file to copy into the target system (the first match wins): site/files/etc/passwd.${hostname} files/etc/passwd.${hostname} site/files/etc/passwd.${hostclass}.${distro} files/etc/passwd.${hostclass}.${distro} site/files/etc/passwd.${distro}.${hostclass} files/etc/passwd.${distro}.${hostclass} site/files/etc/passwd.${hostclass}.${os} files/etc/passwd.${hostclass}.${os} site/files/etc/passwd.${os}.${hostclass} files/etc/passwd.${os}.${hostclass} site/files/etc/passwd.${hostclass} files/etc/passwd.${hostclass} site/files/etc/passwd.${distro} files/etc/passwd.${distro} site/files/etc/passwd.${os} files/etc/passwd.${os} site/files/etc/passwd.common files/etc/passwd.common If you use the `install_template` function, then the same file matching logic applies. However, the content of the matched file will be treated like a heredoc, allowing you to do things like interpolate `${shell_variables}` and perform `$(process_substitution)` within the file content. Note that if you do this, you must esacape any shell characters (like `$`) as needed. ### Copying TLS certificates The `install_certificate` and `install_certificate_key` functions can be used to copy certificates from the `site/ca` directory to the remote host. The certificates should be created and managed using the included [pki](./pki) script. Note that certificate keys are also encrypted with `$BOXCONF_VAULT_PASSWORD`. They are automatically decrypted when generating the configuration tarball. ## vault The [vault](./vault) script is used to manage encrypted files using OpenSSL's [pbkdf2](https://www.openssl.org/docs/man3.0/man1/openssl-enc.html). The encryption password is read from the `BOXCONF_VAULT_PASSWORD` environment variable or the `.vault_password` file. ### Create a new encrypted file The following command will invoke `$EDITOR` to create a new encrypted file at the specified path. ./vault create passwords.txt ### Decrypt file(s) The plaintext content of the file(s) will be written to stdout. ./vault decrypt secrets.txt ### Edit an encrypted file The file will be decrypted to a temporary file before being opened with `$EDITOR`. When the editor is closed, the file is encrypted again. ./vault edit passwords.txt ### Encrypt an existing file Encrypt an existing file in place: ./vault encrypt plain.txt ### Re-encrypt file(s) with a different password The new password is read from the `VAULT_NEW_PASSWORD` environment variable. If this variable is unset, you will be prompted interactively. ./vault reencrypt secrets.txt ## pki The [pki](./pki) script is used to manage an internal certificate authority using OpenSSL. Certificates and private keys are stored in the 'site/ca' directory with human-readable names. The certificatess are mapped to their OpenSSL serial number via symlinks. The private keys are encrypted with the `BOXCONF_VAULT_PASSWORD` variable, as described previously. The private key of the CA itself is acquired from the `CA_PASSWORD` environment variable, or the `.ca_password` file. Every certificate is associated with a single `boxconf` hostname, along with a unique certificate name. This allows you to store multiple certificates per host. ### Initialize the CA `pki init` will create the CA certificate and private key, along with an OpenSSL configuration file. [Name constraints](https://www.openssl.org/docs/man3.0/man5/x509v3_config.html) for the CA can be added with the `-c` option. For example, this command creates a CA for the `example.com` domain. This CA can sign certificates for all subdomains of `example.com` and `example.net`, as well as plain IP addresses in the 192.168.0.0/24 subnet: ./pki init -c 192.168.0.0/255.255.255.0 -c example.net example.com ### Create a server certificate `pki cert` creates a **server** certificate keypair signed by the CA. For example, this command creates a certificate pair for `nginx` for the host `webserver1` with a 365 day expiration (`-d`). After the hostname and certificate name, each additional argument is added to the Subject Alternative Names field. The Common Name is taken from the first specified SAN. If you don't specify a type for the SAN, `DNS` is assumed. ./pki cert -d 365 webserver1 nginx www.example.com DNS:example.com IP:192.168.0.5 ### Create a client certificate `pki client-cert` creates a **client** certificate keypair signed by the CA. After the hostname and certificate name, the first argument must be an LDAP-style DN for the certificate's Common Name value. SANs can be specified using additional arguments in same way as described previously. ./pki client-cert -d 3650 ldap1 replicator cn=replicator,dc=idm,dc=example,dc=com ### Renew a certificate `pki renew` is used to renew an existing certificate. ./pki renew -d 365 webserver1 nginx