diff --git a/containers/jetty/pom.xml b/containers/jetty/pom.xml index f33233bc7a4..57097d146a4 100644 --- a/containers/jetty/pom.xml +++ b/containers/jetty/pom.xml @@ -155,11 +155,6 @@ bcpkix-jdk18on ${bouncycastle.version} - - org.bouncycastle - bcprov-ext-jdk18on - 1.78 - jakarta.ws.rs jakarta.ws.rs-api diff --git a/libs/java/auth_core/pom.xml b/libs/java/auth_core/pom.xml index be16fc39c74..47cf0b3df00 100644 --- a/libs/java/auth_core/pom.xml +++ b/libs/java/auth_core/pom.xml @@ -30,6 +30,18 @@ 0.9007 + + + + software.amazon.awssdk + bom + ${aws2.version} + pom + import + + + + org.slf4j @@ -79,6 +91,10 @@ jjwt-api ${jjwt.version} + + software.amazon.awssdk + ssm + io.jsonwebtoken jjwt-impl diff --git a/libs/java/auth_core/src/main/java/com/yahoo/athenz/auth/impl/AWSParameterStorePrivateKeyStore.java b/libs/java/auth_core/src/main/java/com/yahoo/athenz/auth/impl/AWSParameterStorePrivateKeyStore.java new file mode 100644 index 00000000000..b04ebdbd2c7 --- /dev/null +++ b/libs/java/auth_core/src/main/java/com/yahoo/athenz/auth/impl/AWSParameterStorePrivateKeyStore.java @@ -0,0 +1,93 @@ +/* + * Copyright The Athenz Authors + * + * Licensed 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 com.yahoo.athenz.auth.impl; + +import com.yahoo.athenz.auth.PrivateKeyStore; +import com.yahoo.athenz.auth.ServerPrivateKey; +import com.yahoo.athenz.auth.util.Crypto; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.services.ssm.SsmClient; + +import java.lang.invoke.MethodHandles; +import java.security.PrivateKey; + +public class AWSParameterStorePrivateKeyStore implements PrivateKeyStore { + + private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private static final String ZMS_SERVICE = "zms"; + private static final String ZTS_SERVICE = "zts"; + private static final String MSD_SERVICE = "msd"; + + private static final String ATHENZ_PROP_ZMS_KEY_NAME = "athenz.aws.zms.key_name"; + private static final String ATHENZ_PROP_ZMS_KEY_ID_NAME = "athenz.aws.zms.key_id_name"; + private static final String ATHENZ_PROP_ZTS_KEY_NAME = "athenz.aws.zts.key_name"; + private static final String ATHENZ_PROP_ZTS_KEY_ID_NAME = "athenz.aws.zts.key_id_name"; + private static final String ATHENZ_PROP_MSD_KEY_NAME = "athenz.aws.msd.key_name"; + private static final String ATHENZ_PROP_MSD_KEY_ID_NAME = "athenz.aws.msd.key_id_name"; + + private static final String ATHENZ_DEFAULT_KEY_NAME = "service_private_key"; + private static final String ATHENZ_DEFAULT_KEY_ID_NAME = "service_private_key_id"; + + private final SsmClient ssmClient; + + AWSParameterStorePrivateKeyStore(SsmClient ssmClient) { + this.ssmClient = ssmClient; + } + + @Override + public char[] getSecret(String appName, String keyName) { + return getSsmParameter(keyName).toCharArray(); + } + + @Override + public ServerPrivateKey getPrivateKey(String service, String serverHostName, String serverRegion, String algorithm) { + if (serverRegion == null || serverRegion.isEmpty()) { + LOG.error("server region not specified"); + return null; + } + String keyName; + String keyIdName; + final String objectSuffix = "." + algorithm.toLowerCase(); + if (ZMS_SERVICE.equals(service)) { + keyName = System.getProperty(ATHENZ_PROP_ZMS_KEY_NAME, ATHENZ_DEFAULT_KEY_NAME) + objectSuffix; + keyIdName = System.getProperty(ATHENZ_PROP_ZMS_KEY_ID_NAME, ATHENZ_DEFAULT_KEY_ID_NAME) + objectSuffix; + } else if (ZTS_SERVICE.equals(service)) { + keyName = System.getProperty(ATHENZ_PROP_ZTS_KEY_NAME, ATHENZ_DEFAULT_KEY_NAME) + objectSuffix; + keyIdName = System.getProperty(ATHENZ_PROP_ZTS_KEY_ID_NAME, ATHENZ_DEFAULT_KEY_ID_NAME) + objectSuffix; + } else if (MSD_SERVICE.equals(service)) { + keyName = System.getProperty(ATHENZ_PROP_MSD_KEY_NAME, ATHENZ_DEFAULT_KEY_NAME) + objectSuffix; + keyIdName = System.getProperty(ATHENZ_PROP_MSD_KEY_ID_NAME, ATHENZ_DEFAULT_KEY_ID_NAME) + objectSuffix; + } else { + LOG.error("Unknown service specified: {}", service); + return null; + } + + PrivateKey pkey = null; + try { + pkey = Crypto.loadPrivateKey(getSsmParameter(keyName)); + } catch (Exception ex) { + LOG.error("unable to load private key", ex); + } + return pkey == null ? null : new ServerPrivateKey(pkey, getSsmParameter(keyIdName)); + } + + private String getSsmParameter(final String keyName) { + return ssmClient.getParameter(r -> r.name(keyName).withDecryption(true)).parameter().value(); + } + +} diff --git a/libs/java/auth_core/src/main/java/com/yahoo/athenz/auth/impl/AWSParameterStorePrivateKeyStoreFactory.java b/libs/java/auth_core/src/main/java/com/yahoo/athenz/auth/impl/AWSParameterStorePrivateKeyStoreFactory.java new file mode 100644 index 00000000000..74a5228aec7 --- /dev/null +++ b/libs/java/auth_core/src/main/java/com/yahoo/athenz/auth/impl/AWSParameterStorePrivateKeyStoreFactory.java @@ -0,0 +1,28 @@ +/* + * Copyright The Athenz Authors + * + * Licensed 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 com.yahoo.athenz.auth.impl; + +import com.yahoo.athenz.auth.PrivateKeyStore; +import com.yahoo.athenz.auth.PrivateKeyStoreFactory; +import software.amazon.awssdk.services.ssm.SsmClient; + +public class AWSParameterStorePrivateKeyStoreFactory implements PrivateKeyStoreFactory { + + @Override + public PrivateKeyStore create() { + return new AWSParameterStorePrivateKeyStore(SsmClient.create()); + } +} diff --git a/libs/java/auth_core/src/test/java/com/yahoo/athenz/auth/impl/AWSParameterStorePrivateKeyStoreFactoryTest.java b/libs/java/auth_core/src/test/java/com/yahoo/athenz/auth/impl/AWSParameterStorePrivateKeyStoreFactoryTest.java new file mode 100644 index 00000000000..50763be4d5c --- /dev/null +++ b/libs/java/auth_core/src/test/java/com/yahoo/athenz/auth/impl/AWSParameterStorePrivateKeyStoreFactoryTest.java @@ -0,0 +1,32 @@ +/* + * Copyright The Athenz Authors + * + * Licensed 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 com.yahoo.athenz.auth.impl; + +import com.yahoo.athenz.auth.PrivateKeyStore; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; + +public class AWSParameterStorePrivateKeyStoreFactoryTest { + + @Test + public void createAWSParameterStorePrivateKeyStore() { + System.setProperty("aws.region", "us-west-2"); + PrivateKeyStore privateKeyStore = new AWSParameterStorePrivateKeyStoreFactory().create(); + assertTrue(privateKeyStore instanceof AWSParameterStorePrivateKeyStore); + System.clearProperty("aws.region"); + } +} \ No newline at end of file diff --git a/libs/java/auth_core/src/test/java/com/yahoo/athenz/auth/impl/AWSParameterStorePrivateKeyStoreTest.java b/libs/java/auth_core/src/test/java/com/yahoo/athenz/auth/impl/AWSParameterStorePrivateKeyStoreTest.java new file mode 100644 index 00000000000..16a451b6066 --- /dev/null +++ b/libs/java/auth_core/src/test/java/com/yahoo/athenz/auth/impl/AWSParameterStorePrivateKeyStoreTest.java @@ -0,0 +1,76 @@ +/* + * Copyright The Athenz Authors + * + * Licensed 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 com.yahoo.athenz.auth.impl; +import com.yahoo.athenz.auth.PrivateKeyStore; +import org.mockito.Mockito; +import org.testng.annotations.Test; +import software.amazon.awssdk.services.ssm.SsmClient; +import software.amazon.awssdk.services.ssm.model.GetParameterResponse; +import software.amazon.awssdk.services.ssm.model.Parameter; + +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.function.Consumer; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.testng.Assert.*; + +public class AWSParameterStorePrivateKeyStoreTest { + + private AWSParameterStorePrivateKeyStoreFactory getFactory(final SsmClient ssmClient) { + return new AWSParameterStorePrivateKeyStoreFactory() { + @Override + public PrivateKeyStore create() { + return new AWSParameterStorePrivateKeyStore(ssmClient); + } + }; + } + + @Test + public void testGetSecret() { + SsmClient ssmClient = Mockito.mock(SsmClient.class); + when(ssmClient.getParameter(any(Consumer.class))) + .thenReturn(GetParameterResponse.builder().parameter(Parameter.builder().value("secret").build()).build()); + AWSParameterStorePrivateKeyStore store = (AWSParameterStorePrivateKeyStore)getFactory(ssmClient).create(); + assertEquals(store.getSecret("app1", "key1"), "secret".toCharArray()); + } + + @Test + public void testGetPrivateKey() throws IOException { + SsmClient ssmClient = Mockito.mock(SsmClient.class); + Path path = Paths.get("src/test/resources/unit_test_ec_private.key"); + try (FileInputStream fis = new FileInputStream(path.toFile())) { + String secret = new String(fis.readAllBytes()); + when(ssmClient.getParameter(any(Consumer.class))) + .thenReturn(GetParameterResponse.builder().parameter(Parameter.builder().value(secret).build()).build()); + AWSParameterStorePrivateKeyStore store = (AWSParameterStorePrivateKeyStore)getFactory(ssmClient).create(); + assertNotNull(store.getPrivateKey("zms", "host1", "region1", "EC")); + } + } + + @Test + public void testGetPrivateKeyInvalidInputs() { + SsmClient ssmClient = Mockito.mock(SsmClient.class); + AWSParameterStorePrivateKeyStore store = (AWSParameterStorePrivateKeyStore)getFactory(ssmClient).create(); + assertNull(store.getPrivateKey("unknown", "host1", "region1", "EC")); + assertNull(store.getPrivateKey("zms", "host1", "region1", "unknown")); + assertNull(store.getPrivateKey("zms", "host1", null, "RSA")); + } +} \ No newline at end of file diff --git a/libs/java/server_common/pom.xml b/libs/java/server_common/pom.xml index fbdbfe094fc..0a59a931a15 100644 --- a/libs/java/server_common/pom.xml +++ b/libs/java/server_common/pom.xml @@ -168,11 +168,6 @@ bcutil-jdk18on ${bouncycastle.version} - - org.bouncycastle - bcprov-ext-jdk18on - 1.78 - com.fasterxml.jackson.core jackson-databind