This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
certbot-haproxy/certbot_haproxy/authenticator.py

120 lines
4.2 KiB
Python

"""HAProxy Authenticator.
The HAProxy Authenticator is an extension of the "standalone" authenticator
that is part of certbot. It limits its functionality to only support the
`http-01` challenge because `tls-sni-01` checks the challenge by connecting to
port 443. We can't proxy requests to certbot because we can't see the
requested uri until the request is decrypted, and we can't do decryption in
HAProxy because `tls-sni-01` expects to do a TLS handshake.
This authenticator creates its own ephemeral TCP listener on the necessary port
in order to respond to incoming `http-01` challenges from the certificate
authority. You need to forward port requests for `/.well-known/acme-challenge/`
on port 80 to the configured value for `haproxy-http-01-port` (default:8000).
This can be achieved by adding this example to your haproxy configuration
file::
default_backend nodes
acl is_certbot path_beg -i /.well-known/acme-challenge
use_backend certbot if is_certbot
backend certbot
log global
mode http
server certbot 127.0.0.1:8000
backend nodes
log global
mode http
option tcplog
balance roundrobin
option forwardfor
option http-server-close
option httpclose
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https if { ssl_fc }
option httpchk HEAD / HTTP/1.1\\r\\nHost:localhost
server node1 127.0.0.1:8080 check
server node2 127.0.0.1:8080 check
server node3 127.0.0.1:8080 check
server node4 127.0.0.1:8080 check
For instructions on how to make HAProxy serve certificates that were created
with this authenticator, read the documentation of the
`.certbot_haproxy.installer`
"""
import logging
import zope.component
import zope.interface
from acme import challenges
from certbot import interfaces
from certbot.plugins import standalone
logger = logging.getLogger(__name__) # pylint:disable=invalid-name
@zope.interface.implementer(interfaces.IAuthenticator)
@zope.interface.provider(interfaces.IPluginFactory)
class HAProxyAuthenticator(standalone.Authenticator):
"""HAProxy Authenticator."""
description = "Certbot standalone authenticator with HAProxy preset."
def __init__(self, *args, **kwargs):
super(HAProxyAuthenticator, self).__init__(*args, **kwargs)
self.config.http01_port = self.conf('haproxy_http_01_port')
@classmethod
def add_parser_arguments(cls, add):
"""
This method adds extra CLI arguments to the plugin.
The arguments can be retrieved by asking for corresponding names
in `self.conf([argument name])`
.. note:: This overrides a method defined in the parent, we
are deliberately not calling super() because it would add
arguments that are not supported.
:param func add: The function to be called to add an argument.
"""
add(
"haproxy-http-01-port",
help=(
"Port to open internally (default=8000), you're expected to"
" forward requests to port 80 to it."
),
type=int,
default=8000
)
@property
def supported_challenges(self):
"""
Challenges supported by this plugin: only http-01
See introduction for reasoning.
:returns: List of supported challenges
:rtype: list
"""
return [challenges.HTTP01]
@staticmethod
def more_info():
"""
This info string only appears in the curses UI in the plugin
selection sequence.
"""
return (
"This authenticator creates its own ephemeral TCP listener"
" on the configured internal port (default=8000) in order to"
" respond to incoming http-01 challenges from the certificate"
" authority. In order for this port to be reached, you need to"
" configure HAProxy to forward any requests to any domain on the"
" http-01 port (default:80), ending in"
" `/.well-known/acme-challenge/` to the http-01 port (hint:8000)."
)