ansible-alpine-host/README.md

212 lines
5.5 KiB
Markdown
Raw Permalink Normal View History

2022-04-10 00:29:22 +00:00
# Alpine
An Ansible playbook for installing an encrypted and compressed data
Alpine container host.
The playbook should run from any live Linux system with network, SSH and
a Python interpreter. The goal is to install the host even if the VPS
provider doesn't support Alpine officially.
## How it works
Root (`/`) and boot (`/boot`) directories are unencrypted.
Storage (`/srv`), including Docker volumes and static files, are
encrypted using plain dm-crypt. With plain encryption you don't need to
keep dumped LUKS headers around. Just use a sufficiently long
passphrase and don't lose it.
Both root and data are Btrfs partitions with zstd compression enabled.
After boot, you can open the `/srv` partition and start Docker. This is
a manual process so far.
`/var/log` is mounted in memory, so logs are lost after rebooting.
We're working towards sending every log to a remote database using
syslog-ng, but OpenRC would need to be replaced, most likely by Monit.
### Why isn't LUKS used?
We've had really bad experiences with LUKS2 encryption, where data was
completely lost even when we had external header dumps. The only reply
we had about this on the dm-crypt list is that LVM+LUKS setups are
discouraged.
With plain encryption we don't have to keep extra stuff around, and
according to some obscure comments on dm-crypt documentation, provides
a 1:1 mapping between blocks, so hypothetically SSD storage can perform
trimming.
### Why encrypting everything except root?
To be able to fully encrypt a VM we would need to use GRUB2 with a LUKS1
`/boot`. This is possible to do on a physical machine, where you can
type the password somewhat comfortably, but on a remote VM this would
need to be done over VNC.
Arguably the host operator can access the VM's RAM so it's game over
from the start, but we still want to keep data encrypted at rest.
## Setting the remote hosts
Boot any live system ISO. This playbook is tested with latest
Archlinux.
Network configuration should be automatic, but sometimes you need to do
it manually, specially if the provider doesn't support DHCP or the
gateway is outside the netmask, since it confuses some DHCP clients.
The Python interpreter is required to run Ansible and you also need
`parted` to partitioning the disk.
Once booted, change root password on the VNC console:
```bash
echo "root:something you can type comfortably over VNC" | chpasswd -
```
Make sure you can login via SSH via `root`. This is by default in
Archlinux.
```bash
grep PermitRootLogin /etc/ssh/sshd_config
```
If it's not set to `yes`, edit the file and restart `sshd`.
## Setting the local machine
Clone this repository.
```bash
cd ~/Projects/Sutty
git clone https://gitea.sutty.coop.ar/Sutty/ansible-alpine-host.git
cd ansible-alpine-host
```
Install dependencies.
```bash
# For Arch and Arch-base distros
pacman -Sy ansible make pwgen
```
Copy your SSH public key to remote hosts so Ansible (and you) can login
without a password.
```bash
ssh-copy-id root@your.host.name
```
## Configuring the playbook
2022-06-19 05:56:50 +00:00
### General configuration
#### Syslog
Syslog-ng is used to centralize logging into a single node. Edit the IP
address for your log server on `alpines.yml`:
```yaml
vars:
- log_server: "EKU:MEN:IP:ADD::RESS"
+ log_server: "10.13.12.1"
```
### Create a vault password
2022-04-10 00:29:22 +00:00
```bash
make vault.key
```
> **Important:** Since the `vault.key` file contains the password for
> your vault, **don't commit this into git** and keep a copy on your
> password storage (KeepassXC is a good option). **Always make
> backups!**.
### Inventory
Add your hostnames into the `inventory.yml` file. You can add host
variables here or on separate files in the `host_vars/` directory,
following [Ansible
documentation](https://docs.ansible.com/ansible/latest/network/getting_started/first_inventory.html).
We use `host_vars/`, so after adding a hostname, create a correspoding
file.
```yaml
# inventory.yml
alpines:
hosts:
your.host.name:
another.host.name:
```
```yaml
# host_vars/your.host.name.yml
---
# If you don't have DNS yet, set it to the IP address
ansible_host: "192.50.248.13"
# This isn't used but it's good to have it at hand
gateway: "192.99.148.254"
netmask: "32"
# Optional IPv6 configuration.
ip6: "2607:5300:60:555a::4:0"
gateway6: "2607:5300:60:55ff:ff:ff:ff:ff"
netmask6: "112"
# Device where the installation will be performed. If using VirtIO,
# it'll probably be `vda`
disk_device: "/dev/vda"
```
Finally, add an encryption key for the `/srv` partition and a password
for the `root` user.
```bash
make encrypt-string host="your.host.name" name="key"
make encrypt-string host="your.host.name" name="root"
```
Do all this once per host.
## Installing
Once everything's done, run `make`. You can probably run it several
times in a row and stuff shouldn't break (this is called idempotency).
```bash
make alpines
```
> We use `make` so we don't have to remember a lot of flags. You can
> see what's happening.
## Post-install
Once the hosts are installed and rebooted, you can open their `/srv`
partitions and start the Docker service.
Move the hostname from the `alpines` group into the `dockers` group in
your `inventory.yml`.
```diff
alpines:
hosts:
- your.host.name:
dockers:
hosts:
+ your.host.name:
```
And run the `make` rule:
```bash
make dockers
```
## Development
We test this over VMs managed via `virt-manager`. Follow the GUI for
creating a VM of type Generic OS, load an ISO (we use Archlinux) and
create a virtual hard drive of at least 1.1GB.