Ansible
Ansible is used to configure the web server to host multiple sites.
Directoryansible/ software
Directorygroup_vars/all/
- main.yaml settings
- vault.yaml encrypted secrets
Directoryroles/ ansible code to run tasks
Directorygalaxy/ package manager roles
Directorypaultibbetts.caddy/ public role to install and manage caddy
- …
Directoryinternal/ private roles
Directoryserver/ configures server settings
- …
Directoryweb/ configures multiple static sites
- …
- inventory.yaml set up to use Terraform managed resources
- site.yaml playbook of roles that does the whole setup
Inventory
Section titled “Inventory”Ansible can use the same inventory that Terraform manages by using the Terraform plugin.
This is configured using:
plugin: cloud.terraform.terraform_providerproject_path: ../terraformThe whole setup is declared in site.yaml:
---- hosts: pi become: true roles: - { role: server, tags: [server] } - { role: firewall, tags: [firewall] } - { role: web, tags: [web] } - { role: paultibbetts.caddy, tags: [caddy] }Ansible sets up the following roles:
Server
Section titled “Server”This internal role sets up the machine to act as a server.
#SPDX-License-Identifier: MIT-0---- name: Debian/Ubuntu only ansible.builtin.assert: that: ansible_facts.os_family == "Debian" fail_msg: "This base role targets Debian/Ubuntu."
- name: Ensure apt cache is fresh ansible.builtin.apt: update_cache: true cache_valid_time: 3600
- ansible.builtin.import_tasks: hostname.yaml- ansible.builtin.import_tasks: packages.yaml- ansible.builtin.import_tasks: ssh.yaml- ansible.builtin.import_tasks: time.yaml- ansible.builtin.import_tasks: updates.yaml- ansible.builtin.import_tasks: users.yamlThis internal role sets up the server to host multiple websites.
#SPDX-License-Identifier: MIT-0---- name: Prepare user and tools for deployment ansible.builtin.import_tasks: deploy.yaml
- name: Prepare directories ansible.builtin.import_tasks: directories.yamlThis role is available on Ansible Galaxy.
It installs a custom build of Caddy, that includes the Cloudflare plugin, and configures it to serve web content.
The role writes the Caddyfile that configures Caddy using a template:
{ servers { listener_wrappers { proxy_protocol { # https://www.mythic-beasts.com/support/topics/proxy#sec-proxy-details{% for ip in mythicbeasts_proxy_ipv4 %} allow {{ ip }}/32{% endfor %}{% for ip in mythicbeasts_proxy_ipv6 %} allow {{ ip }}/128{% endfor %} } tls } }}
(cf_tls) { tls { dns cloudflare {env.CF_API_TOKEN} }}
{% for site in web_sites %}{{ site }} { import cf_tls root * {{ web_sites_root }}/{{ site }}/current file_server encode zstd gzip log}{% endfor %}
www.paultibbetts.uk { import cf_tls redir {scheme}://paultibbetts.uk{uri} permanent}Group vars
Section titled “Group vars”The variables that configure each role are in ansible/group_vars/all/main.yaml:
web_sites_root: "/srv/www"web_sites: - "paultibbetts.uk" - "infra.paultibbetts.uk" - "dev.paultibbetts.uk"
web_deploy_user: "deploy"web_deploy_home: "/home/deploy"web_deploy_ssh_keys: - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK+5ESd3EGl8/Wm3VDkQ4PKTp4uDLrEDXpdgYD1nuKmV"web_group: "deploy"web_keep_releases: 3
caddy_caddyfile_template: "{{ playbook_dir }}/templates/Caddyfile.j2"caddy_install_method: downloadcaddy_plugins: - github.com/caddy-dns/cloudflarecaddy_manage_systemd_env_file: truecaddy_systemd_env: CF_API_TOKEN: "{{ vault_cf_api_token }}"Secrets
Section titled “Secrets”Ansible does not need any secrets passing for it to work, since it uses SSH to connect to my server, but it does need to know my Cloudflare API token to pass to Caddy for it to use.
I do this using ansible-vault where the Cloudflare API token is encrypted and saved into the codebase and is available for the Caddy role to write it to the correct file for Caddy to be able to use it.
Right now it looks like:
$ANSIBLE_VAULT;1.1;AES256316237616263666162663634356163326135373036653334363663626363336331386162323364313766356562366238373063636564353832363231636166660a356136323638613661666435626164313238613136393065643230373632646537303937633532646165376339666337653061336663343339666430396366660a6663303162643830346335313230356531616166316361333330623430373562383461393233376531386164313336666437616462353636396132623066313633313938396538636665383961616632366530643263346334363135376662323939326466656636386633633835626263373230633566303534323761326266If you have the right vault-pass.txt file, like I do, it can decrypt it and use it in the role.