diff options
17 files changed, 65 insertions, 204 deletions
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGenerator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGenerator.java index 5138bee1ff6..5143a38b2c1 100644 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGenerator.java +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGenerator.java @@ -3,10 +3,7 @@ package com.yahoo.vespa.hosted.athenz.instanceproviderservice; import com.yahoo.component.annotation.Inject; import com.yahoo.config.provision.Zone; -import com.yahoo.container.jdisc.secretstore.SecretStore; import com.yahoo.net.HostName; -import com.yahoo.security.KeyUtils; - import com.yahoo.vespa.athenz.api.AthenzService; import com.yahoo.vespa.athenz.identityprovider.api.ClusterType; import com.yahoo.vespa.athenz.identityprovider.api.IdentityType; @@ -35,20 +32,17 @@ public class IdentityDocumentGenerator { private final NodeRepository nodeRepository; private final Zone zone; private final KeyProvider keyProvider; - private final SecretStore secretStore; private final AthenzProviderServiceConfig athenzProviderServiceConfig; @Inject public IdentityDocumentGenerator(AthenzProviderServiceConfig config, NodeRepository nodeRepository, Zone zone, - KeyProvider keyProvider, - SecretStore secretStore) { + KeyProvider keyProvider) { this.athenzProviderServiceConfig = config; this.nodeRepository = nodeRepository; this.zone = zone; this.keyProvider = keyProvider; - this.secretStore = secretStore; } public SignedIdentityDocument generateSignedIdentityDocument(String hostname, IdentityType identityType) { @@ -67,7 +61,7 @@ public class IdentityDocumentGenerator { Set<String> ips = new HashSet<>(node.ipConfig().primary()); - PrivateKey privateKey = privateKey(node); + PrivateKey privateKey = keyProvider.getPrivateKey(athenzProviderServiceConfig.secretVersion()); AthenzService providerService = new AthenzService(athenzProviderServiceConfig.domain(), athenzProviderServiceConfig.serviceName()); String configServerHostname = HostName.getLocalhost(); @@ -79,28 +73,11 @@ public class IdentityDocumentGenerator { return new SignedIdentityDocument( signature, athenzProviderServiceConfig.secretVersion(), providerUniqueId, providerService, SignedIdentityDocument.DEFAULT_DOCUMENT_VERSION, configServerHostname, node.hostname(), - createdAt, ips, identityType, clusterType, ztsUrl(node)); + createdAt, ips, identityType, clusterType); } catch (Exception e) { throw new RuntimeException("Exception generating identity document: " + e.getMessage(), e); } } - private PrivateKey privateKey(Node node) { - // return sisSecret for public non-enclave hosts. secret otherwise - if (zone.system().isPublic() && !node.cloudAccount().isEnclave(zone)) { - String keyPem = secretStore.getSecret(athenzProviderServiceConfig.sisSecretName(), athenzProviderServiceConfig.sisSecretVersion()); - return KeyUtils.fromPemEncodedPrivateKey(keyPem); - } else { - return keyProvider.getPrivateKey(athenzProviderServiceConfig.secretVersion()); - } - } - private String ztsUrl(Node node) { - // return sisUrl for public non-enclave hosts, ztsUrl otherwise - if (zone.system().isPublic() && !node.cloudAccount().isEnclave(zone)) { - return athenzProviderServiceConfig.sisUrl(); - } else { - return athenzProviderServiceConfig.ztsUrl(); - } - } } diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidator.java index a349bddc76b..d8bbf743d8c 100644 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidator.java +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidator.java @@ -7,9 +7,6 @@ import com.yahoo.config.model.api.ApplicationInfo; import com.yahoo.config.model.api.ServiceInfo; import com.yahoo.config.model.api.SuperModelProvider; import com.yahoo.config.provision.ApplicationId; -import com.yahoo.config.provision.Zone; -import com.yahoo.container.jdisc.secretstore.SecretStore; -import com.yahoo.security.KeyUtils; import com.yahoo.vespa.athenz.api.AthenzService; import com.yahoo.vespa.athenz.identityprovider.api.ClusterType; import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper; @@ -55,32 +52,25 @@ public class InstanceValidator { private final KeyProvider keyProvider; private final SuperModelProvider superModelProvider; private final NodeRepository nodeRepository; - private final SecretStore secretStore; - private final String sisSecretName; @Inject public InstanceValidator(KeyProvider keyProvider, SuperModelProvider superModelProvider, NodeRepository nodeRepository, - AthenzProviderServiceConfig config, - SecretStore secretStore) { - this(keyProvider, superModelProvider, nodeRepository, new IdentityDocumentSigner(), new AthenzService(config.tenantService()), secretStore, config.sisSecretName()); + AthenzProviderServiceConfig config) { + this(keyProvider, superModelProvider, nodeRepository, new IdentityDocumentSigner(), new AthenzService(config.tenantService())); } public InstanceValidator(KeyProvider keyProvider, SuperModelProvider superModelProvider, NodeRepository nodeRepository, IdentityDocumentSigner identityDocumentSigner, - AthenzService tenantIdentity, - SecretStore secretStore, - String sisSecretName) { + AthenzService tenantIdentity){ this.keyProvider = keyProvider; this.superModelProvider = superModelProvider; this.nodeRepository = nodeRepository; this.signer = identityDocumentSigner; this.tenantDockerContainerIdentity = tenantIdentity; - this.secretStore = secretStore; - this.sisSecretName = sisSecretName; } public boolean isValidInstance(InstanceConfirmation instanceConfirmation) { @@ -112,30 +102,16 @@ public class InstanceValidator { log.log(Level.FINE, () -> String.format("Validating instance %s.", providerUniqueId)); - // Find node matching vespa unique id - Node node = getNode(providerUniqueId); - - PublicKey publicKey = publicKey(node, signedIdentityDocument.signingKeyVersion()); + PublicKey publicKey = keyProvider.getPublicKey(signedIdentityDocument.signingKeyVersion()); if (! signer.hasValidSignature(signedIdentityDocument, publicKey)) { var msg = String.format("Instance %s has invalid signature.", providerUniqueId); throw new ValidationException(Level.SEVERE, () -> msg); } - validateAttributes(node, req, providerUniqueId); + validateAttributes(req, providerUniqueId); log.log(Level.FINE, () -> String.format("Instance %s is valid.", providerUniqueId)); } - private PublicKey publicKey(Node node, int version) { - // return sisSecret for public non-enclave hosts. - Zone zone = nodeRepository.zone(); - if (zone.system().isPublic() && !node.cloudAccount().isEnclave(zone)) { - String keyPem = secretStore.getSecret(sisSecretName, version); - return KeyUtils.extractPublicKey(KeyUtils.fromPemEncodedPrivateKey(keyPem)); - } else { - return keyProvider.getPublicKey(version); - } - } - // TODO Add actual validation. Cannot reuse isValidInstance as identity document is not part of the refresh request. // We'll have to perform some validation on the instance id and other fields of the attribute map. // Separate between tenant and node certificate as well. @@ -145,8 +121,7 @@ public class InstanceValidator { confirmation.provider, confirmation.attributes.get(SAN_DNS_ATTRNAME))); try { - VespaUniqueInstanceId vespaUniqueInstanceId = getVespaUniqueInstanceId(confirmation); - validateAttributes(getNode(vespaUniqueInstanceId), confirmation, vespaUniqueInstanceId); + validateAttributes(confirmation, getVespaUniqueInstanceId(confirmation)); return true; } catch (ValidationException e) { log.log(e.logLevel(), e.messageSupplier()); @@ -171,13 +146,24 @@ public class InstanceValidator { .orElse(null); } - private void validateAttributes(Node node, InstanceConfirmation confirmation, VespaUniqueInstanceId vespaUniqueInstanceId) + private void validateAttributes(InstanceConfirmation confirmation, VespaUniqueInstanceId vespaUniqueInstanceId) throws ValidationException { if(vespaUniqueInstanceId == null) { var msg = "Unable to find unique instance ID in refresh request: " + confirmation.toString(); throw new ValidationException(Level.WARNING, () -> msg); } + // Find node matching vespa unique id + Node node = nodeRepository.nodes().list().stream() + .filter(n -> n.allocation().isPresent()) + .filter(n -> nodeMatchesVespaUniqueId(n, vespaUniqueInstanceId)) + .findFirst() // Should be only one + .orElse(null); + if(node == null) { + var msg = "Invalid InstanceConfirmation, No nodes matching uniqueId: " + vespaUniqueInstanceId; + throw new ValidationException(Level.WARNING, () -> msg); + } + // Find list of ipaddresses List<InetAddress> ips = Optional.ofNullable(confirmation.attributes.get(SAN_IPS_ATTRNAME)) .map(s -> s.split(",")) @@ -213,20 +199,6 @@ public class InstanceValidator { } } - private Node getNode(VespaUniqueInstanceId vespaUniqueInstanceId) throws ValidationException { - // Find node matching vespa unique id - Node node = nodeRepository.nodes().list().stream() - .filter(n -> n.allocation().isPresent()) - .filter(n -> nodeMatchesVespaUniqueId(n, vespaUniqueInstanceId)) - .findFirst() // Should be only one - .orElse(null); - if(node == null) { - var msg = "Invalid InstanceConfirmation, No nodes matching uniqueId: " + vespaUniqueInstanceId; - throw new ValidationException(Level.WARNING, () -> msg); - } - return node; - } - private boolean nodeMatchesVespaUniqueId(Node node, VespaUniqueInstanceId vespaUniqueInstanceId) { return node.allocation().map(allocation -> allocation.membership().index() == vespaUniqueInstanceId.clusterIndex() && diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java index 231f22ac56b..531a815922b 100644 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java @@ -65,7 +65,7 @@ public class CertificateAuthorityApiHandler extends ThreadedHttpRequestHandler { super(ctx); this.secretStore = secretStore; this.certificates = certificates; - this.caPrivateKeySecretName = athenzProviderServiceConfig.sisSecretName(); + this.caPrivateKeySecretName = athenzProviderServiceConfig.secretName(); this.caCertificateSecretName = athenzProviderServiceConfig.caCertSecretName(); this.instanceValidator = instanceValidator; } diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializer.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializer.java index 8c575a6403b..fec03afab69 100644 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializer.java +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializer.java @@ -49,7 +49,6 @@ public class InstanceSerializer { private static final String IDD_IPADDRESSES_FIELD = "ip-addresses"; private static final String IDD_IDENTITY_TYPE_FIELD = "identity-type"; private static final String IDD_CLUSTER_TYPE_FIELD = "cluster-type"; - private static final String IDD_ZTS_URL_FIELD = "zts-url"; private static final ObjectMapper objectMapper = new ObjectMapper(); static { @@ -101,12 +100,10 @@ public class InstanceSerializer { IdentityType identityType = IdentityType.fromId(requireField(IDD_IDENTITY_TYPE_FIELD, root).asString()); var clusterTypeField = root.field(IDD_CLUSTER_TYPE_FIELD); var clusterType = clusterTypeField.valid() ? ClusterType.from(clusterTypeField.asString()) : null; - var ztsUrlField = root.field(IDD_ZTS_URL_FIELD); - var ztsUrl = ztsUrlField.valid() ? ztsUrlField.asString() : ""; return new SignedIdentityDocument(signature, (int)signingKeyVersion, providerUniqueId, athenzService, (int)documentVersion, - configserverHostname, instanceHostname, createdAt, ips, identityType, clusterType, ztsUrl); + configserverHostname, instanceHostname, createdAt, ips, identityType, clusterType); } private static Instant getJsr310Instant(double v) { diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGeneratorTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGeneratorTest.java index 340be33c2a3..9205baff0fc 100644 --- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGeneratorTest.java +++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGeneratorTest.java @@ -18,7 +18,6 @@ import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument; import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId; import com.yahoo.vespa.athenz.identityprovider.client.IdentityDocumentSigner; import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig; -import com.yahoo.vespa.hosted.ca.restapi.mock.SecretStoreMock; import com.yahoo.vespa.hosted.provision.Node; import com.yahoo.vespa.hosted.provision.NodeRepository; import com.yahoo.vespa.hosted.provision.node.Allocation; @@ -75,12 +74,11 @@ public class IdentityDocumentGeneratorTest { when(nodes.node(eq(parentHostname))).thenReturn(Optional.of(parentNode)); when(nodes.node(eq(containerHostname))).thenReturn(Optional.of(containerNode)); AutoGeneratedKeyProvider keyProvider = new AutoGeneratedKeyProvider(); - SecretStoreMock secretStore = new SecretStoreMock(); String dnsSuffix = "vespa.dns.suffix"; AthenzProviderServiceConfig config = getAthenzProviderConfig("domain", "service", dnsSuffix); IdentityDocumentGenerator identityDocumentGenerator = - new IdentityDocumentGenerator(config, nodeRepository, ZONE, keyProvider, secretStore); + new IdentityDocumentGenerator(config, nodeRepository, ZONE, keyProvider); SignedIdentityDocument signedIdentityDocument = identityDocumentGenerator.generateSignedIdentityDocument(containerHostname, IdentityType.TENANT); // Verify attributes diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidatorTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidatorTest.java index 42d6b92dea1..a7947aff283 100644 --- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidatorTest.java +++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidatorTest.java @@ -9,16 +9,9 @@ import com.yahoo.config.model.api.ServiceInfo; import com.yahoo.config.model.api.SuperModel; import com.yahoo.config.model.api.SuperModelProvider; import com.yahoo.config.provision.ApplicationId; -import com.yahoo.config.provision.CloudAccount; import com.yahoo.config.provision.ClusterMembership; -import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.NodeType; -import com.yahoo.config.provision.RegionName; -import com.yahoo.config.provision.SystemName; -import com.yahoo.config.provision.Zone; -import com.yahoo.security.KeyAlgorithm; -import com.yahoo.security.KeyUtils; import com.yahoo.vespa.athenz.api.AthenzService; import com.yahoo.vespa.athenz.identityprovider.api.ClusterType; import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper; @@ -26,19 +19,15 @@ import com.yahoo.vespa.athenz.identityprovider.api.IdentityType; import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument; import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId; import com.yahoo.vespa.athenz.identityprovider.client.IdentityDocumentSigner; -import com.yahoo.vespa.curator.mock.MockCurator; import com.yahoo.vespa.hosted.athenz.instanceproviderservice.InstanceValidator.ValidationException; -import com.yahoo.vespa.hosted.ca.restapi.mock.SecretStoreMock; import com.yahoo.vespa.hosted.provision.Node; import com.yahoo.vespa.hosted.provision.NodeList; import com.yahoo.vespa.hosted.provision.NodeRepository; import com.yahoo.vespa.hosted.provision.node.IP; import com.yahoo.vespa.hosted.provision.node.Nodes; import com.yahoo.vespa.hosted.provision.testutils.MockNodeFlavors; -import com.yahoo.vespa.hosted.provision.testutils.MockNodeRepository; import org.junit.jupiter.api.Test; -import java.security.PrivateKey; import java.time.Instant; import java.util.ArrayList; import java.util.Collections; @@ -75,13 +64,11 @@ public class InstanceValidatorTest { private final AthenzService vespaTenantDomain = new AthenzService("vespa.vespa.tenant"); private final AutoGeneratedKeyProvider keyProvider = new AutoGeneratedKeyProvider(); - private final SecretStoreMock secretStore = new SecretStoreMock(); - private final String sisSecretName = "sis-secret-name"; @Test void application_does_not_exist() { SuperModelProvider superModelProvider = mockSuperModelProvider(); - InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, null, vespaTenantDomain, secretStore, sisSecretName); + InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, null, vespaTenantDomain); assertFalse(instanceValidator.isValidInstance(createRegisterInstanceConfirmation(applicationId, domain, service))); } @@ -89,7 +76,7 @@ public class InstanceValidatorTest { void application_does_not_have_domain_set() { SuperModelProvider superModelProvider = mockSuperModelProvider( mockApplicationInfo(applicationId, 5, Collections.emptyList())); - InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, new IdentityDocumentSigner(), vespaTenantDomain, secretStore, sisSecretName); + InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, new IdentityDocumentSigner(), vespaTenantDomain); assertFalse(instanceValidator.isValidInstance(createRegisterInstanceConfirmation(applicationId, domain, service))); } @@ -101,7 +88,7 @@ public class InstanceValidatorTest { SuperModelProvider superModelProvider = mockSuperModelProvider( mockApplicationInfo(applicationId, 5, Collections.singletonList(serviceInfo))); - InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, null, vespaTenantDomain, secretStore, sisSecretName); + InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, null, vespaTenantDomain); assertFalse(instanceValidator.isValidInstance(createRegisterInstanceConfirmation(applicationId, domain, service))); } @@ -119,7 +106,7 @@ public class InstanceValidatorTest { mockApplicationInfo(applicationId, 5, Collections.singletonList(serviceInfo))); IdentityDocumentSigner signer = mock(IdentityDocumentSigner.class); when(signer.hasValidSignature(any(), any())).thenReturn(true); - InstanceValidator instanceValidator = new InstanceValidator(mock(KeyProvider.class), superModelProvider, mockNodeRepo(), signer, vespaTenantDomain, secretStore, sisSecretName); + InstanceValidator instanceValidator = new InstanceValidator(mock(KeyProvider.class), superModelProvider, mockNodeRepo(), signer, vespaTenantDomain); assertTrue(instanceValidator.isValidInstance(createRegisterInstanceConfirmation(applicationId, domain, service))); } @@ -127,7 +114,7 @@ public class InstanceValidatorTest { @Test void rejects_invalid_provider_unique_id_in_csr() { SuperModelProvider superModelProvider = mockSuperModelProvider(); - InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, null, vespaTenantDomain, secretStore, sisSecretName); + InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, null, vespaTenantDomain); InstanceConfirmation instanceConfirmation = createRegisterInstanceConfirmation(applicationId, domain, service); VespaUniqueInstanceId tamperedId = new VespaUniqueInstanceId(0, "default", "instance", "app", "tenant", "us-north-1", "dev", IdentityType.NODE); instanceConfirmation.set("sanDNS", tamperedId.asDottedString() + ".instanceid.athenz.dev-us-north-1.vespa.yahoo.cloud"); @@ -137,7 +124,7 @@ public class InstanceValidatorTest { @Test void rejects_unknown_ips_in_csr() { NodeRepository nodeRepository = mockNodeRepo(); - InstanceValidator instanceValidator = new InstanceValidator(null, mockSuperModelProvider(), nodeRepository, null, vespaTenantDomain, secretStore, sisSecretName); + InstanceValidator instanceValidator = new InstanceValidator(null, mockSuperModelProvider(), nodeRepository, null, vespaTenantDomain); InstanceConfirmation instanceConfirmation = createRegisterInstanceConfirmation(applicationId, domain, service); Set<String> nodeIp = nodeRepository.nodes().list().owner(applicationId).stream().findFirst() .map(Node::ipConfig) @@ -155,7 +142,7 @@ public class InstanceValidatorTest { var props = Map.of(SERVICE_PROPERTIES_DOMAIN_KEY, domain, SERVICE_PROPERTIES_SERVICE_KEY, service); var info = new ServiceInfo("serviceName", "type", List.of(), props, "confId", "hostName"); var provider = mockSuperModelProvider(mockApplicationInfo(applicationId, 5, List.of(info))); - var instanceValidator = new InstanceValidator(keyProvider, provider, mockNodeRepo(), new IdentityDocumentSigner(), vespaTenantDomain, secretStore, sisSecretName); + var instanceValidator = new InstanceValidator(keyProvider, provider, mockNodeRepo(), new IdentityDocumentSigner(), vespaTenantDomain); var instanceConfirmation = createRegisterInstanceConfirmation(applicationId, domain, service); instanceConfirmation.set("sanURI", "vespa://cluster-type/content"); var exception = assertThrows(ValidationException.class, () -> instanceValidator.validateInstance(instanceConfirmation)); @@ -168,7 +155,7 @@ public class InstanceValidatorTest { NodeRepository nodeRepository = mock(NodeRepository.class); Nodes nodes = mock(Nodes.class); when(nodeRepository.nodes()).thenReturn(nodes); - InstanceValidator instanceValidator = new InstanceValidator(null, null, nodeRepository, new IdentityDocumentSigner(), vespaTenantDomain, secretStore, sisSecretName); + InstanceValidator instanceValidator = new InstanceValidator(null, null, nodeRepository, new IdentityDocumentSigner(), vespaTenantDomain); List<Node> nodeList = createNodes(10); Node node = nodeList.get(0); @@ -183,7 +170,7 @@ public class InstanceValidatorTest { @Test void rejects_refresh_on_ip_mismatch() { NodeRepository nodeRepository = mockNodeRepo(); - InstanceValidator instanceValidator = new InstanceValidator(null, null, nodeRepository, new IdentityDocumentSigner(), vespaTenantDomain, secretStore, sisSecretName); + InstanceValidator instanceValidator = new InstanceValidator(null, null, nodeRepository, new IdentityDocumentSigner(), vespaTenantDomain); Set<String> nodeIp = nodeRepository.nodes().list().owner(applicationId).stream().findFirst() .map(Node::ipConfig) @@ -204,7 +191,7 @@ public class InstanceValidatorTest { Nodes nodes = mock(Nodes.class); when(nodeRepository.nodes()).thenReturn(nodes); - InstanceValidator instanceValidator = new InstanceValidator(null, null, nodeRepository, new IdentityDocumentSigner(), vespaTenantDomain, secretStore, sisSecretName); + InstanceValidator instanceValidator = new InstanceValidator(null, null, nodeRepository, new IdentityDocumentSigner(), vespaTenantDomain); List<Node> nodeList = createNodes(10); @@ -215,40 +202,11 @@ public class InstanceValidatorTest { } - @Test - public void uses_correct_keys_in_public() throws ValidationException { - var sisKeyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA, 2048); - secretStore.setSecret(sisSecretName, KeyUtils.toPem(sisKeyPair.getPrivate())); - - var props = Map.of(SERVICE_PROPERTIES_DOMAIN_KEY, domain, SERVICE_PROPERTIES_SERVICE_KEY, service); - var info = new ServiceInfo("serviceName", "type", List.of(), props, "confId", "hostName"); - var provider = mockSuperModelProvider(mockApplicationInfo(applicationId, 5, List.of(info))); - - { - // non Enclave hosts in public should use the SIS key - var nonEnclaveHosts = createNodes(10, Optional.empty()); - var instanceValidator = new InstanceValidator(keyProvider, provider, mockNodeRepo(nonEnclaveHosts, SystemName.PublicCd), new IdentityDocumentSigner(), vespaTenantDomain, secretStore, sisSecretName); - var instanceConfirmation = createRegisterInstanceConfirmation(applicationId, domain, service, sisKeyPair.getPrivate()); - instanceValidator.validateInstance(instanceConfirmation); - } - { - // Enclave hosts should use the Vespa provider key - var enclaveHosts = createNodes(10, Optional.of(CloudAccount.from("123456789012"))); - var instanceValidator = new InstanceValidator(keyProvider, provider, mockNodeRepo(enclaveHosts, SystemName.PublicCd), new IdentityDocumentSigner(), vespaTenantDomain, secretStore, sisSecretName); - var instanceConfirmation = createRegisterInstanceConfirmation(applicationId, domain, service, keyProvider.getPrivateKey(0)); - instanceValidator.validateInstance(instanceConfirmation); - } - } - private NodeRepository mockNodeRepo() { - return mockNodeRepo(createNodes(10), SystemName.cd); - } - - private NodeRepository mockNodeRepo(List<Node> nodeList, SystemName systemName) { NodeRepository nodeRepository = mock(NodeRepository.class); Nodes nodes = mock(Nodes.class); when(nodeRepository.nodes()).thenReturn(nodes); - when(nodeRepository.zone()).thenReturn(new Zone(systemName, Environment.dev, RegionName.from("us-north-1"))); + List<Node> nodeList = createNodes(10); Node node = nodeList.get(0); nodeList = allocateNode(nodeList, node, applicationId); when(nodes.list()).thenReturn(NodeList.copyOf(nodeList)); @@ -257,11 +215,6 @@ public class InstanceValidatorTest { private InstanceConfirmation createRegisterInstanceConfirmation( ApplicationId applicationId, String domain, String service) { - return createRegisterInstanceConfirmation(applicationId, domain, service, keyProvider.getPrivateKey(0)); - } - - private InstanceConfirmation createRegisterInstanceConfirmation( - ApplicationId applicationId, String domain, String service, PrivateKey signingKey) { VespaUniqueInstanceId vespaUniqueInstanceId = new VespaUniqueInstanceId(0, "default", applicationId.instance().value(), applicationId.application().value(), applicationId.tenant().value(), "us-north-1", "dev", IdentityType.NODE); var domainService = new AthenzService(domain, service); var clock = Instant.now(); @@ -269,12 +222,11 @@ public class InstanceValidatorTest { var signature = new IdentityDocumentSigner() .generateSignature( vespaUniqueInstanceId, domainService, "localhost", "localhost", clock, Set.of(), - IdentityType.NODE, signingKey); + IdentityType.NODE, keyProvider.getPrivateKey(0)); SignedIdentityDocument signedIdentityDocument = new SignedIdentityDocument( signature, 0, vespaUniqueInstanceId, domainService, 0, "localhost", "localhost", - clock, Collections.emptySet(), IdentityType.NODE, clusterType, "https://zts.url"); + clock, Collections.emptySet(), IdentityType.NODE, clusterType); return createInstanceConfirmation(vespaUniqueInstanceId, domain, service, signedIdentityDocument); - } private InstanceConfirmation createRefreshInstanceConfirmation(ApplicationId applicationId, String domain, String service, List<String> ips) { @@ -323,17 +275,11 @@ public class InstanceValidatorTest { } private List<Node> createNodes(int num) { - return createNodes(num, Optional.empty()); - } - - private List<Node> createNodes(int num, Optional<CloudAccount> cloudAccount) { MockNodeFlavors flavors = new MockNodeFlavors(); List<Node> nodeList = new ArrayList<>(); for (int i = 0; i < num; i++) { - Node.Builder builder = Node.create("foo" + i, new IP.Config(Set.of("::1" + i, "::2" + i, "::3" + i), Set.of()), - "foo" + i, flavors.getFlavorOrThrow("default"), NodeType.tenant); - cloudAccount.ifPresent(builder::cloudAccount); - Node node = builder.build(); + Node node = Node.create("foo" + i, new IP.Config(Set.of("::1" + i, "::2" + i, "::3" + i), Set.of()), + "foo" + i, flavors.getFlavorOrThrow("default"), NodeType.tenant).build(); nodeList.add(node); } return nodeList; diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/ContainerTester.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/ContainerTester.java index d880fd5220b..8112f5779e5 100644 --- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/ContainerTester.java +++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/ContainerTester.java @@ -64,8 +64,6 @@ public class ContainerTester { " <serviceName>servicename</serviceName>\n" + " <secretName>secretname</secretName>\n" + " <secretVersion>0</secretVersion>\n" + - " <sisSecretName>secretname</sisSecretName>\n" + - " <sisSecretVersion>0</sisSecretVersion>\n" + " <caCertSecretName>vespa.external.ca.cert</caCertSecretName>\n" + " <certDnsSuffix>suffix</certDnsSuffix>\n" + " <ztsUrl>https://localhost:123/</ztsUrl>\n" + diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java index 02398b19627..ca624918beb 100644 --- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java +++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java @@ -1,7 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.ca.restapi; -import com.yahoo.config.provision.Cloud; import com.yahoo.security.Pkcs10CsrUtils; import com.yahoo.security.X509CertificateUtils; import com.yahoo.slime.Slime; @@ -49,8 +48,7 @@ public class InstanceSerializerTest { Instant.now().truncatedTo(ChronoUnit.MICROS), // Truncate to the precision given from EntityBindingsMapper.toAttestationData() Collections.emptySet(), IdentityType.NODE, - ClusterType.CONTAINER, - "https://zts.url"); + ClusterType.CONTAINER); var json = String.format("{\n" + " \"provider\": \"provider_prod_us-north-1\",\n" + diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/InstanceValidatorMock.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/InstanceValidatorMock.java index 163db3e107e..4151c1f15d7 100644 --- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/InstanceValidatorMock.java +++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/InstanceValidatorMock.java @@ -10,7 +10,7 @@ import com.yahoo.vespa.hosted.athenz.instanceproviderservice.InstanceValidator; public class InstanceValidatorMock extends InstanceValidator { public InstanceValidatorMock() { - super(null, null, null, null, null, null,""); + super(null, null, null, null, null); } @Override diff --git a/configdefinitions/src/vespa/athenz-provider-service.def b/configdefinitions/src/vespa/athenz-provider-service.def index 4c9c74f9b8f..2131aa88d30 100644 --- a/configdefinitions/src/vespa/athenz-provider-service.def +++ b/configdefinitions/src/vespa/athenz-provider-service.def @@ -13,11 +13,6 @@ secretName string # Secret version secretVersion int -# Tempory resources -sisSecretName string default="" -sisSecretVersion int default=0 -sisUrl string default = "" - # Secret name of CA certificate caCertSecretName string diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java index dcb69d5f77b..0aec24d4d24 100644 --- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java +++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java @@ -345,13 +345,6 @@ public class Flags { "Takes effect on the next tick.", ZONE_ID, NODE_TYPE, HOSTNAME); - public static final UnboundBooleanFlag VESPA_ATHENZ_PROVIDER = defineFeatureFlag( - "vespa-athenz-provider", false, - List.of("mortent"), "2023-02-22", "2023-05-01", - "Enable athenz provider in public systems", - "Takes effect on next config server container start", - ZONE_ID); - public static final UnboundBooleanFlag ENABLE_GLOBAL_PHASE = defineFeatureFlag( "enable-global-phase", false, List.of("arnej", "bjorncs"), "2023-02-28", "2024-01-10", diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java index 6bd7d98e207..fc49dcc744c 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java @@ -41,7 +41,6 @@ import java.time.Clock; import java.time.Duration; import java.time.Instant; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; @@ -190,9 +189,11 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer { Pkcs10Csr csr = csrGenerator.generateInstanceCsr( context.identity(), doc.providerUniqueId(), doc.ipAddresses(), doc.clusterType(), keyPair); - // Allow all zts hosts while removing SIS - HostnameVerifier ztsHostNameVerifier = (hostname, sslSession) -> true; - try (ZtsClient ztsClient = new DefaultZtsClient.Builder(ztsEndpoint(doc)).withIdentityProvider(hostIdentityProvider).withHostnameVerifier(ztsHostNameVerifier).build()) { + // Set up a hostname verified for zts if this is configured to use the config server (internal zts) apis + HostnameVerifier ztsHostNameVerifier = useInternalZts + ? new AthenzIdentityVerifier(Set.of(configserverIdentity)) + : null; + try (ZtsClient ztsClient = new DefaultZtsClient.Builder(ztsEndpoint).withIdentityProvider(hostIdentityProvider).withHostnameVerifier(ztsHostNameVerifier).build()) { InstanceIdentity instanceIdentity = ztsClient.registerInstance( configserverIdentity, @@ -205,15 +206,6 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer { } } - /** - * Return zts url from identity document, fallback to ztsEndpoint - */ - private URI ztsEndpoint(SignedIdentityDocument doc) { - return Optional.ofNullable(doc.ztsUrl()) - .filter(s -> !s.isBlank()) - .map(URI::create) - .orElse(ztsEndpoint); - } private void refreshIdentity(NodeAgentContext context, ContainerPath privateKeyFile, ContainerPath certificateFile, ContainerPath identityDocumentFile, SignedIdentityDocument doc) { KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA); @@ -225,9 +217,11 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer { .build(); try { - // Allow all zts hosts while removing SIS - HostnameVerifier ztsHostNameVerifier = (hostname, sslSession) -> true; - try (ZtsClient ztsClient = new DefaultZtsClient.Builder(ztsEndpoint(doc)).withSslContext(containerIdentitySslContext).withHostnameVerifier(ztsHostNameVerifier).build()) { + // Set up a hostname verified for zts if this is configured to use the config server (internal zts) apis + HostnameVerifier ztsHostNameVerifier = useInternalZts + ? new AthenzIdentityVerifier(Set.of(configserverIdentity)) + : null; + try (ZtsClient ztsClient = new DefaultZtsClient.Builder(ztsEndpoint).withSslContext(containerIdentitySslContext).withHostnameVerifier(ztsHostNameVerifier).build()) { InstanceIdentity instanceIdentity = ztsClient.refreshInstance( configserverIdentity, diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java index 067e8a6b00f..9b7b666e353 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java @@ -58,7 +58,6 @@ public class EntityBindingsMapper { entity.ipAddresses(), IdentityType.fromId(entity.identityType()), Optional.ofNullable(entity.clusterType()).map(ClusterType::from).orElse(null), - entity.ztsUrl(), entity.unknownAttributes()); } @@ -75,7 +74,6 @@ public class EntityBindingsMapper { model.ipAddresses(), model.identityType().id(), Optional.ofNullable(model.clusterType()).map(ClusterType::toConfigValue).orElse(null), - model.ztsUrl(), model.unknownAttributes()); } diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java index eba89b72d87..49a39d25e87 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java @@ -3,7 +3,6 @@ package com.yahoo.vespa.athenz.identityprovider.api; import com.yahoo.vespa.athenz.api.AthenzService; -import java.net.URL; import java.time.Instant; import java.util.HashMap; import java.util.Map; @@ -18,7 +17,7 @@ import java.util.Set; public record SignedIdentityDocument(String signature, int signingKeyVersion, VespaUniqueInstanceId providerUniqueId, AthenzService providerService, int documentVersion, String configServerHostname, String instanceHostname, Instant createdAt, Set<String> ipAddresses, - IdentityType identityType, ClusterType clusterType, String ztsUrl, Map<String, Object> unknownAttributes) { + IdentityType identityType, ClusterType clusterType, Map<String, Object> unknownAttributes) { public SignedIdentityDocument { ipAddresses = Set.copyOf(ipAddresses); @@ -34,9 +33,9 @@ public record SignedIdentityDocument(String signature, int signingKeyVersion, Ve public SignedIdentityDocument(String signature, int signingKeyVersion, VespaUniqueInstanceId providerUniqueId, AthenzService providerService, int documentVersion, String configServerHostname, String instanceHostname, Instant createdAt, Set<String> ipAddresses, - IdentityType identityType, ClusterType clusterType, String ztsUrl) { + IdentityType identityType, ClusterType clusterType) { this(signature, signingKeyVersion, providerUniqueId, providerService, documentVersion, configServerHostname, - instanceHostname, createdAt, ipAddresses, identityType, clusterType, ztsUrl, Map.of()); + instanceHostname, createdAt, ipAddresses, identityType, clusterType, Map.of()); } public static final int DEFAULT_DOCUMENT_VERSION = 2; diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java index edbe032ec26..c37dd2f9147 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java @@ -17,7 +17,7 @@ import java.util.Set; public record SignedIdentityDocumentEntity( String signature, int signingKeyVersion, String providerUniqueId, String providerService, int documentVersion, String configServerHostname, String instanceHostname, Instant createdAt, Set<String> ipAddresses, - String identityType, String clusterType, String ztsUrl, Map<String, Object> unknownAttributes) { + String identityType, String clusterType, Map<String, Object> unknownAttributes) { @JsonCreator public SignedIdentityDocumentEntity(@JsonProperty("signature") String signature, @@ -30,10 +30,9 @@ public record SignedIdentityDocumentEntity( @JsonProperty("created-at") Instant createdAt, @JsonProperty("ip-addresses") Set<String> ipAddresses, @JsonProperty("identity-type") String identityType, - @JsonProperty("cluster-type") String clusterType, - @JsonProperty("zts-url") String ztsUrl) { + @JsonProperty("cluster-type") String clusterType) { this(signature, signingKeyVersion, providerUniqueId, providerService, documentVersion, configServerHostname, - instanceHostname, createdAt, ipAddresses, identityType, clusterType, ztsUrl, new HashMap<>()); + instanceHostname, createdAt, ipAddresses, identityType, clusterType, new HashMap<>()); } @JsonProperty("signature") @Override public String signature() { return signature; } @@ -47,7 +46,6 @@ public record SignedIdentityDocumentEntity( @JsonProperty("ip-addresses") @Override public Set<String> ipAddresses() { return ipAddresses; } @JsonProperty("identity-type") @Override public String identityType() { return identityType; } @JsonProperty("cluster-type") @Override public String clusterType() { return clusterType; } - @JsonProperty("zts-url") @Override public String ztsUrl() { return ztsUrl; } @JsonAnyGetter @Override public Map<String, Object> unknownAttributes() { return unknownAttributes; } @JsonAnySetter public void set(String name, Object value) { unknownAttributes.put(name, value); } } diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapperTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapperTest.java index 2a68f6fd231..f8c119190a6 100644 --- a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapperTest.java +++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapperTest.java @@ -30,7 +30,6 @@ class EntityBindingsMapperTest { "ip-addresses": [], "identity-type": "node", "cluster-type": "admin", - "zts-url": "https://zts.url/", "unknown-string": "string-value", "unknown-object": { "member-in-unknown-object": 123 } } diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java index 72798b03fa8..0b8ff4277f1 100644 --- a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java +++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java @@ -36,7 +36,6 @@ public class IdentityDocumentSignerTest { private static final Instant createdAt = Instant.EPOCH; private static final HashSet<String> ipAddresses = new HashSet<>(Arrays.asList("1.2.3.4", "::1")); private static final ClusterType clusterType = ClusterType.CONTAINER; - private static final String ztsUrl = "https://foo"; @Test void generates_and_validates_signature() { @@ -47,27 +46,27 @@ public class IdentityDocumentSignerTest { SignedIdentityDocument signedIdentityDocument = new SignedIdentityDocument( signature, KEY_VERSION, id, providerService, DEFAULT_DOCUMENT_VERSION, configserverHostname, - instanceHostname, createdAt, ipAddresses, identityType, clusterType, ztsUrl); + instanceHostname, createdAt, ipAddresses, identityType, clusterType); assertTrue(signer.hasValidSignature(signedIdentityDocument, keyPair.getPublic())); } @Test - void ignores_cluster_type_and_zts_url() { + void ignores_cluster_type() { IdentityDocumentSigner signer = new IdentityDocumentSigner(); String signature = signer.generateSignature(id, providerService, configserverHostname, instanceHostname, createdAt, ipAddresses, identityType, keyPair.getPrivate()); - var docWithoutIgnoredFields = new SignedIdentityDocument( + var docWithoutClusterType = new SignedIdentityDocument( signature, KEY_VERSION, id, providerService, DEFAULT_DOCUMENT_VERSION, configserverHostname, - instanceHostname, createdAt, ipAddresses, identityType, null, null); - var docWithIgnoredFields = new SignedIdentityDocument( + instanceHostname, createdAt, ipAddresses, identityType, null); + var docWithClusterType = new SignedIdentityDocument( signature, KEY_VERSION, id, providerService, DEFAULT_DOCUMENT_VERSION, configserverHostname, - instanceHostname, createdAt, ipAddresses, identityType, clusterType, ztsUrl); + instanceHostname, createdAt, ipAddresses, identityType, clusterType); - assertTrue(signer.hasValidSignature(docWithoutIgnoredFields, keyPair.getPublic())); - assertEquals(docWithIgnoredFields.signature(), docWithoutIgnoredFields.signature()); + assertTrue(signer.hasValidSignature(docWithoutClusterType, keyPair.getPublic())); + assertEquals(docWithClusterType.signature(), docWithoutClusterType.signature()); } }
\ No newline at end of file |