{"id":14984463,"url":"https://github.com/systemli/ansible-role-letsencrypt","last_synced_at":"2025-04-10T20:34:16.156Z","repository":{"id":24215582,"uuid":"100888354","full_name":"systemli/ansible-role-letsencrypt","owner":"systemli","description":"Ansible role to obtain Let's Encrypt SSL certificates","archived":false,"fork":false,"pushed_at":"2024-02-05T08:16:20.000Z","size":190,"stargazers_count":75,"open_issues_count":2,"forks_count":27,"subscribers_count":11,"default_branch":"main","last_synced_at":"2024-03-26T20:44:24.535Z","etag":null,"topics":["ansible","ansible-role","certbot","dns-challenge","letsencrypt"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/systemli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2017-08-20T20:56:05.000Z","updated_at":"2024-04-15T14:04:36.499Z","dependencies_parsed_at":"2024-04-15T14:04:32.608Z","dependency_job_id":"b7f25a34-441e-4759-b7a3-12c29fa12e5b","html_url":"https://github.com/systemli/ansible-role-letsencrypt","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/systemli%2Fansible-role-letsencrypt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/systemli%2Fansible-role-letsencrypt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/systemli%2Fansible-role-letsencrypt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/systemli%2Fansible-role-letsencrypt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/systemli","download_url":"https://codeload.github.com/systemli/ansible-role-letsencrypt/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248290044,"owners_count":21078923,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ansible","ansible-role","certbot","dns-challenge","letsencrypt"],"created_at":"2024-09-24T14:09:06.320Z","updated_at":"2025-04-10T20:34:16.140Z","avatar_url":"https://github.com/systemli.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ansible role to obtain Let's Encrypt SSL certificates\n\n![Integration](https://github.com/systemli/ansible-role-letsencrypt/workflows/Integration/badge.svg?branch=main)\n[![Ansible Galaxy](http://img.shields.io/badge/ansible--galaxy-letsencrypt-blue.svg)](https://galaxy.ansible.com/systemli/letsencrypt/)\n\nThis role is meant to request SSL certificates from Let's Encrypt,\nusing the HTTP or the DNS challenge for their ACME API.\n\nFeatures:\n* Installs and configures certbot and the DNS challenge helper script\n* Supports both the HTTP and the DNS challenge\n  * For HTTP challenge, the authenticator plugins `apache`, `nginx`, `standalone`\n    and `webroot` are supported\n* The DNS challenge uses a dedicated zone for AMCE challenge tokens\n  only, lowering the security risks of dynamic updates. The concept\n  is explained [here](https://www.crc.id.au/using-centralised-management-with-lets-encrypt/)\n* Restart of services at certificate renewal using post-hooks or custom\n  post-hook command\n* Permission control to certificates using a dedicated system group\n\nSupported distributions:\n* Debian 12 (Bookworm)\n* Debian 11 (Bullseye)\n\nTested on:\n* Debian 12 (Bookworm)\n* Debian 11 (Bullseye)\n* CentOS8\n* Ubuntu 24.04 (Noble Numbat)\n\nIt does the following:\n* When letsencrypt_setup is True (the default) this role will:\n  * Install certbot\n  * Register an account at Let's Encrypt\n  * Install required files/keys for the DNS challenge\n  * Create the system group 'letsencrypt'\n\n* When invoked with filled variable 'letsencrypt_cert':\n  * Requests a SSL certificate via the Let's Encrypt ACME API, either\n    using the HTTP challenge or using the DNS challenge\n  * Optionally sets the post-hook for certificate renewals (to restart\n    required services afterwards)\n  * Optionally adds system users to the 'letsencrypt' system group to\n    grant them read access to the SSL certificates and their private keys\n\n## How it works (examples)\n\n * Installation of certbot\n   ```ansible-playbook site.yml -l localhost -t letsencrypt```\n * Creation of a certificate via HTTP challenge and with `webroot`\n   authenticator (restarting service 'apache2' at renewal):\n   ```ansible-playbook site.yml -l localhost -t letsencrypt -e '{\"letsencrypt_cert\":{\"name\":\"sub.example.org\",\"domains\":[\"sub.example.org\"],\"challenge\":\"http\",\"http_auth\":\"webroot\",\"webroot_path\":\"/var/www/sub.example.org\",\"services\":[\"apache2\"]}}'```\n * Creation of a certificate via DNS challenge (granting read access to\n   certs to user 'Debian-exim', restarting services 'exim4' and 'dovecot`\n   at renewal):\n   ```ansible-playbook site.yml -l localhost -t letsencrypt -e '{\"letsencrypt_cert\":{\"name\":\"sub2\",\"domains\":[\"sub2.example.org\",\"sub2.another.example.org\"],\"challenge\":\"dns\",\"services\":[\"dovecot\",\"exim4\"],\"users\":[\"Debian-exim\"]}}'```\n * Creation of a certificate via HTTP challenge and with `standalone`\n   authenticator (re-using same private key at renewal and running a\n   custom post-hook script at renewal):\n   ```ansible-playbook site.yml -l localhost -t letsencrypt -e '{\"letsencrypt_cert\":{\"name\":\"sub3\",\"domains\":[\"sub3.example.org\"],\"challenge\":\"http\",\"http_auth\":\"standalone\",\"reuse_key\":True,\"post_hook\":\"/usr/local/bin/cert-post-hook.sh\"}}'```\n\n## Expected structure of variable `letsencrypt_cert`\n\nThe variable `letsencrypt_cert` is expected to be a dictionary:\n\n```\nletsencrypt_opts_extra: \"--register-unsafely-without-email\"\nletsencrypt_cert:\n  name: sub.example.org\n  domains:\n    - sub.example.org\n  challenge: http\n  http_auth: webroot\n  webroot_path: /var/www/sub.example.org\n  services:\n    - apache2\n```\n\nor:\n\n```\nletsencrypt_cert:\n  name: sub2\n  domains:\n    - sub2.example.org\n    - sub2.another.example.org\n  challenge: dns\n  services:\n    - dovecot\n    - exim4\n  users:\n    - Debian-exim\n```\n\nor:\n\n```\nletsencrypt_cert:\n  name: sub3\n  domains:\n    - sub3.example.org\n  challenge: http\n  http_auth: standalone\n  reuse_key: True\n  post_hook: \"/usr/local/bin/cert-post-hook.sh\"\n```\n\n```\nletsencrypt_cert:\n  name: sub3\n  domains:\n    - sub3.example.org\n  challenge: http\n  http_auth: standalone\n  reuse_key: True\n  deploy_hook: \"/usr/local/bin/cert-post-hook.sh\"\n```\n\nThe dictionary supports the following keys:\n\n* `name`: name of the certificate [optional]\n* `domains`: list of domains for the certificate [required]\n* `challenge`: 'http' or 'dns' [required]\n  * for challenge 'http': `http_auth`: 'webroot', 'apache' or 'nginx' [optional, default 'webroot']\n    * for http_auth 'webroot': `webroot_path` [optional, default '/var/www']\n* `services`: list of services to be restarted in the post-hook [optional]\n* `reuse_key`: Reuse same private key at certificate renewal. 'True' or 'False'\n   (default 'False')\n* `post_hook`: Custom post-hook to be executed after attempt to obtain/renew\n  a certificate [optional]\n* `deploy_hook`: Custom deploy-hook to be executed after a successful attempt\n  to obtain/renew  a certificate [optional]\n* `renew_hook`: Custom renew-hook to be executed once for each renewed\n  certificate after certificate renewal [optional]\n* `users`: list of users to be added to system group 'letsencrypt' [optional]\n\n## General Preliminaries\n\nThe role takes care of installing certbot and requesting SSL certificates\nusing either the HTTP or the DNS challenge. It doesn't install or configure\nthe required infrastructure (i.e. the Apache webserver or a DNS server).\n\nThe role is tested with Ansible 2.2 only. No guaranties that it runs with\nearlier versions of Ansible.\n\n## The HTTP challenge\n\nRequirements:\n* The domain name(s) of the requested certificate has to point\n  to the system\n* For http_auth 'apache', Apache2 has to be installed (and configured)\n  on the system\n* For http_auth 'nginx', NGINX has to be installed (and configured)\n  on the system\n\n## The DNS challenge\n\nRequirements:\n* A DNS server with a dedicated zone, used for the ACME DNS challenge only.\n  This zone has to allow dynamic DNS updates (NSUPDATE) for TXT records\n  (see below).\n* CNAME records for `_acme-challenge.sub.example.org` for all domain\n  names(s) of the requested certificate have to point to\n  `sub.example.org._le.example.org` (inside the dedicated zone for the\n  ACME DNS challenge).\n* The content of the DNS update key and private DNS update keys need to be\n  available in the Ansible vars `letsencrypt_ddns_key` and\n  `letsencrypt_ddns_privkey` (preferably inside a vault).\n\nThis role installs a helper script for the DNS challenge to\n`/usr/local/bin/certbot-dns-hook.sh`. This script will add the validation\ntoken to the TXT record at `sub.example.org._le.example.org` during the DNS\nchallenge and remove it afterwards.\n\n### Wildcard support with the DNS challenge\n\nObtaining wildcard certificates should work out of the box via DNS challenge.\n\n### Configuring bind9 for the DNS challenge\n*(Another option would be to use the [acme-dns server](https://github.com/joohoi/acme-dns) for this)*\n\nGenerate a key for dynamic updates:\n\n```\ncd /etc/bind/keys\ndnssec-keygen -a HMAC-SHA512 -b 512 -n USER _le.example.org_ddns_update\nchown -R bind:bind /etc/bind/keys\n```\n\nAdd the key to your bind config (e.g. at `/etc/bind/named.conf.options`):\n\n```\nkey \"_le.example.org_ddns_update\" {\n\talgorithm hmac-sha512;\n\tsecret \"...\";\n};\n```\n\nCreate the zone for dynamic updates:\n\n```\n$ORIGIN .\n$TTL 86400\t; 1 day\n_le.example.org\t\tIN SOA\tns1.example.org. postmaster.example.org. (\n\t\t\t\t2017061501 ; serial\n\t\t\t\t86400      ; refresh (1 day)\n\t\t\t\t3600       ; retry (1 hour)\n\t\t\t\t2419200    ; expire (4 weeks)\n\t\t\t\t86400      ; minimum (1 day)\n\t\t\t\t)\n\t\t\tNS\tns1.example.org.\n\t\t\tNS\tns2.example.org.\n\t\t\tTXT\t\"v=spf1 -all\"\n```\n\nand configure it in your bind config (e.g. at `/etc/bind/named.conf.local`):\n\n```\nzone \"_le.example.org\" {\n\ttype master;\n\tfile \"/etc/bind/zones/db._le.example.org\";\n\tupdate-policy { grant _le.example.org_ddns_update wildcard *._le.example.org. TXT; };\n};\n```\n\nFormat for `/etc/letsencrypt/keys/ddns_update.key` (from bind)\n\n```\nkey \"\u003ckey\u003e\" {\n    algorithm HMAC-SHA512;\n    secret \"\u003ckey\u003e\";\n};\n```\n\nFormat for `/etc/letsencrypt/keys/ddns_update.private`\n\n```\nPrivate-key-format: v1.3\nAlgorithm: 165 (HMAC_SHA512)\nKey: \u003ckey\u003e\nBits: AAA=\nCreated: 20181017144534\nPublish: 20181017144534\nActivate: 20181017144534\n```\n\n# Ansible variable defaults\n\n```\n# Perform setup step; set false to disable\nletsencrypt_setup: True\n\n# Provide existing account data to be copied over\nletsencrypt_account: \"\"\n# letsencrypt_account:\n#   hash: 1234567890abcdef1234567890abcdef\n#   id: 123456789\n#   creation_host: localhost\n#   creation_dt: 2020-12-13T13:12:00Z\n#   private_key:\n#     n: 1234\n#     e: 5678\n#     d: 90ab\n#     p: cdef\n#     q: 1234\n#     dp: 5678\n#     dq: 90ab\n#     qi: cdef\n#     kty: RSA\n\n# Set the email address associated with the Let's Encrypt account\nletsencrypt_account_email: \"\"\n\n# Default authenticator for the HTTP challenge ('webroot' or 'apache')\nletsencrypt_http_auth: webroot\n\n# Default webroot path for the authenticator 'webroot'\nletsencrypt_webroot_path: /var/www\n\n# Install the DNS challenge helper script and DNS update key\nletsencrypt_dns_challenge: yes\n\n# Settings for the dynamic DNS zone updates\n# letsencrypt_ddns_server: \"\"\n# letsencrypt_ddns_zone: \"\"\n# letsencrypt_ddns_key: \"\"\n# letsencrypt_ddns_privkey: \"\"\n\n# Create system group 'letsencrypt' for access to certificates\nletsencrypt_group: yes\n\n# Reuse private key at certificate renewal?\nletsencrypt_reuse_key: False\n\n# Allow subset of names?\nletsencrypt_subset_names: True\n\n# Set global extra commandline options for certbot\nletsencrypt_opts_extra: \"\"\n\n# Set path for letsencrypt directory (no trailing \"/\" !!)\nletsencrypt_directory: /etc/letsencrypt\n```\n\n## Testing\n\nFor testing purposes, variable `letsencrypt_test` can be set. If set to\nTrue, the role will use Let's Encrypt test servers for account creation\nand obtaining the certificate.\n\nFor developing and testing the role we use Molecule and Vagrant/Github Actions. On the local environment you can easily test the role with\n\n```\nmolecule test\n```\n\n## License\n\nThis Ansible role is licensed under the GNU GPLv3.\n\n## Author\n\nCopyright 2017-2019 systemli.org (https://www.systemli.org/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsystemli%2Fansible-role-letsencrypt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsystemli%2Fansible-role-letsencrypt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsystemli%2Fansible-role-letsencrypt/lists"}