Repository: knox Updated Branches: refs/heads/master 7edeac5d8 -> 51194fbbe
KNOX-631 - Config Driven Keystore for Signing and Validation Certs in KnoxSSO Project: http://git-wip-us.apache.org/repos/asf/knox/repo Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/51194fbb Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/51194fbb Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/51194fbb Branch: refs/heads/master Commit: 51194fbbe5662b16bd644d64faa450e43f771c80 Parents: 7edeac5 Author: Larry McCay <lmc...@hortonworks.com> Authored: Tue Mar 15 21:23:22 2016 -0400 Committer: Larry McCay <lmc...@hortonworks.com> Committed: Tue Mar 15 21:23:22 2016 -0400 ---------------------------------------------------------------------- .../gateway/config/impl/GatewayConfigImpl.java | 9 +++ .../security/impl/DefaultKeystoreService.java | 72 +++++++++++++++++++- .../impl/DefaultTokenAuthorityService.java | 47 +++++++++++-- .../hadoop/gateway/config/GatewayConfig.java | 6 ++ .../services/ServiceLifecycleException.java | 1 - .../services/security/KeystoreService.java | 6 +- .../security/KeystoreServiceException.java | 3 + .../hadoop/gateway/GatewayTestConfig.java | 16 +++++ .../hadoop/gateway/GatewayTestConfig.java | 16 +++++ 9 files changed, 166 insertions(+), 10 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/knox/blob/51194fbb/gateway-server/src/main/java/org/apache/hadoop/gateway/config/impl/GatewayConfigImpl.java ---------------------------------------------------------------------- diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/config/impl/GatewayConfigImpl.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/config/impl/GatewayConfigImpl.java index 4c0d769..711efc8 100644 --- a/gateway-server/src/main/java/org/apache/hadoop/gateway/config/impl/GatewayConfigImpl.java +++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/config/impl/GatewayConfigImpl.java @@ -563,4 +563,13 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig { return d; } + @Override + public String getSigningKeystoreName() { + return get(SIGNING_KEYSTORE_NAME); + } + + @Override + public String getSigningKeyAlias() { + return get(SIGNING_KEY_ALIAS); + } } http://git-wip-us.apache.org/repos/asf/knox/blob/51194fbb/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultKeystoreService.java ---------------------------------------------------------------------- diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultKeystoreService.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultKeystoreService.java index 4500230..e70da53 100644 --- a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultKeystoreService.java +++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/security/impl/DefaultKeystoreService.java @@ -55,6 +55,9 @@ public class DefaultKeystoreService extends BaseKeystoreService implements Keyst private static GatewayMessages LOG = MessagesFactory.get( GatewayMessages.class ); private static GatewayResources RES = ResourcesFactory.get( GatewayResources.class ); + private String signingKeystoreName = null; + private String signingKeyAlias = null; + @Override public void init(GatewayConfig config, Map<String, String> options) throws ServiceLifecycleException { @@ -65,6 +68,32 @@ public class DefaultKeystoreService extends BaseKeystoreService implements Keyst throw new ServiceLifecycleException( RES.failedToCreateKeyStoreDirectory( ksd.getAbsolutePath() ) ); } } + + signingKeystoreName = config.getSigningKeystoreName(); + // ensure that the keystore actually exists and fail to start if not + if (signingKeystoreName != null) { + File sks = new File(this.keyStoreDir, signingKeystoreName); + if (!sks.exists()) { + throw new ServiceLifecycleException("Configured signing keystore does not exist."); + } + signingKeyAlias = config.getSigningKeyAlias(); + if (signingKeyAlias != null) { + // ensure that the signing key alias exists in the configured keystore + KeyStore ks; + try { + ks = getSigningKeystore(); + if (ks != null) { + if (!ks.containsAlias(signingKeyAlias)) { + throw new ServiceLifecycleException("Configured signing key alias does not exist."); + } + } + } catch (KeystoreServiceException e) { + throw new ServiceLifecycleException("Unable to get the configured signing keystore.", e); + } catch (KeyStoreException e) { + throw new ServiceLifecycleException("Signing keystore has not been loaded.", e); + } + } + } } @Override @@ -86,7 +115,24 @@ public class DefaultKeystoreService extends BaseKeystoreService implements Keyst final File keyStoreFile = new File( keyStoreDir + GATEWAY_KEYSTORE ); return getKeystore(keyStoreFile, "JKS"); } - + + @Override + public KeyStore getSigningKeystore() throws KeystoreServiceException { + File keyStoreFile = null; + + if (signingKeystoreName == null) { + keyStoreFile = new File(keyStoreDir + GATEWAY_KEYSTORE); + } + else { + keyStoreFile = new File(keyStoreDir + signingKeystoreName); + // make sure the keystore exists + if (!keyStoreFile.exists()) { + throw new KeystoreServiceException("Configured signing keystore does not exist."); + } + } + return getKeystore(keyStoreFile, "JKS"); + } + @Override public void addSelfSignedCertForGateway(String alias, char[] passphrase) throws KeystoreServiceException { addSelfSignedCertForGateway(alias, passphrase, null); @@ -196,7 +242,29 @@ public class DefaultKeystoreService extends BaseKeystoreService implements Keyst } return key; } - + + @Override + public Key getSigningKey(String alias, char[] passphrase) throws KeystoreServiceException { + Key key = null; + KeyStore ks = getSigningKeystore(); + if (passphrase == null) { + passphrase = masterService.getMasterSecret(); + LOG.assumingKeyPassphraseIsMaster(); + } + if (ks != null) { + try { + key = ks.getKey(alias, passphrase); + } catch (UnrecoverableKeyException e) { + LOG.failedToGetKeyForGateway( alias, e ); + } catch (KeyStoreException e) { + LOG.failedToGetKeyForGateway( alias, e ); + } catch (NoSuchAlgorithmException e) { + LOG.failedToGetKeyForGateway( alias, e ); + } + } + return key; + } + public KeyStore getCredentialStoreForCluster(String clusterName) throws KeystoreServiceException { final File keyStoreFile = new File( keyStoreDir + clusterName + CREDENTIALS_SUFFIX ); http://git-wip-us.apache.org/repos/asf/knox/blob/51194fbb/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java ---------------------------------------------------------------------- diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java index 3fbc789..bd54956 100644 --- a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java +++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java @@ -45,9 +45,11 @@ import com.nimbusds.jose.crypto.RSASSASigner; import com.nimbusds.jose.crypto.RSASSAVerifier; public class DefaultTokenAuthorityService implements JWTokenAuthority, Service { - + + private static String SIGNING_KEY_PASSPHRASE = "signing.key.passphrase"; private AliasService as = null; private KeystoreService ks = null; + String signingKeyAlias = null; public void setKeystoreService(KeystoreService ks) { this.ks = ks; @@ -108,7 +110,6 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service { claimArray[0] = "KNOXSSO"; claimArray[1] = p.getName(); claimArray[2] = null; - // TODO: make the validity period configurable if (expires == -1) { claimArray[3] = Long.toString( ( System.currentTimeMillis() ) + 30000); } @@ -122,12 +123,12 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service { RSAPrivateKey key; char[] passphrase = null; try { - passphrase = as.getGatewayIdentityPassphrase(); + passphrase = getSigningKeyPassphrase(); } catch (AliasServiceException e) { throw new TokenServiceException(e); } try { - key = (RSAPrivateKey) ks.getKeyForGateway("gateway-identity", + key = (RSAPrivateKey) ks.getSigningKey(getSigningKeyAlias(), passphrase); JWSSigner signer = new RSASSASigner(key); token.sign(signer); @@ -138,17 +139,32 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service { else { throw new TokenServiceException("Cannot issue token - Unsupported algorithm"); } - + return token; } + private char[] getSigningKeyPassphrase() throws AliasServiceException { + char[] phrase = as.getPasswordFromAliasForGateway(SIGNING_KEY_PASSPHRASE); + if (phrase == null) { + phrase = as.getGatewayIdentityPassphrase(); + } + return phrase; + } + + private String getSigningKeyAlias() { + if (signingKeyAlias == null) { + return "gateway-identity"; + } + return signingKeyAlias; + } + @Override public boolean verifyToken(JWTToken token) throws TokenServiceException { boolean rc = false; PublicKey key; try { - key = ks.getKeystoreForGateway().getCertificate("gateway-identity").getPublicKey(); + key = ks.getSigningKeystore().getCertificate(getSigningKeyAlias()).getPublicKey(); JWSVerifier verifier = new RSASSAVerifier((RSAPublicKey) key); // TODO: interrogate the token for issuer claim in order to determine the public key to use for verification // consider jwk for specifying the key too @@ -167,6 +183,25 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service { if (as == null || ks == null) { throw new ServiceLifecycleException("Alias or Keystore service is not set"); } + signingKeyAlias = config.getSigningKeyAlias(); + + @SuppressWarnings("unused") + RSAPrivateKey key; + char[] passphrase = null; + try { + passphrase = as.getPasswordFromAliasForGateway(SIGNING_KEY_PASSPHRASE); + if (passphrase != null) { + key = (RSAPrivateKey) ks.getSigningKey(getSigningKeyAlias(), + passphrase); + if (key == null) { + throw new ServiceLifecycleException("Provisioned passphrase cannot be used to acquire signing key."); + } + } + } catch (AliasServiceException e) { + throw new ServiceLifecycleException("Provisioned signing key passphrase cannot be acquired.", e); + } catch (KeystoreServiceException e) { + throw new ServiceLifecycleException("Provisioned signing key passphrase cannot be acquired.", e); + } } @Override http://git-wip-us.apache.org/repos/asf/knox/blob/51194fbb/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java ---------------------------------------------------------------------- diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java index 91bd64c..5558578 100644 --- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java +++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java @@ -38,6 +38,8 @@ public interface GatewayConfig { public static final String KRB5_DEBUG = "sun.security.krb5.debug"; public static final String KRB5_LOGIN_CONFIG = "java.security.auth.login.config"; public static final String KRB5_USE_SUBJECT_CREDS_ONLY = "javax.security.auth.useSubjectCredsOnly"; + public static final String SIGNING_KEYSTORE_NAME = "gateway.signing.keystore.name"; + public static final String SIGNING_KEY_ALIAS = "gateway.signing.key.alias"; /** * The location of the gateway configuration. @@ -133,4 +135,8 @@ public interface GatewayConfig { long getGatewayDeploymentsBackupAgeLimit(); + String getSigningKeystoreName(); + + String getSigningKeyAlias(); + } http://git-wip-us.apache.org/repos/asf/knox/blob/51194fbb/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/ServiceLifecycleException.java ---------------------------------------------------------------------- diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/ServiceLifecycleException.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/ServiceLifecycleException.java index 11dd81f..a7f2a3c 100644 --- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/ServiceLifecycleException.java +++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/ServiceLifecycleException.java @@ -27,5 +27,4 @@ public class ServiceLifecycleException extends Exception { public ServiceLifecycleException(String message, Exception e) { super(message, e); } - } http://git-wip-us.apache.org/repos/asf/knox/blob/51194fbb/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/KeystoreService.java ---------------------------------------------------------------------- diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/KeystoreService.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/KeystoreService.java index 6ed8990..f85dd35 100644 --- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/KeystoreService.java +++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/KeystoreService.java @@ -29,9 +29,13 @@ public interface KeystoreService { void addSelfSignedCertForGateway(String alias, char[] passphrase, String hostname) throws KeystoreServiceException; public KeyStore getKeystoreForGateway() throws KeystoreServiceException; - + + public KeyStore getSigningKeystore() throws KeystoreServiceException; + public Key getKeyForGateway(String alias, char[] passphrase) throws KeystoreServiceException; + public Key getSigningKey(String alias, char[] passphrase) throws KeystoreServiceException; + public void createCredentialStoreForCluster(String clusterName) throws KeystoreServiceException; public boolean isCredentialStoreForClusterAvailable(String clusterName) throws KeystoreServiceException; http://git-wip-us.apache.org/repos/asf/knox/blob/51194fbb/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/KeystoreServiceException.java ---------------------------------------------------------------------- diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/KeystoreServiceException.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/KeystoreServiceException.java index 52fd26a..ef8d704 100644 --- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/KeystoreServiceException.java +++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/KeystoreServiceException.java @@ -24,4 +24,7 @@ public class KeystoreServiceException extends Exception { super(e); } + public KeystoreServiceException(String message) { + super(message); + } } http://git-wip-us.apache.org/repos/asf/knox/blob/51194fbb/gateway-test-release/webhdfs-kerb-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java ---------------------------------------------------------------------- diff --git a/gateway-test-release/webhdfs-kerb-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java b/gateway-test-release/webhdfs-kerb-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java index 03ce5dc..e5be4be 100644 --- a/gateway-test-release/webhdfs-kerb-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java +++ b/gateway-test-release/webhdfs-kerb-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java @@ -319,4 +319,20 @@ public class GatewayTestConfig extends Configuration implements GatewayConfig { return Long.MAX_VALUE; } + /* (non-Javadoc) + * @see org.apache.hadoop.gateway.config.GatewayConfig#getSigningKeystoreName() + */ + @Override + public String getSigningKeystoreName() { + return null; + } + + /* (non-Javadoc) + * @see org.apache.hadoop.gateway.config.GatewayConfig#getSigningKeyAlias() + */ + @Override + public String getSigningKeyAlias() { + return null; + } + } http://git-wip-us.apache.org/repos/asf/knox/blob/51194fbb/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java ---------------------------------------------------------------------- diff --git a/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java b/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java index 1d97a54..cd71e7f 100644 --- a/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java +++ b/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java @@ -371,4 +371,20 @@ public class GatewayTestConfig extends Configuration implements GatewayConfig { backupAgeLimit = newBackupAgeLimit; } + /* (non-Javadoc) + * @see org.apache.hadoop.gateway.config.GatewayConfig#getSigningKeystoreName() + */ + @Override + public String getSigningKeystoreName() { + return null; + } + + /* (non-Javadoc) + * @see org.apache.hadoop.gateway.config.GatewayConfig#getSigningKeyAlias() + */ + @Override + public String getSigningKeyAlias() { + return null; + } + }