From df984c8d7d91ef37eb3d002d60f4fa88a498e084 Mon Sep 17 00:00:00 2001 From: Simon Caron <8635747+simoncaron@users.noreply.github.com> Date: Wed, 20 Aug 2025 21:04:19 -0400 Subject: [PATCH] Remove webroot/standalone http methods --- README.md | 38 ++-- defaults/main.yml | 19 +- .../default/playbook-standalone-nginx-aws.yml | 181 ------------------ tasks/create-cert-standalone.yml | 63 ------ tasks/create-cert-webroot.yml | 35 ---- tasks/main.yml | 20 +- 6 files changed, 16 insertions(+), 340 deletions(-) delete mode 100644 molecule/default/playbook-standalone-nginx-aws.yml delete mode 100644 tasks/create-cert-standalone.yml delete mode 100644 tasks/create-cert-webroot.yml diff --git a/README.md b/README.md index d22e7c1..631c1d3 100644 --- a/README.md +++ b/README.md @@ -23,17 +23,12 @@ By default, this role configures a cron job to run under the provided user accou ### Automatic Certificate Generation -Currently the `standalone`, `webroot`, and `dns-cloudflare` methods are supported for generating new certificates using this role. - -**For a complete example**: see the fully functional test playbook in [molecule/default/playbook-standalone-nginx-aws.yml](molecule/default/playbook-standalone-nginx-aws.yml). +This role supports generating new certificates using the `dns-cloudflare` method with Cloudflare DNS-01 challenge. certbot_create_if_missing: false Set `certbot_create_if_missing` to `yes` or `True` to let this role generate certs. - certbot_create_method: standalone - -Set the method used for generating certs with the `certbot_create_method` variable — current allowed values are: `standalone`, `webroot`, or `dns-cloudflare`. certbot_testmode: false @@ -49,27 +44,15 @@ The email address used to agree to Let's Encrypt's TOS and subscribe to cert-rel certbot_certs: [] # - email: janedoe@example.com - # webroot: "/var/www/html" # domains: # - example1.com # - example2.com # - domains: # - example3.com -A list of domains (and other data) for which certs should be generated. You can add an `email` key to any list item to override the `certbot_admin_email`. When using the `webroot` creation method, a `webroot` item has to be provided, specifying which directory to use for the authentication. Make sure your webserver correctly delivers contents from this directory. +A list of domains (and other data) for which certs should be generated using Cloudflare DNS-01 challenge. You can add an `email` key to any list item to override the `certbot_admin_email`. - certbot_create_command: "{{ certbot_script }} certonly --standalone --noninteractive --agree-tos --email {{ cert_item.email | default(certbot_admin_email) }} -d {{ cert_item.domains | join(',') }}" - -The `certbot_create_command` defines the command used to generate the cert. See the full default command inside `defaults/main.yml` for a full example—and you can easily add in extra arguments that are not in the default command with the `certbot_create_extra_args` variable. - -#### Standalone Certificate Generation - - certbot_create_stop_services: - - nginx - -Services that should be stopped while `certbot` runs it's own standalone server on ports 80 and 443. If you're running Apache, set this to `apache2` (Ubuntu), or `httpd` (RHEL), or if you have Nginx on port 443 and something else on port 80 (e.g. Varnish, a Java app, or something else), add it to the list so it is stopped when the certificate is generated. - -These services will only be stopped the first time a new cert is generated. +The `certbot_create_command` defines the command used to generate certificates using Cloudflare DNS-01 challenge. See the full default command inside `defaults/main.yml` for a full example—and you can easily add in extra arguments that are not in the default command with the `certbot_create_extra_args` variable. ### Snap Installation @@ -79,13 +62,9 @@ Setting `certbot_install_method: snap` configures this role to install Certbot v This install method is currently experimental and may or may not work across all Linux distributions. -#### Webroot Certificate Generation +### DNS-01 Challenge with Cloudflare -When using the `webroot` creation method, a `webroot` item has to be provided for every `certbot_certs` item, specifying which directory to use for the authentication. Also, make sure your webserver correctly delivers contents from this directory. - -#### DNS-01 Challenge with Cloudflare - -When using the `dns-cloudflare` creation method, you need to configure Cloudflare DNS credentials: +You need to configure Cloudflare DNS credentials: certbot_cloudflare_email: "your-email@example.com" certbot_cloudflare_api_key: "your-global-api-key" @@ -102,6 +81,13 @@ For API token setup: This method supports wildcard certificates and doesn't require your server to be publicly accessible on ports 80/443. +### Service Management During Certificate Generation + + certbot_create_stop_services: + - nginx + +Services that can be stopped during certificate generation if needed. While DNS-01 challenge doesn't require stopping services (since it doesn't use ports 80/443), you may want to restart services after certificate deployment using deploy hooks. + ### Wildcard Certificates diff --git a/defaults/main.yml b/defaults/main.yml index 43df3bb..ecdcb8d 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -12,7 +12,6 @@ certbot_hsts: false # Parameters used when creating new Certbot certs. certbot_create_if_missing: false -certbot_create_method: dns-cloudflare certbot_create_extra_args: "" certbot_admin_email: email@example.com certbot_expand: false @@ -23,13 +22,10 @@ certbot_cloudflare_api_key: "" certbot_cloudflare_api_token: "" certbot_cloudflare_propagation_seconds: 60 -# Default webroot, overwritten by individual per-cert webroot directories (not used with DNS-01) -certbot_webroot: /var/www/letsencrypt certbot_certs: [] # - name: example.com # email: janedoe@example.com -# webroot: "/var/www/html/" # domains: # - example1.com # - example2.com @@ -37,26 +33,17 @@ certbot_certs: [] # - example3.com certbot_create_command: >- - {{ certbot_script }} certonly --{{ certbot_create_method }} + {{ certbot_script }} certonly --dns-cloudflare {{ '--hsts' if certbot_hsts else '' }} {{ '--test-cert' if certbot_testmode else '' }} --noninteractive --agree-tos --email {{ cert_item.email | default(certbot_admin_email) }} {{ '--expand' if certbot_expand else '' }} - {{ '--webroot-path ' if certbot_create_method == 'webroot' else '' }} - {{ cert_item.webroot | default(certbot_webroot) if certbot_create_method == 'webroot' else '' }} - {{ '--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini' if certbot_create_method == 'dns-cloudflare' else '' }} - {{ '--dns-cloudflare-propagation-seconds ' + (certbot_cloudflare_propagation_seconds | string) if certbot_create_method == 'dns-cloudflare' else '' }} + --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini + --dns-cloudflare-propagation-seconds {{ certbot_cloudflare_propagation_seconds }} {{ certbot_create_extra_args }} --cert-name {{ cert_item_name }} -d {{ cert_item.domains | join(',') }} - {{ '--expand' if certbot_expand else '' }} - {{ '--pre-hook /etc/letsencrypt/renewal-hooks/pre/stop_services' - if certbot_create_stop_services and certbot_create_method == 'standalone' - else '' }} - {{ '--post-hook /etc/letsencrypt/renewal-hooks/post/start_services' - if certbot_create_stop_services and certbot_create_method == 'standalone' - else '' }} {{ "--deploy-hook '" ~ cert_item.deploy_hook ~ "'" if 'deploy_hook' in cert_item else '' }} diff --git a/molecule/default/playbook-standalone-nginx-aws.yml b/molecule/default/playbook-standalone-nginx-aws.yml deleted file mode 100644 index 4311190..0000000 --- a/molecule/default/playbook-standalone-nginx-aws.yml +++ /dev/null @@ -1,181 +0,0 @@ ---- -# To run: -# 1. Ensure Ansible and Boto are installed (pip install ansible boto). -# 2. Ensure you have AWS credentials stored where Boto can find them, and they -# are under the profile 'mm'. -# 3. Ensure you have a pubkey available at ~/.ssh/id_rsa.pub. -# 3. Run the playbook: ansible-playbook test-standalone-nginx-aws.yml - -# Play 1: Provision EC2 instance and A record. -- hosts: localhost - connection: local - gather_facts: false - - tasks: - - name: Configure EC2 Security Group. - ec2_group: - profile: mm - name: certbot_test_http - description: HTTP security group for Certbot testing. - region: "us-east-1" - state: present - rules: - - proto: tcp - from_port: 80 - to_port: 80 - cidr_ip: 0.0.0.0/0 - - proto: tcp - from_port: 443 - to_port: 443 - cidr_ip: 0.0.0.0/0 - - proto: tcp - from_port: 22 - to_port: 22 - cidr_ip: 0.0.0.0/0 - rules_egress: [] - - - name: Add EC2 Key Pair. - ec2_key: - profile: mm - region: "us-east-1" - name: certbot_test - key_material: "{{ item }}" - with_file: - - ~/.ssh/id_rsa.pub - - - name: Provision EC2 instance. - ec2: - profile: mm - key_name: certbot_test - instance_tags: - Name: "certbot-standalone-nginx-test" - group: ['default', 'certbot_test_http'] - instance_type: t2.micro - # CentOS Linux 7 x86_64 HVM EBS - image: ami-02e98f78 - region: "us-east-1" - wait: true - wait_timeout: 500 - exact_count: 1 - count_tag: - Name: "certbot-standalone-nginx-test" - register: created_instance - - - name: Add A record for the new EC2 instance IP in Route53. - route53: - profile: mm - command: create - zone: servercheck.in - record: certbot-test.servercheck.in - type: A - ttl: 300 - value: "{{ created_instance.tagged_instances.0.public_ip }}" - wait: true - overwrite: true - - - name: Add EC2 instance to inventory groups. - add_host: - name: "certbot-test.servercheck.in" - groups: "aws,aws_nginx" - ansible_ssh_user: centos - host_key_checking: false - when: created_instance.tagged_instances.0.id is defined - -# Play 2: Configure EC2 instance with Certbot and Nginx. -- hosts: aws_nginx - gather_facts: true - become: true - - vars: - certbot_admin_email: https@servercheck.in - certbot_create_if_missing: true - certbot_create_stop_services: [] - certbot_certs: - - name: certbot-test.servercheck.in - domains: - - certbot-test.servercheck.in - nginx_vhosts: - - listen: "443 ssl http2" - server_name: "certbot-test.servercheck.in" - root: "/usr/share/nginx/html" - index: "index.html index.htm" - state: "present" - template: "{{ nginx_vhost_template }}" - filename: "certbot_test.conf" - extra_parameters: | - ssl_certificate /etc/letsencrypt/live/certbot-test.servercheck.in/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/certbot-test.servercheck.in/privkey.pem; - ssl_protocols TLSv1.1 TLSv1.2; - ssl_ciphers HIGH:!aNULL:!MD5; - - pre_tasks: - - name: Update apt cache. - apt: update_cache=true cache_valid_time=600 - when: ansible_os_family == 'Debian' - changed_when: false - - - name: Install dependencies (RedHat). - yum: name={{ item }} state=present - when: ansible_os_family == 'RedHat' - with_items: - - cronie - - epel-release - - - name: Install cron (Debian). - apt: name=cron state=present - when: ansible_os_family == 'Debian' - - roles: - - simoncaron.certbot - - geerlingguy.nginx - - tasks: - - name: Flush handlers in case any configs have changed. - meta: flush_handlers - - - name: Test secure connection to SSL domain. - uri: - url: https://certbot-test.servercheck.in/ - status_code: 200 - delegate_to: localhost - become: false - -# Play 3: Tear down EC2 instance and A record. -- hosts: localhost - connection: local - gather_facts: false - - tasks: - - name: Destroy EC2 instance. - ec2: - profile: mm - instance_ids: ["{{ created_instance.tagged_instances.0.id }}"] - region: "us-east-1" - state: absent - wait: true - wait_timeout: 500 - - - name: Delete Security Group. - ec2_group: - profile: mm - name: certbot_test_http - region: "us-east-1" - state: absent - - - name: Delete Key Pair. - ec2_key: - profile: mm - name: certbot_test - region: "us-east-1" - state: absent - - - name: Delete Route53 record. - route53: - profile: mm - state: delete - zone: servercheck.in - record: certbot-test.servercheck.in - type: A - ttl: 300 - # See: https://github.com/ansible/ansible/pull/32297 - value: [] diff --git a/tasks/create-cert-standalone.yml b/tasks/create-cert-standalone.yml deleted file mode 100644 index 32c5b82..0000000 --- a/tasks/create-cert-standalone.yml +++ /dev/null @@ -1,63 +0,0 @@ ---- -- name: Determine certificate name - set_fact: - cert_item_name: "{{ cert_item.name | default(cert_item.domains | first | replace('*.', '')) }}" - -- name: Check if certificate already exists. - stat: - path: /etc/letsencrypt/live/{{ cert_item_name }}/cert.pem - register: letsencrypt_cert - -- name: Ensure pre and post hook folders exist. - file: - path: /etc/letsencrypt/renewal-hooks/{{ item }} - state: directory - mode: 0755 - owner: root - group: root - with_items: - - pre - - post - -- name: Create pre hook to stop services. - template: - src: stop_services.j2 - dest: /etc/letsencrypt/renewal-hooks/pre/stop_services - owner: root - group: root - mode: 0750 - when: - - certbot_create_stop_services is defined - - certbot_create_stop_services - -- name: Create post hook to start services. - template: - src: start_services.j2 - dest: /etc/letsencrypt/renewal-hooks/post/start_services - owner: root - group: root - mode: 0750 - when: - - certbot_create_stop_services is defined - - certbot_create_stop_services - -- name: Check if domains have changed - block: - - name: Register certificate domains - shell: "{{ certbot_script }} certificates --cert-name {{ cert_item_name }} | grep Domains | cut -d':' -f2" - changed_when: false - register: letsencrypt_cert_domains_dirty - - - name: Cleanup domain list - set_fact: - letsencrypt_cert_domains: "{{ letsencrypt_cert_domains_dirty.stdout | trim | split(' ') | map('trim') | select('!=', '') | list | sort }}" - - - name: Determine if domains have changed - set_fact: - letsencrypt_cert_domains_changed: "{{ letsencrypt_cert_domains != (cert_item.domains | map('trim') | select('!=', '') | list | sort) }}" - - when: letsencrypt_cert.stat.exists - -- name: Generate new certificate if one doesn't exist. - command: "{{ certbot_create_command }}" - when: not letsencrypt_cert.stat.exists or letsencrypt_cert_domains_changed | default(false) diff --git a/tasks/create-cert-webroot.yml b/tasks/create-cert-webroot.yml deleted file mode 100644 index b379577..0000000 --- a/tasks/create-cert-webroot.yml +++ /dev/null @@ -1,35 +0,0 @@ ---- -- name: Determine certificate name - set_fact: - cert_item_name: "{{ cert_item.name | default(cert_item.domains | first | replace('*.', '')) }}" - -- name: Check if certificate already exists. - stat: - path: /etc/letsencrypt/live/{{ cert_item_name }}/cert.pem - register: letsencrypt_cert - -- name: Create webroot directory if it doesn't exist yet - file: - path: "{{ cert_item.webroot | default(certbot_webroot) }}" - state: directory - -- name: Check if domains have changed - block: - - name: Register certificate domains - shell: "{{ certbot_script }} certificates --cert-name {{ cert_item_name }} | grep Domains | cut -d':' -f2" - changed_when: false - register: letsencrypt_cert_domains_dirty - - - name: Cleanup domain list - set_fact: - letsencrypt_cert_domains: "{{ letsencrypt_cert_domains_dirty.stdout | trim | split(' ') | map('trim') | select('!=', '') | list | sort }}" - - - name: Determine if domains have changed - set_fact: - letsencrypt_cert_domains_changed: "{{ letsencrypt_cert_domains != (cert_item.domains | map('trim') | select('!=', '') | list | sort) }}" - - when: letsencrypt_cert.stat.exists - -- name: Generate new certificate if one doesn't exist. - command: "{{ certbot_create_command }}" - when: not letsencrypt_cert.stat.exists or letsencrypt_cert_domains_changed | default(false) diff --git a/tasks/main.yml b/tasks/main.yml index 9cdf510..1d54f33 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -11,27 +11,9 @@ when: certbot_install_method == 'snap' -- include_tasks: create-cert-standalone.yml - with_items: "{{ certbot_certs }}" - when: - - certbot_create_if_missing - - certbot_create_method == 'standalone' - loop_control: - loop_var: cert_item - -- include_tasks: create-cert-webroot.yml - with_items: "{{ certbot_certs }}" - when: - - certbot_create_if_missing - - certbot_create_method == 'webroot' - loop_control: - loop_var: cert_item - - include_tasks: create-cert-dns-cloudflare.yml with_items: "{{ certbot_certs }}" - when: - - certbot_create_if_missing - - certbot_create_method == 'dns-cloudflare' + when: certbot_create_if_missing loop_control: loop_var: cert_item