-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#21] Enable fetching KeyPair from AWS SecretsManager
This also somewhat reworks the configuration and logic so as to simplify adding more sources for secrets (e.g., Vault, Azure, etc.).
- Loading branch information
Showing
11 changed files
with
340 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
106 changes: 106 additions & 0 deletions
106
jwt-opa/src/main/java/com/alertavert/opa/security/aws/AwsClientConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
// Copyright (c) 2022 AlertAvert.com. All rights reserved. | ||
// | ||
|
||
package com.alertavert.opa.security.aws; | ||
|
||
import com.alertavert.opa.Constants; | ||
import com.alertavert.opa.ExcludeFromCoverageGenerated; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.context.annotation.Profile; | ||
import org.springframework.util.StringUtils; | ||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; | ||
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider; | ||
import software.amazon.awssdk.auth.credentials.WebIdentityTokenFileCredentialsProvider; | ||
import software.amazon.awssdk.regions.Region; | ||
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; | ||
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClientBuilder; | ||
|
||
import java.net.URI; | ||
|
||
import static com.alertavert.opa.Constants.AWS_TOKEN_FILE; | ||
import static com.alertavert.opa.Constants.CREDENTIALS_PROVIDER_LOG; | ||
|
||
|
||
/** | ||
* <H2>PatientsApplicationConfiguration</H2> | ||
* | ||
* <p>System and application configuration goes here. | ||
* | ||
* <p>Here we are trying to configure the AWS client, based on | ||
* whether we auto-detect the Server to be running locally, or on EKS.</p> | ||
* | ||
* @author M. Massenzio, 2020-10-06 | ||
*/ | ||
@Configuration | ||
@Slf4j | ||
@ExcludeFromCoverageGenerated | ||
public class AwsClientConfiguration { | ||
|
||
@Value("${aws.region:}") | ||
String region; | ||
|
||
@Value("${aws.profile:}") | ||
String profile; | ||
|
||
@Value("${aws.endpoint:}") | ||
String endpoint; | ||
|
||
@Value("${aws.keypair.secret_name:}") | ||
private String keypairSecretName; | ||
|
||
public Region region() { | ||
return Region.of(region); | ||
} | ||
|
||
public String awsProfile() { | ||
return profile; | ||
} | ||
|
||
/** | ||
* <p>Instantiates a Client for AWS Secrets Manager service, using a `profile` (whose credentials | ||
* are stored in {@literal ~/.aws/credentials}) if the {@literal aws.profile} property is set | ||
* (typically for local runs/tests); otherwise it uses the | ||
* {@link WebIdentityTokenFileCredentialsProvider Token Provider} which derives the API Token | ||
* from a file stored in the container at the location pointed at by the env var | ||
* {@link Constants#AWS_TOKEN_FILE}; the latter is used when running the service in an EKS Container. | ||
* | ||
* <p><strong>For the above to work, the STS service SDK must be in the classpath</strong> | ||
* <pre> | ||
* implementation 'software.amazon.awssdk:sts' | ||
* </pre> | ||
* | ||
* @return a Client to access Secrets Manager and retrieve secrets (passwords) | ||
*/ | ||
@Bean @Profile("aws") | ||
public SecretsManagerClient secretsManagerClient() { | ||
log.debug("Instantiating the SecretsManager Client for region = {}", region()); | ||
|
||
AwsCredentialsProvider provider; | ||
if (StringUtils.hasText(profile)) { | ||
log.info(CREDENTIALS_PROVIDER_LOG, "Profile", profile); | ||
provider = ProfileCredentialsProvider.create(profile); | ||
} else { | ||
log.info(CREDENTIALS_PROVIDER_LOG, "Token File", System.getenv(AWS_TOKEN_FILE)); | ||
provider = WebIdentityTokenFileCredentialsProvider.create(); | ||
} | ||
|
||
SecretsManagerClientBuilder builder = SecretsManagerClient.builder() | ||
.region(region()) | ||
.credentialsProvider(provider); | ||
|
||
// Typically this is overridden in tests, to point to a local Secrets Manager (e.g. using | ||
// LocalStack, pointing to http://localhost:4566) | ||
if (StringUtils.hasText(endpoint)) { | ||
builder.endpointOverride(URI.create(endpoint)); | ||
} | ||
return builder.build(); | ||
} | ||
|
||
@Bean | ||
String keypairSecretName() { | ||
return keypairSecretName; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
83 changes: 83 additions & 0 deletions
83
jwt-opa/src/test/java/com/alertavert/opa/security/aws/AwsSecretsKeypairReaderTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/* | ||
* Copyright (c) 2022 AlertAvert.com. All rights reserved. | ||
* | ||
* 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. | ||
* | ||
* Author: Marco Massenzio (marco@alertavert.com) | ||
*/ | ||
|
||
package com.alertavert.opa.security.aws; | ||
|
||
import com.alertavert.opa.security.crypto.KeypairReader; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
import org.springframework.test.context.ActiveProfiles; | ||
import org.springframework.test.context.ContextConfiguration; | ||
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; | ||
import software.amazon.awssdk.services.secretsmanager.model.CreateSecretRequest; | ||
|
||
import java.security.KeyPair; | ||
import java.util.Base64; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
|
||
/** | ||
* <H2>AwsSecretsKeypairReaderTest</H2> | ||
* | ||
* @author M. Massenzio, 2022-10-28 | ||
*/ | ||
@SpringBootTest(classes = { | ||
AwsClientConfiguration.class, | ||
}) | ||
@ActiveProfiles(profiles = {"test", "aws"}) | ||
@ContextConfiguration(initializers = {AwsSecretsManagerResolverTest.Initializer.class}) | ||
class AwsSecretsKeypairReaderTest { | ||
|
||
@Autowired | ||
SecretsManagerClient secretsManagerClient; | ||
|
||
String keypairName = "test-keypair"; | ||
String secret = """ | ||
{ | ||
"priv": "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgYEIUVkTtwBilNcNoEzsP3jdslIlOtXQ5pByuzxhLJTChRANCAAS+gitL0EgxyFCvdT6rJ39DbCrLLwLReTA5OXahcIEeCBygfyh35H8T9r9uHszSOCpAk1QQMuhqURzyWEaKjk92", | ||
"pub": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvoIrS9BIMchQr3U+qyd/Q2wqyy8C0XkwOTl2oXCBHggcoH8od+R/E/a/bh7M0jgqQJNUEDLoalEc8lhGio5Pdg==", | ||
"algorithm": "EC" | ||
} | ||
"""; | ||
String priv = "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgYEIUVkTtwBilNcNoEzsP3jdslIlO" | ||
+ "tXQ5pByuzxhLJTChRANCAAS+gitL0EgxyFCvdT6rJ39DbCrLLwLReTA5OXahcIEeCBygfyh35H8T9r9uHs" | ||
+ "zSOCpAk1QQMuhqURzyWEaKjk92"; | ||
|
||
|
||
@BeforeEach | ||
void setUp() { | ||
secretsManagerClient.createSecret(CreateSecretRequest.builder() | ||
.name(keypairName) | ||
.secretString(secret) | ||
.build()); | ||
} | ||
|
||
@Test | ||
public void getSecret() { | ||
KeypairReader reader = new AwsSecretsKeypairReader( | ||
new AwsSecretsManagerResolver(secretsManagerClient), keypairName); | ||
KeyPair keyPair = reader.loadKeys(); | ||
assertNotNull(keyPair); | ||
assertThat(Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded())) | ||
.isEqualTo(priv); | ||
assertThat(reader.algorithm()).isEqualTo("EC"); | ||
} | ||
} |
Oops, something went wrong.