Beispiel #1
0
    def from_url(cls, reactor, url, key, alg=jose.RS256, jws_client=None):
        """
        Construct a client from an ACME directory at a given URL.

        :param url: The ``twisted.python.url.URL`` to fetch the directory from.
            See `txacme.urls` for constants for various well-known public
            directories.
        :param reactor: The Twisted reactor to use.
        :param ~acme.jose.jwk.JWK key: The client key to use.
        :param alg: The signing algorithm to use.  Needs to be compatible with
            the type of key used.
        :param JWSClient jws_client: The underlying client to use, or ``None``
            to construct one.

        :return: The constructed client.
        :rtype: Deferred[`Client`]
        """
        action = LOG_ACME_CONSUME_DIRECTORY(
            url=url, key_type=key.typ, alg=alg.name)
        with action.context():
            check_directory_url_type(url)
            jws_client = _default_client(jws_client, reactor, key, alg)
            return (
                DeferredContext(jws_client.get(url.asText()))
                .addCallback(json_content)
                .addCallback(messages.Directory.from_json)
                .addCallback(
                    tap(lambda d: action.add_success_fields(directory=d)))
                .addCallback(cls, reactor, key, jws_client)
                .addActionFinish())
Beispiel #2
0
    def from_url(cls, reactor, url, key, alg=RS256, jws_client=None):
        """
        Construct a client from an ACME directory at a given URL.

        :param url: The ``twisted.python.url.URL`` to fetch the directory from.
            See `txacme.urls` for constants for various well-known public
            directories.
        :param reactor: The Twisted reactor to use.
        :param ~josepy.jwk.JWK key: The client key to use.
        :param alg: The signing algorithm to use.  Needs to be compatible with
            the type of key used.
        :param JWSClient jws_client: The underlying client to use, or ``None``
            to construct one.

        :return: The constructed client.
        :rtype: Deferred[`Client`]
        """
        action = LOG_ACME_CONSUME_DIRECTORY(
            url=url, key_type=key.typ, alg=alg.name)
        with action.context():
            check_directory_url_type(url)
            jws_client = _default_client(jws_client, reactor, key, alg)
            return (
                DeferredContext(jws_client.get(url.asText()))
                .addCallback(json_content)
                .addCallback(messages.Directory.from_json)
                .addCallback(
                    tap(lambda d: action.add_success_fields(directory=d)))
                .addCallback(cls, reactor, key, jws_client)
                .addActionFinish())
Beispiel #3
0
    def from_url(
        cls,
        reactor,
        url,
        key,
        alg=RS256,
        jws_client=None,
        timeout=_DEFAULT_TIMEOUT,
    ):
        """
        Construct a client from an ACME directory at a given URL.

        At construct time, it validates the ACME directory.

        :param url: The ``twisted.python.url.URL`` to fetch the directory from.
            See `txacme.urls` for constants for various well-known public
            directories.
        :param reactor: The Twisted reactor to use.
        :param ~josepy.jwk.JWK key: The client key to use.
        :param alg: The signing algorithm to use.  Needs to be compatible with
            the type of key used.
        :param JWSClient jws_client: The underlying client to use, or ``None``
            to construct one.
        :param int timeout: Number of seconds to wait for an HTTP response
            during ACME server interaction.

        :return: The constructed client.
        :rtype: Deferred[`Client`]
        """
        action = LOG_ACME_CONSUME_DIRECTORY(url=url,
                                            key_type=key.typ,
                                            alg=alg.name)
        with action.context():
            check_directory_url_type(url)
            directory = url.asText()
            return (DeferredContext(jws_client=_default_client(
                jws_client, reactor, key, alg, directory, timeout
            )).addCallback(
                tap(lambda jws_client: action.add_success_fields(
                    directory=directory))).addCallback(lambda jws_client: cls(
                        reactor, key, jws_client)).addActionFinish())
Beispiel #4
0
class AutoTLSEndpoint(object):
    """
    A server endpoint that does TLS SNI, with certificates automatically
    (re)issued from an ACME certificate authority.

    :param reactor: The Twisted reactor.
    :param directory: ``twisted.python.url.URL`` for the ACME directory to use
        for issuing certs.

    :type client_creator: Callable[[reactor, ``twisted.python.url.URL``],
        Deferred[`txacme.client.Client`]]
    :param client_creator: A callable called with the reactor and directory URL
        for creating the ACME client.  For example, ``partial(Client.from_url,
        key=acme_key, alg=RS256)``.
    :param .ICertificateStore cert_store: The certificate store containing the
        certificates to manage.  For example, `txacme.store.DirectoryStore`.
    :param dict cert_mapping: The certificate mapping to use for SNI; for
        example, ``txsni.snimap.HostDirectoryMap``.  Usually this should
        correspond to the same underlying storage as ``cert_store``.
    :param ~datetime.timedelta check_interval: How often to check for expiring
        certificates.
    :param ~datetime.timedelta reissue_interval: If a certificate is expiring
        in less time than this interval, it will be reissued.
    :param ~datetime.timedelta panic_interval: If a certificate is expiring in
        less time than this interval, and reissuing fails, the panic callback
        will be invoked.

    :type panic: Callable[[Failure, `str`], Deferred]
    :param panic: 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.
    :param generate_key: A 0-arg callable used to generate a private key for a
        new cert.  Normally you would not pass this unless you have specialized
        key generation requirements.
    """
    reactor = attr.ib()
    directory = attr.ib(
        validator=lambda inst, a, value: check_directory_url_type(value))
    client_creator = attr.ib()
    cert_store = attr.ib()
    cert_mapping = attr.ib()
    sub_endpoint = attr.ib()
    check_interval = attr.ib(default=timedelta(days=1))
    reissue_interval = attr.ib(default=timedelta(days=30))
    panic_interval = attr.ib(default=timedelta(days=15))
    _panic = attr.ib(default=_default_panic)
    _generate_key = attr.ib(default=partial(generate_private_key, u'rsa'))

    def listen(self, protocolFactory):  # noqa
        """
        Start an issuing service, and wait until initial issuing is complete.
        """
        def _got_port(port):
            self.service = AcmeIssuingService(
                cert_store=self.cert_store,
                client_creator=partial(
                    self.client_creator, self.reactor, self.directory),
                clock=self.reactor,
                responders=[responder],
                check_interval=self.check_interval,
                reissue_interval=self.reissue_interval,
                panic_interval=self.panic_interval,
                panic=self._panic,
                generate_key=self._generate_key)
            self.service.startService()
            return (
                self.service.when_certs_valid()
                .addCallback(
                    lambda _: _WrapperPort(port=port, service=self.service)))

        responder = TLSSNI01Responder()
        sni_map = SNIMap(responder.wrap_host_map(self.cert_mapping))
        return (
            maybeDeferred(
                self.sub_endpoint.listen,
                TLSMemoryBIOFactory(
                    contextFactory=sni_map,
                    isClient=False,
                    wrappedFactory=protocolFactory))
            .addCallback(_got_port))