diff --git a/src/main/java/bio/terra/buffer/service/resource/flight/CreateDnsZoneStep.java b/src/main/java/bio/terra/buffer/service/resource/flight/CreateDnsZoneStep.java index 6bd26726..6ce8e66c 100644 --- a/src/main/java/bio/terra/buffer/service/resource/flight/CreateDnsZoneStep.java +++ b/src/main/java/bio/terra/buffer/service/resource/flight/CreateDnsZoneStep.java @@ -1,8 +1,10 @@ package bio.terra.buffer.service.resource.flight; import static bio.terra.buffer.service.resource.FlightMapKeys.GOOGLE_PROJECT_ID; +import static bio.terra.buffer.service.resource.flight.GoogleProjectConfigUtils.*; import static bio.terra.buffer.service.resource.flight.GoogleProjectConfigUtils.enableGcrPrivateGoogleAccess; import static bio.terra.buffer.service.resource.flight.GoogleProjectConfigUtils.usePrivateGoogleAccess; +import static bio.terra.buffer.service.resource.flight.GoogleUtils.GAR_MANAGED_ZONE_NAME; import static bio.terra.buffer.service.resource.flight.GoogleUtils.GCR_MANAGED_ZONE_NAME; import static bio.terra.buffer.service.resource.flight.GoogleUtils.MANAGED_ZONE_NAME; import static bio.terra.buffer.service.resource.flight.GoogleUtils.NETWORK_NAME; @@ -54,6 +56,14 @@ public class CreateDnsZoneStep implements Step { .setDnsName("gcr.io.") .setVisibility("private"); + @VisibleForTesting + public static final ManagedZone GAR_MANAGED_ZONE_TEMPLATE = + new ManagedZone() + .setName(GAR_MANAGED_ZONE_NAME) + .setDescription("Routes pkg.dev to restricted.googleapis.com") + .setDnsName("pkg.dev.") + .setVisibility("private"); + private final Logger logger = LoggerFactory.getLogger(CreateDnsZoneStep.class); private final CloudComputeCow computeCow; private final DnsCow dnsCow; @@ -85,6 +95,10 @@ public StepResult doStep(FlightContext flightContext) throws RetryException { createManagedDnsZone(projectId, network, GCR_MANAGED_ZONE_TEMPLATE); } + if (enableGarPrivateGoogleAccess(gcpProjectConfig)) { + createManagedDnsZone(projectId, network, GAR_MANAGED_ZONE_TEMPLATE); + } + } catch (IOException e) { logger.info("Error when configuring DNS ", e); return new StepResult(StepStatus.STEP_RESULT_FAILURE_RETRY, e); diff --git a/src/main/java/bio/terra/buffer/service/resource/flight/CreateResourceRecordSetStep.java b/src/main/java/bio/terra/buffer/service/resource/flight/CreateResourceRecordSetStep.java index 1bdd0997..2e8131e1 100644 --- a/src/main/java/bio/terra/buffer/service/resource/flight/CreateResourceRecordSetStep.java +++ b/src/main/java/bio/terra/buffer/service/resource/flight/CreateResourceRecordSetStep.java @@ -1,8 +1,10 @@ package bio.terra.buffer.service.resource.flight; import static bio.terra.buffer.service.resource.FlightMapKeys.GOOGLE_PROJECT_ID; +import static bio.terra.buffer.service.resource.flight.GoogleProjectConfigUtils.*; import static bio.terra.buffer.service.resource.flight.GoogleProjectConfigUtils.enableGcrPrivateGoogleAccess; import static bio.terra.buffer.service.resource.flight.GoogleProjectConfigUtils.usePrivateGoogleAccess; +import static bio.terra.buffer.service.resource.flight.GoogleUtils.GAR_MANAGED_ZONE_NAME; import static bio.terra.buffer.service.resource.flight.GoogleUtils.GCR_MANAGED_ZONE_NAME; import static bio.terra.buffer.service.resource.flight.GoogleUtils.MANAGED_ZONE_NAME; import static bio.terra.buffer.service.resource.flight.GoogleUtils.getResource; @@ -27,13 +29,15 @@ /** Configs record set for DNS. See {@link CreateDnsZoneStep} */ public class CreateResourceRecordSetStep implements Step { + private static final List RESTRICT_GOOGLE_API_A_RECORD_IP_ADDRESS = + ImmutableList.of("199.36.153.4", "199.36.153.5", "199.36.153.6", "199.36.153.7"); + @VisibleForTesting public static final ResourceRecordSet RESTRICT_API_A_RECORD = new ResourceRecordSet() .setType("A") .setName("restricted.googleapis.com.") - .setRrdatas( - ImmutableList.of("199.36.153.4", "199.36.153.5", "199.36.153.6", "199.36.153.7")) + .setRrdatas(RESTRICT_GOOGLE_API_A_RECORD_IP_ADDRESS) .setTtl(300); @VisibleForTesting @@ -49,8 +53,7 @@ public class CreateResourceRecordSetStep implements Step { new ResourceRecordSet() .setType("A") .setName("gcr.io.") - .setRrdatas( - ImmutableList.of("199.36.153.4", "199.36.153.5", "199.36.153.6", "199.36.153.7")) + .setRrdatas(RESTRICT_GOOGLE_API_A_RECORD_IP_ADDRESS) .setTtl(300); @VisibleForTesting @@ -61,6 +64,22 @@ public class CreateResourceRecordSetStep implements Step { .setRrdatas(ImmutableList.of("gcr.io.")) .setTtl(300); + @VisibleForTesting + public static final ResourceRecordSet GAR_A_RECORD = + new ResourceRecordSet() + .setType("A") + .setName("pkg.dev.") + .setRrdatas(RESTRICT_GOOGLE_API_A_RECORD_IP_ADDRESS) + .setTtl(300); + + @VisibleForTesting + public static final ResourceRecordSet GAR_CNAME_RECORD = + new ResourceRecordSet() + .setType("CNAME") + .setName("*.pkg.dev.") + .setRrdatas(ImmutableList.of("pkg.dev.")) + .setTtl(300); + private final Logger logger = LoggerFactory.getLogger(CreateResourceRecordSetStep.class); private final DnsCow dnsCow; private final GcpProjectConfig gcpProjectConfig; @@ -87,6 +106,10 @@ public StepResult doStep(FlightContext flightContext) throws RetryException { createRecordSetForDnsZone( projectId, GCR_MANAGED_ZONE_NAME, ImmutableList.of(GCR_A_RECORD, GCR_CNAME_RECORD)); } + if (enableGarPrivateGoogleAccess(gcpProjectConfig)) { + createRecordSetForDnsZone( + projectId, GAR_MANAGED_ZONE_NAME, ImmutableList.of(GAR_A_RECORD, GAR_CNAME_RECORD)); + } } catch (IOException e) { logger.info("Error when configuring ResourceRecordSets ", e); return new StepResult(StepStatus.STEP_RESULT_FAILURE_RETRY, e); diff --git a/src/main/java/bio/terra/buffer/service/resource/flight/GoogleProjectConfigUtils.java b/src/main/java/bio/terra/buffer/service/resource/flight/GoogleProjectConfigUtils.java index 438da249..0bf9eb18 100644 --- a/src/main/java/bio/terra/buffer/service/resource/flight/GoogleProjectConfigUtils.java +++ b/src/main/java/bio/terra/buffer/service/resource/flight/GoogleProjectConfigUtils.java @@ -95,6 +95,13 @@ public static boolean enableGcrPrivateGoogleAccess(GcpProjectConfig gcpProjectCo && gcpProjectConfig.getNetwork().isEnableCloudRegistryPrivateGoogleAccess(); } + /** Checks if private Google Access enabled for gcr.io. */ + public static boolean enableGarPrivateGoogleAccess(GcpProjectConfig gcpProjectConfig) { + return gcpProjectConfig.getNetwork() != null + && gcpProjectConfig.getNetwork().isEnableArtifactRegistryPrivateGoogleAccess() != null + && gcpProjectConfig.getNetwork().isEnableArtifactRegistryPrivateGoogleAccess(); + } + /** Whether to allow CGP VMs have internet access. */ public static boolean blockBatchInternetAccess(GcpProjectConfig gcpProjectConfig) { return gcpProjectConfig.getNetwork() != null diff --git a/src/main/java/bio/terra/buffer/service/resource/flight/GoogleUtils.java b/src/main/java/bio/terra/buffer/service/resource/flight/GoogleUtils.java index 0ab4c2f1..38aa0e13 100644 --- a/src/main/java/bio/terra/buffer/service/resource/flight/GoogleUtils.java +++ b/src/main/java/bio/terra/buffer/service/resource/flight/GoogleUtils.java @@ -22,9 +22,11 @@ public class GoogleUtils { @VisibleForTesting public static final String MANAGED_ZONE_NAME = "private-google-access-dns-zone"; - /** The private DNS zone name. */ + /** The private DNS zone name for GCR. */ @VisibleForTesting public static final String GCR_MANAGED_ZONE_NAME = "private-google-access-gcr-dns-zone"; + /** The private DNS zone name for GAR. */ + public static final String GAR_MANAGED_ZONE_NAME = "private-google-access-gar-dns-zone"; /** All project will use the same sub network name. */ @VisibleForTesting public static final String SUBNETWORK_NAME = "subnetwork"; diff --git a/src/main/resources/config/dev/pool_schema.yml b/src/main/resources/config/dev/pool_schema.yml index 9e4e13e8..68f1d5bf 100644 --- a/src/main/resources/config/dev/pool_schema.yml +++ b/src/main/resources/config/dev/pool_schema.yml @@ -13,6 +13,9 @@ poolConfigs: - poolId: "vpc_sc_v11" size: 300 resourceConfigName: "vpc_sc_v11" + - poolId: "vpc_sc_v12" + size: 300 + resourceConfigName: "vpc_sc_v12" - poolId: "workspace_manager_v11" size: 500 resourceConfigName: "workspace_manager_v11" diff --git a/src/main/resources/config/dev/resource-config/vpc_sc_v12.yml b/src/main/resources/config/dev/resource-config/vpc_sc_v12.yml new file mode 100644 index 00000000..b387e013 --- /dev/null +++ b/src/main/resources/config/dev/resource-config/vpc_sc_v12.yml @@ -0,0 +1,41 @@ +# # Projects with VPC-SC configuration +--- +configName: "vpc_sc_v12" +gcpProjectConfig: + projectIdSchema: + prefix: "terra-vpc-sc-dev" + scheme: "RANDOM_CHAR" + # test.firecloud.org/dev/for_vpc_sc_unclaimed + parentFolderId: "1061905712535" + billingAccount: "01A82E-CA8A14-367457" + enabledApis: + - "bigquery-json.googleapis.com" + - "compute.googleapis.com" + - "container.googleapis.com" + - "cloudbilling.googleapis.com" + - "clouderrorreporting.googleapis.com" + - "cloudkms.googleapis.com" + - "cloudtrace.googleapis.com" + - "containerregistry.googleapis.com" + - "dataflow.googleapis.com" + - "dataproc.googleapis.com" + - "dns.googleapis.com" + - "lifesciences.googleapis.com" + - "logging.googleapis.com" + - "monitoring.googleapis.com" + - "serviceusage.googleapis.com" + - "storage-api.googleapis.com" + - "storage-component.googleapis.com" + network: + enableNetworkMonitoring: "true" + enablePrivateGoogleAccess: "true" + enableCloudRegistryPrivateGoogleAccess: "true" + enableArtifactRegistryPrivateGoogleAccess: "true" + blockBatchInternetAccess: "true" + kubernetesEngine: + createGkeDefaultServiceAccount: "true" + serviceUsage: + bigQuery: + overrideBigQueryDailyUsageQuota: true + bigQueryDailyUsageQuotaOverrideValueMebibytes: 38146972 # 40 TB + securityGroup: "high" diff --git a/src/main/resources/config/resource_schema.yaml b/src/main/resources/config/resource_schema.yaml index 78de29cf..3d240565 100644 --- a/src/main/resources/config/resource_schema.yaml +++ b/src/main/resources/config/resource_schema.yaml @@ -139,6 +139,11 @@ components: Whether to config Private Google Access for gcr.io. type: boolean default: false + enableArtifactRegistryPrivateGoogleAccess: + description: |- + Whether to config Private Google Access for Google Artifact Registry. + type: boolean + default: false blockBatchInternetAccess: description: |- Whether to allow GCE VMs have internet access for batch analysis. If ture, few firewall rules will be created to block egress diff --git a/src/test/java/bio/terra/buffer/integration/CreateProjectFlightIntegrationTest.java b/src/test/java/bio/terra/buffer/integration/CreateProjectFlightIntegrationTest.java index 27728d6e..8ec1a505 100644 --- a/src/test/java/bio/terra/buffer/integration/CreateProjectFlightIntegrationTest.java +++ b/src/test/java/bio/terra/buffer/integration/CreateProjectFlightIntegrationTest.java @@ -10,6 +10,7 @@ import static bio.terra.buffer.integration.IntegrationUtils.pollUntilResourcesMatch; import static bio.terra.buffer.integration.IntegrationUtils.preparePool; import static bio.terra.buffer.service.resource.FlightMapKeys.RESOURCE_CONFIG; +import static bio.terra.buffer.service.resource.flight.CreateDnsZoneStep.GAR_MANAGED_ZONE_TEMPLATE; import static bio.terra.buffer.service.resource.flight.CreateDnsZoneStep.GCR_MANAGED_ZONE_TEMPLATE; import static bio.terra.buffer.service.resource.flight.CreateDnsZoneStep.MANAGED_ZONE_TEMPLATE; import static bio.terra.buffer.service.resource.flight.CreateFirewallRuleStep.ALLOW_EGRESS_INTERNAL; @@ -41,10 +42,7 @@ import static bio.terra.buffer.service.resource.flight.CreateProjectStep.SECURITY_GROUP_LABEL_KEY; import static bio.terra.buffer.service.resource.flight.CreateProjectStep.SUB_NETWORK_LABEL_KEY; import static bio.terra.buffer.service.resource.flight.CreateProjectStep.createValidLabelValue; -import static bio.terra.buffer.service.resource.flight.CreateResourceRecordSetStep.GCR_A_RECORD; -import static bio.terra.buffer.service.resource.flight.CreateResourceRecordSetStep.GCR_CNAME_RECORD; -import static bio.terra.buffer.service.resource.flight.CreateResourceRecordSetStep.RESTRICT_API_A_RECORD; -import static bio.terra.buffer.service.resource.flight.CreateResourceRecordSetStep.RESTRICT_API_CNAME_RECORD; +import static bio.terra.buffer.service.resource.flight.CreateResourceRecordSetStep.*; import static bio.terra.buffer.service.resource.flight.CreateRouteStep.DEFAULT_GATEWAY; import static bio.terra.buffer.service.resource.flight.CreateRouteStep.ROUTE_NAME; import static bio.terra.buffer.service.resource.flight.CreateRouterNatStep.NAT_IP_ALLOCATION; @@ -54,16 +52,7 @@ import static bio.terra.buffer.service.resource.flight.DeleteDefaultFirewallRulesStep.DEFAULT_FIREWALL_NAMES; import static bio.terra.buffer.service.resource.flight.GoogleProjectConfigUtils.REGION_TO_IP_RANGE; import static bio.terra.buffer.service.resource.flight.GoogleProjectConfigUtils.getRegionToIpRange; -import static bio.terra.buffer.service.resource.flight.GoogleUtils.DEFAULT_NETWORK_NAME; -import static bio.terra.buffer.service.resource.flight.GoogleUtils.GCR_MANAGED_ZONE_NAME; -import static bio.terra.buffer.service.resource.flight.GoogleUtils.MANAGED_ZONE_NAME; -import static bio.terra.buffer.service.resource.flight.GoogleUtils.NAT_NAME_PREFIX; -import static bio.terra.buffer.service.resource.flight.GoogleUtils.NAT_ROUTER_NAME_PREFIX; -import static bio.terra.buffer.service.resource.flight.GoogleUtils.NETWORK_NAME; -import static bio.terra.buffer.service.resource.flight.GoogleUtils.RESTRICTED_GOOGLE_IP_ADDRESS; -import static bio.terra.buffer.service.resource.flight.GoogleUtils.SUBNETWORK_NAME; -import static bio.terra.buffer.service.resource.flight.GoogleUtils.projectIdToName; -import static bio.terra.buffer.service.resource.flight.GoogleUtils.resourceExists; +import static bio.terra.buffer.service.resource.flight.GoogleUtils.*; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -254,7 +243,8 @@ public void testCreateGoogleProject_enablePrivateGoogleAccessAndMonitoringAndSsh } @Test - public void testCreateGoogleProject_blockInternetAccessWithGcrDnsEnabled() throws Exception { + public void testCreateGoogleProject_blockInternetAccessWithGcrAndGarDnsEnabled() + throws Exception { FlightManager manager = new FlightManager( bufferDao, flightSubmissionFactoryImpl, stairwayComponent, transactionTemplate); @@ -267,6 +257,7 @@ public void testCreateGoogleProject_blockInternetAccessWithGcrDnsEnabled() throw .enableNetworkMonitoring(true) .enablePrivateGoogleAccess(true) .enableCloudRegistryPrivateGoogleAccess(true) + .enableArtifactRegistryPrivateGoogleAccess(true) .blockBatchInternetAccess(true))); String flightId = manager.submitCreationFlight(pool).get(); @@ -278,6 +269,7 @@ public void testCreateGoogleProject_blockInternetAccessWithGcrDnsEnabled() throw assertRouteExists(project); assertDnsExists(project); assertGcrDnsExists(project); + assertGarDnsExists(project); assertDefaultVpcNotExists(project); assertFirewallRulesExistForBlockInternetAccess(project); assertRouterNatNotExists(project); @@ -947,6 +939,29 @@ private void assertGcrDnsExists(Project project) throws Exception { assertResourceRecordSetMatch(GCR_CNAME_RECORD, cnameRecordSet); } + private void assertGarDnsExists(Project project) throws Exception { + String projectId = project.getProjectId(); + + ManagedZone managedZone = dnsCow.managedZones().get(projectId, GAR_MANAGED_ZONE_NAME).execute(); + Map resourceRecordSets = + dnsCow + .resourceRecordSets() + .list(project.getProjectId(), GAR_MANAGED_ZONE_NAME) + .execute() + .getRrsets() + .stream() + .collect(Collectors.toMap(ResourceRecordSet::getType, r -> r)); + ResourceRecordSet aRecordSet = resourceRecordSets.get(GAR_A_RECORD.getType()); + ResourceRecordSet cnameRecordSet = resourceRecordSets.get(GAR_CNAME_RECORD.getType()); + + assertEquals(GAR_MANAGED_ZONE_TEMPLATE.getName(), managedZone.getName()); + assertEquals( + GAR_MANAGED_ZONE_TEMPLATE.getVisibility(), managedZone.getVisibility().toLowerCase()); + assertEquals(GAR_MANAGED_ZONE_TEMPLATE.getDescription(), managedZone.getDescription()); + assertResourceRecordSetMatch(GAR_A_RECORD, aRecordSet); + assertResourceRecordSetMatch(GAR_CNAME_RECORD, cnameRecordSet); + } + private void assertDnsNotExists(Project project) throws Exception { assertFalse( resourceExists(