Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 58 additions & 10 deletions roles/letsencrypt/library/test_challenges.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
import socket

try:
from httplib import HTTPConnection, HTTPException
from httplib import HTTPConnection, HTTPSConnection, HTTPException
except ImportError:
# Python 3
from http.client import HTTPConnection, HTTPException
from http.client import HTTPConnection, HTTPSConnection, HTTPException

DOCUMENTATION = '''
---
Expand All @@ -22,6 +22,12 @@
required: true
default: null
type: list
ssl:
description:
- If true, will check on SSL port as well.
required: false
default: false
type: bool
file:
description:
- The dummy filename in the URL to test.
Expand All @@ -45,43 +51,85 @@
- www.mydomain.com
'''

def get_status(host, path, file):
def get_connection(host, port):
if port == 80:
conn = HTTPConnection(host, port)
elif port == 443:
conn = HTTPSConnection(host, port)
return conn

def get_status(host, port, path, file):
uri = '{0}:{1}/{2}/{3}'.format(host,port,path,file)
request = {
'hostname': host,
'uri': uri,
}

try:
conn = HTTPConnection(host)
conn = get_connection(host, port)
conn.request('HEAD', '/{0}/{1}'.format(path, file))
res = conn.getresponse()
except (HTTPException, socket.timeout, socket.error):
return 0
except (HTTPException, socket.timeout, socket.error) as e:
results = {
'headers': None,
'reason': None,
'status': -1, # a flag indicating failure.
}
exception = {
'exception': str(e),
}
return {**request, **results, **exception}

else:
return res.status
results = {
'status': res.status,
'headers': res.getheaders(),
'reason': res.reason,
}
return {**request, **results}

def main():
module = AnsibleModule(
argument_spec = dict(
file = dict(default='ping.txt'),
hosts = dict(required=True, type='list'),
ssl = dict(default=False, type='bool'),
path = dict(default='.well-known/acme-challenge')
)
)

hosts = module.params['hosts']
path = module.params['path']
file = module.params['file']
ssl = module.params['ssl']

failed_hosts = []

for host in hosts:
status = get_status(host, path, file)
result = get_status(host, 80, path, file)
status = result['status']

if int(status) != 200 and bool(ssl) == True:
failed_hosts.append(result)
# Try again, this time over SSL
result = get_status(host, 443, path, file)
status = result['status']

if int(status) != 200:
failed_hosts.append(host)
failed_hosts.append(result)
Comment on lines +112 to +119
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads better I think?

if int(status) != 200:
  failed_hosts.append(result)

  if bool(ssl) == True:
    # Try again, this time over SSL
    result = get_status(host, 443, path, file)
    status = result['status']

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry just realized this comment had been pending for over a month and I forgot to submit 😓


rc = int(len(failed_hosts) > 0)

module.exit_json(
module_result = dict(
changed=False,
rc=rc,
failed_hosts=failed_hosts
)

if (rc != 0):
module.fail_json(msg='Failed to fetch the ACME test file', **module_result)
else:
module.exit_json(**module_result)

from ansible.module_utils.basic import *
main()
20 changes: 15 additions & 5 deletions roles/letsencrypt/tasks/nginx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,28 @@
- name: Test Acme Challenges
test_challenges:
hosts: "{{ site_hosts }}"
ssl: true
register: letsencrypt_test_challenges
ignore_errors: true
when: site_uses_letsencrypt
with_dict: "{{ wordpress_sites }}"

- name: Notify of challenge failures
fail:
msg: >
Could not access the challenge file for the hosts/domains: {{ item.failed_hosts | join(', ') }}.
msg: |
Could not access the challenge file for the hosts/domains:
{{ item.failed_hosts | join(', ', attribute='hostname') }}

Details:
{% for failed in item.failed_hosts %}
{{ failed.status }} {{ failed.uri }}
{% endfor %}

Let's Encrypt requires every domain/host be publicly accessible.
Make sure that a valid DNS record exists for {{ item.failed_hosts | join(', ') }} and that they point to this server's IP.
If you don't want these domains in your SSL certificate, then remove them from `site_hosts`.
See https://roots.io/trellis/docs/ssl for more details.
Make sure that a valid DNS record exists for each host and that
they point to this server's IP.

If you don't want these domains in your SSL certificate, then
remove them from `site_hosts`. See https://roots.io/trellis/docs/ssl for more details.
when: item is not skipped and item is failed
with_items: "{{ letsencrypt_test_challenges.results }}"