summaryrefslogtreecommitdiffstats
path: root/base/server/cmscore/src/com/netscape/cmscore
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:58:58 -0600
commit6f971cb664dc639398ae3c9c83f8c152883c365d (patch)
treec42f26737a92c07f094fac87dc23ff96cf870c45 /base/server/cmscore/src/com/netscape/cmscore
parent175805ab7e115bc9b031af9b4f23d2520d33275a (diff)
downloadpki-6f971cb664dc639398ae3c9c83f8c152883c365d.tar.gz
pki-6f971cb664dc639398ae3c9c83f8c152883c365d.tar.xz
pki-6f971cb664dc639398ae3c9c83f8c152883c365d.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)
Diffstat (limited to 'base/server/cmscore/src/com/netscape/cmscore')
-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
2 files changed, 172 insertions, 19 deletions
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()));