diff options
| author | Robert Gemmell <robbie@apache.org> | 2012-11-04 21:03:36 +0000 |
|---|---|---|
| committer | Robert Gemmell <robbie@apache.org> | 2012-11-04 21:03:36 +0000 |
| commit | bb9160e892017cd58a14b9eb0ebd282e787229d5 (patch) | |
| tree | f25ded28c3ada1f6611efc49addc2d56cd93cfd0 | |
| parent | b8ea27b54e0dc0e5ebf8d7a17e6cc68e5307a422 (diff) | |
| download | qpid-python-bb9160e892017cd58a14b9eb0ebd282e787229d5.tar.gz | |
QPID-4420: add documentation for SSL and the Anonymous + External AuthenticationManagers along with some general cleanup and expanded testing
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1405636 13f79535-47bb-0310-9956-ffa450edef68
7 files changed, 608 insertions, 130 deletions
diff --git a/qpid/doc/book/src/java-broker/Java-Broker-Security-Authentication-Providers.xml b/qpid/doc/book/src/java-broker/Java-Broker-Security-Authentication-Providers.xml index 58360dc722..96b6f99185 100644 --- a/qpid/doc/book/src/java-broker/Java-Broker-Security-Authentication-Providers.xml +++ b/qpid/doc/book/src/java-broker/Java-Broker-Security-Authentication-Providers.xml @@ -26,55 +26,22 @@ <para> In order to successfully establish a connection to the Java Broker, the connection must be authenticated. The Java Broker supports a number of different authentication schemes, each - with its own "authentication manager". Different managers may be used on different ports. - Each manager has its own configuration element, the presence of which within the - <security> section denotes the use of that authentication provider. Where only one - such manager is configured, that manager will be used on all ports (including JMX). Where - more than one authentication manager is configured the configuration must define which - manager is the "default", and (if required) the mapping of non-default authentication - managers to other ports. - </para> - <para> - The following configuration sets up three authentication managers, using a password file as - the default (e.g. for the JMX port), Kerberos on port 5672 and Anonymous on 5673. + with its own "authentication manager". Each of these are outlined below, along with details + of <link linkend="MultipleAuthProviders"> using more than one at a time</link>. </para> - <example> - <title>Configuring different authentication schemes on different ports</title> - <programlisting><![CDATA[ -<security> - <pd-auth-manager> - <principal-database> - <class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class> - <attributes> - <attribute> - <name>passwordFile</name> - <value>${conf}/passwd</value> - </attribute> - </attributes> - </principal-database> - </pd-auth-manager> - <kerberos-auth-manager><auth-name>sib</auth-name></kerberos-auth-manager> - <anonymous-auth-manager></anonymous-auth-manager> - <default-auth-manager>PrincipalDatabaseAuthenticationManager</default-auth-manager> - <port-mappings> - <port-mapping> - <port>5672</port> - <auth-manager>KerberosAuthenticationManager</auth-manager> - </port-mapping> - <port-mapping> - <port>5673</port> - <auth-manager>AnonymousAuthenticationManager</auth-manager> - </port-mapping> - </port-mappings> -</security>]]> - </programlisting> - </example> + <section> + <title>Password File</title> + <para> + TODO + </para> + + </section> - <section><title>Password File</title></section> - <section><title>LDAP</title> + <section> + <title>LDAP</title> <example> - <title>Configuring a LDAP authentication</title> + <title>Configuring LDAP authentication</title> <programlisting><![CDATA[ <security> <simple-ldap-auth-manager> @@ -82,8 +49,8 @@ <search-context>dc=example\,dc=com</search-context> <search-filter>(uid={0})</search-filter> </simple-ldap-auth-manager> -</security>]]> - </programlisting> + ... +</security>]]></programlisting> </example> <para> @@ -111,9 +78,10 @@ By default com.sun.jndi.ldap.LdapCtxFactory is used to create the context, however this can be overridden by specifying <ldap-context-factory> in the configuration. </para> - </section> - <section><title>Kerberos</title> + + <section> + <title>Kerberos</title> <para> Kereberos Authentication is configured using the <kerberos-auth-manager> element within @@ -128,30 +96,30 @@ </para> <example> - <title>Configuring a Kerberos authentication</title> + <title>Configuring Kerberos authentication</title> <programlisting><![CDATA[ <security> - <pd-auth-manager> - <principal-database> - <class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class> - <attributes> - <attribute> - <name>passwordFile</name> - <value>${conf}/passwd</value> - </attribute> - </attributes> - </principal-database> - </pd-auth-manager> - <kerberos-auth-manager></kerberos-auth-manager> - <default-auth-manager>PrincipalDatabaseAuthenticationManager</default-auth-manager> - <port-mappings> - <port-mapping> - <port>5672</port> - <auth-manager>KerberosAuthenticationManager</auth-manager> - </port-mapping> - </port-mappings> -</security>]]> - </programlisting> + <pd-auth-manager> + <principal-database> + <class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class> + <attributes> + <attribute> + <name>passwordFile</name> + <value>${conf}/passwd</value> + </attribute> + </attributes> + </principal-database> + </pd-auth-manager> + <kerberos-auth-manager/> + <default-auth-manager>PrincipalDatabaseAuthenticationManager</default-auth-manager> + <port-mappings> + <port-mapping> + <port>5672</port> + <auth-manager>KerberosAuthenticationManager</auth-manager> + </port-mapping> + </port-mappings> + ... +</security>]]></programlisting> </example> <para> @@ -177,8 +145,7 @@ com.sun.security.jgss.accept { kdc="kerberos.example.com" keyTab="/path/to/keytab-file" principal="<name>/<host>"; -};]]> - </programlisting> +};]]></programlisting> <para> Where realm, kdc, keyTab and principal should obviously be set correctly for the environment @@ -191,7 +158,137 @@ com.sun.security.jgss.accept { Jurisdiction Policy Files" appropriate for your JDK in order to get Kerberos support working. </para> </section> - <section><title>SSL Client Certificates</title></section> - <section><title>Anonymous</title></section> + + <section id="ExternalAuthManager"> + <title>External (SSL Client Certificates)</title> + + <para> + When <link linkend="SSL-Truststore-ClientCertificate"> requiring SSL Client Certificates</link> be + presented the ExternalAuthenticationManager can be used, such that the user is authenticated based on + trust of their certificate alone, and the X500Principal from the SSL session is then used as the username + for the connection, instead of also requiring the user to present a valid username and password. + </para> + + <para> + The ExternalAuthenticationManager may be enabled by adding an empty <external-auth-manager> element to + the <security> section, as shown below. When referencing it from the default-auth-manager or port-mapping + sections, its name is ExternalAuthenticationManager. + </para> + + <para> + <emphasis role="bold">Note:</emphasis> The ExternalAuthenticationManager should typically only be used on the + AMQP ports, in conjunction with <link linkend="SSL-Truststore-ClientCertificate">SSL client certificate + authentication</link>. It is not intended for other uses such as the JMX management port and will treat any + non-sasl authentication processes on these ports as successfull with the given username. As such you should + <link linkend="MultipleAuthProviders">include another Authentication Manager for use on non-AMQP ports</link>, + as is done in the example below. Perhaps the only exception to this would be where the broker is embedded in a + container that is itself externally protecting the HTTP interface and then providing the remote users name. + </para> + + <example> + <title>Configuring external authentication (SSL client auth)</title> + <programlisting><![CDATA[ +<security> + <pd-auth-manager> + <principal-database> + <class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class> + <attributes> + <attribute> + <name>passwordFile</name> + <value>${conf}/passwd</value> + </attribute> + </attributes> + </principal-database> + </pd-auth-manager> + <external-auth-manager/> + <default-auth-manager>PrincipalDatabaseAuthenticationManager</default-auth-manager> + <port-mappings> + <port-mapping> + <port>5672</port> + <auth-manager>ExternalAuthenticationManager</auth-manager> + </port-mapping> + </port-mappings> + ... +</security>]]></programlisting> + </example> + + </section> + + <section id="AnonymousAuthManager"> + <title>Anonymous</title> + + <para> + The AnonymousAuthenticationManager will allow users to connect with or without credentials and result + in their identification on the broker as the user ANONYMOUS. It may be enabled by adding an empty + anonymous-auth-manager element to the security configuration section, as shown below. + </para> + + <example> + <title>Configuring anonymous authentication</title> + + <programlisting><![CDATA[ +<security> + <anonymous-auth-manager/> + ... +</security>]]></programlisting> + </example> + + <para> + When referencing it from the default-auth-manager or port-mapping sections, its name is + AnonymousAuthenticationManager. + </para> + </section> + + <section id="MultipleAuthProviders"> + <title>Configuring multiple Authentication Providers</title> + <para> + Different managers may be used on different ports. Each manager has its own configuration element, + the presence of which within the <security> section denotes the use of that authentication + provider. Where only one such manager is configured, it will be used on all ports (including JMX + and HTTP). Where more than one authentication manager is configured the configuration must define + which is the "default", and (if required) the mapping of non-default authentication managers to + other ports. + </para> + <para> + The following configuration sets up three authentication managers, using a password file as the + default (e.g. for the JMX and HTTP ports), Kerberos on port 5672 (the regular AMQP port) and Anonymous + on port 5673 (e.g a second AMQP port the broker could have been configured with). + </para> + + <example> + <title>Configuring multiple (per-port) authentication schemes</title> + <programlisting><![CDATA[ +<security> + <pd-auth-manager> + <principal-database> + <class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class> + <attributes> + <attribute> + <name>passwordFile</name> + <value>${conf}/passwd</value> + </attribute> + </attributes> + </principal-database> + </pd-auth-manager> + <kerberos-auth-manager> + <auth-name>sib</auth-name> + </kerberos-auth-manager> + <anonymous-auth-manager/> + <default-auth-manager>PrincipalDatabaseAuthenticationManager</default-auth-manager> + <port-mappings> + <port-mapping> + <port>5672</port> + <auth-manager>KerberosAuthenticationManager</auth-manager> + </port-mapping> + <port-mapping> + <port>5673</port> + <auth-manager>AnonymousAuthenticationManager</auth-manager> + </port-mapping> + </port-mappings> + ... +</security>]]></programlisting> + </example> + </section> + </section> diff --git a/qpid/doc/book/src/java-broker/Java-Broker-Security-SSL.xml b/qpid/doc/book/src/java-broker/Java-Broker-Security-SSL.xml index 8e95caf1c3..541f6cf5d9 100644 --- a/qpid/doc/book/src/java-broker/Java-Broker-Security-SSL.xml +++ b/qpid/doc/book/src/java-broker/Java-Broker-Security-SSL.xml @@ -21,6 +21,85 @@ --> <section id="Java-Broker-Security-SSL"> -<title>SSL</title> + <title>SSL</title> + <para> + This section will show how to use SSL to enable secure + connections between an AMQP message client and the broker. + </para> + <section role="h2" id="SSL-Keystore"> + <title>Keystore Configuration</title> + <para> + The broker configuration file (config.xml) needs to be updated to include the required SSL keystore + configuration, an example of which can be found below. + </para> + + <example> + <title>Configuring an SSL Keystore</title> + <programlisting><![CDATA[ +<connector> + ... + <ssl> + <enabled>true</enabled> + <port>5671</port> + <sslOnly>false</sslOnly> + <keyStorePath>/path/to/keystore.ks</keyStorePath> + <keyStorePassword>keystorepass</keyStorePassword> + <certAlias>alias<certAlias> + </ssl> + ... +<connector>]]></programlisting> + </example> + + <para> + The certAlias element is an optional way of specifying which certificate the broker should use + if the keystore contains multiple entries. + </para> + + <para> + The sslOnly element controls whether the broker will <emphasis role="bold">only</emphasis> bind + the configured SSL port(s) or will also bind the non-SSL port(s). Setting sslOnly to true will + disable the non-SSL ports. + </para> + </section> + + <section role="h2" id="SSL-Truststore-ClientCertificate"> + <title>Truststore / Client Certificate Authentication</title> + <para> + The SSL trustore and related Client Certificate Authentication behaviour can be configured with + additional configuration as shown in the example below, in which the broker requires client + certificate authentication. + </para> + + <example> + <title>Configuring an SSL Truststore and client auth</title> + <programlisting><![CDATA[ +<connector> + ... + <ssl> + ... + <trustStorePath>/path/to/truststore.ks</trustStorePath> + <trustStorePassword>truststorepass</trustStorePassword> + <needClientAuth>true</needClientAuth> + <wantClientAuth>false</wantClientAuth> + ... + </ssl> + ... +<connector>]]></programlisting> + </example> + + <para> + The needClientAuth and wantClientAuth elements allow control of whether the client must present an + SSL certificate. Only one of these elements is needed but both may be used at the same time. + A socket's client authentication setting is one of three states: required (needClientAuth = true), + requested (wantClientAuth = true), or none desired (both false, the default). If both elements are + set to true, needClientAuth takes precedence. + </para> + + <para> + When using Client Certificate Authentication it may be desirable to use the External Authentication + Manager, for details see <xref linkend="ExternalAuthManager"></xref> + </para> + + </section> </section> diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoNetworkTransport.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoNetworkTransport.java index 9b6f0a0b1b..f2bfc14b47 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoNetworkTransport.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoNetworkTransport.java @@ -130,7 +130,6 @@ public class IoNetworkTransport implements OutgoingNetworkTransport, IncomingNet public void accept(NetworkTransportConfiguration config, ProtocolEngineFactory factory, SSLContext sslContext) { - try { _acceptor = new AcceptingThread(config, factory, sslContext); @@ -141,8 +140,6 @@ public class IoNetworkTransport implements OutgoingNetworkTransport, IncomingNet { throw new TransportException("Unable to start server socket", e); } - - } private class AcceptingThread extends Thread @@ -155,8 +152,7 @@ public class IoNetworkTransport implements OutgoingNetworkTransport, IncomingNet private AcceptingThread(NetworkTransportConfiguration config, ProtocolEngineFactory factory, - SSLContext sslContext) - throws IOException + SSLContext sslContext) throws IOException { _config = config; _factory = factory; @@ -172,15 +168,19 @@ public class IoNetworkTransport implements OutgoingNetworkTransport, IncomingNet { SSLServerSocketFactory socketFactory = _sslContext.getServerSocketFactory(); _serverSocket = socketFactory.createServerSocket(); - ((SSLServerSocket)_serverSocket).setNeedClientAuth(config.needClientAuth()); - ((SSLServerSocket)_serverSocket).setWantClientAuth(config.wantClientAuth()); + if(config.needClientAuth()) + { + ((SSLServerSocket)_serverSocket).setNeedClientAuth(true); + } + else if(config.wantClientAuth()) + { + ((SSLServerSocket)_serverSocket).setWantClientAuth(true); + } } _serverSocket.setReuseAddress(true); _serverSocket.bind(address); - - } @@ -224,7 +224,6 @@ public class IoNetworkTransport implements OutgoingNetworkTransport, IncomingNet socket.setSendBufferSize(sendBufferSize); socket.setReceiveBufferSize(receiveBufferSize); - ProtocolEngine engine = _factory.newProtocolEngine(); NetworkConnection connection = new IoNetworkConnection(socket, engine, sendBufferSize, receiveBufferSize, TIMEOUT); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java index ee8855dc6c..4f341e3c60 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java @@ -25,11 +25,13 @@ import static org.apache.qpid.test.utils.TestSSLConstants.KEYSTORE_PASSWORD; import static org.apache.qpid.test.utils.TestSSLConstants.TRUSTSTORE; import static org.apache.qpid.test.utils.TestSSLConstants.TRUSTSTORE_PASSWORD; +import org.apache.commons.configuration.ConfigurationException; import org.apache.qpid.client.AMQConnectionURL; import org.apache.qpid.client.AMQTestConnection_0_10; import org.apache.qpid.test.utils.QpidBrokerTestCase; import javax.jms.Connection; +import javax.jms.JMSException; import javax.jms.Session; import java.io.ByteArrayOutputStream; import java.io.PrintStream; @@ -42,33 +44,24 @@ public class SSLTest extends QpidBrokerTestCase @Override protected void setUp() throws Exception { - if(isJavaBroker()) - { - setTestClientSystemProperty("profile.use_ssl", "true"); - setConfigurationProperty("connector.ssl.enabled", "true"); - setConfigurationProperty("connector.ssl.sslOnly", "true"); - setConfigurationProperty("connector.ssl.wantClientAuth", "true"); - } - - // set the ssl system properties - setSystemProperty("javax.net.ssl.keyStore", KEYSTORE); - setSystemProperty("javax.net.ssl.keyStorePassword", KEYSTORE_PASSWORD); - setSystemProperty("javax.net.ssl.trustStore", TRUSTSTORE); - setSystemProperty("javax.net.ssl.trustStorePassword", TRUSTSTORE_PASSWORD); setSystemProperty("javax.net.debug", "ssl"); - super.setUp(); + + setSslStoreSystemProperties(); + + //We dont call super.setUp, the tests start the broker after deciding + //whether to run and then configuring it appropriately } public void testCreateSSLConnectionUsingConnectionURLParams() throws Exception { - if (Boolean.getBoolean("profile.use_ssl")) + if (shouldPerformTest()) { - // Clear the ssl system properties - setSystemProperty("javax.net.ssl.keyStore", null); - setSystemProperty("javax.net.ssl.keyStorePassword", null); - setSystemProperty("javax.net.ssl.trustStore", null); - setSystemProperty("javax.net.ssl.trustStorePassword", null); + clearSslStoreSystemProperties(); + //Start the broker (NEEDing client certificate authentication) + configureJavaBrokerIfNecessary(true, true, true, false); + super.setUp(); + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:%s" + "?ssl='true'&ssl_verify_hostname='true'" + "&key_store='%s'&key_store_password='%s'" + @@ -82,13 +75,16 @@ public class SSLTest extends QpidBrokerTestCase assertNotNull("connection should be successful", con); Session ssn = con.createSession(false,Session.AUTO_ACKNOWLEDGE); assertNotNull("create session should be successful", ssn); - } + } } public void testCreateSSLConnectionUsingSystemProperties() throws Exception { - if (Boolean.getBoolean("profile.use_ssl")) + if (shouldPerformTest()) { + //Start the broker (NEEDing client certificate authentication) + configureJavaBrokerIfNecessary(true, true, true, false); + super.setUp(); String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:%s?ssl='true''"; @@ -103,8 +99,12 @@ public class SSLTest extends QpidBrokerTestCase public void testMultipleCertsInSingleStore() throws Exception { - if (Boolean.getBoolean("profile.use_ssl")) + if (shouldPerformTest()) { + //Start the broker (NEEDing client certificate authentication) + configureJavaBrokerIfNecessary(true, true, true, false); + super.setUp(); + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:" + QpidBrokerTestCase.DEFAULT_SSL_PORT + "?ssl='true'&ssl_cert_alias='" + CERT_ALIAS_APP1 + "''"; @@ -127,10 +127,14 @@ public class SSLTest extends QpidBrokerTestCase } } - public void testVerifyHostNameWithIncorrectHostname() + public void testVerifyHostNameWithIncorrectHostname() throws Exception { - if (Boolean.getBoolean("profile.use_ssl")) + if (shouldPerformTest()) { + //Start the broker (WANTing client certificate authentication) + configureJavaBrokerIfNecessary(true, true, false, true); + super.setUp(); + String url = "amqp://guest:guest@test/?brokerlist='tcp://127.0.0.1:" + QpidBrokerTestCase.DEFAULT_SSL_PORT + "?ssl='true'&ssl_verify_hostname='true''"; @@ -142,19 +146,27 @@ public class SSLTest extends QpidBrokerTestCase } catch (Exception e) { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - e.printStackTrace(new PrintStream(bout)); - String strace = bout.toString(); - assertTrue("Correct exception not thrown",strace.contains("SSL hostname verification failed")); + verifyExceptionCausesContains(e, "SSL hostname verification failed"); } - } } + + private void verifyExceptionCausesContains(Exception e, String expectedString) + { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + e.printStackTrace(new PrintStream(bout)); + String strace = bout.toString(); + assertTrue("Correct exception not thrown", strace.contains(expectedString)); + } public void testVerifyLocalHost() throws Exception { - if (Boolean.getBoolean("profile.use_ssl")) + if (shouldPerformTest()) { + //Start the broker (WANTing client certificate authentication) + configureJavaBrokerIfNecessary(true, true, false, true); + super.setUp(); + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:" + QpidBrokerTestCase.DEFAULT_SSL_PORT + "?ssl='true'&ssl_verify_hostname='true''"; @@ -166,8 +178,12 @@ public class SSLTest extends QpidBrokerTestCase public void testVerifyLocalHostLocalDomain() throws Exception { - if (Boolean.getBoolean("profile.use_ssl")) + if (shouldPerformTest()) { + //Start the broker (WANTing client certificate authentication) + configureJavaBrokerIfNecessary(true, true, false, true); + super.setUp(); + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost.localdomain:" + QpidBrokerTestCase.DEFAULT_SSL_PORT + "?ssl='true'&ssl_verify_hostname='true''"; @@ -179,13 +195,14 @@ public class SSLTest extends QpidBrokerTestCase public void testCreateSSLConnectionUsingConnectionURLParamsTrustStoreOnly() throws Exception { - if (Boolean.getBoolean("profile.use_ssl")) + if (shouldPerformTest()) { - // Clear the ssl system properties - setSystemProperty("javax.net.ssl.keyStore", null); - setSystemProperty("javax.net.ssl.keyStorePassword", null); - setSystemProperty("javax.net.ssl.trustStore", null); - setSystemProperty("javax.net.ssl.trustStorePassword", null); + clearSslStoreSystemProperties(); + + //Start the broker (WANTing client certificate authentication) + configureJavaBrokerIfNecessary(true, true, false, true); + super.setUp(); + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:%s" + "?ssl='true'&ssl_verify_hostname='true'" + @@ -200,4 +217,120 @@ public class SSLTest extends QpidBrokerTestCase assertNotNull("create session should be successful", ssn); } } + + /** + * Verifies that when the broker is configured to NEED client certificates, + * a client which doesn't supply one fails to connect. + */ + public void testClientCertMissingWhilstNeeding() throws Exception + { + missingClientCertWhileNeedingOrWantingTestImpl(true, false, false); + } + + /** + * Verifies that when the broker is configured to WANT client certificates, + * a client which doesn't supply one succeeds in connecting. + */ + public void testClientCertMissingWhilstWanting() throws Exception + { + missingClientCertWhileNeedingOrWantingTestImpl(false, true, true); + } + + /** + * Verifies that when the broker is configured to WANT and NEED client certificates + * that a client which doesn't supply one fails to connect. + */ + public void testClientCertMissingWhilstWantingAndNeeding() throws Exception + { + missingClientCertWhileNeedingOrWantingTestImpl(true, true, false); + } + + private void missingClientCertWhileNeedingOrWantingTestImpl(boolean needClientCerts, + boolean wantClientCerts, boolean shouldSucceed) throws Exception + { + if (shouldPerformTest()) + { + clearSslStoreSystemProperties(); + + //Start the broker + configureJavaBrokerIfNecessary(true, true, needClientCerts, wantClientCerts); + super.setUp(); + + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:%s" + + "?ssl='true'&trust_store='%s'&trust_store_password='%s''"; + + url = String.format(url,QpidBrokerTestCase.DEFAULT_SSL_PORT,TRUSTSTORE,TRUSTSTORE_PASSWORD); + try + { + Connection con = getConnection(new AMQConnectionURL(url)); + if(!shouldSucceed) + { + fail("Connection succeeded, expected exception was not thrown"); + } + else + { + //Use the connection to verify it works + con.createSession(true, Session.SESSION_TRANSACTED); + } + } + catch(JMSException e) + { + if(shouldSucceed) + { + _logger.error("Caught unexpected exception",e); + fail("Connection failed, unexpected exception thrown"); + } + else + { + //expected + verifyExceptionCausesContains(e, "Caused by: javax.net.ssl.SSLException:"); + } + } + } + } + + private boolean shouldPerformTest() + { + // We run the SSL tests on all the Java broker profiles + if(isJavaBroker()) + { + setTestClientSystemProperty(PROFILE_USE_SSL, "true"); + } + + return Boolean.getBoolean(PROFILE_USE_SSL); + } + + private void configureJavaBrokerIfNecessary(boolean sslEnabled, boolean sslOnly, boolean needClientAuth, boolean wantClientAuth) throws ConfigurationException + { + if(isJavaBroker()) + { + setConfigurationProperty("connector.ssl.enabled", String.valueOf(sslEnabled)); + setConfigurationProperty("connector.ssl.sslOnly", String.valueOf(sslOnly)); + setConfigurationProperty("connector.ssl.needClientAuth", String.valueOf(needClientAuth)); + setConfigurationProperty("connector.ssl.wantClientAuth", String.valueOf(wantClientAuth)); + + if(needClientAuth || wantClientAuth) + { + //TODO: make a broker trust store? + setConfigurationProperty("connector.ssl.trustStorePath", TRUSTSTORE); + setConfigurationProperty("connector.ssl.trustStorePassword", TRUSTSTORE_PASSWORD); + } + } + } + + private void setSslStoreSystemProperties() + { + setSystemProperty("javax.net.ssl.keyStore", KEYSTORE); + setSystemProperty("javax.net.ssl.keyStorePassword", KEYSTORE_PASSWORD); + setSystemProperty("javax.net.ssl.trustStore", TRUSTSTORE); + setSystemProperty("javax.net.ssl.trustStorePassword", TRUSTSTORE_PASSWORD); + } + + private void clearSslStoreSystemProperties() + { + setSystemProperty("javax.net.ssl.keyStore", null); + setSystemProperty("javax.net.ssl.keyStorePassword", null); + setSystemProperty("javax.net.ssl.trustStore", null); + setSystemProperty("javax.net.ssl.trustStorePassword", null); + } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationTest.java new file mode 100644 index 0000000000..295aac9f97 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationTest.java @@ -0,0 +1,171 @@ +/* + * 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.security.auth.manager; + +import javax.jms.Connection; +import javax.jms.JMSException; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.client.AMQConnectionURL; +import org.apache.qpid.test.utils.QpidBrokerTestCase; + +public class ExternalAuthenticationTest extends QpidBrokerTestCase +{ + private static final String EXTERNAL_AUTH_MANAGER = ExternalAuthenticationManager.class.getSimpleName(); + private static final String KEYSTORE = "test-profiles/test_resources/ssl/java_client_keystore.jks"; + private static final String KEYSTORE_PASSWORD = "password"; + private static final String TRUSTSTORE = "test-profiles/test_resources/ssl/java_client_truststore.jks"; + private static final String TRUSTSTORE_PASSWORD = "password"; + + @Override + protected void setUp() throws Exception + { + // not calling super.setUp() to avoid broker start-up + } + + /** + * Tests that when EXTERNAL authentication is used on the SSL port, clients presenting certificates are able to connect. + * Also, checks that default authentication manager PrincipalDatabaseAuthenticationManager is used on non SSL port. + */ + public void testExternalAuthenticationManagerOnSSLPort() throws Exception + { + setCommonBrokerSSLProperties(true); + setConfigurationProperty("security.port-mappings.port-mapping.port", String.valueOf(QpidBrokerTestCase.DEFAULT_SSL_PORT)); + setConfigurationProperty("security.port-mappings.port-mapping.auth-manager", EXTERNAL_AUTH_MANAGER); + setConfigurationProperty("security.default-auth-manager", PrincipalDatabaseAuthenticationManager.class.getSimpleName()); + super.setUp(); + + setClientKeystoreProperties(); + setClientTrustoreProperties(); + + try + { + getExternalSSLConnection(false); + } + catch (JMSException e) + { + fail("Should be able to create a connection to the SSL port: " + e.getMessage()); + } + + try + { + getConnection(); + } + catch (JMSException e) + { + fail("Should be able to create a connection with credentials to the standard port: " + e.getMessage()); + } + + } + + /** + * Tests that when EXTERNAL authentication manager is set as the default, clients presenting certificates are able to connect. + * Also, checks a client with valid username and password but not using ssl is unable to connect to the non SSL port. + */ + public void testExternalAuthenticationManagerAsDefault() throws Exception + { + setCommonBrokerSSLProperties(true); + setConfigurationProperty("security.default-auth-manager", EXTERNAL_AUTH_MANAGER); + super.setUp(); + + setClientKeystoreProperties(); + setClientTrustoreProperties(); + + try + { + getConnection(); + fail("Connection should not succeed"); + } + catch (JMSException e) + { + // pass + } + + try + { + getExternalSSLConnection(false); + } + catch (JMSException e) + { + fail("Should be able to create a connection to the SSL port. " + e.getMessage()); + } + } + + /** + * Tests that when EXTERNAL authentication manager is set as the default, clients without certificates are unable to connect to the SSL port + * even with valid username and password. + */ + public void testExternalAuthenticationManagerWithoutClientKeyStore() throws Exception + { + setCommonBrokerSSLProperties(false); + setConfigurationProperty("security.default-auth-manager", EXTERNAL_AUTH_MANAGER); + super.setUp(); + + setClientTrustoreProperties(); + + try + { + getExternalSSLConnection(true); + fail("Connection should not succeed"); + } + catch (JMSException e) + { + // pass + } + } + + private Connection getExternalSSLConnection(boolean includeUserNameAndPassword) throws Exception + { + String url = "amqp://%s@test/?brokerlist='tcp://localhost:%s?ssl='true'&sasl_mechs='EXTERNAL''"; + if (includeUserNameAndPassword) + { + url = String.format(url, "guest:guest", String.valueOf(QpidBrokerTestCase.DEFAULT_SSL_PORT)); + } + else + { + url = String.format(url, ":", String.valueOf(QpidBrokerTestCase.DEFAULT_SSL_PORT)); + } + return getConnection(new AMQConnectionURL(url)); + } + + private void setCommonBrokerSSLProperties(boolean needClientAuth) throws ConfigurationException + { + setConfigurationProperty("connector.ssl.enabled", "true"); + setConfigurationProperty("connector.ssl.sslOnly", "false"); + setConfigurationProperty("connector.ssl.trustStorePath", TRUSTSTORE); + setConfigurationProperty("connector.ssl.trustStorePassword", TRUSTSTORE_PASSWORD); + setConfigurationProperty("connector.ssl.needClientAuth", String.valueOf(needClientAuth)); + setConfigurationProperty("security.external-auth-manager", ""); + } + + private void setClientKeystoreProperties() + { + setSystemProperty("javax.net.ssl.keyStore", KEYSTORE); + setSystemProperty("javax.net.ssl.keyStorePassword", KEYSTORE_PASSWORD); + } + + private void setClientTrustoreProperties() + { + setSystemProperty("javax.net.ssl.trustStore", TRUSTSTORE); + setSystemProperty("javax.net.ssl.trustStorePassword", TRUSTSTORE_PASSWORD); + setSystemProperty("javax.net.debug", "ssl"); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java index 4da3baaf73..b9f3bd7c23 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java @@ -129,6 +129,7 @@ public class QpidBrokerTestCase extends QpidTestCase private static final String BROKER_PERSITENT = "broker.persistent"; public static final String BROKER_PROTOCOL_EXCLUDES = "broker.protocol.excludes"; public static final String BROKER_PROTOCOL_INCLUDES = "broker.protocol.includes"; + public static final String PROFILE_USE_SSL = "profile.use_ssl"; // values protected static final String JAVA = "java"; @@ -1066,7 +1067,7 @@ public class QpidBrokerTestCase extends QpidTestCase _logger.info("get ConnectionFactory"); if (_connectionFactory == null) { - if (Boolean.getBoolean("profile.use_ssl")) + if (Boolean.getBoolean(PROFILE_USE_SSL)) { _connectionFactory = getConnectionFactory("default.ssl"); } @@ -1356,11 +1357,6 @@ public class QpidBrokerTestCase extends QpidTestCase _messageSize = byteSize; } - public ConnectionURL getConnectionURL() throws NamingException - { - return getConnectionFactory().getConnectionURL(); - } - public BrokerDetails getBroker() { try diff --git a/qpid/java/test-profiles/CPPExcludes b/qpid/java/test-profiles/CPPExcludes index f15673da02..fa3a2bc262 100755 --- a/qpid/java/test-profiles/CPPExcludes +++ b/qpid/java/test-profiles/CPPExcludes @@ -178,3 +178,6 @@ org.apache.qpid.systest.rest.acl.* // Exclude failover tests requiring virtual host functionality org.apache.qpid.client.failover.MultipleBrokersFailoverTest#* + +// Uses Java broker specific configuration +org.apache.qpid.client.ssl.SSLTest#testClientCertMissingWhilstWanting |
