package com.intrbiz.bergamot.worker.engine.http; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpVersion; import org.apache.log4j.Logger; import com.codahale.metrics.Counter; import com.codahale.metrics.Timer; import com.intrbiz.Util; import com.intrbiz.bergamot.check.http.HTTPCheckBuilder; import com.intrbiz.bergamot.crypto.util.CertInfo; import com.intrbiz.bergamot.crypto.util.TLSInfo; import com.intrbiz.bergamot.model.message.ParameterMO; import com.intrbiz.bergamot.model.message.check.ExecuteCheck; import com.intrbiz.bergamot.model.message.result.ActiveResultMO; import com.intrbiz.bergamot.worker.engine.AbstractExecutor; import com.intrbiz.gerald.source.IntelligenceSource; import com.intrbiz.gerald.witchcraft.Witchcraft; /** * Execute TLS Certificate checks */ public class CertificateExecutor extends AbstractExecutor<HTTPEngine> { public static final String NAME = "certificate"; private Logger logger = Logger.getLogger(CertificateExecutor.class); private final Timer requestTimer; private final Counter failedRequests; public CertificateExecutor() { super(); // setup metrics IntelligenceSource source = Witchcraft.get().source("com.intrbiz.bergamot.http"); this.requestTimer = source.getRegistry().timer("all-http-requests"); this.failedRequests = source.getRegistry().counter("failed-http-requests"); } /** * Only execute Checks where the engine == "http" and executor = "certificate" */ @Override public boolean accept(ExecuteCheck task) { return HTTPEngine.NAME.equalsIgnoreCase(task.getEngine()) && CertificateExecutor.NAME.equalsIgnoreCase(task.getExecutor()); } @Override public void execute(final ExecuteCheck executeCheck) { logger.info("Executing check : " + executeCheck.getEngine() + "::" + executeCheck.getExecutor() + "::" + executeCheck.getName() + " for " + executeCheck.getCheckType() + " " + executeCheck.getCheckId()); // time it final Timer.Context tctx = this.requestTimer.time(); try { if (Util.isEmpty(executeCheck.getParameter("host"))) throw new RuntimeException("The host must be defined!"); // HTTPCheckBuilder check = this.getEngine().getChecker().check(); // force ssl check.ssl(); // allow bad certs, so we can still check check.permitInvalidCerts(); // required parameters check.connect(executeCheck.getParameter("host")); // optional parameters // virtual host if (executeCheck.containsParameter("virtual_host")) check.host(executeCheck.getParameter("virtual_host", null)); // port number if (executeCheck.containsParameter("port")) check.port(executeCheck.getIntParameter("port", -1)); // the request if (executeCheck.containsParameter("version")) check.version(HttpVersion.valueOf(executeCheck.getParameter("version", "HTTP/1.1").toUpperCase())); if (executeCheck.containsParameter("method")) check.method(HttpMethod.valueOf(executeCheck.getParameter("method", "GET").toUpperCase())); if (executeCheck.containsParameter("path")) check.path(executeCheck.getParameter("path", "/")); // any headers for (ParameterMO header : executeCheck.getParametersStartingWith("header:")) { check.header(header.getName().substring("header:".length() + 1), header.getValue()); } // execute check.execute((response) -> { logger.info("Got response for TLS Certificate check (" + executeCheck.getCheckId() + "/" + executeCheck.getId() + ")\n" + response); // compute the result ActiveResultMO resultMO = new ActiveResultMO().fromCheck(executeCheck); // check the response TLSInfo tls = response.getTlsInfo(); CertInfo serverCert = tls.getServerCertInfo(); // first, is the certificate valid? if (tls.isValidCertificate()) { // check the expiry date, if it is close raise an error long tillExpiry = (serverCert.getNotAfter().getTime() - System.currentTimeMillis()) / (1000 * 60 * 60 * 24); /* days */ // check how long till the certificate expires if (tillExpiry <= executeCheck.getIntParameter("expires_in", 28)) { resultMO.action("TLS certificate is valid but expires in " + tillExpiry + " days!"); } else { resultMO.ok("TLS certificate is valid and expires in " + tillExpiry + " days"); } } else { // well, little point in going further, the certificate is crap resultMO.critical("TLS certificate is not valid: " + Util.coalesceEmpty(Util.nullable(tls.getCertificateValidationError(), Throwable::getMessage), " for some reason") + "."); } // submit the result resultMO.setRuntime(response.getRuntime()); tctx.stop(); this.publishActiveResult(executeCheck, resultMO); }, (error) -> { tctx.stop(); failedRequests.inc(); logger.error("Error for TLS Certificate check (" + executeCheck.getCheckId() + "/" + executeCheck.getId() + ")", error); this.publishActiveResult(executeCheck, new ActiveResultMO().fromCheck(executeCheck).error(error)); }); } catch (Exception e) { logger.error("Failed to execute TLS Certificate check", e); tctx.stop(); this.failedRequests.inc(); this.publishActiveResult(executeCheck, new ActiveResultMO().fromCheck(executeCheck).error(e)); } } }