跳转到主要内容

自动管理Mesosphere Marathon上运行的应用程序的Let's Encrypt证书

项目描述

PyPI Build Status codecov

自动为ACME证书Marathon应用程序marathon-lb

工作原理

部署marathon-acme的一个主要要求是:必须在marathon-acme和所有marathon-lb实例之间有共享持久存储。这将用于存储证书。

  1. marathon-acme监视Marathon以检测应用程序定义的更改。

  2. 它收集所有应用程序上MARATHON_ACME_{n}_DOMAIN标签的值。这将形成要获取证书的域名集。

  3. 它使用配置的ACME证书颁发机构为任何新的域名生成、验证和存储证书。

  4. 它通过marathon-lb HTTP API告诉marathon-lb重新加载。

  5. 它每天为即将到期的证书颁发新的证书。

marathon-acme是用Python编写的,使用了Twisted。证书颁发功能得益于txacme库。

大多数人可能最有可能使用的ACME提供商是Let’s Encrypt。在使用Let’s Encrypt与marathon-acme之前,请确保您了解他们的速率限制

以下展示了整个证书颁发工作流程

issue-certificate.svg

用法

marathon-acme可作为Python包在PyPI上安装。然而,大多数用户可能会选择使用来自Docker Hub的Docker镜像。

> $ 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应用定义

marathon-acme应作为Marathon应用部署。

{
  "id": "/marathon-acme",
  "cpus": 0.01,
  "mem": 128.0,
  "args": [
    "--email", "letsencrypt@example.com",
    "--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"
        }
      ],
    }
  }
}

上述内容在不同部署中应大多相同。卷参数将取决于您的特定网络存储解决方案。

HAPROXY标签

"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"
}

为了将所有以/.well-known/acme-challenge/开头的HTTP请求转发到marathon-acme以服务ACME HTTP挑战响应,需要几个特殊的marathon-lb标签。

HAPROXY_GROUP
external

marathon-lb实例被分配一个组。只有具有与其组匹配的HAPROXY_GROUP标签的Marathon应用才会通过该实例进行路由。“external”是面向公众的负载均衡器的常用名称。

HAPROXY_0_VHOST
marathon-acme.example.com

marathon-acme需要一个自己的域名来响应ACME挑战请求。此域名必须解析到您的marathon-lb实例。

HAPROXY_0_BACKEND_WEIGHT
1

我们希望这个规则在HAProxy的配置文件中位于其他规则之前,以便在为其他Marathon应用进行(通常基于域名的)路由之前将请求路由到marathon-acme。默认权重是0,因此将其设置为1,以便该规则优先。

HAPROXY_0_PATH
/.well-known/acme-challenge/

这是ACME验证挑战的HTTP路径的开始。

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}

这变得复杂了……可以使用标签按应用编辑用于生成HAProxy的模板。这是必要的,因为默认情况下,marathon-lb将首先根据域名进行路由,但我们不想这样做。您可以在此处看到标准模板。

在此,我们添加了一个额外的redirect规则。该规则将所有匹配ACME挑战路径的请求重定向到marathon-acme,但不会将已指向marathon-acme的请求重定向。Let’s Encrypt服务器将遵循重定向。

HAPROXY HTTPS标签

虽然通常不需要,但marathon-acme可以在HTTPS上服务ACME挑战请求。在这种情况下,需要为marathon-acme颁发证书,并且需要修改HTTP重定向标签。

"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"
}

请注意,使用marathon-acmeHAPROXY_0_REDIRECT_TO_HTTPS标签将破坏一切。由于marathon-lb模板的方式,该标签对我们来说很难使用。

MARATHON_ACME_0_DOMAIN
marathon-acme.example.com

在此,我们为marathon-acme设置获取证书。

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}

我们将所有域名(包括 marathon-acme 的)对 ACME 挑战路径的请求重定向到 HTTPS 地址(https://{hostname})。现在可以删除 use_backend 指令,因为后端永远不会通过 HTTP 使用,因为所有请求都进行了重定向。

注意,此标签只能在 marathon-acme 获取其自身域的第一个证书之后设置。换句话说,首先设置 MARATHON_ACME_0_DOMAIN,并确保它已经生效,然后再设置此标签。

Docker 镜像

Docker Hub(https://hub.docker.com/r/praekeltfoundation/marathon-acme/)上提供了 Docker 镜像。有两种不同的 Docker 镜像流可供使用

有关 Docker 镜像的更多详细信息,请参阅 praekeltfoundation/docker-marathon-acme 仓库。

卷和端口

marathon-acme 容器默认将 /var/lib/marathon-acme 目录用于存储证书和 ACME 客户端私钥。这是容器内部应该挂载为共享卷的路径。

容器还默认在所有接口上监听端口 8000。

您可以通过向 Docker 容器提供参数来覆盖这些值。

证书文件

marathon-acme 创建以下目录/文件结构

  • /var/lib/marathon-acme/

    • client.key:ACME 客户端私钥

    • default.pem:为 HAProxy 备用的自签名通配符证书

    • certs/

      • www.example.com.pem:针对域签发的 ACME 证书

    • unmanaged-certs/:未管理证书的目录

在创建 unmanaged-certs/ 目录后,marathon-acme 对其不做任何操作。如果 HAProxy 的证书配置中的任何路径不存在,则会失败,因此将未管理的证书放置在标准位置可以减少设置摩擦。

marathon-lb 配置

为了能够触发 HAProxy 重新加载,marathon-acme 需要 marathon-lb 1.4.0 或更高版本。

如前所述,marathon-lb 必须与 marathon-acme 共享持久存储。BYONS:自携网络存储。

marathon-lb 所需的唯一真实配置是将 marathon-acme 的证书存储目录路径添加为证书来源。HAProxy 支持从目录加载证书。您应将 marathon-lb--ssl-certs CLI 选项设置为证书目录路径以及备用证书(如果 HAProxy 在提供的路径中找不到任何证书,则无法启动)。

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

应用配置

marathon-acme 使用单个类似 marathon-lb 的标签将域名分配给应用程序端口:MARATHON_ACME_{n}_DOMAIN,其中 {n} 是端口号索引。标签的值是一组以逗号和/或空格分隔的域名,尽管默认情况下只考虑第一个域名。

目前,marathon-acme 只能针对单个域名颁发证书。这意味着对于配置了多个域名的应用程序,需要颁发多个证书。

增加了一个限制,将应用程序限制为单个域名。可以通过传递 --allow-multiple-certs 命令行选项来移除此限制,尽管这不建议使用,因为这使得可以为单个应用程序颁发大量证书,可能会耗尽 Let’s Encrypt 的速率限制。

应用程序或其端口必须与在启动时配置 marathon-acme 的同一 HAPROXY_GROUP

我们决定不重复使用 HAPROXY_{n}_VHOST 标签,以限制颁发的证书所针对的域名数量。

限制

用于 ACME 证书管理的库 txacme,目前其功能相当有限。最大的两个限制是

  • 目前还没有 Subject Alternative Name (SAN) 支持 (#37)。每个证书将对应于确切的一个域名。这个限制使得更容易达到 Let’s Encrypt 的速率限制。

  • 没有支持从 txacme 的证书存储中 删除 证书 (#77)。一旦 marathon-acme 为应用程序颁发了一个证书,它将尝试无限期地续订该证书,除非手动从证书存储中删除。

有关问题的更完整列表,请参阅此存储库的问题页面。

故障排除

挑战 ping 端点

一个常见问题是 marathon-lb 配置错误,ACME 挑战请求无法到达 marathon-acme。您可以使用挑战 ping 端点测试挑战请求路由到 marathon-acme

应该能够从 marathon-lb 服务的所有域名访问 /.well-known/acme-challenge/ping 路径。

> $ 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-0.6.1.tar.gz (198.1 kB 查看散列)

上传时间

构建分布

marathon_acme-0.6.1-py2.py3-none-any.whl (54.1 kB 查看哈希)

上传于 Python 2 Python 3

由以下支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误日志 StatusPage StatusPage 状态页面