Giter Club home page Giter Club logo

marathon-acme's Introduction

marathon-acme

PyPI Build Status codecov

Automate ACME certificates for Marathon apps served by marathon-lb

How it works

There is one big requirement for deploying marathon-acme: there must be shared persistent storage between marathon-acme and all marathon-lb instances. This will be used to store the certificates.

  1. marathon-acme watches Marathon for changes to app definitions.
  2. It collects the values of all MARATHON_ACME_{n}_DOMAIN labels on apps. This will form the set of domains to fetch certificates for.
  3. It generates, verifies and stores certificates for any new domains using the configured ACME certificate authority.
  4. It tells marathon-lb to reload using the marathon-lb HTTP API.
  5. It issues new certificates for soon-to-expire certificates once a day.

marathon-acme is written in Python using Twisted. The certificate issuing functionality is possible thanks to the txacme library.

The ACME provider that most people are likely to use is Let's Encrypt. Before using marathon-acme with Let's Encrypt, make sure you are aware of their rate limits.

The entire certificate-issuing workflow is shown below:

Usage

marathon-acme is available as a pip-installable Python package on PyPI. However, most users will probably want to use the Docker image available from Docker Hub.

> $ docker run --rm praekeltfoundation/marathon-acme --help
usage: marathon-acme [-h] [-a ACME] [-e EMAIL] [-m MARATHON[,MARATHON,...]]
                     [-l LB[,LB,...]] [-g GROUP] [--allow-multiple-certs]
                     [--listen LISTEN] [--sse-timeout SSE_TIMEOUT]
                     [--log-level {debug,info,warn,error,critical}]
                     storage-dir

Automatically manage ACME certificates for Marathon apps

positional arguments:
  storage-dir           Path to directory for storing certificates

optional arguments:
  -h, --help            show this help message and exit
  -a ACME, --acme ACME  The address for the ACME Directory Resource (default:
                        https://acme-v01.api.letsencrypt.org/directory)
  -e EMAIL, --email EMAIL
                        An email address to register with the ACME service
                        (optional)
  -m MARATHON[,MARATHON,...], --marathon MARATHON[,MARATHON,...]
                        The addresses for the Marathon HTTP API (default:
                        http://marathon.mesos:8080)
  -l LB[,LB,...], --lb LB[,LB,...]
                        The addresses for the marathon-lb HTTP API (default:
                        http://marathon-lb.marathon.mesos:9090)
  -g GROUP, --group GROUP
                        The marathon-lb group to issue certificates for
                        (default: external)
  --allow-multiple-certs
                        Allow multiple certificates for a single app port.
                        This allows multiple domains for an app, but is not
                        recommended.
  --listen LISTEN       The address for the port to listen on (default: :8000)
  --sse-timeout SSE_TIMEOUT
                        Amount of time in seconds to wait for some event data
                        to be received from Marathon. Set to 0 to disable.
                        (default: 60)
  --log-level {debug,info,warn,error,critical}
                        The minimum severity level to log messages at
                        (default: info)
  --version             show program's version number and exit

marathon-acme app definition

marathon-acme should be deployed as a Marathon app.

{
  "id": "/marathon-acme",
  "cpus": 0.01,
  "mem": 128.0,
  "args": [
    "--email", "[email protected]",
    "--marathon", "http://marathon1:8080,http://marathon2:8080,http://marathon3:8080",
    "--lb", "http://lb1:9090,http://lb2:9090",
    "/var/lib/marathon-acme"
  ],
  "labels": {
    "HAPROXY_GROUP": "external",
    "HAPROXY_0_VHOST": "marathon-acme.example.com",
    "HAPROXY_0_BACKEND_WEIGHT": "1",
    "HAPROXY_0_PATH": "/.well-known/acme-challenge/",
    "HAPROXY_0_HTTP_FRONTEND_ACL_WITH_PATH": "  acl host_{cleanedUpHostname} hdr(host) -i {hostname}\n  acl path_{backend} path_beg {path}\n  redirect prefix http://{hostname} code 302 if !host_{cleanedUpHostname} path_{backend}\n  use_backend {backend} if host_{cleanedUpHostname} path_{backend}\n"
  },
  "container": {
    "type": "DOCKER",
    "docker": {
      "image": "praekeltfoundation/marathon-acme",
      "network": "BRIDGE",
      "portMappings": [
        { "containerPort": 8000, "hostPort": 0 }
      ],
      "parameters": [
        {
          "value": "my-volume-driver",
          "key": "volume-driver"
        },
        {
          "value": "marathon-acme-certs:/var/lib/marathon-acme",
          "key": "volume"
        }
      ],
    }
  }
}

The above should mostly be standard across different deployments. The volume parameters will depend on your particular networked storage solution.

HAPROXY labels

"labels": {
  "HAPROXY_GROUP": "external",
  "HAPROXY_0_VHOST": "marathon-acme.example.com",
  "HAPROXY_0_BACKEND_WEIGHT": "1",
  "HAPROXY_0_PATH": "/.well-known/acme-challenge/",
  "HAPROXY_0_HTTP_FRONTEND_ACL_WITH_PATH": "  acl host_{cleanedUpHostname} hdr(host) -i {hostname}\n  acl path_{backend} path_beg {path}\n  redirect prefix http://{hostname} code 302 if !host_{cleanedUpHostname} path_{backend}\n  use_backend {backend} if host_{cleanedUpHostname} path_{backend}\n"
}

Several special marathon-lb labels are needed in order to forward all HTTP requests whose path begins with /.well-known/acme-challenge/ to marathon-acme, in order to serve ACME HTTP challenge responses.

HAPROXY_GROUP
external

marathon-lb instances are assigned a group. Only Marathon apps with a HAPROXY_GROUP label that matches their group are routed with that instance. "external" is the common name for publicly-facing load balancers.

HAPROXY_0_VHOST
marathon-acme.example.com

marathon-acme needs its own domain to respond to ACME challenge requests on. This domain must resolve to your marathon-lb instance(s).

HAPROXY_0_BACKEND_WEIGHT
1

We want this rule in HAProxy's config file to come before any others so that requests are routed to marathon-acme before we do the (usually) domain-based routing for the other Marathon apps. The default weight is 0, so we set to 1 so that the rule comes first.

HAPROXY_0_PATH
/.well-known/acme-challenge/

This is the beginning of the HTTP path to ACME validation challenges.

HAPROXY_0_HTTP_FRONTEND_ACL_WITH_PATH
acl host_{cleanedUpHostname} hdr(host) -i {hostname}
acl path_{backend} path_beg {path}
redirect prefix http://{hostname} code 302 if !host_{cleanedUpHostname} path_{backend}
use_backend {backend} if host_{cleanedUpHostname} path_{backend}

This is where it gets complicated... It’s possible to edit the templates used for generating the HAProxy on a per-app basis using labels. This is necessary because by default marathon-lb will route based on domain first, but we don’t want to do that. You can see the standard template here.

Here, we add an extra redirect rule. This redirects all requests matching the ACME challenge path to marathon-acme, except those requests already headed for marathon-acme. The Let's Encrypt server will follow redirects.

HAPROXY HTTPS labels

It is possible to have marathon-acme serve ACME challenge requests over HTTPS, although this is usually not necessary. In this case, a certificate needs to be issued for marathon-acme and the HTTP redirect label needs to be modified:

"labels": {
  ...,
  "MARATHON_ACME_0_DOMAIN": "marathon-acme.example.com",
  "HAPROXY_0_HTTP_FRONTEND_ACL_WITH_PATH": "  acl host_{cleanedUpHostname} hdr(host) -i {hostname}\n  acl path_{backend} path_beg {path}\n  redirect prefix https://{hostname} code 302 if path_{backend}\n"
}

Note that using the HAPROXY_0_REDIRECT_TO_HTTPS label for marathon-acme will break things. This label is difficult for us to use because of the way marathon-lb's templating works.

MARATHON_ACME_0_DOMAIN
marathon-acme.example.com

Here we set up marathon-acme to fetch a certificate for itself.

HAPROXY_0_HTTP_FRONTEND_ACL_WITH_PATH
acl host_{cleanedUpHostname} hdr(host) -i {hostname}
acl path_{backend} path_beg {path}
redirect prefix https://{hostname} code 302 if path_{backend}

We redirect to the HTTPS address (https://{hostname}) for all domains (including marathon-acme's) for requests to the ACME challenge path. The use_backend directive can now be removed since the backend is never used over HTTP as all requests are redirected.

Note that this label can only be set after marathon-acme has fetched the first certificate for its own domain. In other words, set the MARATHON_ACME_0_DOMAIN first and make sure it has taken effect before setting this one.

Docker images

Docker images are available from Docker Hub. There are two different streams of Docker images available:

For more details on the Docker images, see the praekeltfoundation/docker-marathon-acme repo.

Volumes and ports

The marathon-acme container defaults to the /var/lib/marathon-acme directory to store certificates and the ACME client private key. This is the path inside the container that should be mounted as a shared volume.

The container also defaults to listening on port 8000 on all interfaces.

You can override these values by providing arguments to the Docker container.

Certificate files

marathon-acme creates the following directory/file structure:

  • /var/lib/marathon-acme/
    • client.key: The ACME client private key
    • default.pem: A self-signed wildcard cert for HAProxy to fallback to
    • certs/
    • unmanaged-certs/: A directory for certs that marathon-acme doesn't manage

marathon-acme does nothing with the unmanaged-certs/ directory after creating it. HAProxy fails if any path in its certificate config doesn't exist, so it reduces setup friction to have a standard place to put unmanaged certificates.

marathon-lb configuration

marathon-acme requires marathon-lb 1.4.0 or later in order to be able to trigger HAProxy reloads.

As mentioned earlier, marathon-lb must share persistent storage with marathon-acme. BYONS: bring your own networked storage.

The only real configuration needed for marathon-lb is to add the path to marathon-acme's certificate storage directory as a source of certificates. HAProxy supports loading certificates from a directory. You should set marathon-lb's --ssl-certs CLI option to the certificate directory path as well as the fallback certificate (if HAProxy cannot find any certificates in the paths it is given it will fail to start).

--ssl-certs <storage-dir>/certs,<storage-dir>/default.pem

App configuration

marathon-acme uses a single marathon-lb-like label to assign domains to app ports: MARATHON_ACME_{n}_DOMAIN, where {n} is the port index. The value of the label is a set of comma- and/or whitespace-separated domain names, although by default only the first domain name will be considered.

Currently, marathon-acme can only issue certificates with a single domain. This means multiple certificates need to be issued for apps with multiple configured domains.

A limitation was added that limits apps to a single domain. This limit can be removed by passing the --allow-multiple-certs command-line option, although this is not recommended as it makes it possible for a large number of certificates to be issued for a single app, potentially exhausting the Let's Encrypt rate limit.

The app or its port must must be in the same HAPROXY_GROUP as marathon-acme was configured with at start-up.

We decided not to reuse the HAPROXY_{n}_VHOST label so as to limit the number of domains that certificates are issued for.

Limitations

The library used for ACME certificate management, txacme, is currently quite limited in its functionality. The two biggest limitations are:

  • There is no Subject Alternative Name (SAN) support yet (#37). Each certificate will correspond to exactly one domain name. This limitation makes it easier to hit Let's Encrypt's rate limits.
  • There is no support for removing certificates from txacme's certificate store (#77). Once marathon-acme issues a certificate for an app it will try to renew that certificate forever unless it is manually deleted from the certificate store.

For a more complete list of issues, see the issues page for this repo.

Troubleshooting

Challenge ping endpoint

One common problem is that marathon-lb is misconfigured and ACME challenge requests are unable to reach marathon-acme. You can test challenge request routing to marathon-acme using the challenge ping endpoint.

It should be possible to reach the /.well-known/acme-challenge/ping path from all domains served by marathon-lb:

> $ curl cake-service.example.com/.well-known/acme-challenge/ping
{"message": "pong"}

> $ curl soda-service.example.com/.well-known/acme-challenge/ping
{"message": "pong"}

marathon-acme's People

Contributors

calston avatar jayh5 avatar jerith avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

bobhenkel luckysb

marathon-acme's Issues

txacme.client.AuthorizationFailed:

Hey,

i set up the containers as in the example mentioned. but still getting that error. i tried the latest and develo branch version of marathon-acme. marathon-lb has also access to the shared storage

Here is the error ouput:
2017-07-28T14:45:25+0000 [twisted.internet.defer#critical] Unhandled error in Deferred: 2017-07-28T14:45:25+0000 [twisted.internet.defer#critical] Traceback (most recent call last): File "/usr/local/lib/python3.6/site-packages/treq/client.py", line 80, in _deliverWaiting waiter.connectionLost(reason) File "/usr/local/lib/python3.6/site-packages/treq/content.py", line 36, in connectionLost self.finished.callback(None) File "/usr/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 459, in callback self._startRunCallbacks(result) File "/usr/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 567, in _startRunCallbacks self._runCallbacks() --- <exception caught here> --- File "/usr/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 653, in _runCallbacks current.result = callback(current.result, *args, **kw) File "/usr/local/lib/python3.6/site-packages/txacme/client.py", line 593, in repoll raise AuthorizationFailed(authzr) txacme.client.AuthorizationFailed: AuthorizationFailed(<Status(invalid) Identifier(typ=IdentifierType(dns), value='my-domain.com') [Error(typ='urn:acme:error:connection', title=None, detail='Fetching http://my-domain.com/.well-known/acme-challenge/KbPgliqvF4BOQP92qF2JKvxhmALCHC8vnt6u_rz4yQk: Timeout')]>)

I don't know if this is the same error like that issue #52
In that ticket it seems to be fixed. is there a solution for that?

thanks

Support for scaling Marathon-LB

Hi, I've been wondering for a while if it would be possible for marathon-acme to support notifying all members of a multi-instance scaled marathon-lb app. Looking around the source code, I don't seem to find anything that could resolve a hostname to multiple A records. As far as I can see, only one instance is notified of a new certificate, the others will happily keep serving the hostname with the default certificate.

eg.:

dig marathon-lb.marathon.mesos
...
;; ANSWER SECTION:
marathon-lb.marathon.mesos. 60  IN      A       10.1.2.3
marathon-lb.marathon.mesos. 60  IN      A       10.1.2.4
marathon-lb.marathon.mesos. 60  IN      A       10.1.2.5

Is this something marathon-acme could support?

Thanks!

Set up Docker images

Probably makes sense to have a development Docker image with a Dockerfile in this repo and then a release version with a Dockerfile in a separate repo.

Extra syncs run every time another client attaches to the event stream

We currently listen for event_stream_attached events and run a sync when an event is received. This is so that we always run a sync as soon as we attach to sync any changes we missed while we weren't attached. Unfortunately, other things also subscribe to the event stream triggering the event. This happens more often than originally expected. We should only run a sync on the first event received.

Reduce log spam

The log is full of 2016-11-16T15:16:51+0200 [twisted.web.client._HTTP11ClientFactory#info] Starting factory <twisted.web.client._HTTP11ClientFactory instance at 0x109336290> that we don't really want.

Older versions of Mesos have issues with newlines in app labels

In certain versions of Mesos prior to 1.2.1/1.3.0, a bug in Mesos (MESOS-6951) causes problems with app labels that contain new lines when Marathon also provides those app labels as environment variables. This issue would cause the marathon-acme container to fail to start.

An ugly workaround is to force Marathon to not store the app label as an environment variable by making the label longer than 512 bytes:

"HAPROXY_0_HTTP_FRONTEND_ACL_WITH_PATH": "  acl path_{backend} path_beg {path}\n  use_backend {backend} if path_{backend}\n  ##################################################################\n  # This is a terrible hack to make the value of the marathon app\n  # label containing this config snippet longer than 512 bytes,\n  # which is the threshold beyond which marathon doesn't try to turn\n  # it into an environment variable.\n  # See https://issues.apache.org/jira/browse/MESOS-6951 for an\n  # explanation of why this is necessary.\n  ##################################################################\n",

Hook up the CLI

The CLI isn't plugged into anything yet but stuff is nearly ready to go :)

ACME authorizations failing

2016-11-14T15:51:37+0000 [twisted.python.log#info] "10.215.33.19" - - [14/Nov/2016:15:51:36 +0000] "GET /.well-known/acme-challenge/XQtPxyQTsdBwG-jGKnGgsiGaq3tQtF3Tkft-ixSwj-c HTTP/1.1" 404 233 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"

and..
txacme.client.AuthorizationFailed: AuthorizationFailed(<Status(invalid) Identifier(value='qa-mesos-mlb.za.prk-host.net', typ=IdentifierType(dns)) [Error(detail='Invalid response from http://qa-mesos-mlb.za.prk-host.net/.well-known/acme-challenge/XQtPxyQTsdBwG-jGKnGgsiGaq3tQtF3Tkft-ixSwj-c: "<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n<title>404 Not Found</title>\n<h1>Not Found</h1>\n<p>The requested URL was"', title=None, typ='urn:acme:error:unauthorized')]>)

Tests for ACME things are quite limited

We don't have a fake ACME service to test against and txacme's fake client doesn't really do much. It's difficult to run proper tests around ACME things like this, e.g. to test we send the right email address on registration, or respond to ACME challenges at the right path.

Transient test failure on Python 3.7

On Travis sometimes we see a failure like:

�[31m�[1m______________________ TestCli.test_storage_dir_provided _______________________�[0m
NOTE: Incompatible Exception Representation, displaying natively:

testtools.testresult.real._StringException: logged-error: {{{
Traceback (most recent call last):
  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/twisted/internet/defer.py", line 654, in _runCallbacks
    current.result = callback(current.result, *args, **kw)
  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/eliot/twisted.py", line 83, in callbackWithContext
    return self._action.run(callback, *args, **kwargs)
  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/eliot/_action.py", line 400, in run
    return f(*args, **kwargs)
  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/txacme/client.py", line 724, in _got_json
    messages.Error.from_json(jobj), response)
txacme.client.ServerError: (Error(typ='urn:acme:error:badNonce', title=None, detail='JWS has invalid anti-replay nonce 4Bzgo2qBE9sJAVesEgDpx36_G-PCGLeH3xM5A0mXhQM'), <treq.response._Response 400 'application/problem+json' 149 bytes>)
}}}

traceback-1: {{{
Traceback (most recent call last):
  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/testtools/twistedsupport/_runtest.py", line 386, in _log_user_exception
    raise e
testtools.twistedsupport._runtest.UncleanReactorError: The reactor still thinks it needs to do things. Close all connections, kill all processes and make sure all delayed calls have either fired or been cancelled:
  <DelayedCall 0x7faa364520b8 [0.17857933044433594s] called=0 cancelled=1>
  <DelayedCall 0x7faa364d3588 [86390.16879081726s] called=0 cancelled=1>
  <<class 'twisted.internet.tcp.Port'> of <class 'twisted.web.server.Site'> on 8000>
}}}

twisted-log: {{{
2018-09-11 09:45:09+0000 [-] Running marathon-acme 0.6.2.dev0 with: storage-dir='/tmp/tmphw7n631j', acme='https://acme-staging.api.letsencrypt.org/directory', email=None, allow-multiple-certs=False, marathon=['http://localhost:28080'], sse-timeout=60, lb=['http://marathon-lb.marathon.mesos:9090'], group='external', endpoint-description='tcp:8000'
2018-09-11 09:45:09+0000 [-] Starting marathon-acme...
2018-09-11 09:45:09+0000 [-] Site starting on 8000
2018-09-11 09:45:09+0000 [-] Starting factory <twisted.web.server.Site object at 0x7faa36461a20>
2018-09-11 09:45:09+0000 [-] Starting scheduled check for expired certificates.
2018-09-11 09:45:09+0000 [-] Starting factory _HTTP11ClientFactory(<function HTTPConnectionPool._newConnection.<locals>.quiescentCallback at 0x7faa363dc0d0>, <twisted.internet.endpoints._WrapperEndpoint object at 0x7faa364520f0>)
2018-09-11 09:45:09+0000 [-] Stopping factory _HTTP11ClientFactory(<function HTTPConnectionPool._newConnection.<locals>.quiescentCallback at 0x7faa363dc0d0>, <twisted.internet.endpoints._WrapperEndpoint object at 0x7faa364520f0>)
2018-09-11 09:45:09+0000 [-] Starting factory _HTTP11ClientFactory(<function HTTPConnectionPool._newConnection.<locals>.quiescentCallback at 0x7faa364e7ae8>, <twisted.internet.endpoints._WrapperEndpoint object at 0x7faa3656ad30>)
2018-09-11 09:45:09+0000 [-] Stopping factory _HTTP11ClientFactory(<function HTTPConnectionPool._newConnection.<locals>.quiescentCallback at 0x7faa364e7ae8>, <twisted.internet.endpoints._WrapperEndpoint object at 0x7faa3656ad30>)
2018-09-11 09:45:09+0000 [-] Starting factory _HTTP11ClientFactory(<function HTTPConnectionPool._newConnection.<locals>.quiescentCallback at 0x7faa364f30d0>, <twisted.internet.endpoints._WrapperEndpoint object at 0x7faa364c7f98>)
2018-09-11 09:45:09+0000 [-] Stopping factory _HTTP11ClientFactory(<function HTTPConnectionPool._newConnection.<locals>.quiescentCallback at 0x7faa364f30d0>, <twisted.internet.endpoints._WrapperEndpoint object at 0x7faa364c7f98>)
2018-09-11 09:45:09+0000 [-] Starting factory _HTTP11ClientFactory(<function HTTPConnectionPool._newConnection.<locals>.quiescentCallback at 0x7faa365309d8>, <twisted.internet.endpoints._WrapperEndpoint object at 0x7faa3658f9e8>)
2018-09-11 09:45:10+0000 [-] Error in scheduled certificate check.
	Traceback (most recent call last):
	  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/treq/client.py", line 61, in connectionLost
	    self.original.connectionLost(reason)
	  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/treq/content.py", line 39, in connectionLost
	    self.finished.callback(None)
	  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/twisted/internet/defer.py", line 460, in callback
	    self._startRunCallbacks(result)
	  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/twisted/internet/defer.py", line 568, in _startRunCallbacks
	    self._runCallbacks()
	--- <exception caught here> ---
	  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/twisted/internet/defer.py", line 654, in _runCallbacks
	    current.result = callback(current.result, *args, **kw)
	  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/eliot/twisted.py", line 83, in callbackWithContext
	    return self._action.run(callback, *args, **kwargs)
	  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/eliot/_action.py", line 400, in run
	    return f(*args, **kwargs)
	  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/txacme/client.py", line 724, in _got_json
	    messages.Error.from_json(jobj), response)
	txacme.client.ServerError: (Error(typ='urn:acme:error:badNonce', title=None, detail='JWS has invalid anti-replay nonce 4Bzgo2qBE9sJAVesEgDpx36_G-PCGLeH3xM5A0mXhQM'), <treq.response._Response 400 'application/problem+json' 149 bytes>)
	
2018-09-11 09:45:10+0000 [-] Stopping factory _HTTP11ClientFactory(<function HTTPConnectionPool._newConnection.<locals>.quiescentCallback at 0x7faa365309d8>, <twisted.internet.endpoints._WrapperEndpoint object at 0x7faa3658f9e8>)
2018-09-11 09:45:19+0000 [-] Main loop terminated.
}}}

Traceback (most recent call last):
  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/testtools/twistedsupport/_runtest.py", line 393, in _blocking_run_deferred
    spinner.run, self._timeout, self._run_deferred)
  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/testtools/twistedsupport/_spinner.py", line 100, in trap_unhandled_errors
    result = function(*args, **kwargs)
  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/testtools/twistedsupport/_spinner.py", line 51, in decorated
    return function(*args, **kwargs)
  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/testtools/twistedsupport/_spinner.py", line 326, in run
    return self._get_result()
  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/testtools/twistedsupport/_spinner.py", line 190, in _get_result
    self._failure.raiseException()
  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/twisted/python/failure.py", line 467, in raiseException
    raise self.value.with_traceback(self.tb)
testtools.twistedsupport._spinner.TimeoutError: <bound method AsynchronousDeferredRunTest._run_deferred of <testtools.twistedsupport._runtest.AsynchronousDeferredRunTest object at 0x7faa364c75c0>> took longer than 10.0 seconds

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/testtools/twistedsupport/_runtest.py", line 386, in _log_user_exception
    raise e
testtools.twistedsupport._spinner.TimeoutError: <marathon_acme.tests.test_cli.TestCli.test_storage_dir_provided id=0x7faa364c73c8> took longer than 10.0 seconds


----------------------------- Captured stdout call -----------------------------
2018-09-11T09:45:09+0000 [marathon_acme.cli#info] Running marathon-acme 0.6.2.dev0 with: storage-dir='/tmp/tmphw7n631j', acme='https://acme-staging.api.letsencrypt.org/directory', email=None, allow-multiple-certs=False, marathon=['http://localhost:28080'], sse-timeout=60, lb=['http://marathon-lb.marathon.mesos:9090'], group='external', endpoint-description='tcp:8000'
2018-09-11T09:45:09+0000 [marathon_acme.service.MarathonAcme#info] Starting marathon-acme...
2018-09-11T09:45:09+0000 [-] Site starting on 8000
2018-09-11T09:45:09+0000 [twisted.web.server.Site#info] Starting factory <twisted.web.server.Site object at 0x7faa36461a20>
2018-09-11T09:45:09+0000 [txacme.service#info] Starting scheduled check for expired certificates.
2018-09-11T09:45:10+0000 [txacme.service#critical] Error in scheduled certificate check.
	Traceback (most recent call last):
	  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/treq/client.py", line 61, in connectionLost
	    self.original.connectionLost(reason)
	  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/treq/content.py", line 39, in connectionLost
	    self.finished.callback(None)
	  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/twisted/internet/defer.py", line 460, in callback
	    self._startRunCallbacks(result)
	  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/twisted/internet/defer.py", line 568, in _startRunCallbacks
	    self._runCallbacks()
	--- <exception caught here> ---
	  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/twisted/internet/defer.py", line 654, in _runCallbacks
	    current.result = callback(current.result, *args, **kw)
	  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/eliot/twisted.py", line 83, in callbackWithContext
	    return self._action.run(callback, *args, **kwargs)
	  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/eliot/_action.py", line 400, in run
	    return f(*args, **kwargs)
	  File "/home/travis/build/praekeltfoundation/marathon-acme/.tox/py37/lib/python3.7/site-packages/txacme/client.py", line 724, in _got_json
	    messages.Error.from_json(jobj), response)
	txacme.client.ServerError: (Error(typ='urn:acme:error:badNonce', title=None, detail='JWS has invalid anti-replay nonce 4Bzgo2qBE9sJAVesEgDpx36_G-PCGLeH3xM5A0mXhQM'), <treq.response._Response 400 'application/problem+json' 149 bytes>)
	
2018-09-11T09:45:19+0000 [-] Main loop terminated.

ACME challenges fail if an HTTPS redirect is active with strict-sni

If there is no certificate for the domain yet, the HAProxy redirect takes precedence over other routing rules and, if strict-sni is enabled, the challenge will fail when there is an SSL error connecting to the HTTPS endpoint.

The workaround for now is to only set HAPROXY_{n}_REDIRECT_TO_HTTPS to true after the first certificate for the domain has been fetched.

Incompatible with older versions of Marathon without `portDefinitions` in app definition

2016-11-30T14:55:58+0000 [marathon_acme.service.MarathonAcme#error] Sync failed
    Traceback (most recent call last):
      File "/usr/local/lib/python3.5/site-packages/treq/client.py", line 62, in connectionLost
        self.original.connectionLost(reason)
      File "/usr/local/lib/python3.5/site-packages/treq/content.py", line 41, in connectionLost
        self.finished.callback(None)
      File "/usr/local/lib/python3.5/site-packages/twisted/internet/defer.py", line 455, in callback
        self._startRunCallbacks(result)
      File "/usr/local/lib/python3.5/site-packages/twisted/internet/defer.py", line 563, in _startRunCallbacks
        self._runCallbacks()
    --- <exception caught here> ---
      File "/usr/local/lib/python3.5/site-packages/twisted/internet/defer.py", line 649, in _runCallbacks
        current.result = callback(current.result, *args, **kw)
      File "/marathon-acme/marathon_acme/service.py", line 154, in _apps_acme_domains
        domains.extend(self._app_acme_domains(app))
      File "/marathon-acme/marathon_acme/service.py", line 167, in _app_acme_domains
        for port_index, _ in enumerate(app['portDefinitions']):
    builtins.KeyError: 'portDefinitions'

Fallback to the older ports field.

Hook up txacme's "panic" callable to do something more visible

http://txacme.readthedocs.io/en/latest/using.html#issuing-service

panic (Callable[[Failure, str], Deferred]) – A callable invoked with the failure and server name when reissuing fails for a certificate expiring in the panic_interval. For example, you could generate a monitoring alert. The default callback logs a message at CRITICAL level.

Logging as CRITICAL may not be the most visible thing to do if marathon-acme is just quietly running in a container somewhere... Send an email? Sentry error?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.