package com.intrbiz.bergamot.agent.manager;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.cert.Certificate;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import com.intrbiz.Util;
import com.intrbiz.bergamot.agent.manager.config.BergamotAgentManagerCfg;
import com.intrbiz.bergamot.agent.manager.signer.CertificateManager;
import com.intrbiz.bergamot.agent.manager.store.BergamotKeyStore;
import com.intrbiz.bergamot.crypto.util.CertificatePair;
import com.intrbiz.bergamot.crypto.util.PEMUtil;
import com.intrbiz.bergamot.health.HealthAgent;
import com.intrbiz.bergamot.model.message.agent.manager.AgentManagerRequest;
import com.intrbiz.bergamot.model.message.agent.manager.AgentManagerResponse;
import com.intrbiz.bergamot.model.message.agent.manager.request.CreateSiteCA;
import com.intrbiz.bergamot.model.message.agent.manager.request.GetAgent;
import com.intrbiz.bergamot.model.message.agent.manager.request.GetRootCA;
import com.intrbiz.bergamot.model.message.agent.manager.request.GetServer;
import com.intrbiz.bergamot.model.message.agent.manager.request.GetSiteCA;
import com.intrbiz.bergamot.model.message.agent.manager.request.SignAgent;
import com.intrbiz.bergamot.model.message.agent.manager.request.SignServer;
import com.intrbiz.bergamot.model.message.agent.manager.request.SignTemplate;
import com.intrbiz.bergamot.model.message.agent.manager.response.AgentManagerError;
import com.intrbiz.bergamot.model.message.agent.manager.response.CreatedSiteCA;
import com.intrbiz.bergamot.model.message.agent.manager.response.GotAgent;
import com.intrbiz.bergamot.model.message.agent.manager.response.GotRootCA;
import com.intrbiz.bergamot.model.message.agent.manager.response.GotServer;
import com.intrbiz.bergamot.model.message.agent.manager.response.GotSiteCA;
import com.intrbiz.bergamot.model.message.agent.manager.response.SignedAgent;
import com.intrbiz.bergamot.model.message.agent.manager.response.SignedServer;
import com.intrbiz.bergamot.model.message.agent.manager.response.SignedTemplate;
import com.intrbiz.bergamot.queue.BergamotAgentManagerQueue;
import com.intrbiz.configuration.Configurable;
import com.intrbiz.configuration.Configuration;
import com.intrbiz.queue.QueueManager;
import com.intrbiz.queue.RPCHandler;
import com.intrbiz.queue.RPCServer;
import com.intrbiz.queue.rabbit.RabbitPool;
/**
* The Bergamot Agent Manager is a dedicated services which managed the handling of TLS certificates and keys for Bergamot Agents.
*
* This allows the Bergamot Agent Manager to be run on a dedicated, highly secured machine which only has outbound access to RabbitMQ, this isolation is designed to keep the CA private keys as safe as possible without requiring the use of a HSM.
*
*/
public class BergamotAgentManager implements Configurable<BergamotAgentManagerCfg>, RPCHandler<AgentManagerRequest, AgentManagerResponse>
{
private Logger logger = Logger.getLogger(BergamotAgentManager.class);
private BergamotAgentManagerCfg cfg;
private BergamotKeyStore keyStore;
private CertificateManager certificateManager;
private BergamotAgentManagerQueue queue;
private RPCServer<AgentManagerRequest, AgentManagerResponse> server;
public BergamotAgentManager()
{
super();
}
@Override
public void configure(BergamotAgentManagerCfg cfg) throws Exception
{
this.cfg = cfg;
// setup the key store
this.keyStore = (BergamotKeyStore) cfg.getKeyStore().create();
// setup the certificate manager
this.certificateManager = new CertificateManager(this.keyStore, this.cfg.getCertName());
}
@Override
public BergamotAgentManagerCfg getConfiguration()
{
return this.cfg;
}
public void start()
{
// ensure we have a root cert
this.certificateManager.generateRootCA();
// verify the keystore
this.keyStore.check();
// setup the RPC server
this.queue = BergamotAgentManagerQueue.open();
this.server = this.queue.createBergamotAgentManagerRPCServer(this);
// start the health agent
HealthAgent.getInstance().init("agent-manager", "bergamot-agent-manager");
// whoo all up
logger.info("Bergamot Agent Manager started!");
}
@Override
public AgentManagerResponse handleDevliery(AgentManagerRequest event) throws IOException
{
try
{
if (event instanceof GetRootCA)
{
return new GotRootCA(this.keyStore.loadRootCA().getCertificateAsPEM());
}
else if (event instanceof GetSiteCA)
{
return new GotSiteCA(this.keyStore.loadSiteCA(((GetSiteCA) event).getSiteId()).getCertificateAsPEM());
}
else if (event instanceof GetAgent)
{
GetAgent agent = (GetAgent) event;
return new GotAgent(this.keyStore.loadAgent(agent.getSiteId(), agent.getId()).getCertificateAsPEM());
}
else if (event instanceof GetServer)
{
GetServer server = (GetServer) event;
// do we have the server
if (this.keyStore.hasServer(server.getCommonName()))
{
// load the server key
CertificatePair serverPair = this.keyStore.loadServer(server.getCommonName());
GotServer resp = new GotServer(serverPair.getCertificateAsPEM());
// do we want the key?
if (server.isIncludeKey() && serverPair.getKey() != null)
resp.setKeyPEM(serverPair.getKeyAsPEM());
// do we want the CA cert
if (server.isIncludeRoot())
resp.setRootCertificatePEM(this.keyStore.loadRootCA().getCertificateAsPEM());
return resp;
}
else if (server.isGenerate())
{
// generate the certificate
CertificatePair serverPair = this.certificateManager.signServer(server.getCommonName(), null);
GotServer resp = new GotServer();
resp.setCertificatePEM(serverPair.getCertificateAsPEM());
// do we want the key?
if (server.isIncludeKey() && serverPair.getKey() != null)
resp.setKeyPEM(serverPair.getKeyAsPEM());
// do we want the CA cert
if (server.isIncludeRoot())
resp.setRootCertificatePEM(this.keyStore.loadRootCA().getCertificateAsPEM());
return resp;
}
else
{
throw new RuntimeException("Failed to load certificate for server: " + server.getCommonName());
}
}
else if (event instanceof CreateSiteCA)
{
CreateSiteCA createSite = (CreateSiteCA) event;
if (createSite.getSiteId() == null || Util.isEmpty(createSite.getSiteName())) return new AgentManagerError("Invalid request");
// create a site CA
Certificate cert = this.certificateManager.generateSiteCA(createSite.getSiteId(), createSite.getSiteName());
// respond
return new CreatedSiteCA(PEMUtil.saveCertificate(cert));
}
else if (event instanceof SignAgent)
{
SignAgent sign = (SignAgent) event;
// sign the agent
Certificate cert = this.certificateManager.signAgent(sign.getSiteId(), sign.getId(), sign.getCommonName(), PEMUtil.loadPublicKey(sign.getPublicKeyPEM()));
// respond
return new SignedAgent(PEMUtil.saveCertificate(cert));
}
else if (event instanceof SignServer)
{
SignServer sign = (SignServer) event;
// sign the server
CertificatePair cert = this.certificateManager.signServer(sign.getCommonName(), PEMUtil.loadPublicKey(sign.getPublicKeyPEM()));
// respond
return new SignedServer(cert.getCertificateAsPEM());
}
else if (event instanceof SignTemplate)
{
SignTemplate sign = (SignTemplate) event;
// sign the agent
Certificate cert = this.certificateManager.signTemplate(sign.getSiteId(), sign.getId(), sign.getTemplateName(), PEMUtil.loadPublicKey(sign.getPublicKeyPEM()));
// respond
return new SignedTemplate(PEMUtil.saveCertificate(cert));
}
}
catch (Exception e)
{
this.logger.error("Error processing manager request", e);
// we don't provide error messages to the client for security reasons!
return new AgentManagerError("Request failed");
}
return new AgentManagerError("Request not supported");
}
public void shutdown()
{
this.server.close();
this.queue.close();
}
public static void main(String[] args) throws Exception
{
// setup logging
BasicConfigurator.configure();
Logger.getRootLogger().setLevel(Level.INFO);
// load the config
File configFile = new File(Util.coalesceEmpty(System.getProperty("bergamot.config"), System.getenv("bergamot_config"), System.getenv("BERGAMOT_CONFIG"), "/etc/bergamot/agent-manager.xml"));
Logger.getLogger(BergamotAgentManager.class).info("Reading configuration file " + configFile.getAbsolutePath());
BergamotAgentManagerCfg config = Configuration.read(BergamotAgentManagerCfg.class, new FileInputStream(configFile));
config.applyDefaults();
// setup the queue broker
QueueManager.getInstance().registerDefaultBroker(new RabbitPool(config.getBroker().getUrl(), config.getBroker().getUsername(), config.getBroker().getPassword()));
// create the manager
BergamotAgentManager manager = new BergamotAgentManager();
manager.configure(config);
// start
manager.start();
}
}