diff options
author | Charles-Henri de Boysson <ceache@users.noreply.github.com> | 2018-12-17 22:44:07 +0000 |
---|---|---|
committer | Stephen SORRIAUX <stephen.sorriaux@gmail.com> | 2020-02-23 11:58:28 +0100 |
commit | 5c461bdff50efd762805b40fbe283b194d175c61 (patch) | |
tree | ebfcc98b9d40154ec56b9a0be0b89dab50125224 | |
parent | 7a0d54eec99f55705f5ddc7592a05a28aba47efc (diff) | |
download | kazoo-5c461bdff50efd762805b40fbe283b194d175c61.tar.gz |
feat(core): run SASL Kerberos tests as part of build
* Install debian packages for KDC as part of Travis init.
* Setup a loopback mini KDC for running tests.
* Run SASL tests as part of Travis builds.
* Improve harness cluster to support:
* Reconfiguration when environment changes.
* Different JAAS configurations (DIGEST/GSSAPI).
* Moved SASL tests into own module, with specially configured harness.
* Bumped default timeout to 15 sec to mitigate false negatives on
Travis.
-rw-r--r-- | .travis.yml | 2 | ||||
-rwxr-xr-x | ensure-zookeeper-env.sh | 2 | ||||
-rwxr-xr-x | init_krb5.sh | 108 | ||||
-rw-r--r-- | kazoo/testing/common.py | 28 | ||||
-rw-r--r-- | kazoo/testing/harness.py | 145 | ||||
-rw-r--r-- | kazoo/tests/test_client.py | 108 | ||||
-rw-r--r-- | kazoo/tests/test_sasl.py | 186 | ||||
-rw-r--r-- | requirements.txt | 6 | ||||
-rw-r--r-- | requirements_test.txt | 5 | ||||
-rw-r--r-- | setup.py | 2 | ||||
-rw-r--r-- | tox.ini | 14 |
11 files changed, 431 insertions, 175 deletions
diff --git a/.travis.yml b/.travis.yml index aa98e5d..ef6ef66 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,8 @@ addons: apt: packages: - libevent-dev + - krb5-kdc + - krb5-admin-server cache: pip: true directories: diff --git a/ensure-zookeeper-env.sh b/ensure-zookeeper-env.sh index 7d6063e..17cbc58 100755 --- a/ensure-zookeeper-env.sh +++ b/ensure-zookeeper-env.sh @@ -4,7 +4,7 @@ set -e HERE=`pwd` ZOO_BASE_DIR="$HERE/zookeeper" -ZOOKEEPER_VERSION=${ZOOKEEPER_VERSION:-3.4.10} +ZOOKEEPER_VERSION=${ZOOKEEPER_VERSION:-3.4.13} ZOOKEEPER_PATH="$ZOO_BASE_DIR/$ZOOKEEPER_VERSION" ZOOKEEPER_PREFIX=${ZOOKEEPER_PREFIX} ZOOKEEPER_SUFFIX=${ZOOKEEPER_SUFFIX} diff --git a/init_krb5.sh b/init_krb5.sh new file mode 100755 index 0000000..a44bbe1 --- /dev/null +++ b/init_krb5.sh @@ -0,0 +1,108 @@ +#!/bin/bash + +set -e + +KRB5KDC=$(which krb5kdc || true) +KDB5_UTIL=$(which kdb5_util || true) +KADMIN=$(which kadmin.local || true) + +if [ $# -lt 2 ]; then + echo "Usage $0 TARGET_DIR CMD..." + exit 1 +fi +WRK_DIR=$1 +shift + +# Check installed packages +if [ -z ${KRB5KDC:+x} ] || [ -z ${KDB5_UTIL:+x} ] || [ -z ${KADMIN:+x} ]; then + echo "Missing Kerberos utilities, skipping environment setup." + exec $@ +fi + +if [ -e ${WRK_DIR} ]; then + echo "Working directory kdc already exists!" + exit 1 +fi + +WRK_DIR=$(readlink -f ${WRK_DIR}) +KDC_DIR="${WRK_DIR}/krb5kdc" + +############################################################################### +# Cleanup handlers + +function kdclogs { + echo "Kerberos environment logs:" + tail -v -n50 ${KDC_DIR}/*.log +} + +function killkdc { + if [ -e ${KDC_DIR}/kdc.pid ]; then + echo "Terminating KDC server listening on ${KDC_PORT}..." + kill -TERM $(cat ${KDC_DIR}/kdc.pid) + fi + rm -vfr ${WRK_DIR} +} +trap killkdc EXIT +trap kdclogs ERR + +############################################################################### +export KRB5_TEST_ENV=${WRK_DIR} +export KRB5_CONFIG=${WRK_DIR}/krb5.conf + +KDC_PORT=$((${RANDOM}+1024)) +mkdir -vp ${WRK_DIR} +mkdir -vp ${KDC_DIR} + +cat <<EOF >${WRK_DIR}/krb5.conf +[logging] + default = FILE:${KDC_DIR}/krb5libs.log + kdc = FILE:${KDC_DIR}/krb5kdc.log + admin_server = FILE:${KDC_DIR}/kadmind.log + +[libdefaults] + dns_lookup_realm = false + ticket_lifetime = 24h + renew_lifetime = 7d + forwardable = true + rdns = false + default_realm = KAZOOTEST.ORG + #default_ccache_name = KEYRING:persistent:%{uid} + +[realms] + KAZOOTEST.ORG = { + database_name = ${KDC_DIR}/principal + admin_keytab = FILE:${KDC_DIR}/kadm5.keytab + key_stash_file = ${KDC_DIR}/stash + kdc_listen = 127.0.0.1:${KDC_PORT} + kdc_tcp_listen = 127.0.0.1:${KDC_PORT} + kdc = 127.0.0.1:${KDC_PORT} + default_domain = kazootest.org + } + +[domain_realm] + .kazootest.org = KAZOOTEST.ORG + kazootest.org = KAZOOTEST.ORG +EOF + +cat <<EOF | ${KDB5_UTIL} create -s +passwd123 +passwd123 +EOF + +cat <<EOF | ${KADMIN} +add_principal -randkey client@KAZOOTEST.ORG +ktadd -k ${WRK_DIR}/client.keytab -norandkey client@KAZOOTEST.ORG +add_principal -randkey zookeeper/127.0.0.1@KAZOOTEST.ORG +ktadd -k ${WRK_DIR}/server.keytab -norandkey zookeeper/127.0.0.1@KAZOOTEST.ORG +quit +EOF + +# Starting KDC +echo "Starting KDC listening on ${KDC_PORT}..." +KRB5_KDC_PROFILE=${KRB5_CONFIG} ${KRB5KDC} \ + -P ${KDC_DIR}/kdc.pid \ + -p ${KDC_PORT} \ + -r KAZOOTEST.ORG + +# Execute the next command +$@ diff --git a/kazoo/testing/common.py b/kazoo/testing/common.py index 226e5f7..c8005df 100644 --- a/kazoo/testing/common.py +++ b/kazoo/testing/common.py @@ -75,7 +75,8 @@ class ManagedZooKeeper(object): Hudson/Buildbot context, to ensure more test robustness.""" def __init__(self, software_path, server_info, peers=(), classpath=None, - configuration_entries=[], java_system_properties=[]): + configuration_entries=[], java_system_properties=[], + jaas_config=None): """Define the ZooKeeper test instance. @param install_path: The path to the install for ZK @@ -90,6 +91,7 @@ class ManagedZooKeeper(object): self._running = False self.configuration_entries = configuration_entries self.java_system_properties = java_system_properties + self.jaas_config = jaas_config def run(self): """Run the ZooKeeper instance under a temporary directory. @@ -99,7 +101,7 @@ class ManagedZooKeeper(object): if self.running: return config_path = os.path.join(self.working_path, "zoo.cfg") - jass_config_path = os.path.join(self.working_path, "jaas.conf") + jaas_config_path = os.path.join(self.working_path, "jaas.conf") log_path = os.path.join(self.working_path, "log") log4j_path = os.path.join(self.working_path, "log4j.properties") data_path = os.path.join(self.working_path, "data") @@ -126,7 +128,6 @@ authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider self.server_info.admin_port, "\n".join(self.configuration_entries))) # NOQA - # setup a replicated setup if peers are specified if self.peers: servers_cfg = [] @@ -146,14 +147,8 @@ peerType=%s with open(os.path.join(data_path, "myid"), "w") as myid_file: myid_file.write(str(self.server_info.server_id)) # Write JAAS configuration - with open(jass_config_path, "w") as jaas_file: - jaas_file.write(""" -Server { - org.apache.zookeeper.server.auth.DigestLoginModule required - user_super="super_secret" - user_jaasuser="jaas_password"; -};""") - + with open(jaas_config_path, "w") as jaas_file: + jaas_file.write(self.jaas_config or '') with open(log4j_path, "w") as log4j: log4j.write(""" # DEFAULT: console appender only @@ -186,7 +181,7 @@ log4j.appender.ROLLINGFILE.File=""" + to_java_compatible_path( # NOQA "-Djava.awt.headless=true", # JAAS configuration for SASL authentication - "-Djava.security.auth.login.config=%s" % jass_config_path, + "-Djava.security.auth.login.config=%s" % jaas_config_path, ] + self.java_system_properties + [ "org.apache.zookeeper.server.quorum.QuorumPeerMain", config_path, @@ -279,12 +274,12 @@ class ZookeeperCluster(object): def __init__(self, install_path=None, classpath=None, size=3, port_offset=20000, observer_start_id=-1, configuration_entries=[], - java_system_properties=[]): + java_system_properties=[], + jaas_config=None): self._install_path = install_path self._classpath = classpath self._servers = [] - # Calculate ports and peer group port = port_offset peers = [] @@ -309,7 +304,10 @@ class ZookeeperCluster(object): self._install_path, server_info, server_peers, classpath=self._classpath, configuration_entries=configuration_entries, - java_system_properties=java_system_properties)) + java_system_properties=java_system_properties, + jaas_config=jaas_config + ) + ) def __getitem__(self, k): return self._servers[k] diff --git a/kazoo/testing/harness.py b/kazoo/testing/harness.py index de56795..6cb5cb1 100644 --- a/kazoo/testing/harness.py +++ b/kazoo/testing/harness.py @@ -16,52 +16,104 @@ from kazoo.testing.common import ZookeeperCluster log = logging.getLogger(__name__) CLUSTER = None +CLUSTER_CONF = None +CLUSTER_DEFAULTS = { + "ZOOKEEPER_PORT_OFFSET": 20000, + "ZOOKEEPER_CLUSTER_SIZE": 3, + "ZOOKEEPER_OBSERVER_START_ID": -1, +} def get_global_cluster(): - global CLUSTER - if CLUSTER is None: - ZK_HOME = os.environ.get("ZOOKEEPER_PATH") - ZK_CLASSPATH = os.environ.get("ZOOKEEPER_CLASSPATH") - ZK_PORT_OFFSET = int(os.environ.get("ZOOKEEPER_PORT_OFFSET", 20000)) - ZK_CLUSTER_SIZE = int(os.environ.get("ZOOKEEPER_CLUSTER_SIZE", 3)) - ZK_VERSION = os.environ.get("ZOOKEEPER_VERSION") - if '-' in ZK_VERSION: - # Ignore pre-release markers like -alpha - ZK_VERSION = ZK_VERSION.split('-')[0] - ZK_VERSION = tuple([int(n) for n in ZK_VERSION.split('.')]) - - ZK_OBSERVER_START_ID = int( - os.environ.get("ZOOKEEPER_OBSERVER_START_ID", -1)) - - assert ZK_HOME or ZK_CLASSPATH or ZK_VERSION, ( - "Either ZOOKEEPER_PATH or ZOOKEEPER_CLASSPATH or " - "ZOOKEEPER_VERSION environment variable must be defined.\n" - "For deb package installations this is /usr/share/java") - - if ZK_VERSION >= (3, 5): - additional_configuration_entries = [ - "4lw.commands.whitelist=*", - "reconfigEnabled=true" - ] - # If defines, this sets the superuser password to "test" - additional_java_system_properties = [ - "-Dzookeeper.DigestAuthenticationProvider.superDigest=" - "super:D/InIHSb7yEEbrWz8b9l71RjZJU=" - ] + global CLUSTER, CLUSTER_CONF + cluster_conf = { + k: os.environ.get(k, CLUSTER_DEFAULTS.get(k)) + for k in ["ZOOKEEPER_PATH", + "ZOOKEEPER_CLASSPATH", + "ZOOKEEPER_PORT_OFFSET", + "ZOOKEEPER_CLUSTER_SIZE", + "ZOOKEEPER_VERSION", + "ZOOKEEPER_OBSERVER_START_ID", + "ZOOKEEPER_JAAS_AUTH"] + } + if CLUSTER is not None: + if CLUSTER_CONF == cluster_conf: + return CLUSTER else: - additional_configuration_entries = [] - additional_java_system_properties = [] - CLUSTER = ZookeeperCluster( - install_path=ZK_HOME, - classpath=ZK_CLASSPATH, - port_offset=ZK_PORT_OFFSET, - size=ZK_CLUSTER_SIZE, - observer_start_id=ZK_OBSERVER_START_ID, - configuration_entries=additional_configuration_entries, - java_system_properties=additional_java_system_properties - ) - atexit.register(lambda cluster: cluster.terminate(), CLUSTER) + log.info('Config change detected. Reconfiguring cluster...') + CLUSTER.terminate() + CLUSTER = None + # Create a new cluster + ZK_HOME = cluster_conf.get("ZOOKEEPER_PATH") + ZK_CLASSPATH = cluster_conf.get("ZOOKEEPER_CLASSPATH") + ZK_PORT_OFFSET = int(cluster_conf.get("ZOOKEEPER_PORT_OFFSET")) + ZK_CLUSTER_SIZE = int(cluster_conf.get("ZOOKEEPER_CLUSTER_SIZE")) + ZK_VERSION = cluster_conf.get("ZOOKEEPER_VERSION") + if '-' in ZK_VERSION: + # Ignore pre-release markers like -alpha + ZK_VERSION = ZK_VERSION.split('-')[0] + ZK_VERSION = tuple([int(n) for n in ZK_VERSION.split('.')]) + ZK_OBSERVER_START_ID = int(cluster_conf.get("ZOOKEEPER_OBSERVER_START_ID")) + + assert ZK_HOME or ZK_CLASSPATH or ZK_VERSION, ( + "Either ZOOKEEPER_PATH or ZOOKEEPER_CLASSPATH or " + "ZOOKEEPER_VERSION environment variable must be defined.\n" + "For deb package installations this is /usr/share/java") + + if ZK_VERSION >= (3, 5): + additional_configuration_entries = [ + "4lw.commands.whitelist=*", + "reconfigEnabled=true" + ] + # If defined, this sets the superuser password to "test" + additional_java_system_properties = [ + "-Dzookeeper.DigestAuthenticationProvider.superDigest=" + "super:D/InIHSb7yEEbrWz8b9l71RjZJU=" + ] + else: + additional_configuration_entries = [] + additional_java_system_properties = [] + ZOOKEEPER_JAAS_AUTH = cluster_conf.get("ZOOKEEPER_JAAS_AUTH") + if ZOOKEEPER_JAAS_AUTH == "digest": + jaas_config = """ +Server { + org.apache.zookeeper.server.auth.DigestLoginModule required + user_super="super_secret" + user_jaasuser="jaas_password"; +};""" + elif ZOOKEEPER_JAAS_AUTH == "gssapi": + # Configure Zookeeper to use our test KDC. + additional_java_system_properties += [ + "-Djava.security.krb5.conf=%s" % os.path.expandvars( + "${KRB5_CONFIG}" + ), + "-Dsun.security.krb5.debug=true", + ] + jaas_config = """ +Server { + com.sun.security.auth.module.Krb5LoginModule required + debug=true + useKeyTab=true + keyTab="%s" + storeKey=true + useTicketCache=false + principal="zookeeper/127.0.0.1@KAZOOTEST.ORG"; +};""" % os.path.expandvars("${KRB5_TEST_ENV}/server.keytab") + else: + jaas_config = None + + CLUSTER = ZookeeperCluster( + install_path=ZK_HOME, + classpath=ZK_CLASSPATH, + port_offset=ZK_PORT_OFFSET, + size=ZK_CLUSTER_SIZE, + observer_start_id=ZK_OBSERVER_START_ID, + configuration_entries=additional_configuration_entries, + java_system_properties=additional_java_system_properties, + jaas_config=jaas_config + ) + CLUSTER_CONF = cluster_conf + atexit.register(lambda cluster: cluster.terminate(), CLUSTER) return CLUSTER @@ -90,6 +142,7 @@ class KazooTestHarness(unittest.TestCase): something_that_needs_zk_servers(self.servers) """ + DEFAULT_CLIENT_TIMEOUT = 15 def __init__(self, *args, **kw): super(KazooTestHarness, self).__init__(*args, **kw) @@ -109,8 +162,10 @@ class KazooTestHarness(unittest.TestCase): self._clients.append(c) return c - def _get_client(self, **kwargs): - c = KazooClient(self.hosts, **kwargs) + def _get_client(self, **client_options): + if 'timeout' not in client_options: + client_options['timeout'] = self.DEFAULT_CLIENT_TIMEOUT + c = KazooClient(self.hosts, **client_options) self._clients.append(c) return c @@ -139,7 +194,7 @@ class KazooTestHarness(unittest.TestCase): namespace = "/kazootests" + uuid.uuid4().hex self.hosts = self.servers + namespace if 'timeout' not in client_options: - client_options['timeout'] = 0.8 + client_options['timeout'] = self.DEFAULT_CLIENT_TIMEOUT self.client = self._get_client(**client_options) self.client.start() self.client.ensure_path("/") diff --git a/kazoo/tests/test_client.py b/kazoo/tests/test_client.py index 719b24f..fb6d2d9 100644 --- a/kazoo/tests/test_client.py +++ b/kazoo/tests/test_client.py @@ -179,74 +179,6 @@ class TestAuthentication(KazooTestCase): client.stop() client.close() - def test_connect_sasl_auth(self): - from kazoo.security import make_acl - - if TRAVIS_ZK_VERSION: - version = TRAVIS_ZK_VERSION - else: - version = self.client.server_version() - if not version or version < (3, 4): - raise SkipTest("Must use Zookeeper 3.4 or above") - try: - import puresasl # NOQA - except ImportError: - raise SkipTest('PureSASL not available.') - - username = "jaasuser" - password = "jaas_password" - - acl = make_acl('sasl', credential=username, all=True) - - client = self._get_client( - sasl_options={'mechanism': 'DIGEST-MD5', - 'username': username, - 'password': password} - ) - client.start() - try: - client.create('/1', acl=(acl,)) - # give ZK a chance to copy data to other node - time.sleep(0.1) - self.assertRaises(NoAuthError, self.client.get, "/1") - finally: - client.delete('/1') - client.stop() - client.close() - - def test_connect_sasl_auth_leg(self): - from kazoo.security import make_acl - - if TRAVIS_ZK_VERSION: - version = TRAVIS_ZK_VERSION - else: - version = self.client.server_version() - if not version or version < (3, 4): - raise SkipTest("Must use Zookeeper 3.4 or above") - try: - import puresasl # NOQA - except ImportError: - raise SkipTest('PureSASL not available.') - - username = "jaasuser" - password = "jaas_password" - - sasl_auth = "%s:%s" % (username, password) - - acl = make_acl('sasl', credential=username, all=True) - client = self._get_client(auth_data=[('sasl', sasl_auth)]) - - client.start() - try: - client.create('/1', acl=(acl,)) - # give ZK a chance to copy data to other node - time.sleep(0.1) - self.assertRaises(NoAuthError, self.client.get, "/1") - finally: - client.delete('/1') - client.stop() - client.close() - def test_unicode_auth(self): username = u("xe4/\hm") password = u("/\xe4hm") @@ -285,38 +217,6 @@ class TestAuthentication(KazooTestCase): self.assertRaises(TypeError, client.add_auth, None, ('user', 'pass')) - def test_invalid_sasl_auth(self): - if TRAVIS_ZK_VERSION: - version = TRAVIS_ZK_VERSION - else: - version = self.client.server_version() - if not version or version < (3, 4): - raise SkipTest("Must use Zookeeper 3.4 or above") - try: - import puresasl # NOQA - except ImportError: - raise SkipTest('PureSASL not available.') - client = self._get_client( - sasl_options={'mechanism': 'DIGEST-MD5', - 'username': 'baduser', - 'password': 'badpassword'} - ) - self.assertRaises(AuthFailedError, client.start) - - def test_invalid_sasl_auth_leg(self): - if TRAVIS_ZK_VERSION: - version = TRAVIS_ZK_VERSION - else: - version = self.client.server_version() - if not version or version < (3, 4): - raise SkipTest("Must use Zookeeper 3.4 or above") - try: - import puresasl # NOQA - except ImportError: - raise SkipTest('PureSASL not available.') - client = self._get_client(auth_data=[('sasl', 'baduser:badpassword')]) - self.assertRaises(AuthFailedError, client.start) - def test_async_auth(self): client = self._get_client() client.start() @@ -1347,13 +1247,13 @@ class TestReconfig(KazooTestCase): joining = 'server.100=0.0.0.0:%d:%d:observer;0.0.0.0:%d' % ( port1, port2, port3) data, _ = client.reconfig(joining=joining, - leaving=None, - new_members=None) + leaving=None, + new_members=None) self.assertIn(joining.encode('utf8'), data) data, _ = client.reconfig(joining=None, - leaving='100', - new_members=None) + leaving='100', + new_members=None) self.assertNotIn(joining.encode('utf8'), data) # try to add it again, but a config number in the future diff --git a/kazoo/tests/test_sasl.py b/kazoo/tests/test_sasl.py new file mode 100644 index 0000000..08839b6 --- /dev/null +++ b/kazoo/tests/test_sasl.py @@ -0,0 +1,186 @@ +import os +import subprocess +import time + +from nose import SkipTest + +from kazoo.testing import KazooTestHarness +from kazoo.exceptions import ( + AuthFailedError, + NoAuthError, +) +from kazoo.tests.util import TRAVIS_ZK_VERSION + + +class TestLegacySASLDigestAuthentication(KazooTestHarness): + + def setUp(self): + try: + import puresasl # NOQA + except ImportError: + raise SkipTest('PureSASL not available.') + + os.environ['ZOOKEEPER_JAAS_AUTH'] = 'digest' + self.setup_zookeeper() + + if TRAVIS_ZK_VERSION: + version = TRAVIS_ZK_VERSION + else: + version = self.client.server_version() + if not version or version < (3, 4): + raise SkipTest("Must use Zookeeper 3.4 or above") + + def tearDown(self): + self.teardown_zookeeper() + + def test_connect_sasl_auth(self): + from kazoo.security import make_acl + + username = "jaasuser" + password = "jaas_password" + + acl = make_acl('sasl', credential=username, all=True) + + sasl_auth = "%s:%s" % (username, password) + client = self._get_client(auth_data=[('sasl', sasl_auth)]) + + client.start() + try: + client.create('/1', acl=(acl,)) + # give ZK a chance to copy data to other node + time.sleep(0.1) + self.assertRaises(NoAuthError, self.client.get, "/1") + finally: + client.delete('/1') + client.stop() + client.close() + + def test_invalid_sasl_auth(self): + client = self._get_client(auth_data=[('sasl', 'baduser:badpassword')]) + self.assertRaises(AuthFailedError, client.start) + + +class TestSASLDigestAuthentication(KazooTestHarness): + + def setUp(self): + try: + import puresasl # NOQA + except ImportError: + raise SkipTest('PureSASL not available.') + + os.environ['ZOOKEEPER_JAAS_AUTH'] = 'digest' + self.setup_zookeeper() + + if TRAVIS_ZK_VERSION: + version = TRAVIS_ZK_VERSION + else: + version = self.client.server_version() + if not version or version < (3, 4): + raise SkipTest("Must use Zookeeper 3.4 or above") + + def tearDown(self): + self.teardown_zookeeper() + + def test_connect_sasl_auth(self): + from kazoo.security import make_acl + + username = "jaasuser" + password = "jaas_password" + + acl = make_acl('sasl', credential=username, all=True) + + client = self._get_client( + sasl_options={'mechanism': 'DIGEST-MD5', + 'username': username, + 'password': password} + ) + client.start() + try: + client.create('/1', acl=(acl,)) + # give ZK a chance to copy data to other node + time.sleep(0.1) + self.assertRaises(NoAuthError, self.client.get, "/1") + finally: + client.delete('/1') + client.stop() + client.close() + + def test_invalid_sasl_auth(self): + client = self._get_client( + sasl_options={'mechanism': 'DIGEST-MD5', + 'username': 'baduser', + 'password': 'badpassword'} + ) + self.assertRaises(AuthFailedError, client.start) + + +class TestSASLGSSAPIAuthentication(KazooTestHarness): + + def setUp(self): + try: + import puresasl # NOQA + except ImportError: + raise SkipTest('PureSASL not available.') + try: + import kerberos # NOQA + except ImportError: + raise SkipTest('Kerberos support not available.') + if not os.environ.get('KRB5_TEST_ENV'): + raise SkipTest('Test Kerberos environ not setup.') + + os.environ['ZOOKEEPER_JAAS_AUTH'] = 'gssapi' + self.setup_zookeeper() + + if TRAVIS_ZK_VERSION: + version = TRAVIS_ZK_VERSION + else: + version = self.client.server_version() + if not version or version < (3, 4): + raise SkipTest("Must use Zookeeper 3.4 or above") + + def tearDown(self): + self.teardown_zookeeper() + + def test_connect_gssapi_auth(self): + from kazoo.security import make_acl + + # Ensure we have a client ticket + subprocess.check_call( + [ + 'kinit', + '-kt', os.path.expandvars('${KRB5_TEST_ENV}/client.keytab'), + 'client' + ] + ) + + acl = make_acl('sasl', credential='client@KAZOOTEST.ORG', all=True) + + client = self._get_client( + sasl_options={'mechanism': 'GSSAPI'} + ) + client.start() + try: + client.create('/1', acl=(acl,)) + # give ZK a chance to copy data to other node + time.sleep(0.1) + self.assertRaises(NoAuthError, self.client.get, "/1") + finally: + client.delete('/1') + client.stop() + client.close() + + def test_invalid_gssapi_auth(self): + # Request a post-datated ticket, so that it is currently invalid. + subprocess.check_call( + [ + 'kinit', + '-kt', os.path.expandvars('${KRB5_TEST_ENV}/client.keytab'), + '-s', '30min', + 'client' + ] + ) + + client = self._get_client( + sasl_options={'mechanism': 'GSSAPI'} + ) + self.assertRaises(AuthFailedError, client.start) diff --git a/requirements.txt b/requirements.txt index f385468..ffe2fce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1 @@ -coverage==3.7.1 -mock==1.0.1 -nose==1.3.3 -flake8==2.3.0 -objgraph==3.4.0 +six diff --git a/requirements_test.txt b/requirements_test.txt new file mode 100644 index 0000000..f385468 --- /dev/null +++ b/requirements_test.txt @@ -0,0 +1,5 @@ +coverage==3.7.1 +mock==1.0.1 +nose==1.3.3 +flake8==2.3.0 +objgraph==3.4.0 @@ -78,7 +78,7 @@ setup( tests_require=tests_require, extras_require={ 'test': tests_require, - 'sasl': ['pure-sasl'], + 'sasl': ['pure-sasl==0.5.1'], }, long_description_content_type="text/markdown", ) @@ -1,5 +1,5 @@ [tox] -minversion = 1.6 +minversion = 3.6 skipsdist = True envlist = pep8, @@ -27,12 +27,18 @@ setenv = ZOOKEEPER_PREFIX={env:ZOOKEEPER_PREFIX:} ZOOKEEPER_SUFFIX={env:ZOOKEEPER_SUFFIX:} ZOOKEEPER_LIB={env:ZOOKEEPER_LIB:} -deps = -r{toxinidir}/requirements.txt +deps = + -r{toxinidir}/requirements.txt + -r{toxinidir}/requirements_test.txt docs: -r{toxinidir}/requirements_sphinx.txt gevent: -r{toxinidir}/requirements_gevent.txt - sasl: -r{toxinidir}/requirements_sasl.txt eventlet: -r{toxinidir}/requirements_eventlet.txt -commands = {toxinidir}/ensure-zookeeper-env.sh nosetests {posargs: -d -v --with-coverage kazoo.tests} + sasl: -r{toxinidir}/requirements_sasl.txt + sasl: kerberos +commands = + sasl: {toxinidir}/init_krb5.sh {envtmpdir}/kerberos \ + {toxinidir}/ensure-zookeeper-env.sh \ + nosetests {posargs: -d -v --with-coverage kazoo.tests} [flake8] builtins = _ |