package com.intrbiz.bergamot.ui.router.agent; import java.io.IOException; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.sql.Timestamp; import java.util.UUID; import com.intrbiz.balsa.engine.route.Router; import com.intrbiz.balsa.error.http.BalsaNotFound; import com.intrbiz.balsa.metadata.WithDataAdapter; import com.intrbiz.bergamot.agent.config.BergamotAgentCfg; import com.intrbiz.bergamot.agent.config.CfgParameter; import com.intrbiz.bergamot.crypto.util.CertificatePair; import com.intrbiz.bergamot.crypto.util.CertificateRequest; import com.intrbiz.bergamot.crypto.util.PEMUtil; import com.intrbiz.bergamot.crypto.util.SerialNum; import com.intrbiz.bergamot.data.BergamotDB; import com.intrbiz.bergamot.metadata.GetBergamotSite; import com.intrbiz.bergamot.metadata.IsaObjectId; import com.intrbiz.bergamot.model.AgentRegistration; import com.intrbiz.bergamot.model.AgentTemplate; import com.intrbiz.bergamot.model.Config; import com.intrbiz.bergamot.model.Contact; import com.intrbiz.bergamot.model.Site; import com.intrbiz.bergamot.ui.BergamotApp; import com.intrbiz.metadata.Any; import com.intrbiz.metadata.CheckStringLength; import com.intrbiz.metadata.Get; import com.intrbiz.metadata.Param; import com.intrbiz.metadata.Post; import com.intrbiz.metadata.Prefix; import com.intrbiz.metadata.RequirePermission; import com.intrbiz.metadata.RequireValidPrincipal; import com.intrbiz.metadata.Template; @Prefix("/agent") @Template("layout/main") @RequireValidPrincipal() @RequirePermission("ui.sign.agent") @RequirePermission("sign.agent") public class AgentRouter extends Router<BergamotApp> { @Any("/") @WithDataAdapter(BergamotDB.class) public void listAgents(BergamotDB db, @GetBergamotSite() Site site) { model("agents", db.listAgentRegistrations(site.getId())); model("agentTemplates", db.listAgentTemplates(site.getId())); encode("agent/index"); } @Get("/generate-template") @WithDataAdapter(BergamotDB.class) public void showGenerateAgentTemplate(BergamotDB db, @GetBergamotSite() Site site) { model("hostTemplates", db.listHostTemplatesWithoutCertificates(site.getId())); encode("agent/generate-template"); } @Get("/generate-config") @RequirePermission("ui.generate.agent") public void showGenerateAgentConfig() { encode("agent/generate-config"); } @Get("/sign") public void showSignAgentConfig() { encode("agent/sign-agent"); } @Get("/show-template/:id") @WithDataAdapter(BergamotDB.class) public void showGenerateAgentTemplate(BergamotDB db, @GetBergamotSite() Site site, @IsaObjectId() UUID id) { // get the template AgentTemplate template = db.getAgentTemplate(id); // get the certs Certificate rootCert = action("get-root-ca"); Certificate siteCert = action("get-site-ca", site.getId()); // build the config BergamotAgentCfg cfg = new BergamotAgentCfg(); cfg.setCaCertificate(padCert(PEMUtil.saveCertificate(rootCert))); cfg.setSiteCaCertificate(padCert(PEMUtil.saveCertificate(siteCert))); cfg.setCertificate(padCert(template.getCertificate())); cfg.setKey(padCert(template.getPrivateKey())); cfg.setName(template.getName()); cfg.addParameter(new CfgParameter("template-id", null, null, id.toString())); // display var("agentConfig", cfg.toString() + "\n<!-- Template: UUID=" + id + " CN=" + template.getName() + " -->"); encode("agent/generated-template"); } @Post("/generate-template") @WithDataAdapter(BergamotDB.class) public void generateTemplate(BergamotDB db, @GetBergamotSite() Site site, @Param("template") @IsaObjectId() UUID templateId) { // get the template Config hostTemplate = notNull(db.getConfig(templateId), "Invalid host template"); if (! "host".equals(hostTemplate.getType())) throw new BalsaNotFound("Invalid host template"); // generate Certificate rootCert = action("get-root-ca"); Certificate siteCert = action("get-site-ca", site.getId()); CertificatePair pair = action("generate-template", site.getId(), hostTemplate, ((Contact) currentPrincipal()).getId()); // build the config BergamotAgentCfg cfg = new BergamotAgentCfg(); cfg.setCaCertificate(padCert(PEMUtil.saveCertificate(rootCert))); cfg.setSiteCaCertificate(padCert(PEMUtil.saveCertificate(siteCert))); cfg.setCertificate(padCert(pair.getCertificateAsPEM())); cfg.setKey(padCert(pair.getKeyAsPEM())); cfg.setName(hostTemplate.getName()); cfg.addParameter(new CfgParameter("template-id", null, null, templateId.toString())); // store the agent registration db.setAgentTemplate(new AgentTemplate(site.getId(), templateId, hostTemplate.getName(), hostTemplate.getSummary(), SerialNum.fromBigInt(pair.getCertificate().getSerialNumber()).toString(), pair.getCertificateAsPEM(), pair.getKeyAsPEM())); // display var("agentConfig", cfg.toString() + "\n<!-- Template: UUID=" + templateId + " CN=" + hostTemplate.getName() + " -->"); encode("agent/generated-template"); } @Post("/generate-config") @RequirePermission("ui.generate.agent") @WithDataAdapter(BergamotDB.class) public void generateAgentConfig(BergamotDB db, @GetBergamotSite() Site site, @Param("common-name") @CheckStringLength(min = 1, max = 255, mandatory = true) String commonName) { // is an agent already registered AgentRegistration existingAgent = db.getAgentRegistrationByName(site.getId(), commonName); // assign id UUID agentId = var("agentId", existingAgent != null ? existingAgent.getId() : Site.randomId(site.getId())); var("commonName", commonName); // generate Certificate rootCert = action("get-root-ca"); Certificate siteCert = action("get-site-ca", site.getId()); CertificatePair pair = action("generate-agent", site.getId(), agentId, commonName, ((Contact) currentPrincipal()).getId()); // build the config BergamotAgentCfg cfg = new BergamotAgentCfg(); cfg.setCaCertificate(padCert(PEMUtil.saveCertificate(rootCert))); cfg.setSiteCaCertificate(padCert(PEMUtil.saveCertificate(siteCert))); cfg.setCertificate(padCert(pair.getCertificateAsPEM())); cfg.setKey(padCert(pair.getKeyAsPEM())); cfg.setName(commonName); cfg.addParameter(new CfgParameter("agent-id", null, null, agentId.toString())); // store the agent registration db.setAgentRegistration(new AgentRegistration(site.getId(), agentId, commonName, SerialNum.fromBigInt(pair.getCertificate().getSerialNumber()).toString())); // display var("agentConfig", cfg.toString() + "\n<!-- Agent: UUID=" + agentId + " CN=" + commonName + " -->"); encode("agent/generated-config"); } @Post("/sign") @WithDataAdapter(BergamotDB.class) public void signAgent(BergamotDB db, @GetBergamotSite() Site site, @Param("certificate-request") @CheckStringLength(min = 1, max = 16384, mandatory = true) String certReq) throws IOException { // parse the certificate request CertificateRequest req = PEMUtil.loadCertificateRequest(certReq); // is an agent already registered AgentRegistration existingAgent = db.getAgentRegistrationByName(site.getId(), req.getCommonName()); // generate agent it UUID agentId = var("agentId", existingAgent != null ? existingAgent.getId() : Site.randomId(site.getId())); // sign Certificate rootCrt = action("get-root-ca"); Certificate siteCrt = action("get-site-ca", site.getId()); Certificate agentCrt = action("sign-agent", site.getId(), agentId, req, ((Contact) currentPrincipal()).getId()); // store the registration db.setAgentRegistration(new AgentRegistration(site.getId(), agentId, req.getCommonName(), SerialNum.fromBigInt(((X509Certificate) agentCrt).getSerialNumber()).toString())); // display var("agentCrt", PEMUtil.saveCertificate(agentCrt)); var("siteCaCrt", PEMUtil.saveCertificate(siteCrt)); var("caCrt", PEMUtil.saveCertificate(rootCrt)); encode("agent/signed-agent"); } @Post("/revoke") @WithDataAdapter(BergamotDB.class) public void revokeAgent(BergamotDB db, @GetBergamotSite() Site site, @Param("id") @IsaObjectId() UUID agentId) throws Exception { // lookup the agent registration AgentRegistration agent = db.getAgentRegistration(agentId); if (agent != null) { agent.setRevoked(true); agent.setRevokedOn(new Timestamp(System.currentTimeMillis())); db.setAgentRegistration(agent); // TODO: we should publish a CRL to the agent workers and force a disconnect } // encode the index redirect(path("/agent/")); } public static String padCert(String cert) { StringBuilder sb = new StringBuilder("\r\n"); for (String s : cert.split("\n")) { sb.append(" ").append(s).append("\n"); } //sb.append("\n"); return sb.toString(); } }