summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristina Fu <cfu@redhat.com>2015-09-30 13:55:05 +0200
committerMatthew Harmsen <mharmsen@redhat.com>2015-10-01 14:22:38 -0600
commited98129b58b5b13031331fb88eb14d7c33474a59 (patch)
tree7bf8646833eba4cf7be8293b449ca75b89b96909
parentb67a17f29a5a5312847c1188607a7fa7b33e034f (diff)
downloadpki-ed98129b58b5b13031331fb88eb14d7c33474a59.tar.gz
pki-ed98129b58b5b13031331fb88eb14d7c33474a59.tar.xz
pki-ed98129b58b5b13031331fb88eb14d7c33474a59.zip
Ticket #1593 auto-shutdown - for HSM failover support
This is an interim solution for supporting HSM failover by automatically shutting down the server when signing key becomes inaccessible. At auto-shutdown, a crumb fiile will be left in the instance directory for an external daemon to detect and restart, if necessary. Due to limitation of the watch dog (nuxwdog) at present time, the restart option currently only works if started with watch dog (nuxwdog), and it will prompt for passwords on the terminals. The restart counter is to prevent the server from going into an infinite restart loop. Administrator will have to reset autoShutdown.restart.count to 0 when max is reached. (cherry picked from commit 5a9ecad9172f76ca1b94b40aedcdd49d009aceb1)
-rw-r--r--base/ca/shared/conf/CS.cfg.in5
-rw-r--r--base/ca/src/com/netscape/ca/SigningUnit.java13
-rw-r--r--base/common/src/com/netscape/certsrv/apps/CMS.java9
-rw-r--r--base/common/src/com/netscape/certsrv/apps/ICMSEngine.java7
-rw-r--r--base/common/src/com/netscape/certsrv/kra/ProofOfArchival.java2
-rw-r--r--base/ocsp/shared/conf/CS.cfg.in5
-rw-r--r--base/ocsp/src/com/netscape/ocsp/SigningUnit.java2
-rw-r--r--base/server/cmscore/src/com/netscape/cmscore/apps/CMSEngine.java189
-rw-r--r--base/server/cmscore/src/com/netscape/cmscore/security/KeyCertUtil.java2
-rw-r--r--base/server/test/com/netscape/cmscore/app/CMSEngineDefaultStub.java6
10 files changed, 220 insertions, 20 deletions
diff --git a/base/ca/shared/conf/CS.cfg.in b/base/ca/shared/conf/CS.cfg.in
index 564ee3a6b..53e65de69 100644
--- a/base/ca/shared/conf/CS.cfg.in
+++ b/base/ca/shared/conf/CS.cfg.in
@@ -1133,6 +1133,11 @@ selftests.container.order.startup=CAPresence:critical, SystemCertsVerification:c
selftests.plugin.CAPresence.CaSubId=ca
selftests.plugin.CAValidity.CaSubId=ca
selftests.plugin.SystemCertsVerification.SubId=ca
+autoShutdown.allowed=false
+autoShutdown.crumbFile=[PKI_INSTANCE_PATH]/logs/autoShutdown.crumb
+autoShutdown.restart.count=0
+autoShutdown.restart.enable=false
+autoShutdown.restart.max=3
smtp.host=localhost
smtp.port=25
subsystem.0.class=com.netscape.ca.CertificateAuthority
diff --git a/base/ca/src/com/netscape/ca/SigningUnit.java b/base/ca/src/com/netscape/ca/SigningUnit.java
index 2466fb652..3946fdef3 100644
--- a/base/ca/src/com/netscape/ca/SigningUnit.java
+++ b/base/ca/src/com/netscape/ca/SigningUnit.java
@@ -272,6 +272,16 @@ public final class SigningUnit implements ISigningUnit {
signer.initSign(mPrivk);
signer.update(data);
+
+ /* debugging
+ boolean testAutoShutdown = false;
+ testAutoShutdown = mConfig.getBoolean("autoShutdown.test", false);
+ if (testAutoShutdown) {
+ CMS.debug("SigningUnit.sign: test auto shutdown");
+ CMS.checkForAndAutoShutdown();
+ }
+ */
+
// XXX add something more descriptive.
CMS.debug("Signing Certificate");
return signer.sign();
@@ -289,6 +299,8 @@ public final class SigningUnit implements ISigningUnit {
throw new EBaseException(e.toString());
} catch (SignatureException e) {
log(ILogger.LL_FAILURE, CMS.getLogMessage("OPERATION_ERROR", e.toString()));
+ CMS.debug("SigningUnit.sign: " + e.toString());
+ CMS.checkForAndAutoShutdown();
// XXX fix this exception later.
throw new EBaseException(e.toString());
}
@@ -328,6 +340,7 @@ public final class SigningUnit implements ISigningUnit {
throw new EBaseException(e.toString());
} catch (SignatureException e) {
log(ILogger.LL_FAILURE, CMS.getLogMessage("OPERATION_ERROR", e.toString()));
+ CMS.checkForAndAutoShutdown();
// XXX fix this exception later.
throw new EBaseException(e.toString());
}
diff --git a/base/common/src/com/netscape/certsrv/apps/CMS.java b/base/common/src/com/netscape/certsrv/apps/CMS.java
index 3ba6d7577..187b1028a 100644
--- a/base/common/src/com/netscape/certsrv/apps/CMS.java
+++ b/base/common/src/com/netscape/certsrv/apps/CMS.java
@@ -237,12 +237,19 @@ public final class CMS {
* Shuts down subsystems in backwards order
* exceptions are ignored. process exists at end to force exit.
*/
-
public static void forceShutdown() {
_engine.forceShutdown();
}
+ public static void autoShutdown() {
+ _engine.autoShutdown();
+ }
+
+ public static void checkForAndAutoShutdown() {
+ _engine.checkForAndAutoShutdown();
+ }
+
/**
* mode = 0 (pre-operational)
* mode = 1 (running)
diff --git a/base/common/src/com/netscape/certsrv/apps/ICMSEngine.java b/base/common/src/com/netscape/certsrv/apps/ICMSEngine.java
index 57c2b6cb0..bf1d3ff61 100644
--- a/base/common/src/com/netscape/certsrv/apps/ICMSEngine.java
+++ b/base/common/src/com/netscape/certsrv/apps/ICMSEngine.java
@@ -1131,6 +1131,13 @@ public interface ICMSEngine extends ISubsystem {
*/
public void forceShutdown();
+ /**
+ * graceful shutdown, same as forceShutdown, but allowing
+ * option to restart
+ */
+ public void autoShutdown();
+ public void checkForAndAutoShutdown();
+
public IPasswordStore getPasswordStore() throws EBaseException;
public ISecurityDomainSessionTable getSecurityDomainSessionTable();
diff --git a/base/common/src/com/netscape/certsrv/kra/ProofOfArchival.java b/base/common/src/com/netscape/certsrv/kra/ProofOfArchival.java
index 7774ca400..3146240e7 100644
--- a/base/common/src/com/netscape/certsrv/kra/ProofOfArchival.java
+++ b/base/common/src/com/netscape/certsrv/kra/ProofOfArchival.java
@@ -341,6 +341,8 @@ public class ProofOfArchival implements IDBObj, IProofOfArchival, Serializable {
} catch (InvalidKeyException e) {
throw new EKRAException(CMS.getUserMessage("CMS_KRA_POA_ENCODE_FAILED_1", e.toString()));
} catch (SignatureException e) {
+ CMS.debug("ProofOfArchival.encodeAndSign: " + e.toString());
+ CMS.checkForAndAutoShutdown();
throw new EKRAException(CMS.getUserMessage("CMS_KRA_POA_ENCODE_FAILED_1", e.toString()));
} catch (IOException e) {
throw new EKRAException(CMS.getUserMessage("CMS_KRA_POA_ENCODE_FAILED_1", e.toString()));
diff --git a/base/ocsp/shared/conf/CS.cfg.in b/base/ocsp/shared/conf/CS.cfg.in
index 0cbe20bed..cea8e6512 100644
--- a/base/ocsp/shared/conf/CS.cfg.in
+++ b/base/ocsp/shared/conf/CS.cfg.in
@@ -321,6 +321,11 @@ selftests.container.order.startup=OCSPPresence:critical, SystemCertsVerification
selftests.plugin.OCSPPresence.OcspSubId=ocsp
selftests.plugin.OCSPValidity.OcspSubId=ocsp
selftests.plugin.SystemCertsVerification.SubId=ocsp
+autoShutdown.allowed=false
+autoShutdown.crumbFile=[PKI_INSTANCE_PATH]/logs/autoShutdown.crumb
+autoShutdown.restart.count=0
+autoShutdown.restart.enable=false
+autoShutdown.restart.max=3
smtp.host=localhost
smtp.port=25
subsystem.0.class=com.netscape.ocsp.OCSPAuthority
diff --git a/base/ocsp/src/com/netscape/ocsp/SigningUnit.java b/base/ocsp/src/com/netscape/ocsp/SigningUnit.java
index 0413f0c5c..5aff29144 100644
--- a/base/ocsp/src/com/netscape/ocsp/SigningUnit.java
+++ b/base/ocsp/src/com/netscape/ocsp/SigningUnit.java
@@ -275,6 +275,7 @@ public final class SigningUnit implements ISigningUnit {
throw new EOCSPException(CMS.getUserMessage("CMS_BASE_INTERNAL_ERROR", e.toString()));
} catch (SignatureException e) {
log(ILogger.LL_FAILURE, CMS.getLogMessage("OPERATION_ERROR", e.toString()));
+ CMS.checkForAndAutoShutdown();
throw new EOCSPException(CMS.getUserMessage("CMS_BASE_INTERNAL_ERROR", e.toString()));
}
}
@@ -310,6 +311,7 @@ public final class SigningUnit implements ISigningUnit {
throw new EOCSPException(CMS.getUserMessage("CMS_BASE_INTERNAL_ERROR", e.toString()));
} catch (SignatureException e) {
log(ILogger.LL_FAILURE, CMS.getLogMessage("OPERATION_ERROR", e.toString()));
+ CMS.checkForAndAutoShutdown();
throw new EOCSPException(CMS.getUserMessage("CMS_BASE_INTERNAL_ERROR", e.toString()));
}
}
diff --git a/base/server/cmscore/src/com/netscape/cmscore/apps/CMSEngine.java b/base/server/cmscore/src/com/netscape/cmscore/apps/CMSEngine.java
index 467836bfc..2452a417f 100644
--- a/base/server/cmscore/src/com/netscape/cmscore/apps/CMSEngine.java
+++ b/base/server/cmscore/src/com/netscape/cmscore/apps/CMSEngine.java
@@ -24,6 +24,8 @@ import java.io.FileReader;
import java.io.IOException;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509CRL;
@@ -60,8 +62,14 @@ import netscape.security.x509.X509CertInfo;
import org.apache.commons.lang.StringUtils;
import org.apache.xerces.parsers.DOMParser;
+import org.mozilla.jss.CryptoManager;
import org.mozilla.jss.CryptoManager.CertificateUsage;
import org.mozilla.jss.util.PasswordCallback;
+import org.mozilla.jss.crypto.PrivateKey;
+import org.mozilla.jss.crypto.Signature;
+import org.mozilla.jss.crypto.SignatureAlgorithm;
+import org.mozilla.jss.crypto.CryptoToken;
+
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
@@ -177,6 +185,7 @@ import com.netscape.cmsutil.net.ISocketFactory;
import com.netscape.cmsutil.password.IPasswordStore;
import com.netscape.cmsutil.password.NuxwdogPasswordStore;
import com.netscape.cmsutil.util.Utils;
+import com.netscape.cmsutil.util.Cert;
public class CMSEngine implements ICMSEngine {
private static final String ID = "MAIN";
@@ -187,13 +196,27 @@ public class CMSEngine implements ICMSEngine {
private static final String PROP_ENABLED = "enabled";
private static final String SERVER_XML = "server.xml";
+ // used for testing HSM issues
+ public static final String PROP_SIGNED_AUDIT_CERT_NICKNAME =
+ "log.instance.SignedAudit.signedAuditCertNickname";
+
public static final SubsystemRegistry mSSReg = SubsystemRegistry.getInstance();
public String instanceDir; /* path to instance <server-root>/cert-<instance-name> */
private String instanceId;
private int pid;
+ private CryptoManager mManager = null;
+
private IConfigStore mConfig = null;
+ // AutoSD : AutoShutdown
+ private String mAutoSD_CrumbFile = null;
+ private boolean mAutoSD_Restart = false;
+ private int mAutoSD_RestartMax = 3;
+ private int mAutoSD_RestartCount = 0;
+ private String mSAuditCertNickName = null;
+ private PrivateKey mSigningKey = null;
+ private byte[] mSigningData = null;
@SuppressWarnings("unused")
private ISubsystem mOwner;
private long mStartupTime = 0;
@@ -1142,6 +1165,58 @@ public class CMSEngine implements ICMSEngine {
}
CMS.debug("CMSEngine: ready to init id=" + id);
ss.init(this, ssConfig);
+
+ try {
+ /*
+ * autoShutdown.allowed=false
+ * autoShutdown.crumbFile=[PKI_INSTANCE_PATH]/logs/autoShutdown.crumb
+ * autoShutdown.restart.enable=false
+ * autoShutdown.restart.max=3
+ * autoShutdown.restart.count=0
+ */
+
+ mAutoSD_Restart = mConfig.getBoolean("autoShutdown.restart.enable", false);
+ CMS.debug("CMSEngine: restart at autoShutdown? " + mAutoSD_Restart);
+ if (mAutoSD_Restart) {
+ mAutoSD_RestartMax = mConfig.getInteger("autoShutdown.restart.max", 3);
+ CMS.debug("CMSEngine: restart max? " + mAutoSD_RestartMax);
+ mAutoSD_RestartCount = mConfig.getInteger("autoShutdown.restart.count", 0);
+ CMS.debug("CMSEngine: current restart count? " + mAutoSD_RestartCount);
+ } else { //!mAutoSD_Restart
+ mAutoSD_CrumbFile = mConfig.getString("autoShutdown.crumbFile",
+ instanceDir + "/logs/autoShutdown.crumb");
+ CMS.debug("CMSEngine: autoShutdown crumb file path? " + mAutoSD_CrumbFile);
+ File crumb = new File(mAutoSD_CrumbFile);
+ try {
+ if (crumb.exists()) {
+ CMS.debug("CMSEngine: delete autoShutdown crumb file");
+ crumb.delete();
+ }
+ } catch (Exception e) {
+ CMS.debug("CMSEngine: delete autoShutdown crumb file failed; continue: " + e.toString());
+ e.printStackTrace();
+ }
+ }
+
+ /*
+ * establish signing key reference using audit signing cert
+ * for HSM failover detection
+ */
+ mSAuditCertNickName = mConfig.getString(PROP_SIGNED_AUDIT_CERT_NICKNAME);
+ mManager = CryptoManager.getInstance();
+ org.mozilla.jss.crypto.X509Certificate cert = mManager.findCertByNickname(mSAuditCertNickName);
+ if (cert != null) {
+ CMS.debug("CMSEngine: found cert:" + mSAuditCertNickName);
+ } else {
+ CMS.debug("CMSEngine: cert not found:" + mSAuditCertNickName);
+ }
+ mSigningKey = mManager.findPrivKeyByCert(cert);
+ mSigningData = cert.getPublicKey().getEncoded();
+
+ } catch (Exception e) {
+ CMS.debug("CMSEngine: " + e.toString());
+ }
+
// add to id - subsystem hash table.
CMS.debug("CMSEngine: done init id=" + id);
CMS.debug("CMSEngine: initialized " + id);
@@ -1168,6 +1243,39 @@ public class CMSEngine implements ICMSEngine {
}
}
+ /**
+ * sign some known data to determine if signing key is botched;
+ * if so, proceed to graceful shutdown
+ */
+ public void checkForAndAutoShutdown() {
+ String method= "CMSEngine: checkForAndAutoShutdown: ";
+ CMS.debug(method + "begins");
+ try {
+ boolean allowShutdown = mConfig.getBoolean("autoShutdown.allowed", false);
+ if ((!allowShutdown) || (mSigningKey == null) ||
+ (mSigningData == null)) {
+ CMS.debug(method + "autoShutdown not allowed");
+ return;
+ }
+ CMS.debug(method + "autoShutdown allowed");
+ CryptoToken token =
+ ((org.mozilla.jss.pkcs11.PK11PrivKey) mSigningKey).getOwningToken();
+ SignatureAlgorithm signAlg = Cert.mapAlgorithmToJss("SHA256withRSA");
+ Signature signer = token.getSignatureContext(signAlg);
+
+ signer.initSign(mSigningKey);
+ signer.update(mSigningData);
+ byte[] result = signer.sign();
+ CMS.debug(method + " signining successful: " + new String(result));
+ } catch (SignatureException e) {
+ CMS.debug(method + "autoShutdown for " + e.toString());
+ CMS.autoShutdown();
+ } catch (Exception e) {
+ CMS.debug(method + "continue for " + e.toString());
+ }
+ CMS.debug(method + "passed; continue");
+ }
+
public void reinit(String id) throws EBaseException {
ISubsystem system = getSubsystem(id);
IConfigStore cs = mConfig.getSubStore(id);
@@ -1745,6 +1853,8 @@ public class CMSEngine implements ICMSEngine {
}
public boolean areRequestsDisabled() {
+ CMS.debug("CMSEngine: in areRequestsDisabled");
+ System.out.println("CMSEngine: in areRequestsDisabled");
return CommandQueue.mShuttingDown;
}
@@ -1767,23 +1877,21 @@ public class CMSEngine implements ICMSEngine {
return (File.separator.equals("\\"));
}
- private void shutdownHttpServer() {
-
+ private void shutdownHttpServer(boolean restart) {
try {
String cmds[] = null;
- String cmd = "stop-cert";
- if (isNT()) {
- // NT
- cmds = new String[3];
- cmds[0] = "cmd";
- cmds[1] = "/c";
- cmds[2] = instanceDir + "\\" + cmd;
+ String cmd = "stop";
+ if (restart) {
+ cmd = "restart";
+ }
+
+ cmds = new String[3];
+ cmds[0] = "/usr/bin/systemctl";
+ cmds[1] = cmd;
+ if (startedByNuxwdog()) {
+ cmds[2] = "pki-tomcatd-nuxwdog@" + instanceId + ".service";
} else {
- // UNIX
- cmds = new String[3];
- cmds[0] = "/bin/sh";
- cmds[1] = "-c";
- cmds[2] = instanceDir + "/" + cmd;
+ cmds[2] = "pki-tomcatd@" + instanceId + ".service";
}
Process process = Runtime.getRuntime().exec(cmds);
@@ -1848,14 +1956,52 @@ public class CMSEngine implements ICMSEngine {
* exceptions are ignored. process exists at end to force exit.
* Added extra call to shutdown the web server.
*/
-
public void forceShutdown() {
+ CMS.debug("CMSEngine.forceShutdown()...begins graceful shutdown.");
+ autoShutdown(false /*no restart*/);
+ }
+ public void autoShutdown() {
+ autoShutdown(mAutoSD_Restart /* controlled by config */);
+ }
+
+ public void autoShutdown(boolean restart) {
+ String method = "CMSEngine.autoShutdown(): ";
Logger.getLogger().log(ILogger.EV_SYSTEM, ILogger.S_ADMIN,
ILogger.LL_INFO, Constants.SERVER_SHUTDOWN_MESSAGE);
+ CMS.debug(method + "...with restart=" + restart);
+
+ // update restart tracker so we don't go into infinite restart loop
+ if (restart == true) {
+ CMS.debug(method + "...checking autoShutdown.restart trackers");
+ if (mAutoSD_RestartCount >= mAutoSD_RestartMax) {
+ mAutoSD_Restart = false;
+ mConfig.putBoolean("autoShutdown.restart.enable", mAutoSD_Restart);
+ CMS.debug(method + "...autoShutdown.restart.max reached, disabled autoShutdown.restart.enable");
+ } else {
+ mAutoSD_RestartCount++;
+ mConfig.putInteger("autoShutdown.restart.count", mAutoSD_RestartCount);
+ CMS.debug(method + "...autoShutdown.restart.max not reached, increment autoShutdown.restart.count");
+ }
+ try {
+ mConfig.commit(false);
+ } catch (EBaseException e) {
+ }
+ } else {
+ // leave a crumb file to be monitored by external monitor
+ File crumb = new File(mAutoSD_CrumbFile);
+ try {
+ crumb.createNewFile();
+ } catch (IOException e) {
+ CMS.debug(method + " create autoShutdown crumb file failed on " +
+ mAutoSD_CrumbFile + "; nothing to do...keep shutting down:" + e.toString());
+ e.printStackTrace();
+ }
+ }
- CMS.debug("CMSEngine.forceShutdown()");
-
+/* cfu: not sure why it's doing a commandQueue but not registering any
+ * service to wait on... what does this do to wait on an empty queue?
+ *
CommandQueue commandQueue = new CommandQueue();
Thread t1 = new Thread(commandQueue);
@@ -1875,12 +2021,17 @@ public class CMSEngine implements ICMSEngine {
}
timeOut = time.getTime();
}
- terminateRequests();
+*/
+ if (areRequestsDisabled() == false) {
+ disableRequests();
+ }
+ terminateRequests();
shutdownSubsystems(mFinalSubsystems);
shutdownSubsystems(mDynSubsystems);
shutdownSubsystems(mStaticSubsystems);
- shutdownHttpServer();
+
+ shutdownHttpServer(restart);
}
diff --git a/base/server/cmscore/src/com/netscape/cmscore/security/KeyCertUtil.java b/base/server/cmscore/src/com/netscape/cmscore/security/KeyCertUtil.java
index b91b4cf93..989be3f05 100644
--- a/base/server/cmscore/src/com/netscape/cmscore/security/KeyCertUtil.java
+++ b/base/server/cmscore/src/com/netscape/cmscore/security/KeyCertUtil.java
@@ -268,6 +268,8 @@ public class KeyCertUtil {
} catch (TokenException e) {
throw new EBaseException(CMS.getUserMessage("CMS_BASE_TOKEN_ERROR_1", e.toString()));
} catch (SignatureException e) {
+ CMS.debug("CertKeyUtil.signCert: "+ e.toString());
+ CMS.checkForAndAutoShutdown();
throw new EBaseException(CMS.getUserMessage("CMS_BASE_SIGNED_FAILED", e.toString()));
} catch (InvalidKeyException e) {
throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_KEY_1", e.toString()));
diff --git a/base/server/test/com/netscape/cmscore/app/CMSEngineDefaultStub.java b/base/server/test/com/netscape/cmscore/app/CMSEngineDefaultStub.java
index 404832c0d..36a263e92 100644
--- a/base/server/test/com/netscape/cmscore/app/CMSEngineDefaultStub.java
+++ b/base/server/test/com/netscape/cmscore/app/CMSEngineDefaultStub.java
@@ -545,6 +545,12 @@ public class CMSEngineDefaultStub implements ICMSEngine {
public void forceShutdown() {
}
+ public void autoShutdown() {
+ }
+
+ public void checkForAndAutoShutdown() {
+ }
+
public IPasswordStore getPasswordStore() {
return null;
}