All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.predic8.membrane.core.interceptor.AcmeHttpChallengeInterceptor Maven / Gradle / Ivy

There is a newer version: 5.7.3
Show newest version
/* Copyright 2022 predic8 GmbH, www.predic8.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License. */
package com.predic8.membrane.core.interceptor;

import com.predic8.membrane.annot.MCElement;
import com.predic8.membrane.core.exchange.Exchange;
import com.predic8.membrane.core.http.Response;
import com.predic8.membrane.core.rules.AbstractProxy;
import com.predic8.membrane.core.rules.Rule;
import com.predic8.membrane.core.transport.ssl.AcmeSSLContext;
import com.predic8.membrane.core.transport.ssl.SSLContext;
import com.predic8.membrane.core.transport.ssl.acme.AcmeClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;

import static com.predic8.membrane.core.http.MimeType.APPLICATION_OCTET_STREAM;
import static java.util.Arrays.stream;

/**
 * @description
 * See the documentation of the <acme /> element for usage details.
 */
@MCElement(name = "acmeHttpChallenge")
public class AcmeHttpChallengeInterceptor extends AbstractInterceptor {

    private static final Logger LOG = LoggerFactory.getLogger(AcmeHttpChallengeInterceptor.class);
    public static final String PREFIX = "/.well-known/acme-challenge/";

    private boolean ignorePort;

    public AcmeHttpChallengeInterceptor() {
        name = "ACME HTTP Challenge";
    }

    @Override
    public Outcome handleRequest(Exchange exc) throws Exception {
        if (exc.getRequest().getUri().startsWith(PREFIX)) {
            String token = exc.getRequest().getUri().substring(PREFIX.length());
            String host = ignorePort
                    ? exc.getRequest().getHeader().getHost().replaceAll(":.*", "")
                    : exc.getRequest().getHeader().getHost();

            for (Rule rule : router.getRules()) {
                if (!(rule instanceof AbstractProxy))
                    continue;
                SSLContext sslInboundContext = rule.getSslInboundContext();
                if (!(sslInboundContext instanceof AcmeSSLContext))
                    continue;
                AcmeSSLContext acmeSSLContext = (AcmeSSLContext) sslInboundContext;

                if (stream(acmeSSLContext.getHosts()).noneMatch(h -> h.equals(host)))
                    continue;

                AcmeClient acmeClient = acmeSSLContext.getClient();

                String correctToken = acmeClient.getToken(host);

                if (correctToken != null && correctToken.equals(token)) {
                    String keyAuth = token + "." + acmeClient.getThumbprint();

                    exc.setResponse(Response.ok()
                            .header("Content-Type", APPLICATION_OCTET_STREAM)
                            .body(keyAuth).build());
                    return Outcome.RETURN;
                }
            }

            LOG.warn("Returning 404 in response to ACME challenge token " + token);
            exc.setResponse(Response.notFound().build());
            return Outcome.RETURN;
        }
        return super.handleRequest(exc);
    }

    public boolean isIgnorePort() {
        return ignorePort;
    }

    /**
     * @description For testing purposes only.
     */
    public void setIgnorePort(boolean ignorePort) {
        this.ignorePort = ignorePort;
    }

    @Override
    public String getShortDescription() {
        return "Responds to HTTP requests starting with /.well-known/acme-challenge/.";
    }

    @Override
    public String getLongDescription() {
        return "
Responds to HTTP requests starting with /.well-known/acme-" + "challenge/.
" + "See ACME (RFC 8555, also known as \"Let's Encrypt\") HTTP Challenges for details.
"; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy