diff options
author | Ola Aunrønning <olaa@verizonmedia.com> | 2021-03-05 13:05:17 +0100 |
---|---|---|
committer | Ola Aunrønning <olaa@verizonmedia.com> | 2021-03-05 13:08:08 +0100 |
commit | a274f9d5b8f24676a576e12ef35423549eea0d56 (patch) | |
tree | e24a785b362d2c87cf1f98285c5545b36726f988 | |
parent | 1acaa2e62aecdda2b4c321ae133654cc1a0316f3 (diff) |
Include region and parameter name when validating secret store. Don't inject AwsParameterStore to AwsParameterStoreValidationHandler
13 files changed, 147 insertions, 103 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java index 54ff693dbf5..c044a545b6f 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java @@ -29,6 +29,7 @@ import com.yahoo.docproc.jdisc.metric.NullMetric; import com.yahoo.io.IOUtils; import com.yahoo.jdisc.Metric; import com.yahoo.path.Path; +import com.yahoo.slime.Slime; import com.yahoo.transaction.NestedTransaction; import com.yahoo.transaction.Transaction; import com.yahoo.vespa.config.server.application.Application; @@ -702,9 +703,9 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye : applicationSet.get().getAllVersions(applicationId); } - public HttpResponse validateSecretStore(ApplicationId applicationId, TenantSecretStore tenantSecretStore, String tenantSecretName) { + public HttpResponse validateSecretStore(ApplicationId applicationId, SystemName systemName, Slime slime) { Application application = getApplication(applicationId); - return secretStoreValidator.validateSecretStore(application, tenantSecretStore, tenantSecretName); + return secretStoreValidator.validateSecretStore(application, systemName, slime); } // ---------------- Convergence ---------------------------------------------------------------- diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/SecretStoreValidator.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/SecretStoreValidator.java index 72f5747e15c..71eed450955 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/SecretStoreValidator.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/SecretStoreValidator.java @@ -3,12 +3,16 @@ package com.yahoo.vespa.config.server.http; import ai.vespa.util.http.VespaHttpClientBuilder; import com.yahoo.config.model.api.HostInfo; +import com.yahoo.config.provision.SystemName; +import com.yahoo.config.provision.TenantName; +import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.container.jdisc.HttpResponse; import com.yahoo.container.jdisc.secretstore.SecretStore; import com.yahoo.slime.Slime; import com.yahoo.slime.SlimeUtils; import com.yahoo.vespa.config.server.application.Application; import com.yahoo.config.model.api.TenantSecretStore; +import com.yahoo.vespa.config.server.tenant.SecretStoreExternalIdRetriever; import com.yahoo.yolean.Exceptions; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ByteArrayEntity; @@ -22,6 +26,7 @@ import static com.yahoo.yolean.Exceptions.uncheck; /** * @author olaa + * Takes the payload received from the controller, adds external ID and acts as a proxy for the AwsParameterStoreValidationHandler result */ public class SecretStoreValidator { @@ -35,22 +40,12 @@ public class SecretStoreValidator { this.secretStore = secretStore; } - public HttpResponse validateSecretStore(Application application, TenantSecretStore tenantSecretStore, String tenantSecretName) { - var slime = toSlime(tenantSecretStore, tenantSecretName); + public HttpResponse validateSecretStore(Application application, SystemName system, Slime slime) { + addExternalId(application.getId().tenant(), system, slime); var uri = getUri(application); return postRequest(uri, slime); } - private Slime toSlime(TenantSecretStore tenantSecretStore, String tenantSecretName) { - var slime = new Slime(); - var cursor = slime.setObject(); - cursor.setString("externalId", secretStore.getSecret(tenantSecretName)); - cursor.setString("awsId", tenantSecretStore.getAwsId()); - cursor.setString("name", tenantSecretStore.getName()); - cursor.setString("role", tenantSecretStore.getRole()); - return slime; - } - private URI getUri(Application application) { var hostname = application.getModel().getHosts() .stream() @@ -78,4 +73,11 @@ public class SecretStoreValidator { } } + private void addExternalId(TenantName tenantName, SystemName system, Slime slime) { + var data = slime.get(); + var name = data.field("name").asString(); + var secretName = SecretStoreExternalIdRetriever.secretName(tenantName, system, name); + data.setString("externalId", secretStore.getSecret(secretName)); + } + } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java index ba2989164ee..3634a6825a3 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java @@ -29,7 +29,6 @@ import com.yahoo.vespa.config.server.http.HttpHandler; import com.yahoo.vespa.config.server.http.JSONResponse; import com.yahoo.vespa.config.server.http.NotFoundException; import com.yahoo.vespa.config.server.tenant.Tenant; -import com.yahoo.config.model.api.TenantSecretStore; import java.io.IOException; import java.net.URLDecoder; @@ -47,7 +46,6 @@ import java.util.stream.Stream; import static com.yahoo.yolean.Exceptions.uncheck; import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.Map.Entry.comparingByKey; import static java.util.stream.Collectors.toList; /** @@ -70,7 +68,7 @@ public class ApplicationHandler extends HttpHandler { "http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/metrics/*", "http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/metrics/*", "http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/logs", - "http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/validate-secret-store/*", + "http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/validate-secret-store", "http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/tester/*/*", "http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/tester/*", "http://*/application/v2/tenant/*/application/*/environment/*/region/*/instance/*/quota", @@ -231,9 +229,8 @@ public class ApplicationHandler extends HttpHandler { } if (isValidateSecretStoreRequest(request)) { - var tenantSecretStore = tenantSecretStoreFromRequest(request); - var tenantSecretName = tenantSecretNameFromRequest(request); - return applicationRepository.validateSecretStore(applicationId, tenantSecretStore, tenantSecretName); + var slime = uncheck(() -> SlimeUtils.jsonToSlime(request.getData().readAllBytes())); + return applicationRepository.validateSecretStore(applicationId, zone.system(), slime); } throw new NotFoundException("Illegal POST request '" + request.getUri() + "'"); @@ -360,8 +357,8 @@ public class ApplicationHandler extends HttpHandler { } private static boolean isValidateSecretStoreRequest(HttpRequest request) { - return getBindingMatch(request).groupCount() == 8 && - request.getUri().getPath().contains("/validate-secret-store/"); + return getBindingMatch(request).groupCount() == 7 && + request.getUri().getPath().endsWith("/validate-secret-store"); } private static boolean isServiceConvergeListRequest(HttpRequest request) { @@ -424,11 +421,6 @@ public class ApplicationHandler extends HttpHandler { return bm.group(8); } - private static String tenantSecretNameFromRequest(HttpRequest req) { - BindingMatch<?> bm = getBindingMatch(req); - return bm.group(7); - } - private static ApplicationId getApplicationIdFromRequest(HttpRequest req) { // Two bindings for this: with full app id or only application name BindingMatch<?> bm = getBindingMatch(req); @@ -533,14 +525,6 @@ public class ApplicationHandler extends HttpHandler { } - private TenantSecretStore tenantSecretStoreFromRequest(HttpRequest httpRequest) { - var data = uncheck(() -> SlimeUtils.jsonToSlime(httpRequest.getData().readAllBytes()).get()); - var awsId = data.field("awsId").asString(); - var name = data.field("name").asString(); - var role = data.field("role").asString(); - return new TenantSecretStore(name, awsId, role); - } - private static JSONResponse createMessageResponse(String message) { return new JSONResponse(Response.Status.OK) { { object.setString("message", message); } }; } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/SecretStoreExternalIdRetriever.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/SecretStoreExternalIdRetriever.java index cd2ae9d9d0c..0c254606169 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/SecretStoreExternalIdRetriever.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/SecretStoreExternalIdRetriever.java @@ -25,7 +25,7 @@ public class SecretStoreExternalIdRetriever { .collect(Collectors.toList()); } - private static String secretName(TenantName tenant, SystemName system, String storeName) { + public static String secretName(TenantName tenant, SystemName system, String storeName) { return String.format(SECRET_NAME_FORMAT, tenantSecretGroup(system), tenant.value(), storeName); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/MockSecretStoreValidator.java b/configserver/src/test/java/com/yahoo/vespa/config/server/MockSecretStoreValidator.java index c464af404d9..a02e8a3f3a6 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/MockSecretStoreValidator.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/MockSecretStoreValidator.java @@ -1,9 +1,12 @@ // Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.config.server; +import com.yahoo.config.provision.SystemName; import com.yahoo.container.jdisc.HttpResponse; import com.yahoo.container.jdisc.SecretStoreProvider; +import com.yahoo.restapi.SlimeJsonResponse; import com.yahoo.restapi.StringResponse; +import com.yahoo.slime.Slime; import com.yahoo.vespa.config.server.application.Application; import com.yahoo.config.model.api.TenantSecretStore; import com.yahoo.vespa.config.server.http.SecretStoreValidator; @@ -17,7 +20,7 @@ public class MockSecretStoreValidator extends SecretStoreValidator { super(new SecretStoreProvider().get()); } - public HttpResponse validateSecretStore(Application application, TenantSecretStore tenantSecretStore, String tenantSecretName) { - return new StringResponse(tenantSecretStore.toString() + " - " + tenantSecretName); + public HttpResponse validateSecretStore(Application application, SystemName system, Slime slime) { + return new SlimeJsonResponse(slime); } } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/SecretStoreValidatorTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/SecretStoreValidatorTest.java index d308cd72b55..8e5dd16bc05 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/SecretStoreValidatorTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/SecretStoreValidatorTest.java @@ -4,9 +4,13 @@ import com.github.tomakehurst.wiremock.junit.WireMockRule; import com.yahoo.config.model.api.HostInfo; import com.yahoo.config.model.api.Model; import com.yahoo.config.model.api.ServiceInfo; +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.SystemName; +import com.yahoo.config.provision.TenantName; import com.yahoo.container.jdisc.secretstore.SecretStore; +import com.yahoo.slime.SlimeUtils; import com.yahoo.vespa.config.server.application.Application; -import com.yahoo.config.model.api.TenantSecretStore; +import com.yahoo.vespa.config.server.tenant.SecretStoreExternalIdRetriever; import org.junit.Rule; import org.junit.Test; @@ -35,20 +39,26 @@ public class SecretStoreValidatorTest { @Test public void createsCorrectRequestData() throws IOException { var app = mockApplication(); - var tenantSecretStore = new TenantSecretStore("store", "123", "role"); - var tenantSecretName = "some-secret"; - when(secretStore.getSecret(tenantSecretName)).thenReturn("some-secret-value"); - + var requestBody = SlimeUtils.jsonToSlime("{\"awsId\":\"123\"," + + "\"name\":\"store\"," + + "\"role\":\"role\"," + + "\"region\":\"some-region\"," + + "\"parameterName\":\"some-parameter\"" + + "}"); + var expectedSecretName = SecretStoreExternalIdRetriever.secretName(TenantName.defaultName(), SystemName.PublicCd, "store"); + when(secretStore.getSecret(expectedSecretName)).thenReturn("some-secret-value"); stubFor(post(urlEqualTo("/validate-secret-store")) - .withRequestBody(equalToJson("{\"externalId\":\"some-secret-value\"," + - "\"awsId\":\"123\"," + + .withRequestBody(equalToJson("{\"awsId\":\"123\"," + "\"name\":\"store\"," + - "\"role\":\"role\"" + + "\"role\":\"role\"," + + "\"region\":\"some-region\"," + + "\"parameterName\":\"some-parameter\"," + + "\"externalId\":\"some-secret-value\"" + "}")) .willReturn(aResponse() .withStatus(200) .withBody("is ok"))); - var response = secretStoreValidator.validateSecretStore(app, tenantSecretStore, tenantSecretName); + var response = secretStoreValidator.validateSecretStore(app, SystemName.PublicCd, requestBody); var body = new ByteArrayOutputStream(); response.render(body); assertEquals("is ok", body.toString()); @@ -60,6 +70,7 @@ public class SecretStoreValidatorTest { var hostList = createHostList(); when(app.getModel()).thenReturn(model); when(model.getHosts()).thenReturn(hostList); + when(app.getId()).thenReturn(ApplicationId.defaultId()); return app; } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java index ab9457c5d2a..d364785f415 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java @@ -401,17 +401,17 @@ public class ApplicationHandlerTest { @Test public void testValidateSecretStore() throws IOException { applicationRepository.deploy(new File("src/test/apps/app-logserver-with-container"), prepareParams(applicationId)); - var url = toUrlPath(applicationId, Zone.defaultZone(), true) + "/validate-secret-store/some-secret-name"; + var url = toUrlPath(applicationId, Zone.defaultZone(), true) + "/validate-secret-store"; var mockHandler = createApplicationHandler(); - var requestData = new ByteArrayInputStream("{\"name\": \"store\", \"awsId\":\"aws-id\", \"role\":\"role\"}".getBytes(StandardCharsets.UTF_8)); + var requestString = "{\"name\":\"store\",\"awsId\":\"aws-id\",\"role\":\"role\",\"region\":\"us-west-1\",\"parameterName\":\"some-parameter\"}"; + var requestData = new ByteArrayInputStream(requestString.getBytes(StandardCharsets.UTF_8)); var response = mockHandler.handle(createTestRequest(url, POST, requestData)); assertEquals(200, response.getStatus()); - // MockSecretStoreValidator returns response on format tenantSecretStore.toString() - tenantSecretName - var expectedResponse = "TenantSecretStore{name='store', awsId='aws-id', role='role'} - some-secret-name"; - assertEquals(expectedResponse, getRenderedString(response)); + // MockSecretStoreValidator simply returns the request body + assertEquals(requestString, getRenderedString(response)); } @Test diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java index 315665dbffc..ed545dc35d1 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/ConfigServer.java @@ -151,6 +151,6 @@ public interface ConfigServer { void setSuspension(DeploymentId deploymentId, boolean suspend); /** Validates secret store configuration. */ - String validateSecretStore(DeploymentId deploymentId, TenantSecretStore tenantSecretStore); + String validateSecretStore(DeploymentId deploymentId, TenantSecretStore tenantSecretStore, String region, String parameterName); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java index 4ecd972fc24..228a7bab439 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java @@ -223,7 +223,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { if (path.matches("/application/v4/tenant")) return tenants(request); if (path.matches("/application/v4/tenant/{tenant}")) return tenant(path.get("tenant"), request); if (path.matches("/application/v4/tenant/{tenant}/info")) return tenantInfo(path.get("tenant"), request); - if (path.matches("/application/v4/tenant/{tenant}/secret-store/{name}/validate")) return validateSecretStore(path.get("tenant"), path.get("name")); + if (path.matches("/application/v4/tenant/{tenant}/secret-store/{name}/region/{region}/parameter-name/{parameter-name}/validate")) return validateSecretStore(path.get("tenant"), path.get("name"), path.get("region"), path.get("parameter-name")); if (path.matches("/application/v4/tenant/{tenant}/application")) return applications(path.get("tenant"), Optional.empty(), request); if (path.matches("/application/v4/tenant/{tenant}/application/{application}")) return application(path.get("tenant"), path.get("application"), request); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/compile-version")) return compileVersion(path.get("tenant"), path.get("application")); @@ -584,7 +584,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { } - private HttpResponse validateSecretStore(String tenantName, String name) { + private HttpResponse validateSecretStore(String tenantName, String name, String region, String parameterName) { var tenant = TenantName.from(tenantName); if (controller.tenants().require(tenant).type() != Tenant.Type.cloud) return ErrorResponse.badRequest("Tenant '" + tenant + "' is not a cloud tenant"); @@ -601,7 +601,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { if (tenantSecretStore.isEmpty()) return ErrorResponse.notFoundError("No secret store '" + name + "' configured for tenant '" + tenantName + "'"); - var response = controller.serviceRegistry().configServer().validateSecretStore(deployment.get(), tenantSecretStore.get()); + var response = controller.serviceRegistry().configServer().validateSecretStore(deployment.get(), tenantSecretStore.get(), region, parameterName); return new MessageResponse(response); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java index b669e942494..a5c2176b321 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java @@ -576,7 +576,7 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer } @Override - public String validateSecretStore(DeploymentId deployment, TenantSecretStore tenantSecretStore) { + public String validateSecretStore(DeploymentId deployment, TenantSecretStore tenantSecretStore, String region, String parameterName) { return deployment.toString() + " - " + tenantSecretStore.toString(); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java index 4d18388dda6..84ef800b2f2 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java @@ -152,7 +152,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { @Test public void validate_secret_store() { var secretStoreRequest = - request("/application/v4/tenant/scoober/secret-store/secret-foo/validate", GET) + request("/application/v4/tenant/scoober/secret-store/secret-foo/region/us-west-1/parameter-name/foo/validate", GET) .roles(Set.of(Role.administrator(tenantName))); tester.assertResponse(secretStoreRequest, "{" + "\"error-code\":\"BAD_REQUEST\"," + @@ -161,7 +161,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { deployApplication(); secretStoreRequest = - request("/application/v4/tenant/scoober/secret-store/secret-foo/validate", GET) + request("/application/v4/tenant/scoober/secret-store/secret-foo/region/us-west-1/parameter-name/foo/validate", GET) .roles(Set.of(Role.administrator(tenantName))); tester.assertResponse(secretStoreRequest, "{" + "\"error-code\":\"NOT_FOUND\"," + @@ -175,7 +175,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { // ConfigServerMock returns message on format deployment.toString() + " - " + tenantSecretStore.toString() secretStoreRequest = - request("/application/v4/tenant/scoober/secret-store/secret-foo/validate", GET) + request("/application/v4/tenant/scoober/secret-store/secret-foo/region/us-west-1/parameter-name/foo/validate", GET) .roles(Set.of(Role.administrator(tenantName))); tester.assertResponse(secretStoreRequest, "{" + "\"message\":\"scoober.albums in prod.us-central-1 - TenantSecretStore{name='secret-foo', awsId='123', role='some-role'}\"" + diff --git a/jdisc-cloud-aws/src/main/java/com/yahoo/jdisc/cloud/aws/AwsParameterStore.java b/jdisc-cloud-aws/src/main/java/com/yahoo/jdisc/cloud/aws/AwsParameterStore.java index 3e90e4ca204..48436a086ee 100644 --- a/jdisc-cloud-aws/src/main/java/com/yahoo/jdisc/cloud/aws/AwsParameterStore.java +++ b/jdisc-cloud-aws/src/main/java/com/yahoo/jdisc/cloud/aws/AwsParameterStore.java @@ -14,6 +14,11 @@ import com.yahoo.component.AbstractComponent; import com.yahoo.container.jdisc.secretstore.SecretNotFoundException; import com.yahoo.container.jdisc.secretstore.SecretStore; import com.yahoo.container.jdisc.secretstore.SecretStoreConfig; +import com.yahoo.slime.Cursor; +import com.yahoo.slime.Slime; + +import java.util.List; +import java.util.stream.Collectors; /** * @author mortent @@ -21,32 +26,36 @@ import com.yahoo.container.jdisc.secretstore.SecretStoreConfig; public class AwsParameterStore extends AbstractComponent implements SecretStore { private final VespaAwsCredentialsProvider credentialsProvider; - private final SecretStoreConfig secretStoreConfig; + private final List<AwsSettings> configuredStores; @Inject public AwsParameterStore(SecretStoreConfig secretStoreConfig) { - this.secretStoreConfig = secretStoreConfig; + this(translateConfig(secretStoreConfig)); + } + + public AwsParameterStore(List<AwsSettings> configuredStores) { + this.configuredStores = configuredStores; this.credentialsProvider = new VespaAwsCredentialsProvider(); } @Override public String getSecret(String key) { - for (var group : secretStoreConfig.groups()) { + for (var store : configuredStores) { AWSSecurityTokenService tokenService = AWSSecurityTokenServiceClientBuilder .standard() - .withRegion(group.region()) + .withRegion(store.getRegion()) .withCredentials(credentialsProvider) .build(); STSAssumeRoleSessionCredentialsProvider assumeExtAccountRole = new STSAssumeRoleSessionCredentialsProvider - .Builder(toRoleArn(group.awsId(), group.role()), "vespa") - .withExternalId(group.externalId()) + .Builder(toRoleArn(store.getAwsId(), store.getRole()), "vespa") + .withExternalId(store.getExternalId()) .withStsClient(tokenService) .build(); AWSSimpleSystemsManagement client = AWSSimpleSystemsManagementClient.builder() .withCredentials(assumeExtAccountRole) - .withRegion(group.region()) + .withRegion(store.getRegion()) .build(); GetParametersRequest parametersRequest = new GetParametersRequest().withNames(key).withWithDecryption(true); @@ -70,4 +79,67 @@ public class AwsParameterStore extends AbstractComponent implements SecretStore private String toRoleArn(String awsId, String role) { return "arn:aws:iam::" + awsId + ":role/" + role; } + + private static List<AwsSettings> translateConfig(SecretStoreConfig secretStoreConfig) { + return secretStoreConfig.groups() + .stream() + .map(config -> new AwsSettings(config.name(), config.role(), config.awsId(), config.externalId(), config.region())) + .collect(Collectors.toList()); + } + + public static class AwsSettings { + String name; + String role; + String awsId; + String externalId; + String region; + + AwsSettings(String name, String role, String awsId, String externalId, String region) { + this.name = name; + this.role = role; + this.awsId = awsId; + this.externalId = externalId; + this.region = region; + } + + + public String getName() { + return name; + } + + public String getRole() { + return role; + } + + public String getAwsId() { + return awsId; + } + + public String getExternalId() { + return externalId; + } + + public String getRegion() { + return region; + } + + static AwsSettings fromSlime(Slime slime) { + var json = slime.get(); + return new AwsSettings( + json.field("name").asString(), + json.field("role").asString(), + json.field("awsId").asString(), + json.field("externalId").asString(), + json.field("region").asString() + ); + } + + void toSlime(Cursor slime) { + slime.setString("name", name); + slime.setString("role", role); + slime.setString("awsId", awsId); + slime.setString("externalId", "*****"); + slime.setString("region", region); + } + } } diff --git a/jdisc-cloud-aws/src/main/java/com/yahoo/jdisc/cloud/aws/AwsParameterStoreValidationHandler.java b/jdisc-cloud-aws/src/main/java/com/yahoo/jdisc/cloud/aws/AwsParameterStoreValidationHandler.java index d45ead37480..d813f04512a 100644 --- a/jdisc-cloud-aws/src/main/java/com/yahoo/jdisc/cloud/aws/AwsParameterStoreValidationHandler.java +++ b/jdisc-cloud-aws/src/main/java/com/yahoo/jdisc/cloud/aws/AwsParameterStoreValidationHandler.java @@ -8,13 +8,14 @@ import com.yahoo.container.jdisc.LoggingRequestHandler; import com.yahoo.io.IOUtils; import com.yahoo.restapi.ErrorResponse; import com.yahoo.restapi.SlimeJsonResponse; -import com.yahoo.slime.Cursor; import com.yahoo.slime.Slime; import com.yahoo.slime.SlimeUtils; import com.yahoo.yolean.Exceptions; +import com.yahoo.jdisc.cloud.aws.AwsParameterStore.AwsSettings; import java.io.IOException; import java.io.InputStream; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -27,12 +28,10 @@ import java.util.logging.Logger; public class AwsParameterStoreValidationHandler extends LoggingRequestHandler { private static final Logger log = Logger.getLogger(AwsParameterStoreValidationHandler.class.getName()); - private final AwsParameterStore awsParameterStore; @Inject - public AwsParameterStoreValidationHandler(Context ctx, AwsParameterStore awsParameterStore) { + public AwsParameterStoreValidationHandler(Context ctx) { super(ctx); - this.awsParameterStore = awsParameterStore; } @Override @@ -57,7 +56,9 @@ public class AwsParameterStoreValidationHandler extends LoggingRequestHandler { settings.toSlime(root.setObject("settings")); try { - awsParameterStore.getSecret("vespa-secret"); + var parameterName = json.get().field("parameterName").asString(); + var store = new AwsParameterStore(List.of(settings)); + store.getSecret(parameterName); root.setString("status", "ok"); } catch (RuntimeException e) { root.setString("status", "error"); @@ -78,34 +79,4 @@ public class AwsParameterStoreValidationHandler extends LoggingRequestHandler { } } - private static class AwsSettings { - String name; - String role; - String awsId; - String externalId; - - AwsSettings(String name, String role, String awsId, String externalId) { - this.name = name; - this.role = role; - this.awsId = awsId; - this.externalId = externalId; - } - - static AwsSettings fromSlime(Slime slime) { - var json = slime.get(); - return new AwsSettings( - json.field("name").asString(), - json.field("role").asString(), - json.field("awsId").asString(), - json.field("externalId").asString() - ); - } - - void toSlime(Cursor slime) { - slime.setString("name", name); - slime.setString("role", role); - slime.setString("awsId", awsId); - slime.setString("externalId", "*****"); - } - } -} +}
\ No newline at end of file |