pumbaasdad / system-restore

1 stars 0 forks source link

This project uses ansible to provision a machine running ubuntu to run several useful services, and to keep that machine up to date with the latest version of those services.

Prerequisites

Target System

The system being configured must have two network interfaces, one of which may be virtual.

Ansible

The included playbook runs and produces no warnings with python 3.10.4 and the packages in poetry.toml.

poetry can be installed by running pip install poetry. After that, dependencies can be installed and updated by running poetry install.

Information about variables that must be available to ansible, as well as any manual setup steps can be found in the README files under the roles directory.

Required Variables

The following variables must be defined for the playbook to run:

Variable Description
docker_compose_dir The root directory that will contain configuration for all services running on the server being configured.

A <ansible_username>_password must also be defined in your secrets that specifies the password the ansible user should use when using sudo.

Running

The host you wish to configure should be setup in /etc/ansible/hosts. The name of the host should be the hostname and zone as defined in your network configuration. You should set the ansible_ssh_host to the IP address of that host.

To run the playbook, execute the following command:

ansible-playbook /path/to/playbook.yml

Ansible

Oddities

Instead of having tasks spilt between multiple roles, this playbook uses variables defined in different roles to define the inputs for certain common tasks.

If a role needs to specify values, it should define a variable named <role name>_role. This variable can include any of the following keys:

Defaults vs Variables

The roles defined in this project define both defaults and variables. The intention is that anything defined as a default can be overwritten, and the playbook will still execute as expected. Anything defined as a variable is meant to represent constants so magic numbers do not appear through the project. Overwriting these values will result in undefined behaviour.

Secrets

Secrets are stored in the cloud. These are loaded in files stored in the secrets directory at the root of this repository. One file from this directory will be loaded based upon where the secrets are stored. Currently, the only supported storage location is LastPass.

Secrets defined in these files can be overridden by extra vars provided on the command line (i.e. -e var=value).

LastPass

Before running the playbook, you must have the LastPass CLI installed on the ansible controller. It can be installed by running: apt install lastpass-cli. To log into LastPass, run the command lpass login <your email address>. When the playbook completes, you should lo gout with the command lpass logout.

Entries in LastPass should have the same name as the variable that they are being loaded into. Entries must also be prefixed with a path to maintain LastPass hygiene. The path is specified by the variable lastpass_secrets_path which defaults to Infrastructure/Secrets.

Note that the playbook uses the pipe lookup instead of the official lastpass lookup. This is done because the official lookup doesn't support bulk loading of variables.

Network Configuration

Your network configuration is stored as yaml with your secrets. If your secrets are stored in LastPass, the yaml must be attached as a file to the network.yml secure note (the name of the attachment is not important), and it must be the only file attached to that note. The file must have the following format:

network:
  domain: The name of the domain that will be used to access hosts on your network with the TLD
  suffix: The TLD that will be used to access hosts on your network
  nameservers: A list of hostnames and IP addresses that will be used as nameservers in your network
  routers: A list of routers in your network.  Typically there will only be a single entry, byt DHCP allows multiple
  ipv4_subnet: The IPv4 subnet of your network in CIDR notation.
  ipv6_subnet: The IPv6 subnet of your network in CIDR notation.
  dhcpv4_subnet: The IPv4 subnet in which DHCP addresses will be allocated.  Must be a subset of ipv4_subnet.
  zones: A list of zones in your network.

Zones are defined as follows:

name: The name of the zone.  This will be prepended to your domain name for all hosts in this zone.
ipv4_subnet: The IPv4 subnet to which hosts in this zone will belong, in CIDR notation.
ipv6_prefix: The IPv6 prefix that will be used by hosts in this zone, in CIDR notation.
hosts: A list of hosts in this zone.

Hosts are defined as follows:

name: The name of the host.  The zone name and network domain and suffix will be added to create a FQDN.
ipv4_offset: The offset of this host's wired IP address from the start of the zone's IPv4 subnet.  256 will be added to
             this value for the host's wireless IP address.
ethernet: Optional details about this hosts wired connection to the network.  Must be specified if wifi is not.
wifi: Optional details about this hosts wifi connection to the network.  Must be specified if ethernet is not.
alias: A list of strings that will be used to create aliases for this host.  Aliases will not have the zone name in
       their FQDN.
connected: An optional boolean that specifies if the host is currently connected to the internet.  If not present, it is
           assumed that the host is connected.  At present, this value is not used, but it may be in the future.
external: An optional boolean that should only be set to true on the host being configured by ansible.  It indicates
          that this is the interface of the host that is exposed to the public internet.  If unspecified, false is
          assumed.
internal: An optional boolean that should only be set to true on the host being configured by ansible.  It indicates
          that this is the interface of the host that is not exposed to the public internet.  If unspecified, false is
          assumed (i.e. this is not the internal interface of the host being configured).
wemo: If this is a wemo device that will be controlled by home-automation services.  If unspeciid, false is assumed.
nas: Optional details about a NAS host.  Only one device may have this configuration.

If both ethernet and wifi are specified, two FQDNs will be generated one prefixed with ethernet and the other with wifi.

ethernet and wifi details are defined as follows:

mac: The MAC address of the interface used to connect to the network.
static: Optional boolean that indicates if this interface's IP address is configured statically.  If not specified, it
        is assumed that this device uses DHCP to obtain it's address.  Currently this value is not used by may be in the
        future.
ipv6_interface: Optional value that can be used to specify the interface portion of this devices IPv6 address.  The
                special value `none` can be used to indicate that the interface does not support IPv6.  The special
                value `unknown` can be used to indicate that the interface supports IPv6, but it's unknown how the
                interface portion is assigned.  If this value is not specified, the it's assumed that the interface
                uses a link local IPv6 address.

nas details are defined as follows:

mountpoints: A dictionary of mountpoints provided by this NAS device.  The key is the name of the mountpoint and the
             value is the path that can be mounted.  Currently the only supported key is `media`, which is the mount
             point used to store media files.

Users

Users that are not defined by roles are stored as yaml with your secrets. If your secrets are stored in LastPass, the yaml must be attached as a file to the users.yml secure note (the name of the attachment is not important), and it must be the only file attached to that note. Lastpass requires attachments to be at least 1Kb, so you may need to add additional content to the file. Any valid yaml may be used for this purpose and if it is not expected, will be ignored. The file must have the following format:

users:
  - name: The name of the user.
    uses_docker: A boolean indicating whether the user can run docker without sudo.
    salt: Salt to be used when hashing the users password.

For each user defined in users.yml, the following secrets must also be defined: