Author: rgodfrey
Date: Wed Feb 24 13:30:55 2016
New Revision: 1732154
URL: http://svn.apache.org/viewvc?rev=1732154&view=rev
Log:
QPID-6995 : Keystores should log when then contain certificates which will soon
be expiring
Added:
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/BrokerPrincipal.java
- copied, changed from r1731900,
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostPrincipal.java
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/util/HousekeepingExecutor.java
(with props)
Modified:
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/messages/KeyStoreMessages.java
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/messages/KeyStore_logmessages.properties
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/Broker.java
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/KeyStore.java
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStoreImpl.java
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStoreImpl.java
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java
qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/security/NonJavaKeyStoreTest.java
Copied:
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/BrokerPrincipal.java
(from r1731900,
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostPrincipal.java)
URL:
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/BrokerPrincipal.java?p2=qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/BrokerPrincipal.java&p1=qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostPrincipal.java&r1=1731900&r2=1732154&rev=1732154&view=diff
==============================================================================
---
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostPrincipal.java
(original)
+++
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/BrokerPrincipal.java
Wed Feb 24 13:30:55 2016
@@ -18,21 +18,21 @@
* under the License.
*
*/
-package org.apache.qpid.server.virtualhost;
+package org.apache.qpid.server;
import java.security.Principal;
-import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.model.Broker;
-public class VirtualHostPrincipal implements Principal
+public class BrokerPrincipal implements Principal
{
- private final VirtualHost<?> _virtualHost;
+ private final Broker<?> _broker;
private final String _name;
- public VirtualHostPrincipal(VirtualHost<?> virtualHost)
+ public BrokerPrincipal(Broker<?> broker)
{
- _virtualHost = virtualHost;
- _name = "virtualhost:" + virtualHost.getName() + "-" +
virtualHost.getId();
+ _broker = broker;
+ _name = "broker:" + broker.getName() + "-" + broker.getId();
}
@Override
@@ -53,13 +53,13 @@ public class VirtualHostPrincipal implem
return false;
}
- VirtualHostPrincipal that = (VirtualHostPrincipal) o;
- return _virtualHost.equals(that._virtualHost);
+ BrokerPrincipal that = (BrokerPrincipal) o;
+ return _broker.equals(that._broker);
}
@Override
public int hashCode()
{
- return _virtualHost.hashCode();
+ return _broker.hashCode();
}
}
Modified:
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/messages/KeyStoreMessages.java
URL:
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/messages/KeyStoreMessages.java?rev=1732154&r1=1732153&r2=1732154&view=diff
==============================================================================
---
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/messages/KeyStoreMessages.java
(original)
+++
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/messages/KeyStoreMessages.java
Wed Feb 24 13:30:55 2016
@@ -46,6 +46,7 @@ public class KeyStoreMessages
public static final String KEYSTORE_LOG_HIERARCHY =
DEFAULT_LOG_HIERARCHY_PREFIX + "keystore";
public static final String OPEN_LOG_HIERARCHY =
DEFAULT_LOG_HIERARCHY_PREFIX + "keystore.open";
+ public static final String EXPIRING_LOG_HIERARCHY =
DEFAULT_LOG_HIERARCHY_PREFIX + "keystore.expiring";
public static final String CREATE_LOG_HIERARCHY =
DEFAULT_LOG_HIERARCHY_PREFIX + "keystore.create";
public static final String DELETE_LOG_HIERARCHY =
DEFAULT_LOG_HIERARCHY_PREFIX + "keystore.delete";
public static final String CLOSE_LOG_HIERARCHY =
DEFAULT_LOG_HIERARCHY_PREFIX + "keystore.close";
@@ -54,6 +55,7 @@ public class KeyStoreMessages
{
LoggerFactory.getLogger(KEYSTORE_LOG_HIERARCHY);
LoggerFactory.getLogger(OPEN_LOG_HIERARCHY);
+ LoggerFactory.getLogger(EXPIRING_LOG_HIERARCHY);
LoggerFactory.getLogger(CREATE_LOG_HIERARCHY);
LoggerFactory.getLogger(DELETE_LOG_HIERARCHY);
LoggerFactory.getLogger(CLOSE_LOG_HIERARCHY);
@@ -87,6 +89,64 @@ public class KeyStoreMessages
}
@Override
+ public boolean equals(final Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass())
+ {
+ return false;
+ }
+
+ final LogMessage that = (LogMessage) o;
+
+ return getLogHierarchy().equals(that.getLogHierarchy()) &&
toString().equals(that.toString());
+
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = toString().hashCode();
+ result = 31 * result + getLogHierarchy().hashCode();
+ return result;
+ }
+ };
+ }
+
+ /**
+ * Log a KeyStore message of the Format:
+ * <pre>KST-1005 : KeyStore {0} Certificate expires in {1} days : {2}</pre>
+ * Optional values are contained in [square brackets] and are numbered
+ * sequentially in the method call.
+ *
+ */
+ public static LogMessage EXPIRING(String param1, String param2, String
param3)
+ {
+ String rawMessage = _messages.getString("EXPIRING");
+
+ final Object[] messageArguments = {param1, param2, param3};
+ // Create a new MessageFormat to ensure thread safety.
+ // Sharing a MessageFormat and using applyPattern is not thread safe
+ MessageFormat formatter = new MessageFormat(rawMessage,
_currentLocale);
+
+ final String message = formatter.format(messageArguments);
+
+ return new LogMessage()
+ {
+ public String toString()
+ {
+ return message;
+ }
+
+ public String getLogHierarchy()
+ {
+ return EXPIRING_LOG_HIERARCHY;
+ }
+
+ @Override
public boolean equals(final Object o)
{
if (this == o)
Modified:
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/messages/KeyStore_logmessages.properties
URL:
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/messages/KeyStore_logmessages.properties?rev=1732154&r1=1732153&r2=1732154&view=diff
==============================================================================
---
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/messages/KeyStore_logmessages.properties
(original)
+++
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/messages/KeyStore_logmessages.properties
Wed Feb 24 13:30:55 2016
@@ -22,4 +22,5 @@ CREATE = KST-1001 : Create "{0}"
OPEN = KST-1002 : Open
CLOSE = KST-1003 : Close
DELETE = KST-1004 : Delete "{0}"
+EXPIRING = KST-1005 : KeyStore {0} Certificate expires in {1} days : {2}
Modified:
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/Broker.java
URL:
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/Broker.java?rev=1732154&r1=1732153&r2=1732154&view=diff
==============================================================================
---
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/Broker.java
(original)
+++
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/Broker.java
Wed Feb 24 13:30:55 2016
@@ -22,6 +22,8 @@ package org.apache.qpid.server.model;
import java.util.Collection;
import java.util.List;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
import org.apache.qpid.configuration.CommonProperties;
import org.apache.qpid.server.logging.EventLogger;
@@ -134,6 +136,15 @@ public interface Broker<X extends Broker
@ManagedAttribute( defaultValue = "false")
boolean getStatisticsReportingResetEnabled();
+
+ @ManagedContextDefault( name = "broker.housekeepingThreadCount")
+ public static final int DEFAULT_HOUSEKEEPING_THREAD_COUNT = 2;
+
+
+ @ManagedAttribute( defaultValue = "${broker.housekeepingThreadCount}")
+ int getHousekeepingThreadCount();
+
+
String BROKER_MESSAGE_COMPRESSION_ENABLED =
"broker.messageCompressionEnabled";
@ManagedContextDefault(name = BROKER_MESSAGE_COMPRESSION_ENABLED)
boolean DEFAULT_MESSAGE_COMPRESSION_ENABLED = true;
@@ -267,4 +278,9 @@ public interface Broker<X extends Broker
void assignTargetSizes();
int getNetworkBufferSize();
+
+ ScheduledFuture<?> scheduleHouseKeepingTask(long period, final TimeUnit
unit, Runnable task);
+
+ ScheduledFuture<?> scheduleTask(long delay, final TimeUnit unit, Runnable
task);
+
}
Modified:
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/KeyStore.java
URL:
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/KeyStore.java?rev=1732154&r1=1732153&r2=1732154&view=diff
==============================================================================
---
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/KeyStore.java
(original)
+++
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/KeyStore.java
Wed Feb 24 13:30:55 2016
@@ -26,5 +26,16 @@ import javax.net.ssl.KeyManager;
@ManagedObject( defaultType = "FileKeyStore" )
public interface KeyStore<X extends KeyStore<X>> extends ConfiguredObject<X>
{
- public KeyManager[] getKeyManagers() throws GeneralSecurityException;
+ String CERTIFICATE_EXPIRY_WARN_PERIOD =
"qpid.keystore.certificateExpiryWarnPeriod";
+
+ @ManagedContextDefault(name = CERTIFICATE_EXPIRY_WARN_PERIOD)
+ int DEFAULT_CERTIFICATE_EXPIRY_WARN_PERIOD = 30;
+
+ String CERTIFICATE_EXPIRY_CHECK_FREQUENCY =
"qpid.keystore.certificateExpiryCheckFrequency";
+
+ @ManagedContextDefault(name = CERTIFICATE_EXPIRY_CHECK_FREQUENCY)
+ int DEFAULT_CERTIFICATE_EXPIRY_CHECK_FREQUENCY = 1;
+
+
+ KeyManager[] getKeyManagers() throws GeneralSecurityException;
}
Modified:
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java
URL:
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java?rev=1732154&r1=1732153&r2=1732154&view=diff
==============================================================================
---
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java
(original)
+++
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java
Wed Feb 24 13:30:55 2016
@@ -44,6 +44,8 @@ import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -52,9 +54,11 @@ import javax.security.auth.Subject;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import org.apache.qpid.bytebuffer.QpidByteBuffer;
+import org.apache.qpid.server.BrokerPrincipal;
import org.apache.qpid.server.logging.QpidLoggerTurboFilter;
import org.apache.qpid.server.logging.StartupAppender;
import org.apache.qpid.server.security.access.Operation;
+import org.apache.qpid.server.util.HousekeepingExecutor;
import org.apache.qpid.server.util.ServerScopedRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -85,8 +89,10 @@ public class BrokerAdapter extends Abstr
private static final Pattern MODEL_VERSION_PATTERN =
Pattern.compile("^\\d+\\.\\d+$");
+ private static final int HOUSEKEEPING_SHUTDOWN_TIMEOUT = 5;
public static final String MANAGEMENT_MODE_AUTHENTICATION =
"MANAGEMENT_MODE_AUTHENTICATION";
+ private final BrokerPrincipal _principal;
private String[] POSITIVE_NUMERIC_ATTRIBUTES = {
CONNECTION_SESSION_COUNT_LIMIT,
CONNECTION_HEART_BEAT_DELAY, STATISTICS_REPORTING_PERIOD };
@@ -118,6 +124,8 @@ public class BrokerAdapter extends Abstr
private boolean _statisticsReportingResetEnabled;
@ManagedAttributeField
private boolean _messageCompressionEnabled;
+ @ManagedAttributeField
+ private int _housekeepingThreadCount;
@ManagedAttributeField(afterSet = "postEncrypterProviderSet")
private String _confidentialConfigurationEncryptionProvider;
@@ -129,6 +137,7 @@ public class BrokerAdapter extends Abstr
private final long _maximumDirectMemorySize = getMaxDirectMemorySize();
private final BufferPoolMXBean _bufferPoolMXBean;
private final List<String> _jvmArguments;
+ private HousekeepingExecutor _houseKeepingTaskExecutor;
@ManagedObjectFactoryConstructor
public BrokerAdapter(Map<String, Object> attributes,
@@ -138,6 +147,8 @@ public class BrokerAdapter extends Abstr
_parent = parent;
_eventLogger = parent.getEventLogger();
_securityManager = new SecurityManager(this,
parent.isManagementMode());
+ _principal = new BrokerPrincipal(this);
+
if (parent.isManagementMode())
{
Map<String,Object> authManagerAttrs = new HashMap<String,
Object>();
@@ -366,6 +377,11 @@ public class BrokerAdapter extends Abstr
initialiseStatisticsReporting();
+ _houseKeepingTaskExecutor = new HousekeepingExecutor("virtualhost-" +
getName() + "-pool",
+
getHousekeepingThreadCount(),
+ _principal);
+
+
if (isManagementMode())
{
_eventLogger.message(BrokerMessages.MANAGEMENT_MODE(BrokerOptions.MANAGEMENT_MODE_USER_NAME,
@@ -470,6 +486,12 @@ public class BrokerAdapter extends Abstr
}
@Override
+ public int getHousekeepingThreadCount()
+ {
+ return _housekeepingThreadCount;
+ }
+
+ @Override
public String getModelVersion()
{
return BrokerModel.MODEL_VERSION;
@@ -666,6 +688,8 @@ public class BrokerAdapter extends Abstr
_reportingTimer.cancel();
}
+ shutdownHouseKeeping();
+
_eventLogger.message(BrokerMessages.STOPPED());
try
@@ -1196,6 +1220,39 @@ public class BrokerAdapter extends Abstr
return dump.toString();
}
+ protected void shutdownHouseKeeping()
+ {
+ if(_houseKeepingTaskExecutor != null)
+ {
+ _houseKeepingTaskExecutor.shutdown();
+
+ try
+ {
+ if
(!_houseKeepingTaskExecutor.awaitTermination(HOUSEKEEPING_SHUTDOWN_TIMEOUT,
TimeUnit.SECONDS))
+ {
+ _houseKeepingTaskExecutor.shutdownNow();
+ }
+ }
+ catch (InterruptedException e)
+ {
+ LOGGER.warn("Interrupted during Housekeeping shutdown:", e);
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleHouseKeepingTask(long period, final
TimeUnit unit, Runnable task)
+ {
+ return _houseKeepingTaskExecutor.scheduleAtFixedRate(task, period / 2,
period, unit);
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleTask(long delay, final TimeUnit unit,
Runnable task)
+ {
+ return _houseKeepingTaskExecutor.schedule(task, delay, unit);
+ }
+
public static class ThreadStackContent implements Content,
CustomRestHeaders
{
private final String _threadStackTraces;
Modified:
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStoreImpl.java
URL:
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStoreImpl.java?rev=1732154&r1=1732153&r2=1732154&view=diff
==============================================================================
---
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStoreImpl.java
(original)
+++
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStoreImpl.java
Wed Feb 24 13:30:55 2016
@@ -29,16 +29,26 @@ import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.X509KeyManager;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.logging.EventLogger;
@@ -62,6 +72,9 @@ import org.apache.qpid.transport.network
@ManagedObject( category = false )
public class FileKeyStoreImpl extends
AbstractConfiguredObject<FileKeyStoreImpl> implements
FileKeyStore<FileKeyStoreImpl>
{
+ private static Logger LOGGER =
LoggerFactory.getLogger(FileKeyStoreImpl.class);
+
+ private static final long ONE_DAY = 24l * 60l * 60l * 1000l;
private final Broker<?> _broker;
private final EventLogger _eventLogger;
@@ -85,6 +98,8 @@ public class FileKeyStoreImpl extends Ab
Handler.register();
}
+ private ScheduledFuture<?> _checkExpiryTaskFuture;
+
@ManagedObjectFactoryConstructor
public FileKeyStoreImpl(Map<String, Object> attributes, Broker<?> broker)
{
@@ -96,6 +111,41 @@ public class FileKeyStoreImpl extends Ab
}
@Override
+ protected void onOpen()
+ {
+ super.onOpen();
+ int checkFrequency;
+ try
+ {
+ checkFrequency = getContextValue(Integer.class,
CERTIFICATE_EXPIRY_CHECK_FREQUENCY);
+ }
+ catch(IllegalArgumentException | NullPointerException e)
+ {
+ LOGGER.warn("Cannot parse the context variable {} ",
CERTIFICATE_EXPIRY_CHECK_FREQUENCY, e);
+ checkFrequency = DEFAULT_CERTIFICATE_EXPIRY_CHECK_FREQUENCY;
+ }
+ _checkExpiryTaskFuture =
_broker.scheduleHouseKeepingTask(checkFrequency, TimeUnit.DAYS, new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ checkCertificateExpiry();
+ }
+ });
+ }
+
+ @Override
+ protected void onClose()
+ {
+ super.onClose();
+ if(_checkExpiryTaskFuture != null)
+ {
+ _checkExpiryTaskFuture.cancel(false);
+ _checkExpiryTaskFuture = null;
+ }
+ }
+
+ @Override
public void onValidate()
{
super.onValidate();
@@ -143,6 +193,7 @@ public class FileKeyStoreImpl extends Ab
throw new IllegalConfigurationException("Changing the key store
name is not allowed");
}
validateKeyStoreAttributes(changedStore);
+ checkCertificateExpiry();
}
private void validateKeyStoreAttributes(FileKeyStore<?> fileKeyStore)
@@ -155,7 +206,6 @@ public class FileKeyStoreImpl extends Ab
String keyStoreType = fileKeyStore.getKeyStoreType();
keyStore = SSLUtil.getInitializedKeyStore(url, password,
keyStoreType);
}
-
catch (Exception e)
{
final String message;
@@ -308,4 +358,74 @@ public class FileKeyStoreImpl extends Ab
_path = null;
}
}
+
+ private void checkCertificateExpiry()
+ {
+ try
+ {
+
+ int expiryWarning = getContextValue(Integer.class,
CERTIFICATE_EXPIRY_WARN_PERIOD);
+ if(expiryWarning > 0)
+ {
+ long currentTime = System.currentTimeMillis();
+ Date expiryTestDate = new Date(currentTime + (ONE_DAY *
(long)expiryWarning));
+
+ try
+ {
+ URL url = getUrlFromString(_storeUrl);
+ final java.security.KeyStore ks =
SSLUtil.getInitializedKeyStore(url, getPassword(), _keyStoreType);
+
+ char[] keyStoreCharPassword = getPassword() == null ? null
: getPassword().toCharArray();
+
+ final KeyManagerFactory kmf =
KeyManagerFactory.getInstance(_keyManagerFactoryAlgorithm);
+
+ kmf.init(ks, keyStoreCharPassword);
+
+
+ for (KeyManager km : kmf.getKeyManagers())
+ {
+ if (km instanceof X509KeyManager)
+ {
+ X509KeyManager x509KeyManager = (X509KeyManager)
km;
+
+ for(String alias : Collections.list(ks.aliases()))
+ {
+ final X509Certificate[] chain =
+
x509KeyManager.getCertificateChain(alias);
+ if(chain != null)
+ {
+ for(X509Certificate cert : chain)
+ {
+ try
+ {
+ cert.checkValidity(expiryTestDate);
+ }
+ catch(CertificateExpiredException e)
+ {
+ long timeToExpiry =
cert.getNotAfter().getTime() - currentTime;
+ int days =
Math.max(0,(int)(timeToExpiry / (ONE_DAY)));
+
+
_eventLogger.message(KeyStoreMessages.EXPIRING(getName(), String.valueOf(days),
cert.getSubjectDN().toString()));
+ }
+ catch(CertificateNotYetValidException
e)
+ {
+ // ignore
+ }
+ }
+ }
+ }
+
+ }
+ }
+ }
+ catch (GeneralSecurityException | IOException e)
+ {
+
+ }
+ }
+ }
+ catch(IllegalArgumentException | NullPointerException e)
+ {
+ }
+ }
}
Modified:
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStoreImpl.java
URL:
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStoreImpl.java?rev=1732154&r1=1732153&r2=1732154&view=diff
==============================================================================
---
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStoreImpl.java
(original)
+++
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStoreImpl.java
Wed Feb 24 13:30:55 2016
@@ -29,13 +29,18 @@ import java.nio.charset.StandardCharsets
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.SecureRandom;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
@@ -68,6 +73,7 @@ import org.apache.qpid.transport.network
public class NonJavaKeyStoreImpl extends
AbstractConfiguredObject<NonJavaKeyStoreImpl> implements
NonJavaKeyStore<NonJavaKeyStoreImpl>
{
private static final Logger LOGGER =
LoggerFactory.getLogger(NonJavaKeyStoreImpl.class);
+ private static final long ONE_DAY = 24l * 60l * 60l * 1000l;
private final Broker<?> _broker;
private final EventLogger _eventLogger;
@@ -90,6 +96,9 @@ public class NonJavaKeyStoreImpl extends
private X509Certificate _certificate;
+ private ScheduledFuture<?> _checkExpiryTaskFuture;
+ private int _checkFrequency;
+
@ManagedObjectFactoryConstructor
public NonJavaKeyStoreImpl(final Map<String, Object> attributes, Broker<?>
broker)
{
@@ -100,6 +109,43 @@ public class NonJavaKeyStoreImpl extends
}
@Override
+ protected void onOpen()
+ {
+ super.onOpen();
+ int checkFrequency;
+ try
+ {
+ checkFrequency = getContextValue(Integer.class,
CERTIFICATE_EXPIRY_CHECK_FREQUENCY);
+ }
+ catch(IllegalArgumentException | NullPointerException e)
+ {
+ LOGGER.warn("Cannot parse the context variable {} ",
CERTIFICATE_EXPIRY_CHECK_FREQUENCY, e);
+ checkFrequency = DEFAULT_CERTIFICATE_EXPIRY_CHECK_FREQUENCY;
+ }
+
+ _checkExpiryTaskFuture =
+ _broker.scheduleHouseKeepingTask(checkFrequency,
TimeUnit.DAYS, new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ checkCertificateExpiry();
+ }
+ });
+ }
+
+ @Override
+ protected void onClose()
+ {
+ super.onClose();
+ if(_checkExpiryTaskFuture != null)
+ {
+ _checkExpiryTaskFuture.cancel(false);
+ _checkExpiryTaskFuture = null;
+ }
+ }
+
+ @Override
public String getPrivateKeyUrl()
{
return _privateKeyUrl;
@@ -250,7 +296,7 @@ public class NonJavaKeyStoreImpl extends
allCerts.addAll(Arrays.asList(SSLUtil.readCertificates(getUrlFromString(_intermediateCertificateUrl))));
certs = allCerts.toArray(new
X509Certificate[allCerts.size()]);
}
-
+ checkCertificateExpiry(certs);
java.security.KeyStore inMemoryKeyStore =
java.security.KeyStore.getInstance(java.security.KeyStore.getDefaultType());
byte[] bytes = new byte[64];
@@ -274,6 +320,63 @@ public class NonJavaKeyStoreImpl extends
}
}
+ private void checkCertificateExpiry()
+ {
+ try
+ {
+ if (_privateKeyUrl != null && _certificateUrl != null)
+ {
+ X509Certificate[] certs =
SSLUtil.readCertificates(getUrlFromString(_certificateUrl));
+ if (_intermediateCertificateUrl != null)
+ {
+ List<X509Certificate> allCerts = new
ArrayList<>(Arrays.asList(certs));
+
allCerts.addAll(Arrays.asList(SSLUtil.readCertificates(getUrlFromString(_intermediateCertificateUrl))));
+ certs = allCerts.toArray(new
X509Certificate[allCerts.size()]);
+ }
+ checkCertificateExpiry(certs);
+ }
+ }
+ catch (GeneralSecurityException | IOException e)
+ {
+ LOGGER.info("Unexpected exception while trying to check
certificate validity", e);
+ }
+ }
+
+ private void checkCertificateExpiry(final X509Certificate... certificates)
+ {
+ int expiryWarning = getContextValue(Integer.class,
CERTIFICATE_EXPIRY_WARN_PERIOD);
+ if(expiryWarning > 0)
+ {
+ long currentTime = System.currentTimeMillis();
+ Date expiryTestDate = new Date(currentTime + (ONE_DAY * (long)
expiryWarning));
+
+
+ if (certificates != null)
+ {
+ for (X509Certificate cert : certificates)
+ {
+ try
+ {
+ cert.checkValidity(expiryTestDate);
+ }
+ catch (CertificateExpiredException e)
+ {
+ long timeToExpiry = cert.getNotAfter().getTime() -
currentTime;
+ int days = Math.max(0, (int) (timeToExpiry /
(ONE_DAY)));
+
+
_eventLogger.message(KeyStoreMessages.EXPIRING(getName(),
+
String.valueOf(days),
+
cert.getSubjectDN().toString()));
+ }
+ catch (CertificateNotYetValidException e)
+ {
+ // ignore
+ }
+ }
+ }
+ }
+ }
+
private URL getUrlFromString(String urlString) throws MalformedURLException
{
URL url;
Added:
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/util/HousekeepingExecutor.java
URL:
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/util/HousekeepingExecutor.java?rev=1732154&view=auto
==============================================================================
---
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/util/HousekeepingExecutor.java
(added)
+++
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/util/HousekeepingExecutor.java
Wed Feb 24 13:30:55 2016
@@ -0,0 +1,101 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.util;
+
+import java.security.Principal;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+
+import com.google.common.util.concurrent.UncheckedExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import
org.apache.qpid.pool.SuppressingInheritedAccessControlContextThreadFactory;
+import org.apache.qpid.server.security.SecurityManager;
+
+public class HousekeepingExecutor extends ScheduledThreadPoolExecutor
+{
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(HousekeepingExecutor.class);
+
+ public HousekeepingExecutor(final String threadPrefix, final int
threadCount, final Principal principal)
+ {
+ super(threadCount, createThreadFactory(threadPrefix, threadCount,
principal));
+
+ }
+
+ private static SuppressingInheritedAccessControlContextThreadFactory
createThreadFactory(String threadPrefix, int threadCount, Principal principal)
+ {
+ return new
SuppressingInheritedAccessControlContextThreadFactory(threadPrefix,
+
SecurityManager.getSystemTaskSubject("Housekeeping", principal));
+
+ }
+
+ @Override
+ protected void afterExecute(Runnable r, Throwable t)
+ {
+ super.afterExecute(r, t);
+ if (t == null && r instanceof Future<?>)
+ {
+ Future future = (Future<?>) r;
+ try
+ {
+ if (future.isDone())
+ {
+ Object result = future.get();
+ }
+ }
+ catch (CancellationException ce)
+ {
+ LOGGER.debug("Housekeeping task got cancelled");
+ // Ignore cancellation of task
+ }
+ catch (ExecutionException | UncheckedExecutionException ee)
+ {
+ t = ee.getCause();
+ }
+ catch (InterruptedException ie)
+ {
+ Thread.currentThread().interrupt(); // ignore/reset
+ }
+ catch (Throwable t1)
+ {
+ t = t1;
+ }
+ }
+ if (t != null)
+ {
+ LOGGER.error("Houskeeping task threw an exception:", t);
+
+ final Thread.UncaughtExceptionHandler uncaughtExceptionHandler =
Thread.getDefaultUncaughtExceptionHandler();
+ if (uncaughtExceptionHandler != null)
+ {
+
uncaughtExceptionHandler.uncaughtException(Thread.currentThread(), t);
+ }
+ else
+ {
+ Runtime.getRuntime().halt(1);
+ }
+ }
+ }
+}
Propchange:
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/util/HousekeepingExecutor.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java
URL:
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java?rev=1732154&r1=1732153&r2=1732154&view=diff
==============================================================================
---
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java
(original)
+++
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java
Wed Feb 24 13:30:55 2016
@@ -38,8 +38,6 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.*;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Pattern;
@@ -53,7 +51,6 @@ import com.google.common.util.concurrent
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
-import com.google.common.util.concurrent.UncheckedExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -99,6 +96,7 @@ import org.apache.qpid.server.txn.LocalT
import org.apache.qpid.server.txn.ServerTransaction;
import org.apache.qpid.server.util.Action;
import org.apache.qpid.server.util.ConnectionScopedRuntimeException;
+import org.apache.qpid.server.util.HousekeepingExecutor;
import org.apache.qpid.server.util.MapValueConverter;
public abstract class AbstractVirtualHost<X extends AbstractVirtualHost<X>>
extends AbstractConfiguredObject<X>
@@ -1838,58 +1836,10 @@ public abstract class AbstractVirtualHos
@StateTransition(currentState = {State.UNINITIALIZED, State.ERRORED},
desiredState = State.ACTIVE)
private ListenableFuture<Void> onActivate()
{
- final SuppressingInheritedAccessControlContextThreadFactory
housekeepingThreadFactory =
- new
SuppressingInheritedAccessControlContextThreadFactory("virtualhost-" +
getName() + "-pool",
-
SecurityManager.getSystemTaskSubject("Housekeeping", getPrincipal()));
- _houseKeepingTaskExecutor = new
ScheduledThreadPoolExecutor(getHousekeepingThreadCount(),
housekeepingThreadFactory){
- @Override
- protected void afterExecute(Runnable r, Throwable t)
- {
- super.afterExecute(r, t);
- if (t == null && r instanceof Future<?>)
- {
- Future future = (Future<?>) r;
- try
- {
- if (future.isDone())
- {
- Object result = future.get();
- }
- }
- catch (CancellationException ce)
- {
- _logger.debug("Housekeeping task got cancelled");
- // Ignore cancellation of task
- }
- catch (ExecutionException | UncheckedExecutionException ee)
- {
- t = ee.getCause();
- }
- catch (InterruptedException ie)
- {
- Thread.currentThread().interrupt(); // ignore/reset
- }
- catch (Throwable t1)
- {
- t = t1;
- }
- }
- if (t != null)
- {
- _logger.error("Houskeeping task threw an exception:", t);
- final Thread.UncaughtExceptionHandler
uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
- if (uncaughtExceptionHandler != null)
- {
-
uncaughtExceptionHandler.uncaughtException(Thread.currentThread(), t);
- }
- else
- {
- Runtime.getRuntime().halt(1);
- }
- }
- }
- };
+ _houseKeepingTaskExecutor = new HousekeepingExecutor("virtualhost-" +
getName() + "-pool",
+
getHousekeepingThreadCount(),
+ getPrincipal());
long threadPoolKeepAliveTimeout = getContextValue(Long.class,
CONNECTION_THREAD_POOL_KEEP_ALIVE_TIMEOUT);
Modified:
qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/security/NonJavaKeyStoreTest.java
URL:
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/security/NonJavaKeyStoreTest.java?rev=1732154&r1=1732153&r2=1732154&view=diff
==============================================================================
---
qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/security/NonJavaKeyStoreTest.java
(original)
+++
qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/security/NonJavaKeyStoreTest.java
Wed Feb 24 13:30:55 2016
@@ -21,8 +21,16 @@ package org.apache.qpid.server.security;
import static org.apache.qpid.test.utils.TestSSLConstants.KEYSTORE_PASSWORD;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.mockito.internal.verification.VerificationModeFactory.times;
import javax.net.ssl.KeyManager;
import javax.xml.bind.DatatypeConverter;
@@ -32,16 +40,28 @@ import java.io.FileOutputStream;
import java.io.InputStream;
import java.security.Key;
import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.configuration.updater.CurrentThreadTaskExecutor;
import org.apache.qpid.server.configuration.updater.TaskExecutor;
import org.apache.qpid.server.logging.EventLogger;
+import org.apache.qpid.server.logging.LogMessage;
+import org.apache.qpid.server.logging.MessageLogger;
+import org.apache.qpid.server.logging.messages.KeyStoreMessages;
import org.apache.qpid.server.model.Broker;
import org.apache.qpid.server.model.BrokerModel;
import org.apache.qpid.server.model.ConfiguredObjectFactory;
@@ -58,17 +78,18 @@ public class NonJavaKeyStoreTest extends
private final Model _model = BrokerModel.getInstance();
private final ConfiguredObjectFactory _factory = _model.getObjectFactory();
private List<File> _testResources;
+ private MessageLogger _messageLogger;
@Override
public void setUp() throws Exception
{
super.setUp();
-
+ _messageLogger = mock(MessageLogger.class);
when(_broker.getTaskExecutor()).thenReturn(_taskExecutor);
when(_broker.getChildExecutor()).thenReturn(_taskExecutor);
when(_broker.getModel()).thenReturn(_model);
when(_broker.getSecurityManager()).thenReturn(_securityManager);
- when(_broker.getEventLogger()).thenReturn(new EventLogger());
+ when(_broker.getEventLogger()).thenReturn(new
EventLogger(_messageLogger));
_testResources = new ArrayList<>();
}
@@ -237,4 +258,55 @@ public class NonJavaKeyStoreTest extends
// pass
}
}
+
+ public void testExpiryCheckingFindsExpired() throws Exception
+ {
+ doCertExpiryChecking(1);
+
+ verify(_messageLogger, times(1)).message(argThat(new
LogMessageArgumentMatcher()));
+
+ }
+
+ public void testExpiryCheckingIgnoresValid() throws Exception
+ {
+ doCertExpiryChecking(-1);
+
+ verify(_messageLogger, never()).message(argThat(new
LogMessageArgumentMatcher()));
+
+ }
+
+ private void doCertExpiryChecking(final int expiryOffset) throws Exception
+ {
+ when(_broker.scheduleHouseKeepingTask(anyLong(), any(TimeUnit.class),
any(Runnable.class))).thenReturn(mock(ScheduledFuture.class));
+
+ java.security.KeyStore ks =
java.security.KeyStore.getInstance(java.security.KeyStore.getDefaultType());
+ try(InputStream is =
getClass().getResourceAsStream("/java_broker_keystore.jks"))
+ {
+ ks.load(is, KEYSTORE_PASSWORD.toCharArray() );
+ }
+ X509Certificate cert = (X509Certificate) ks.getCertificate("rootca");
+ int expiryDays = (int)((cert.getNotAfter().getTime() -
System.currentTimeMillis()) / (24l * 60l * 60l * 1000l));
+
+ File[] resources = extractResourcesFromTestKeyStore(false);
+ _testResources.addAll(Arrays.asList(resources));
+
+ Map<String,Object> attributes = new HashMap<>();
+ attributes.put(NonJavaKeyStore.NAME, "myTestTrustStore");
+ attributes.put("privateKeyUrl",
resources[0].toURI().toURL().toExternalForm());
+ attributes.put("certificateUrl",
resources[1].toURI().toURL().toExternalForm());
+ attributes.put("context",
Collections.singletonMap(KeyStore.CERTIFICATE_EXPIRY_WARN_PERIOD, expiryDays +
expiryOffset));
+ attributes.put(NonJavaKeyStore.TYPE, "NonJavaKeyStore");
+ _factory.create(KeyStore.class, attributes, _broker);
+ }
+
+
+ private static class LogMessageArgumentMatcher extends
ArgumentMatcher<LogMessage>
+ {
+ @Override
+ public boolean matches(final Object argument)
+ {
+ LogMessage arg = (LogMessage)argument;
+ return
arg.getLogHierarchy().equals(KeyStoreMessages.EXPIRING_LOG_HIERARCHY);
+ }
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]