ZTSImpl implements KeyStore, ZTSHandler { @Override public Response postInstanceRegisterInformation(ResourceContext ctx, InstanceRegisterInformation info) { final String caller = ctx.getApiName(); final String principalDomain = logPrincipalAndGetDomain(ctx); if (readOnlyMode) { throw requestError("Server in Maintenance Read-Only mode. Please try your request later", caller, ZTSConsts.ZTS_UNKNOWN_DOMAIN, principalDomain); } validateRequest(ctx.request(), principalDomain, caller); validate(info, TYPE_INSTANCE_REGISTER_INFO, principalDomain, caller); AthenzObject.INSTANCE_REGISTER_INFO.convertToLowerCase(info); final String domain = info.getDomain(); setRequestDomain(ctx, domain); final String service = info.getService(); final String cn = domain + "." + service; ((RsrcCtxWrapper) ctx).logPrincipal(cn); final String provider = info.getProvider(); final String ipAddress = ServletRequestUtil.getRemoteAddress(ctx.request()); if (!instanceCertManager.verifyInstanceCertIPAddress(provider, ipAddress)) { throw forbiddenError("Unknown IP: " + ipAddress + " for Provider: " + provider, caller, domain, principalDomain); } Principal providerService = createPrincipalForName(provider); StringBuilder errorMsg = new StringBuilder(256); if (!instanceCertManager.authorizeLaunch(providerService, domain, service, errorMsg)) { throw forbiddenError(errorMsg.toString(), caller, domain, principalDomain); } X509ServiceCertRequest certReq; try { certReq = new X509ServiceCertRequest(info.getCsr()); } catch (CryptoException ex) { throw requestError("unable to parse PKCS10 CSR: " + ex.getMessage(), caller, domain, principalDomain); } final String serviceDnsSuffix = dataStore.getDomainData(domain).getCertDnsDomain(); final DataCache athenzSysDomainCache = dataStore.getDataCache(ATHENZ_SYS_DOMAIN); if (!certReq.validate(domain, service, provider, validCertSubjectOrgValues, athenzSysDomainCache, serviceDnsSuffix, info.getHostname(), info.getHostCnames(), hostnameResolver, errorMsg)) { throw requestError("CSR validation failed - " + errorMsg.toString(), caller, domain, principalDomain); } final String certReqInstanceId = certReq.getInstanceId(); InstanceProvider instanceProvider = instanceProviderManager.getProvider(provider, hostnameResolver); if (instanceProvider == null) { throw requestError("unable to get instance for provider: " + provider, caller, domain, principalDomain); } ctx.request().setAttribute(ACCESS_LOG_ADDL_QUERY, getInstanceRegisterQueryLog(provider, certReqInstanceId, info.getHostname())); InstanceConfirmation instance = generateInstanceConfirmObject(ctx, provider, domain, service, info.getAttestationData(), certReqInstanceId, info.getHostname(), certReq, instanceProvider.getProviderScheme()); Object timerProviderMetric = metric.startTiming("providerregister_timing", provider, principalDomain); try { instance = instanceProvider.confirmInstance(instance); } catch (com.yahoo.athenz.instance.provider.ResourceException ex) { metric.increment("providerconfirm_failure", domain, provider); int code = (ex.getCode() == ResourceException.GATEWAY_TIMEOUT) ? ResourceException.GATEWAY_TIMEOUT : ResourceException.FORBIDDEN; throw error(code, getExceptionMsg("unable to verify attestation data: ", ctx, ex, info.getHostname()), caller, domain, principalDomain); } catch (Exception ex) { metric.increment("providerconfirm_failure", domain, provider); throw forbiddenError(getExceptionMsg("unable to verify attestation data: ", ctx, ex, info.getHostname()), caller, domain, principalDomain); } finally { metric.stopTiming(timerProviderMetric, provider, principalDomain); instanceProvider.close(); } metric.increment("providerconfirm_success", domain, provider); String certUsage = null; String certSubjectOU = null; String instancePrivateIp = null; int certExpiryTime = 0; boolean certRefresh = true; boolean sshCertAllowed = false; Map<String, String> instanceAttrs = instance.getAttributes(); if (instanceAttrs != null) { certUsage = instanceAttrs.remove(InstanceProvider.ZTS_CERT_USAGE); certSubjectOU = instanceAttrs.remove(InstanceProvider.ZTS_CERT_SUBJECT_OU); instancePrivateIp = instanceAttrs.remove(InstanceProvider.ZTS_INSTANCE_PRIVATE_IP); certExpiryTime = ZTSUtils.parseInt(instanceAttrs.remove(InstanceProvider.ZTS_CERT_EXPIRY_TIME), 0); certRefresh = ZTSUtils.parseBoolean(instanceAttrs.remove(InstanceProvider.ZTS_CERT_REFRESH), true); sshCertAllowed = ZTSUtils.parseBoolean(instanceAttrs.remove(InstanceProvider.ZTS_CERT_SSH), false); } if (verifyCertSubjectOU && !certReq.validateSubjectOUField(provider, certSubjectOU, validCertSubjectOrgUnitValues)) { throw requestError("CSR Subject OrgUnit validation failed", caller, domain, principalDomain); } certExpiryTime = getServiceCertRequestExpiryTime(certExpiryTime, info.getExpiryTime()); Object timerX509CertMetric = metric.startTiming("certsignx509_timing", null, principalDomain); InstanceIdentity identity = instanceCertManager.generateIdentity(info.getCsr(), cn, certUsage, certExpiryTime); metric.stopTiming(timerX509CertMetric, null, principalDomain); if (identity == null) { throw serverError("unable to generate identity", caller, domain, principalDomain); } if (sshCertAllowed) { Object timerSSHCertMetric = metric.startTiming("certsignssh_timing", null, principalDomain); SSHCertRecord certRecord = generateSSHCertRecord(ctx, cn, certReqInstanceId, instancePrivateIp); instanceCertManager.generateSSHIdentity(null, identity, info.getHostname(), info.getSsh(), certRecord, ZTSConsts.ZTS_SSH_HOST); metric.stopTiming(timerSSHCertMetric, null, principalDomain); } identity.setAttributes(instanceAttrs); identity.setProvider(provider); identity.setInstanceId(certReqInstanceId); X509Certificate newCert = Crypto.loadX509Certificate(identity.getX509Certificate()); final String certSerial = newCert.getSerialNumber().toString(); if (certRefresh) { if (insertX509CertRecord(ctx, cn, provider, certReqInstanceId, certSerial, InstanceProvider.ZTS_CERT_USAGE_CLIENT.equalsIgnoreCase(certUsage), newCert.getNotAfter(), info.getHostname()) == null) { throw serverError("unable to update cert db", caller, domain, principalDomain); } } if (info.getToken() == Boolean.TRUE) { PrincipalToken svcToken = new PrincipalToken.Builder("S1", domain, service) .expirationWindow(svcTokenTimeout).keyId(privateKey.getId()).host(serverHostName) .ip(ipAddress).keyService(ZTSConsts.ZTS_SERVICE).build(); svcToken.sign(privateKey.getKey()); identity.setServiceToken(svcToken.getSignedToken()); } instanceCertManager.logX509Cert(null, ipAddress, provider, certReqInstanceId, newCert); final String location = "/zts/v1/instance/" + provider + "/" + domain + "/" + service + "/" + certReqInstanceId; return Response.status(ResourceException.CREATED).entity(identity) .header("Location", location).build(); } ZTSImpl();  ZTSImpl(CloudStore implCloudStore, DataStore implDataStore); @Override String getPublicKey(String domain, String service, String keyId); ServiceIdentity getServiceIdentity(ResourceContext ctx, String domainName, String serviceName); PublicKeyEntry getPublicKeyEntry(ResourceContext ctx, String domainName,
            String serviceName, String keyId); ServiceIdentityList getServiceIdentityList(ResourceContext ctx, String domainName); HostServices getHostServices(ResourceContext ctx, String host); Response getDomainSignedPolicyData(ResourceContext ctx, String domainName, String matchingTag); TenantDomains getTenantDomains(ResourceContext ctx, String providerDomainName,
            String userName, String roleName, String serviceName); @Override RoleToken getRoleToken(ResourceContext ctx, String domainName, String roleNames,
            Integer minExpiryTime, Integer maxExpiryTime, String proxyForPrincipal); @Override AccessTokenResponse postAccessTokenRequest(ResourceContext ctx, String request); RoleAccess getRoleAccess(ResourceContext ctx, String domainName, String principal); @Override RoleToken postRoleCertificateRequest(ResourceContext ctx, String domainName,
            String roleName, RoleCertificateRequest req); @Override RoleCertificate postRoleCertificateRequestExt(ResourceContext ctx, RoleCertificateRequest req); AWSTemporaryCredentials getAWSTemporaryCredentials(ResourceContext ctx, String domainName,
            String roleName, Integer durationSeconds, String externalId); @Override Response postInstanceRegisterInformation(ResourceContext ctx, InstanceRegisterInformation info); @Override InstanceIdentity postInstanceRefreshInformation(ResourceContext ctx, String provider,
            String domain, String service, String instanceId, InstanceRefreshInformation info); @Override void deleteInstanceIdentity(ResourceContext ctx, String provider,
            String domain, String service, String instanceId); @Deprecated @Override Identity postInstanceRefreshRequest(ResourceContext ctx, String domain,
            String service, InstanceRefreshRequest req); @Override Response postSSHCertRequest(ResourceContext ctx, SSHCertRequest certRequest); @Override JWKList getJWKList(ResourceContext ctx, Boolean rfc); @Deprecated @Override Identity postOSTKInstanceRefreshRequest(ResourceContext ctx, String domain,
            String service, OSTKInstanceRefreshRequest req); @Override ResourceAccess getResourceAccessExt(ResourceContext ctx, String action, String resource,
            String trustDomain, String checkPrincipal); @Override ResourceAccess getResourceAccess(ResourceContext ctx, String action, String resource,
            String trustDomain, String checkPrincipal); @Override Access getAccess(ResourceContext ctx, String domainName, String roleName,
            String principal); CertificateAuthorityBundle getCertificateAuthorityBundle(ResourceContext ctx, String name); @Override DomainMetrics postDomainMetrics(ResourceContext ctx, String domainName,
            DomainMetrics domainMetrics); @Override Status getStatus(ResourceContext ctx); @Override Schema getRdlSchema(ResourceContext context); ResourceContext newResourceContext(HttpServletRequest request,
                                              HttpServletResponse response,
                                              String apiName); static String getRootDir(); void recordMetrics(ResourceContext ctx, int httpStatus);  }
@Test public void testPostInstanceRegisterInformation() throws IOException { ChangeLogStore structStore = new ZMSFileChangeLogStore("/tmp/zts_server_unit_tests/zts_root", privateKey, "0"); DataStore store = new DataStore(structStore, null); ZTSImpl ztsImpl = new ZTSImpl(mockCloudStore, store); SignedDomain providerDomain = signedAuthorizedProviderDomain(); store.processDomain(providerDomain, false); SignedDomain tenantDomain = signedBootstrapTenantDomain("athenz.provider", "athenz", "production"); store.processDomain(tenantDomain, false); Path path = Paths.get("src/test/resources/athenz.instanceid.csr"); String certCsr = new String(Files.readAllBytes(path)); InstanceProviderManager instanceProviderManager = Mockito.mock(InstanceProviderManager.class); InstanceProvider providerClient = Mockito.mock(InstanceProvider.class); Mockito.when(providerClient.getProviderScheme()).thenReturn(InstanceProvider.Scheme.CLASS); Map<String, String> attrs = new HashMap<>(); attrs.put("certSSH", "true"); InstanceConfirmation confirmation = new InstanceConfirmation() .setDomain("athenz").setService("production").setProvider("athenz.provider") .setAttributes(attrs); InstanceCertManager instanceManager = Mockito.spy(ztsImpl.instanceCertManager); Mockito.when(instanceProviderManager.getProvider(eq("athenz.provider"), Mockito.any())).thenReturn(providerClient); Mockito.when(providerClient.confirmInstance(Mockito.any())).thenReturn(confirmation); Mockito.when(instanceManager.insertX509CertRecord(Mockito.any())).thenReturn(true); path = Paths.get("src/test/resources/athenz.instanceid.pem"); String pem = new String(Files.readAllBytes(path)); InstanceIdentity identity = new InstanceIdentity().setName("athenz.production") .setX509Certificate(pem); Mockito.doReturn(identity).when(instanceManager).generateIdentity(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyInt()); ztsImpl.instanceProviderManager = instanceProviderManager; ztsImpl.instanceCertManager = instanceManager; InstanceRegisterInformation info = new InstanceRegisterInformation() .setAttestationData("attestationData").setCsr(certCsr) .setDomain("athenz").setService("production") .setProvider("athenz.provider").setToken(true); ResourceContext context = createResourceContext(null); Response response = ztsImpl.postInstanceRegisterInformation(context, info); assertEquals(response.getStatus(), 201); InstanceIdentity resIdentity = (InstanceIdentity) response.getEntity(); assertNotNull(resIdentity.getX509Certificate()); }
@Test public void testPostInstanceRegisterInformationInvalidIP() throws IOException { ChangeLogStore structStore = new ZMSFileChangeLogStore("/tmp/zts_server_unit_tests/zts_root", privateKey, "0"); DataStore store = new DataStore(structStore, null); ZTSImpl ztsImpl = new ZTSImpl(mockCloudStore, store); SignedDomain providerDomain = signedAuthorizedProviderDomain(); store.processDomain(providerDomain, false); SignedDomain tenantDomain = signedBootstrapTenantDomain("athenz.provider", "athenz", "production"); store.processDomain(tenantDomain, false); Path path = Paths.get("src/test/resources/athenz.instanceid.csr"); String certCsr = new String(Files.readAllBytes(path)); InstanceCertManager instanceManager = Mockito.mock(InstanceCertManager.class); Mockito.when(instanceManager.verifyInstanceCertIPAddress(Mockito.any(), Mockito.any())) .thenReturn(false); ztsImpl.instanceCertManager = instanceManager; InstanceRegisterInformation info = new InstanceRegisterInformation() .setAttestationData("attestationData").setCsr(certCsr) .setDomain("athenz").setService("production") .setProvider("athenz.provider").setToken(true); ResourceContext context = createResourceContext(null); try { ztsImpl.postInstanceRegisterInformation(context, info); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), 403); assertTrue(ex.getMessage().contains("Unknown IP")); } }
@Test public void testPostInstanceRegisterInformationNoProviderClient() throws IOException { ChangeLogStore structStore = new ZMSFileChangeLogStore("/tmp/zts_server_unit_tests/zts_root", privateKey, "0"); DataStore store = new DataStore(structStore, null); ZTSImpl ztsImpl = new ZTSImpl(mockCloudStore, store); SignedDomain providerDomain = signedAuthorizedProviderDomain(); store.processDomain(providerDomain, false); SignedDomain tenantDomain = signedBootstrapTenantDomain("athenz.provider", "athenz", "production"); store.processDomain(tenantDomain, false); Path path = Paths.get("src/test/resources/athenz.instanceid.csr"); String certCsr = new String(Files.readAllBytes(path)); InstanceProviderManager instanceProviderManager = Mockito.mock(InstanceProviderManager.class); Mockito.when(instanceProviderManager.getProvider(eq("athenz.provider"), Mockito.any())).thenReturn(null); ztsImpl.instanceProviderManager = instanceProviderManager; InstanceRegisterInformation info = new InstanceRegisterInformation() .setAttestationData("attestationData").setCsr(certCsr) .setDomain("athenz").setService("production") .setProvider("athenz.provider"); ResourceContext context = createResourceContext(null); try { ztsImpl.postInstanceRegisterInformation(context, info); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), 400); assertTrue(ex.getMessage().contains("unable to get instance for provider")); } }
@Test public void testPostInstanceRegisterInformationAttestationFailure() throws IOException { ChangeLogStore structStore = new ZMSFileChangeLogStore("/tmp/zts_server_unit_tests/zts_root", privateKey, "0"); DataStore store = new DataStore(structStore, null); ZTSImpl ztsImpl = new ZTSImpl(mockCloudStore, store); SignedDomain providerDomain = signedAuthorizedProviderDomain(); store.processDomain(providerDomain, false); SignedDomain tenantDomain = signedBootstrapTenantDomain("athenz.provider", "athenz", "production"); store.processDomain(tenantDomain, false); Path path = Paths.get("src/test/resources/athenz.instanceid.csr"); String certCsr = new String(Files.readAllBytes(path)); InstanceProviderManager instanceProviderManager = Mockito.mock(InstanceProviderManager.class); InstanceProvider providerClient = Mockito.mock(InstanceProvider.class); Mockito.when(instanceProviderManager.getProvider(eq("athenz.provider"), Mockito.any())).thenReturn(providerClient); Mockito.when(providerClient.confirmInstance(Mockito.any())).thenThrow(new ResourceException(400)); ztsImpl.instanceProviderManager = instanceProviderManager; InstanceRegisterInformation info = new InstanceRegisterInformation() .setAttestationData("attestationData").setCsr(certCsr) .setDomain("athenz").setService("production") .setProvider("athenz.provider"); ResourceContext context = createResourceContext(null); try { ztsImpl.postInstanceRegisterInformation(context, info); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), 403); assertTrue(ex.getMessage().contains("unable to verify attestation data")); } }
@Test public void testPostInstanceRegisterInformationNetworkFailure() throws IOException { ChangeLogStore structStore = new ZMSFileChangeLogStore("/tmp/zts_server_unit_tests/zts_root", privateKey, "0"); DataStore store = new DataStore(structStore, null); ZTSImpl ztsImpl = new ZTSImpl(mockCloudStore, store); SignedDomain providerDomain = signedAuthorizedProviderDomain(); store.processDomain(providerDomain, false); SignedDomain tenantDomain = signedBootstrapTenantDomain("athenz.provider", "athenz", "production"); store.processDomain(tenantDomain, false); Path path = Paths.get("src/test/resources/athenz.instanceid.csr"); String certCsr = new String(Files.readAllBytes(path)); InstanceProviderManager instanceProviderManager = Mockito.mock(InstanceProviderManager.class); InstanceProvider providerClient = Mockito.mock(InstanceProvider.class); Mockito.when(instanceProviderManager.getProvider(eq("athenz.provider"), Mockito.any())).thenReturn(providerClient); Mockito.when(providerClient.confirmInstance(Mockito.any())) .thenThrow(new com.yahoo.athenz.instance.provider.ResourceException(504, "Connect Timeout")) .thenThrow(new com.yahoo.athenz.instance.provider.ResourceException(403, "Instance Revoked")); ztsImpl.instanceProviderManager = instanceProviderManager; InstanceRegisterInformation info = new InstanceRegisterInformation() .setAttestationData("attestationData").setCsr(certCsr) .setDomain("athenz").setService("production") .setProvider("athenz.provider"); ResourceContext context = createResourceContext(null); try { ztsImpl.postInstanceRegisterInformation(context, info); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), 504); assertTrue(ex.getMessage().contains("Connect Timeout")); } try { ztsImpl.postInstanceRegisterInformation(context, info); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), 403); assertTrue(ex.getMessage().contains("Instance Revoked")); } }
@Test public void testPostInstanceRegisterInformationNoAuthorizedProvider() throws IOException { ChangeLogStore structStore = new ZMSFileChangeLogStore("/tmp/zts_server_unit_tests/zts_root", privateKey, "0"); DataStore store = new DataStore(structStore, null); ZTSImpl ztsImpl = new ZTSImpl(mockCloudStore, store); SignedDomain providerDomain = signedAuthorizedProviderDomain(); store.processDomain(providerDomain, false); SignedDomain tenantDomain = signedBootstrapTenantDomain("athenz.provider", "athenz", "production"); store.processDomain(tenantDomain, false); Path path = Paths.get("src/test/resources/athenz.instanceid.csr"); String certCsr = new String(Files.readAllBytes(path)); InstanceRegisterInformation info = new InstanceRegisterInformation() .setAttestationData("attestationData").setCsr(certCsr) .setDomain("athenz").setService("production") .setProvider("athenz.nonprovider"); ResourceContext context = createResourceContext(null); try { ztsImpl.postInstanceRegisterInformation(context, info); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), 403); assertTrue(ex.getMessage().contains("not authorized to launch instances in Athenz")); } }
@Test public void testPostInstanceRegisterInformationServiceNotAuthorizedProvider() throws IOException { ChangeLogStore structStore = new ZMSFileChangeLogStore("/tmp/zts_server_unit_tests/zts_root", privateKey, "0"); DataStore store = new DataStore(structStore, null); ZTSImpl ztsImpl = new ZTSImpl(mockCloudStore, store); SignedDomain providerDomain = signedAuthorizedProviderDomain(); store.processDomain(providerDomain, false); SignedDomain tenantDomain = signedBootstrapTenantDomain("athenz.provider", "athenz", "production"); store.processDomain(tenantDomain, false); Path path = Paths.get("src/test/resources/athenz.instanceid.csr"); String certCsr = new String(Files.readAllBytes(path)); InstanceRegisterInformation info = new InstanceRegisterInformation() .setAttestationData("attestationData").setCsr(certCsr) .setDomain("athenz").setService("production2") .setProvider("athenz.provider"); ResourceContext context = createResourceContext(null); try { ztsImpl.postInstanceRegisterInformation(context, info); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), 403); assertTrue(ex.getMessage().contains("not authorized to launch athenz.production2 instances")); } }
@Test public void testPostInstanceRegisterInformationInvalidCSR() { ChangeLogStore structStore = new ZMSFileChangeLogStore("/tmp/zts_server_unit_tests/zts_root", privateKey, "0"); DataStore store = new DataStore(structStore, null); ZTSImpl ztsImpl = new ZTSImpl(mockCloudStore, store); SignedDomain providerDomain = signedAuthorizedProviderDomain(); store.processDomain(providerDomain, false); SignedDomain tenantDomain = signedBootstrapTenantDomain("athenz.provider", "athenz", "production"); store.processDomain(tenantDomain, false); InstanceRegisterInformation info = new InstanceRegisterInformation() .setAttestationData("attestationData").setCsr("csr") .setDomain("athenz").setService("production") .setProvider("athenz.provider"); ResourceContext context = createResourceContext(null); try { ztsImpl.postInstanceRegisterInformation(context, info); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), 400); assertTrue(ex.getMessage().contains("unable to parse PKCS10 CSR")); } }