From 55c325a2c2bc2b34ac094d0a7f2b25e42244fce6 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Tue, 8 Aug 2023 17:27:56 +0100 Subject: [PATCH 01/38] :wrench: Add a default reject incoming/outgoing rule --- .../pulumi/components/sre_networking.py | 235 ++++++++++++++++++ 1 file changed, 235 insertions(+) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index ef8c7115e1..707a7a6602 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -127,6 +127,7 @@ def __init__( network_security_group_name=f"{stack_name}-nsg-application-gateway", resource_group_name=resource_group.name, security_rules=[ + # Inbound network.SecurityRuleArgs( access="Allow", description="Allow inbound gateway management service traffic.", @@ -163,6 +164,31 @@ def __init__( source_address_prefix=props.public_ip_range_users, source_port_range="*", ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other inbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="DenyAllOtherInbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other outbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.OUTBOUND, + name="DenyAllOtherOutbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), ], opts=child_opts, ) @@ -170,42 +196,238 @@ def __init__( f"{self._name}_nsg_guacamole_containers", network_security_group_name=f"{stack_name}-nsg-guacamole-containers", resource_group_name=resource_group.name, + security_rules=[ + # Inbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other inbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="DenyAllOtherInbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other outbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.OUTBOUND, + name="DenyAllOtherOutbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + ], opts=child_opts, ) nsg_guacamole_containers_support = network.NetworkSecurityGroup( f"{self._name}_nsg_guacamole_containers_support", network_security_group_name=f"{stack_name}-nsg-guacamole-containers-support", resource_group_name=resource_group.name, + security_rules=[ + # Inbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other inbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="DenyAllOtherInbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other outbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.OUTBOUND, + name="DenyAllOtherOutbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + ], opts=child_opts, ) nsg_private_data = network.NetworkSecurityGroup( f"{self._name}_nsg_private_data", network_security_group_name=f"{stack_name}-nsg-private-data", resource_group_name=resource_group.name, + security_rules=[ + # Inbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other inbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="DenyAllOtherInbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other outbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.OUTBOUND, + name="DenyAllOtherOutbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + ], opts=child_opts, ) nsg_user_services_containers = network.NetworkSecurityGroup( f"{self._name}_nsg_user_services_containers", network_security_group_name=f"{stack_name}-nsg-user-services-containers", resource_group_name=resource_group.name, + security_rules=[ + # Inbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other inbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="DenyAllOtherInbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other outbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.OUTBOUND, + name="DenyAllOtherOutbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + ], opts=child_opts, ) nsg_user_services_containers_support = network.NetworkSecurityGroup( f"{self._name}_nsg_user_services_containers_support", network_security_group_name=f"{stack_name}-nsg-user-services-containers-support", resource_group_name=resource_group.name, + security_rules=[ + # Inbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other inbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="DenyAllOtherInbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other outbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.OUTBOUND, + name="DenyAllOtherOutbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + ], opts=child_opts, ) nsg_user_services_databases = network.NetworkSecurityGroup( f"{self._name}_nsg_user_services_databases", network_security_group_name=f"{stack_name}-nsg-user-services-databases", resource_group_name=resource_group.name, + security_rules=[ + # Inbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other inbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="DenyAllOtherInbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other outbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.OUTBOUND, + name="DenyAllOtherOutbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + ], opts=child_opts, ) nsg_user_services_software_repositories = network.NetworkSecurityGroup( f"{self._name}_nsg_user_services_software_repositories", network_security_group_name=f"{stack_name}-nsg-user-services-software-repositories", resource_group_name=resource_group.name, + security_rules=[ + # Inbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other inbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="DenyAllOtherInbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other outbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.OUTBOUND, + name="DenyAllOtherOutbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + ], opts=child_opts, ) nsg_workspaces = network.NetworkSecurityGroup( @@ -326,6 +548,19 @@ def __init__( source_address_prefix=subnet_workspaces_prefix, source_port_range="*", ), + # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other outbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.OUTBOUND, + name="DenyAllOtherOutbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), ], opts=child_opts, ) From 2fb33c6a68a18bf470f95c237fe30f86fb7c7e3e Mon Sep 17 00:00:00 2001 From: James Robinson Date: Tue, 8 Aug 2023 20:52:47 +0100 Subject: [PATCH 02/38] :wrench: Allow ApplicationGateway -> GuacamoleContainers --- data_safe_haven/pulumi/common/enums.py | 9 ++++--- .../pulumi/components/sre_networking.py | 26 ++++++++++++++++++- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/data_safe_haven/pulumi/common/enums.py b/data_safe_haven/pulumi/common/enums.py index ec53f22474..fbf708785c 100644 --- a/data_safe_haven/pulumi/common/enums.py +++ b/data_safe_haven/pulumi/common/enums.py @@ -15,10 +15,11 @@ class NetworkingPriorities(int, Enum): INTERNAL_SHM_LDAP_UDP = 1250 INTERNAL_SHM_MONITORING_TOOLS = 1300 INTERNAL_SHM_UPDATE_SERVERS = 1400 - INTERNAL_SRE_PRIVATE_DATA = 1500 - INTERNAL_SRE_REMOTE_DESKTOP = 1600 - INTERNAL_SRE_USER_SERVICES_CONTAINERS = 1700 - INTERNAL_SRE_USER_SERVICES_DATABASES = 1800 + INTERNAL_SRE_APPLICATION_GATEWAY = 1500 + INTERNAL_SRE_PRIVATE_DATA = 1600 + INTERNAL_SRE_REMOTE_DESKTOP = 1700 + INTERNAL_SRE_USER_SERVICES_CONTAINERS = 1800 + INTERNAL_SRE_USER_SERVICES_DATABASES = 1900 INTERNAL_DSH_VIRTUAL_NETWORK = 1999 # Authorised external IPs: 2000-2999 AUTHORISED_EXTERNAL_ADMIN_IPS = 2000 diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 707a7a6602..72a14356f4 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -154,7 +154,7 @@ def __init__( ), network.SecurityRuleArgs( access="Allow", - description="Allow inbound internet to Application Gateway.", + description="Allow inbound connections from users over the internet.", destination_address_prefix=subnet_application_gateway_prefix, destination_port_ranges=["80", "443"], direction="Inbound", @@ -177,6 +177,18 @@ def __init__( source_port_range="*", ), # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Allow outbound connections to the Guacamole server.", + destination_address_prefix=subnet_guacamole_containers_prefix, + destination_port_ranges=["80"], + direction=network.SecurityRuleDirection.OUTBOUND, + name="AllowGuacamoleContainersOutbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix=subnet_application_gateway_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other outbound traffic.", @@ -198,6 +210,18 @@ def __init__( resource_group_name=resource_group.name, security_rules=[ # Inbound + network.SecurityRuleArgs( + access="Allow", + description="Allow inbound connections from the Application Gateway.", + destination_address_prefix=subnet_guacamole_containers_prefix, + destination_port_ranges=["80"], + direction="Inbound", + name="AllowApplicationGatewayInbound", + priority=NetworkingPriorities.INTERNAL_SRE_APPLICATION_GATEWAY, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix=subnet_application_gateway_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other inbound traffic.", From 2aaa14398ce964f2d9f5f98307b3af5276d13259 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Wed, 9 Aug 2023 15:38:52 +0100 Subject: [PATCH 03/38] :wrench: Allow GuacamoleContainers -> GuacamoleContainersSupport --- data_safe_haven/pulumi/common/enums.py | 7 +-- .../pulumi/components/sre_networking.py | 46 ++++++++++++++----- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/data_safe_haven/pulumi/common/enums.py b/data_safe_haven/pulumi/common/enums.py index fbf708785c..816f5b3b8f 100644 --- a/data_safe_haven/pulumi/common/enums.py +++ b/data_safe_haven/pulumi/common/enums.py @@ -16,11 +16,12 @@ class NetworkingPriorities(int, Enum): INTERNAL_SHM_MONITORING_TOOLS = 1300 INTERNAL_SHM_UPDATE_SERVERS = 1400 INTERNAL_SRE_APPLICATION_GATEWAY = 1500 - INTERNAL_SRE_PRIVATE_DATA = 1600 - INTERNAL_SRE_REMOTE_DESKTOP = 1700 + INTERNAL_SRE_GUACAMOLE_REMOTE_DESKTOP = 1600 + INTERNAL_SRE_GUACAMOLE_REMOTE_DESKTOP_SUPPORT = 1650 + INTERNAL_SRE_PRIVATE_DATA = 1700 INTERNAL_SRE_USER_SERVICES_CONTAINERS = 1800 + INTERNAL_SRE_USER_SERVICES_CONTAINERS_SUPPORT = 1850 INTERNAL_SRE_USER_SERVICES_DATABASES = 1900 - INTERNAL_DSH_VIRTUAL_NETWORK = 1999 # Authorised external IPs: 2000-2999 AUTHORISED_EXTERNAL_ADMIN_IPS = 2000 AUTHORISED_EXTERNAL_USER_IPS = 2100 diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 72a14356f4..2136b1c3d1 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -133,10 +133,10 @@ def __init__( description="Allow inbound gateway management service traffic.", destination_address_prefix="*", destination_port_range="*", - direction="Inbound", + direction=network.SecurityRuleDirection.INBOUND, name="AllowGatewayManagerServiceInbound", priority=NetworkingPriorities.AZURE_GATEWAY_MANAGER, - protocol="*", + protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix="GatewayManager", source_port_range="*", ), @@ -145,10 +145,10 @@ def __init__( description="Allow inbound gateway management traffic over the internet.", destination_address_prefix=subnet_application_gateway_prefix, destination_port_range="65200-65535", - direction="Inbound", + direction=network.SecurityRuleDirection.INBOUND, name="AllowGatewayManagerInternetInbound", priority=NetworkingPriorities.EXTERNAL_INTERNET, - protocol="*", + protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix="Internet", source_port_range="*", ), @@ -157,10 +157,10 @@ def __init__( description="Allow inbound connections from users over the internet.", destination_address_prefix=subnet_application_gateway_prefix, destination_port_ranges=["80", "443"], - direction="Inbound", + direction=network.SecurityRuleDirection.INBOUND, name="AllowInternetInbound", priority=NetworkingPriorities.AUTHORISED_EXTERNAL_USER_IPS, - protocol="TCP", + protocol=network.SecurityRuleProtocol.TCP, source_address_prefix=props.public_ip_range_users, source_port_range="*", ), @@ -179,7 +179,7 @@ def __init__( # Outbound network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, - description="Allow outbound connections to the Guacamole server.", + description="Allow outbound connections to the Guacamole remote desktop gateway.", destination_address_prefix=subnet_guacamole_containers_prefix, destination_port_ranges=["80"], direction=network.SecurityRuleDirection.OUTBOUND, @@ -215,7 +215,7 @@ def __init__( description="Allow inbound connections from the Application Gateway.", destination_address_prefix=subnet_guacamole_containers_prefix, destination_port_ranges=["80"], - direction="Inbound", + direction=network.SecurityRuleDirection.INBOUND, name="AllowApplicationGatewayInbound", priority=NetworkingPriorities.INTERNAL_SRE_APPLICATION_GATEWAY, protocol=network.SecurityRuleProtocol.TCP, @@ -235,6 +235,18 @@ def __init__( source_port_range="*", ), # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Allow outbound connections to Guacamole support services.", + destination_address_prefix=subnet_guacamole_containers_support_prefix, + destination_port_ranges=["5432"], + direction=network.SecurityRuleDirection.OUTBOUND, + name="AllowGuacamoleContainersSupportOutbound", + priority=NetworkingPriorities.INTERNAL_SRE_GUACAMOLE_REMOTE_DESKTOP_SUPPORT, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix=subnet_guacamole_containers_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other outbound traffic.", @@ -256,6 +268,18 @@ def __init__( resource_group_name=resource_group.name, security_rules=[ # Inbound + network.SecurityRuleArgs( + access="Allow", + description="Allow inbound connections from Guacamole remote desktop gateway.", + destination_address_prefix=subnet_guacamole_containers_support_prefix, + destination_port_ranges=["5432"], + direction=network.SecurityRuleDirection.INBOUND, + name="AllowGuacamoleContainersSupportInbound", + priority=NetworkingPriorities.INTERNAL_SRE_APPLICATION_GATEWAY, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix=subnet_guacamole_containers_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other inbound traffic.", @@ -462,12 +486,12 @@ def __init__( # Inbound network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, - description="Allow connections to SRDs from remote desktop gateway.", + description="Allow connections to SRDs from Guacamole remote desktop gateway.", destination_address_prefix=subnet_workspaces_prefix, destination_port_ranges=["22", "3389"], direction=network.SecurityRuleDirection.INBOUND, - name="AllowRemoteDesktopGatewayInbound", - priority=NetworkingPriorities.INTERNAL_SRE_REMOTE_DESKTOP, + name="AllowGuacamoleContainersInbound", + priority=NetworkingPriorities.INTERNAL_SRE_GUACAMOLE_REMOTE_DESKTOP, protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix=subnet_guacamole_containers_prefix, source_port_range="*", From ded708c02190011fe18712890b2842ea40688986 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Wed, 9 Aug 2023 15:44:39 +0100 Subject: [PATCH 04/38] :wrench: Allow GuacamoleContainers -> Workspaces --- data_safe_haven/pulumi/common/enums.py | 6 +++-- .../pulumi/components/sre_networking.py | 27 ++++++++++++++----- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/data_safe_haven/pulumi/common/enums.py b/data_safe_haven/pulumi/common/enums.py index 816f5b3b8f..656e298dbe 100644 --- a/data_safe_haven/pulumi/common/enums.py +++ b/data_safe_haven/pulumi/common/enums.py @@ -20,8 +20,10 @@ class NetworkingPriorities(int, Enum): INTERNAL_SRE_GUACAMOLE_REMOTE_DESKTOP_SUPPORT = 1650 INTERNAL_SRE_PRIVATE_DATA = 1700 INTERNAL_SRE_USER_SERVICES_CONTAINERS = 1800 - INTERNAL_SRE_USER_SERVICES_CONTAINERS_SUPPORT = 1850 - INTERNAL_SRE_USER_SERVICES_DATABASES = 1900 + INTERNAL_SRE_USER_SERVICES_CONTAINERS_SUPPORT = 1825 + INTERNAL_SRE_USER_SERVICES_DATABASES = 1850 + INTERNAL_SRE_USER_SERVICES_SOFTWARE_REPOSITORIES = 1875 + INTERNAL_SRE_WORKSPACES = 1900 # Authorised external IPs: 2000-2999 AUTHORISED_EXTERNAL_ADMIN_IPS = 2000 AUTHORISED_EXTERNAL_USER_IPS = 2100 diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 2136b1c3d1..e174505817 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -129,7 +129,7 @@ def __init__( security_rules=[ # Inbound network.SecurityRuleArgs( - access="Allow", + access=network.SecurityRuleAccess.ALLOW, description="Allow inbound gateway management service traffic.", destination_address_prefix="*", destination_port_range="*", @@ -141,7 +141,7 @@ def __init__( source_port_range="*", ), network.SecurityRuleArgs( - access="Allow", + access=network.SecurityRuleAccess.ALLOW, description="Allow inbound gateway management traffic over the internet.", destination_address_prefix=subnet_application_gateway_prefix, destination_port_range="65200-65535", @@ -153,7 +153,7 @@ def __init__( source_port_range="*", ), network.SecurityRuleArgs( - access="Allow", + access=network.SecurityRuleAccess.ALLOW, description="Allow inbound connections from users over the internet.", destination_address_prefix=subnet_application_gateway_prefix, destination_port_ranges=["80", "443"], @@ -211,7 +211,7 @@ def __init__( security_rules=[ # Inbound network.SecurityRuleArgs( - access="Allow", + access=network.SecurityRuleAccess.ALLOW, description="Allow inbound connections from the Application Gateway.", destination_address_prefix=subnet_guacamole_containers_prefix, destination_port_ranges=["80"], @@ -236,7 +236,7 @@ def __init__( ), # Outbound network.SecurityRuleArgs( - access=network.SecurityRuleAccess.DENY, + access=network.SecurityRuleAccess.ALLOW, description="Allow outbound connections to Guacamole support services.", destination_address_prefix=subnet_guacamole_containers_support_prefix, destination_port_ranges=["5432"], @@ -247,6 +247,18 @@ def __init__( source_address_prefix=subnet_guacamole_containers_prefix, source_port_range="*", ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow outbound connections to SRE workspaces.", + destination_address_prefix=subnet_workspaces_prefix, + destination_port_ranges=["22", "3389"], + direction=network.SecurityRuleDirection.OUTBOUND, + name="AllowWorkspacesOutbound", + priority=NetworkingPriorities.INTERNAL_SRE_WORKSPACES, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix=subnet_guacamole_containers_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other outbound traffic.", @@ -269,7 +281,7 @@ def __init__( security_rules=[ # Inbound network.SecurityRuleArgs( - access="Allow", + access=network.SecurityRuleAccess.ALLOW, description="Allow inbound connections from Guacamole remote desktop gateway.", destination_address_prefix=subnet_guacamole_containers_support_prefix, destination_port_ranges=["5432"], @@ -486,7 +498,7 @@ def __init__( # Inbound network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, - description="Allow connections to SRDs from Guacamole remote desktop gateway.", + description="Allow inbound connections from Guacamole remote desktop gateway.", destination_address_prefix=subnet_workspaces_prefix, destination_port_ranges=["22", "3389"], direction=network.SecurityRuleDirection.INBOUND, @@ -496,6 +508,7 @@ def __init__( source_address_prefix=subnet_guacamole_containers_prefix, source_port_range="*", ), + network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other inbound traffic.", From 6c4dd581235fbcb12c5332df74443ad782995cf6 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Wed, 9 Aug 2023 15:48:00 +0100 Subject: [PATCH 05/38] :wrench: Allow Workspaces -> UserServicesContainers --- data_safe_haven/pulumi/components/sre_networking.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index e174505817..4769db8b06 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -394,6 +394,18 @@ def __init__( resource_group_name=resource_group.name, security_rules=[ # Inbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound connections from SRE workspaces.", + destination_address_prefix=subnet_user_services_containers_prefix, + destination_port_ranges=["22", "80", "443"], + direction=network.SecurityRuleDirection.INBOUND, + name="AllowWorkspacesInbound", + priority=NetworkingPriorities.INTERNAL_SRE_WORKSPACES, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix=subnet_workspaces_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other inbound traffic.", @@ -609,7 +621,6 @@ def __init__( source_address_prefix=subnet_workspaces_prefix, source_port_range="*", ), - # Outbound network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other outbound traffic.", From 0da2db0313290492742743f760b881f5cf39025a Mon Sep 17 00:00:00 2001 From: James Robinson Date: Wed, 9 Aug 2023 15:52:00 +0100 Subject: [PATCH 06/38] :wrench: Allow UserServicesContainers -> UserServicesContainersSupport --- .../pulumi/components/sre_networking.py | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 4769db8b06..cf4c85fe52 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -360,6 +360,18 @@ def __init__( resource_group_name=resource_group.name, security_rules=[ # Inbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound connections from SRE workspaces.", + destination_address_prefix=subnet_user_services_containers_prefix, + destination_port_ranges=["22", "80", "443"], + direction=network.SecurityRuleDirection.INBOUND, + name="AllowWorkspacesInbound", + priority=NetworkingPriorities.INTERNAL_SRE_WORKSPACES, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix=subnet_workspaces_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other inbound traffic.", @@ -373,6 +385,18 @@ def __init__( source_port_range="*", ), # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow outbound connections to container support services.", + destination_address_prefix=subnet_user_services_containers_support_prefix, + destination_port_ranges=["5432"], + direction=network.SecurityRuleDirection.OUTBOUND, + name="AllowUserServicesContainersSupportOutbound", + priority=NetworkingPriorities.INTERNAL_SRE_USER_SERVICES_CONTAINERS_SUPPORT, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix=subnet_user_services_containers_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other outbound traffic.", @@ -396,14 +420,14 @@ def __init__( # Inbound network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, - description="Allow inbound connections from SRE workspaces.", - destination_address_prefix=subnet_user_services_containers_prefix, - destination_port_ranges=["22", "80", "443"], + description="Allow inbound connections from containers.", + destination_address_prefix=subnet_guacamole_containers_support_prefix, + destination_port_ranges=["5432"], direction=network.SecurityRuleDirection.INBOUND, - name="AllowWorkspacesInbound", - priority=NetworkingPriorities.INTERNAL_SRE_WORKSPACES, + name="AllowUserServicesContainersInbound", + priority=NetworkingPriorities.INTERNAL_SRE_USER_SERVICES_CONTAINERS, protocol=network.SecurityRuleProtocol.TCP, - source_address_prefix=subnet_workspaces_prefix, + source_address_prefix=subnet_guacamole_containers_prefix, source_port_range="*", ), network.SecurityRuleArgs( From 235de298f7c98eb6314b39b8fd2d774a9b507d21 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Wed, 9 Aug 2023 15:54:40 +0100 Subject: [PATCH 07/38] :wrench: Allow Workspaces -> UserServicesDatabases --- .../pulumi/components/sre_networking.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index cf4c85fe52..563db7068e 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -464,6 +464,18 @@ def __init__( resource_group_name=resource_group.name, security_rules=[ # Inbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound connections from SRE workspaces.", + destination_address_prefix=subnet_user_services_databases_prefix, + destination_port_ranges=["1433", "5432"], + direction=network.SecurityRuleDirection.INBOUND, + name="AllowWorkspacesInbound", + priority=NetworkingPriorities.INTERNAL_SRE_WORKSPACES, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix=subnet_workspaces_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other inbound traffic.", @@ -637,7 +649,7 @@ def __init__( access=network.SecurityRuleAccess.ALLOW, description="Allow outbound connections to user services databases.", destination_address_prefix=subnet_user_services_databases_prefix, - destination_port_ranges=["5432"], + destination_port_ranges=["1433", "5432"], direction=network.SecurityRuleDirection.OUTBOUND, name="AllowUserServicesDatabasesOutbound", priority=NetworkingPriorities.INTERNAL_SRE_USER_SERVICES_DATABASES, From 09dfac61eb6b7e8f714b1be6e7b504c776cede55 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Wed, 9 Aug 2023 16:06:21 +0100 Subject: [PATCH 08/38] :wrench: Allow Workspaces -> UserServicesSoftwareRepositories --- .../pulumi/components/sre_networking.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 563db7068e..b1fdcf0f9e 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -510,6 +510,18 @@ def __init__( resource_group_name=resource_group.name, security_rules=[ # Inbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound connections from SRE workspaces.", + destination_address_prefix=subnet_user_services_software_repositories_prefix, + destination_port_ranges=["80", "443", "3128"], + direction=network.SecurityRuleDirection.INBOUND, + name="AllowWorkspacesInbound", + priority=NetworkingPriorities.INTERNAL_SRE_WORKSPACES, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix=subnet_workspaces_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other inbound traffic.", @@ -657,6 +669,18 @@ def __init__( source_address_prefix=subnet_workspaces_prefix, source_port_range="*", ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow outbound connections to user services software repositories.", + destination_address_prefix=subnet_user_services_software_repositories_prefix, + destination_port_ranges=["80", "443", "3128"], + direction=network.SecurityRuleDirection.OUTBOUND, + name="AllowUserServicesSoftwareRepositoriesOutbound", + priority=NetworkingPriorities.INTERNAL_SRE_USER_SERVICES_SOFTWARE_REPOSITORIES, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix=subnet_workspaces_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other outbound traffic.", From 6bcfd1acea51a741444dd9cd47afafd1196b1bc7 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Wed, 9 Aug 2023 16:36:09 +0100 Subject: [PATCH 09/38] :wrench: Allow Workspaces, UserServicesContainers, UserServicesDatabases, GuacamoleContainers -> PrivateData --- .../pulumi/components/sre_networking.py | 86 ++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index b1fdcf0f9e..12fdf30a96 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -247,6 +247,18 @@ def __init__( source_address_prefix=subnet_guacamole_containers_prefix, source_port_range="*", ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow outbound connections to private data endpoints.", + destination_address_prefix=subnet_private_data_prefix, + destination_port_range="*", + direction=network.SecurityRuleDirection.OUTBOUND, + name="AllowPrivateDataEndpointsOutbound", + priority=NetworkingPriorities.INTERNAL_SRE_PRIVATE_DATA, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix=subnet_guacamole_containers_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow outbound connections to SRE workspaces.", @@ -338,6 +350,54 @@ def __init__( source_address_prefix="*", source_port_range="*", ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound connections from Guacamole remote desktop gateway.", + destination_address_prefix=subnet_private_data_prefix, + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="AllowGuacamoleContainersInbound", + priority=NetworkingPriorities.INTERNAL_SRE_GUACAMOLE_CONTAINERS, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix=subnet_guacamole_containers_prefix, + source_port_range="*", + ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound connections from user services containers.", + destination_address_prefix=subnet_private_data_prefix, + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="AllowUserServicesContainersInbound", + priority=NetworkingPriorities.INTERNAL_SRE_USER_SERVICES_CONTAINERS, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix=subnet_user_services_containers_prefix, + source_port_range="*", + ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound connections from user services software repositories.", + destination_address_prefix=subnet_private_data_prefix, + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="AllowUserServicesSoftwareRepositoriesInbound", + priority=NetworkingPriorities.INTERNAL_SRE_USER_SERVICES_SOFTWARE_REPOSITORIES, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix=subnet_user_services_software_repositories_prefix, + source_port_range="*", + ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound connections from SRE workspaces.", + destination_address_prefix=subnet_private_data_prefix, + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="AllowWorkspacesInbound", + priority=NetworkingPriorities.INTERNAL_SRE_WORKSPACES, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix=subnet_workspaces_prefix, + source_port_range="*", + ), # Outbound network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, @@ -385,6 +445,18 @@ def __init__( source_port_range="*", ), # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow outbound connections to private data endpoints.", + destination_address_prefix=subnet_private_data_prefix, + destination_port_range="*", + direction=network.SecurityRuleDirection.OUTBOUND, + name="AllowPrivateDataEndpointsOutbound", + priority=NetworkingPriorities.INTERNAL_SRE_PRIVATE_DATA, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix=subnet_user_services_containers_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow outbound connections to container support services.", @@ -420,7 +492,7 @@ def __init__( # Inbound network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, - description="Allow inbound connections from containers.", + description="Allow inbound connections from user services containers.", destination_address_prefix=subnet_guacamole_containers_support_prefix, destination_port_ranges=["5432"], direction=network.SecurityRuleDirection.INBOUND, @@ -489,6 +561,18 @@ def __init__( source_port_range="*", ), # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow outbound connections to private data endpoints.", + destination_address_prefix=subnet_private_data_prefix, + destination_port_range="*", + direction=network.SecurityRuleDirection.OUTBOUND, + name="AllowPrivateDataEndpointsOutbound", + priority=NetworkingPriorities.INTERNAL_SRE_PRIVATE_DATA, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix=subnet_user_services_software_repositories_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other outbound traffic.", From 3f2a8202c95737c5ba9704306a5ed4180f6873ad Mon Sep 17 00:00:00 2001 From: James Robinson Date: Wed, 9 Aug 2023 16:54:38 +0100 Subject: [PATCH 10/38] :memo: Update rule description --- data_safe_haven/pulumi/components/sre_networking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 12fdf30a96..b5c1805eba 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -668,7 +668,7 @@ def __init__( # Outbound network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, - description="Allow outbound connections to local monitoring tools.", + description="Allow outbound connections to SHM monitoring tools.", destination_address_prefix=str(props.shm_subnet_monitoring_prefix), destination_port_ranges=["443"], direction=network.SecurityRuleDirection.OUTBOUND, From 1102a4744a97a9d3a298cfad168c78208dbe2f28 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Wed, 9 Aug 2023 17:17:15 +0100 Subject: [PATCH 11/38] :truck: Renamed rule priorities --- data_safe_haven/pulumi/common/enums.py | 4 ++-- data_safe_haven/pulumi/components/sre_networking.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/data_safe_haven/pulumi/common/enums.py b/data_safe_haven/pulumi/common/enums.py index 656e298dbe..7946cb80f9 100644 --- a/data_safe_haven/pulumi/common/enums.py +++ b/data_safe_haven/pulumi/common/enums.py @@ -16,8 +16,8 @@ class NetworkingPriorities(int, Enum): INTERNAL_SHM_MONITORING_TOOLS = 1300 INTERNAL_SHM_UPDATE_SERVERS = 1400 INTERNAL_SRE_APPLICATION_GATEWAY = 1500 - INTERNAL_SRE_GUACAMOLE_REMOTE_DESKTOP = 1600 - INTERNAL_SRE_GUACAMOLE_REMOTE_DESKTOP_SUPPORT = 1650 + INTERNAL_SRE_GUACAMOLE_CONTAINERS = 1600 + INTERNAL_SRE_GUACAMOLE_CONTAINERS_SUPPORT = 1650 INTERNAL_SRE_PRIVATE_DATA = 1700 INTERNAL_SRE_USER_SERVICES_CONTAINERS = 1800 INTERNAL_SRE_USER_SERVICES_CONTAINERS_SUPPORT = 1825 diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index b5c1805eba..3980c80fdb 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -184,7 +184,7 @@ def __init__( destination_port_ranges=["80"], direction=network.SecurityRuleDirection.OUTBOUND, name="AllowGuacamoleContainersOutbound", - priority=NetworkingPriorities.ALL_OTHER, + priority=NetworkingPriorities.INTERNAL_SRE_GUACAMOLE_CONTAINERS, protocol=network.SecurityRuleProtocol.TCP, source_address_prefix=subnet_application_gateway_prefix, source_port_range="*", @@ -242,7 +242,7 @@ def __init__( destination_port_ranges=["5432"], direction=network.SecurityRuleDirection.OUTBOUND, name="AllowGuacamoleContainersSupportOutbound", - priority=NetworkingPriorities.INTERNAL_SRE_GUACAMOLE_REMOTE_DESKTOP_SUPPORT, + priority=NetworkingPriorities.INTERNAL_SRE_GUACAMOLE_CONTAINERS_SUPPORT, protocol=network.SecurityRuleProtocol.TCP, source_address_prefix=subnet_guacamole_containers_prefix, source_port_range="*", @@ -647,7 +647,7 @@ def __init__( destination_port_ranges=["22", "3389"], direction=network.SecurityRuleDirection.INBOUND, name="AllowGuacamoleContainersInbound", - priority=NetworkingPriorities.INTERNAL_SRE_GUACAMOLE_REMOTE_DESKTOP, + priority=NetworkingPriorities.INTERNAL_SRE_GUACAMOLE_CONTAINERS, protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix=subnet_guacamole_containers_prefix, source_port_range="*", From 0560a27f81be1d8a871e0474ec54575128612e30 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Wed, 9 Aug 2023 17:22:39 +0100 Subject: [PATCH 12/38] :truck: Renamed user connection rule --- data_safe_haven/pulumi/components/sre_networking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 3980c80fdb..5cce82e969 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -158,7 +158,7 @@ def __init__( destination_address_prefix=subnet_application_gateway_prefix, destination_port_ranges=["80", "443"], direction=network.SecurityRuleDirection.INBOUND, - name="AllowInternetInbound", + name="AllowUsersInternetInbound", priority=NetworkingPriorities.AUTHORISED_EXTERNAL_USER_IPS, protocol=network.SecurityRuleProtocol.TCP, source_address_prefix=props.public_ip_range_users, From c4832e94445b872755cfd3b19d8cd8d3c6fdacc5 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Wed, 9 Aug 2023 17:39:16 +0100 Subject: [PATCH 13/38] :wrench: Allow outgoing Gateway Manager traffic --- .../pulumi/components/sre_networking.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 5cce82e969..a71e93f008 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -143,7 +143,7 @@ def __init__( network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow inbound gateway management traffic over the internet.", - destination_address_prefix=subnet_application_gateway_prefix, + destination_address_prefix="*", # this must be '*' or Azure validation will fail destination_port_range="65200-65535", direction=network.SecurityRuleDirection.INBOUND, name="AllowGatewayManagerInternetInbound", @@ -189,6 +189,18 @@ def __init__( source_address_prefix=subnet_application_gateway_prefix, source_port_range="*", ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow outbound gateway management traffic over the internet.", + destination_address_prefix="Internet", + destination_port_range="65200-65535", + direction=network.SecurityRuleDirection.OUTBOUND, + name="AllowGatewayManagerInternetOutbound", + priority=NetworkingPriorities.EXTERNAL_INTERNET, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", # this must be '*' or Azure validation will fail + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other outbound traffic.", From f7a8ce586f2866b2e1e810e4911329499ea6a61e Mon Sep 17 00:00:00 2001 From: James Robinson Date: Wed, 9 Aug 2023 17:49:47 +0100 Subject: [PATCH 14/38] :truck: Reordered SHM NSGs --- .../pulumi/components/shm_networking.py | 138 +++++++++--------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/data_safe_haven/pulumi/components/shm_networking.py b/data_safe_haven/pulumi/components/shm_networking.py index 837588444c..05436ad702 100644 --- a/data_safe_haven/pulumi/components/shm_networking.py +++ b/data_safe_haven/pulumi/components/shm_networking.py @@ -58,13 +58,6 @@ def __init__( ) # Define NSGs - nsg_monitoring = network.NetworkSecurityGroup( - f"{self._name}_nsg_monitoring", - network_security_group_name=f"{stack_name}-nsg-monitoring", - resource_group_name=resource_group.name, - security_rules=[], - opts=child_opts, - ) nsg_bastion = network.NetworkSecurityGroup( f"{self._name}_nsg_bastion", network_security_group_name=f"{stack_name}-nsg-bastion", @@ -195,6 +188,64 @@ def __init__( ], opts=child_opts, ) + nsg_identity_servers = network.NetworkSecurityGroup( + f"{self._name}_nsg_identity", + network_security_group_name=f"{stack_name}-nsg-identity", + resource_group_name=resource_group.name, + security_rules=[ + # Inbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound LDAP to domain controllers.", + destination_address_prefix=str( + props.subnet_identity_servers_iprange + ), + destination_port_ranges=["389", "636"], + direction=network.SecurityRuleDirection.INBOUND, + name="AllowLDAPClientUDPInbound", + priority=NetworkingPriorities.INTERNAL_SHM_LDAP_UDP, + protocol=network.SecurityRuleProtocol.UDP, + source_address_prefix="VirtualNetwork", + source_port_range="*", + ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound LDAP to domain controllers.", + destination_address_prefix=str( + props.subnet_identity_servers_iprange + ), + destination_port_ranges=["389", "636"], + direction=network.SecurityRuleDirection.INBOUND, + name="AllowLDAPClientTCPInbound", + priority=NetworkingPriorities.INTERNAL_SHM_LDAP_TCP, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix="VirtualNetwork", + source_port_range="*", + ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound RDP connections from admins using AzureBastion.", + destination_address_prefix=str( + props.subnet_identity_servers_iprange + ), + destination_port_ranges=["3389"], + direction=network.SecurityRuleDirection.INBOUND, + name="AllowBastionAdminsInbound", + priority=NetworkingPriorities.INTERNAL_SHM_BASTION, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix=str(props.subnet_bastion_iprange), + source_port_range="*", + ), + ], + opts=child_opts, + ) + nsg_monitoring = network.NetworkSecurityGroup( + f"{self._name}_nsg_monitoring", + network_security_group_name=f"{stack_name}-nsg-monitoring", + resource_group_name=resource_group.name, + security_rules=[], + opts=child_opts, + ) nsg_update_servers = network.NetworkSecurityGroup( f"{self._name}_nsg_update_servers", network_security_group_name=f"{stack_name}-nsg-update-servers", @@ -208,7 +259,7 @@ def __init__( destination_port_range="*", direction=network.SecurityRuleDirection.INBOUND, name="AllowVirtualNetworkInbound", - priority=NetworkingPriorities.INTERNAL_DSH_VIRTUAL_NETWORK, + priority=NetworkingPriorities.INTERNAL_SELF, protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix="VirtualNetwork", source_port_range="*", @@ -265,57 +316,6 @@ def __init__( ], opts=child_opts, ) - nsg_identity_servers = network.NetworkSecurityGroup( - f"{self._name}_nsg_identity", - network_security_group_name=f"{stack_name}-nsg-identity", - resource_group_name=resource_group.name, - security_rules=[ - # Inbound - network.SecurityRuleArgs( - access=network.SecurityRuleAccess.ALLOW, - description="Allow inbound LDAP to domain controllers.", - destination_address_prefix=str( - props.subnet_identity_servers_iprange - ), - destination_port_ranges=["389", "636"], - direction=network.SecurityRuleDirection.INBOUND, - name="AllowLDAPClientUDPInbound", - priority=NetworkingPriorities.INTERNAL_SHM_LDAP_UDP, - protocol=network.SecurityRuleProtocol.UDP, - source_address_prefix="*", - source_port_range="*", - ), - network.SecurityRuleArgs( - access=network.SecurityRuleAccess.ALLOW, - description="Allow inbound LDAP to domain controllers.", - destination_address_prefix=str( - props.subnet_identity_servers_iprange - ), - destination_port_ranges=["389", "636"], - direction=network.SecurityRuleDirection.INBOUND, - name="AllowLDAPClientTCPInbound", - priority=NetworkingPriorities.INTERNAL_SHM_LDAP_TCP, - protocol=network.SecurityRuleProtocol.TCP, - source_address_prefix="*", - source_port_range="*", - ), - network.SecurityRuleArgs( - access=network.SecurityRuleAccess.ALLOW, - description="Allow inbound RDP connections from admins using AzureBastion.", - destination_address_prefix=str( - props.subnet_identity_servers_iprange - ), - destination_port_ranges=["3389"], - direction=network.SecurityRuleDirection.INBOUND, - name="AllowBastionAdminsInbound", - priority=NetworkingPriorities.INTERNAL_SHM_BASTION, - protocol=network.SecurityRuleProtocol.TCP, - source_address_prefix=str(props.subnet_bastion_iprange), - source_port_range="*", - ), - ], - opts=child_opts, - ) # Define route table route_table = network.RouteTable( @@ -335,9 +335,9 @@ def __init__( # Define the virtual network and its subnets subnet_firewall_name = "AzureFirewallSubnet" # this name is forced by https://docs.microsoft.com/en-us/azure/firewall/tutorial-firewall-deploy-portal subnet_bastion_name = "AzureBastionSubnet" # this name is forced by https://learn.microsoft.com/en-us/azure/bastion/configuration-settings#subnet + subnet_identity_servers_name = "IdentityServersSubnet" subnet_monitoring_name = "MonitoringSubnet" subnet_update_servers_name = "UpdateServersSubnet" - subnet_identity_servers_name = "IdentityServersSubnet" virtual_network = network.VirtualNetwork( f"{self._name}_virtual_network", address_space=network.AddressSpaceArgs( @@ -361,6 +361,15 @@ def __init__( ), route_table=None, # the bastion subnet must NOT be attached to the route table ), + # Identity servers subnet + network.SubnetArgs( + address_prefix=str(props.subnet_identity_servers_iprange), + name=subnet_identity_servers_name, + network_security_group=network.NetworkSecurityGroupArgs( + id=nsg_identity_servers.id + ), + route_table=network.RouteTableArgs(id=route_table.id), + ), # Monitoring subnet network.SubnetArgs( address_prefix=str(props.subnet_monitoring_iprange), @@ -379,15 +388,6 @@ def __init__( ), route_table=network.RouteTableArgs(id=route_table.id), ), - # Identity servers subnet - network.SubnetArgs( - address_prefix=str(props.subnet_identity_servers_iprange), - name=subnet_identity_servers_name, - network_security_group=network.NetworkSecurityGroupArgs( - id=nsg_identity_servers.id - ), - route_table=network.RouteTableArgs(id=route_table.id), - ), ], virtual_network_name=f"{stack_name}-vnet", virtual_network_peerings=[], From b0ae30b6a038f9daf0993b341fd890c7fc5d45c7 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Thu, 10 Aug 2023 12:36:28 +0100 Subject: [PATCH 15/38] :bug: Fix error in Guacamole NSG rule --- .../pulumi/components/sre_networking.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index a71e93f008..c01332a5ef 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -140,18 +140,6 @@ def __init__( source_address_prefix="GatewayManager", source_port_range="*", ), - network.SecurityRuleArgs( - access=network.SecurityRuleAccess.ALLOW, - description="Allow inbound gateway management traffic over the internet.", - destination_address_prefix="*", # this must be '*' or Azure validation will fail - destination_port_range="65200-65535", - direction=network.SecurityRuleDirection.INBOUND, - name="AllowGatewayManagerInternetInbound", - priority=NetworkingPriorities.EXTERNAL_INTERNET, - protocol=network.SecurityRuleProtocol.ASTERISK, - source_address_prefix="Internet", - source_port_range="*", - ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow inbound connections from users over the internet.", @@ -164,6 +152,18 @@ def __init__( source_address_prefix=props.public_ip_range_users, source_port_range="*", ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound gateway management traffic over the internet.", + destination_address_prefix="*", # this must be '*' or Azure validation will fail + destination_port_range="65200-65535", + direction=network.SecurityRuleDirection.INBOUND, + name="AllowGatewayManagerInternetInbound", + priority=NetworkingPriorities.EXTERNAL_INTERNET, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="Internet", + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other inbound traffic.", @@ -178,7 +178,7 @@ def __init__( ), # Outbound network.SecurityRuleArgs( - access=network.SecurityRuleAccess.DENY, + access=network.SecurityRuleAccess.ALLOW, description="Allow outbound connections to the Guacamole remote desktop gateway.", destination_address_prefix=subnet_guacamole_containers_prefix, destination_port_ranges=["80"], From b5b64ff709bca33181f8bc22191ff47cef6ef3b4 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Thu, 10 Aug 2023 12:48:38 +0100 Subject: [PATCH 16/38] :sparkles: Add configuration data subnet --- .../pulumi/components/sre_backup.py | 2 +- data_safe_haven/pulumi/components/sre_data.py | 70 +++++++++++++++---- .../pulumi/components/sre_networking.py | 63 ++++++++++++++++- data_safe_haven/pulumi/declarative_sre.py | 9 +-- 4 files changed, 125 insertions(+), 19 deletions(-) diff --git a/data_safe_haven/pulumi/components/sre_backup.py b/data_safe_haven/pulumi/components/sre_backup.py index 58681ebdcc..e0785a86a9 100644 --- a/data_safe_haven/pulumi/components/sre_backup.py +++ b/data_safe_haven/pulumi/components/sre_backup.py @@ -1,4 +1,4 @@ -"""Pulumi component for SRE state""" +"""Pulumi component for SRE backup""" from pulumi import ComponentResource, Input, ResourceOptions from pulumi_azure_native import dataprotection, resources diff --git a/data_safe_haven/pulumi/components/sre_data.py b/data_safe_haven/pulumi/components/sre_data.py index 7b236171e1..77e6aa6cb1 100644 --- a/data_safe_haven/pulumi/components/sre_data.py +++ b/data_safe_haven/pulumi/components/sre_data.py @@ -1,4 +1,4 @@ -"""Pulumi component for SRE state""" +"""Pulumi component for SRE data""" from collections.abc import Sequence from pulumi import ComponentResource, Config, Input, Output, ResourceOptions @@ -47,6 +47,7 @@ def __init__( networking_resource_group: Input[resources.ResourceGroup], pulumi_opts: Config, sre_fqdn: Input[str], + subnet_configuration_data: Input[network.GetSubnetResult], subnet_private_data: Input[network.GetSubnetResult], subscription_id: Input[str], subscription_name: Input[str], @@ -86,6 +87,9 @@ def __init__( pulumi_opts, "shm-networking-private_dns_zone_base_id" ) self.sre_fqdn = sre_fqdn + self.subnet_configuration_data_id = Output.from_input( + subnet_configuration_data + ).apply(get_id_from_subnet) self.subnet_private_data_id = Output.from_input(subnet_private_data).apply( get_id_from_subnet ) @@ -98,7 +102,7 @@ def get_secret(self, pulumi_opts: Config, secret_name: str) -> Output[str]: class SREDataComponent(ComponentResource): - """Deploy SRE state with Pulumi""" + """Deploy SRE data with Pulumi""" def __init__( self, @@ -296,21 +300,21 @@ def __init__( opts=ResourceOptions(parent=key_vault), ) - # Deploy state storage account - storage_account_state = storage.StorageAccount( - f"{self._name}_storage_account_state", + # Deploy configuration data storage account + storage_account_configuration_data = storage.StorageAccount( + f"{self._name}_storage_account_configuration_data", # Note that account names have a maximum of 24 characters account_name=alphanumeric( - f"{''.join(truncate_tokens(stack_name.split('-'), 19))}state" + f"{''.join(truncate_tokens(stack_name.split('-'), 11))}configuration" )[:24], kind=storage.Kind.STORAGE_V2, resource_group_name=resource_group.name, sku=storage.SkuArgs(name=storage.SkuName.STANDARD_GRS), opts=child_opts, ) - # Retrieve storage account keys - storage_account_state_keys = Output.all( - account_name=storage_account_state.name, + # Retrieve configuration data storage account keys + storage_account_configuration_data_keys = Output.all( + account_name=storage_account_configuration_data.name, resource_group_name=resource_group.name, ).apply( lambda kwargs: storage.list_storage_account_keys( @@ -318,6 +322,46 @@ def __init__( resource_group_name=kwargs["resource_group_name"], ) ) + # Set up a private endpoint for the configuration data storage account + storage_account_configuration_data_private_endpoint = network.PrivateEndpoint( + f"{self._name}_storage_account_configuration_data_private_endpoint", + location=props.location, + private_endpoint_name=f"{stack_name}-pep-storage-account-configuration-data", + private_link_service_connections=[ + network.PrivateLinkServiceConnectionArgs( + group_ids=["blob"], + name=f"{stack_name}-cnxn-pep-storage-account-configuration-data", + private_link_service_id=storage_account_configuration_data.id, + ) + ], + resource_group_name=resource_group.name, + subnet=network.SubnetArgs(id=props.subnet_configuration_data_id), + opts=ResourceOptions.merge( + child_opts, ResourceOptions(parent=storage_account_configuration_data) + ), + ) + # Add a private DNS record for each configuration data custom DNS config + network.PrivateDnsZoneGroup( + f"{self._name}_storage_account_configuration_data_private_dns_zone_group", + private_dns_zone_configs=[ + network.PrivateDnsZoneConfigArgs( + name=replace_separators( + f"{stack_name}-storage-account-configuration-data-to-{dns_zone_name}", + "-", + ), + private_dns_zone_id=Output.concat( + props.private_dns_zone_base_id, dns_zone_name + ), + ) + for dns_zone_name in ordered_private_dns_zones("Storage account") + ], + private_dns_zone_group_name=f"{stack_name}-dzg-storage-account-configuration-data", + private_endpoint_name=storage_account_configuration_data_private_endpoint.name, + resource_group_name=resource_group.name, + opts=ResourceOptions.merge( + child_opts, ResourceOptions(parent=storage_account_configuration_data) + ), + ) # Deploy secure data blob storage account # - Azure blobs have worse NFS support but can be accessed with Azure Storage Explorer @@ -594,10 +638,12 @@ def __init__( self.storage_account_userdata_name = storage_account_userdata.name self.storage_account_securedata_id = storage_account_securedata.id self.storage_account_securedata_name = storage_account_securedata.name - self.storage_account_state_key = Output.secret( - storage_account_state_keys.keys[0].value + self.storage_account_configuration_data_key = Output.secret( + storage_account_configuration_data_keys.keys[0].value + ) + self.storage_account_configuration_data_name = ( + storage_account_configuration_data.name ) - self.storage_account_state_name = storage_account_state.name self.managed_identity = identity_key_vault_reader self.password_nexus_admin = Output.secret(props.password_nexus_admin) self.password_database_service_admin = Output.secret( diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index c01332a5ef..1daa15c303 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -30,6 +30,9 @@ def __init__( self.subnet_application_gateway_iprange = self.vnet_iprange.apply( lambda r: r.next_subnet(256) ) + self.subnet_configuration_data_iprange = self.vnet_iprange.apply( + lambda r: r.next_subnet(8) + ) self.subnet_guacamole_containers_iprange = self.vnet_iprange.apply( lambda r: r.next_subnet(8) ) @@ -37,7 +40,7 @@ def __init__( lambda r: r.next_subnet(8) ) self.subnet_private_data_iprange = self.vnet_iprange.apply( - lambda r: r.next_subnet(16) + lambda r: r.next_subnet(8) ) self.subnet_user_services_containers_iprange = self.vnet_iprange.apply( lambda r: r.next_subnet(8) @@ -101,6 +104,9 @@ def __init__( subnet_private_data_prefix = props.subnet_private_data_iprange.apply( lambda r: str(r) ) + subnet_configuration_data_prefix = ( + props.subnet_configuration_data_iprange.apply(lambda r: str(r)) + ) subnet_user_services_containers_prefix = ( props.subnet_user_services_containers_iprange.apply(lambda r: str(r)) ) @@ -216,6 +222,40 @@ def __init__( ], opts=child_opts, ) + nsg_configuration_data = network.NetworkSecurityGroup( + f"{self._name}_nsg_configuration_data", + network_security_group_name=f"{stack_name}-nsg-configuration-data", + resource_group_name=resource_group.name, + security_rules=[ + # Inbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other inbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="DenyAllOtherInbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other outbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.OUTBOUND, + name="DenyAllOtherOutbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + ], + opts=child_opts, + ) nsg_guacamole_containers = network.NetworkSecurityGroup( f"{self._name}_nsg_guacamole_containers", network_security_group_name=f"{stack_name}-nsg-guacamole-containers", @@ -664,7 +704,6 @@ def __init__( source_address_prefix=subnet_guacamole_containers_prefix, source_port_range="*", ), - network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other inbound traffic.", @@ -795,6 +834,7 @@ def __init__( # Define the virtual network and its subnets subnet_application_gateway_name = "ApplicationGatewaySubnet" + subnet_configuration_data_name = "ConfigurationDataSubnet" subnet_guacamole_containers_name = "GuacamoleContainersSubnet" subnet_guacamole_containers_support_name = "GuacamoleContainersSupportSubnet" subnet_private_data_name = "PrivateDataSubnet" @@ -822,6 +862,20 @@ def __init__( id=nsg_application_gateway.id ), ), + # Configuration data subnet + network.SubnetArgs( + address_prefix=subnet_configuration_data_prefix, + name=subnet_configuration_data_name, + network_security_group=network.NetworkSecurityGroupArgs( + id=nsg_configuration_data.id + ), + service_endpoints=[ + network.ServiceEndpointPropertiesFormatArgs( + locations=[props.location], + service="Microsoft.Storage", + ) + ], + ), # Guacamole containers network.SubnetArgs( address_prefix=subnet_guacamole_containers_prefix, @@ -1069,6 +1123,11 @@ def __init__( resource_group_name=resource_group.name, virtual_network_name=sre_virtual_network.name, ) + self.subnet_configuration_data = network.get_subnet_output( + subnet_name=subnet_configuration_data_name, + resource_group_name=resource_group.name, + virtual_network_name=sre_virtual_network.name, + ) self.subnet_guacamole_containers = network.get_subnet_output( subnet_name=subnet_guacamole_containers_name, resource_group_name=resource_group.name, diff --git a/data_safe_haven/pulumi/declarative_sre.py b/data_safe_haven/pulumi/declarative_sre.py index 7d2a867bb9..b08c8a8fbb 100644 --- a/data_safe_haven/pulumi/declarative_sre.py +++ b/data_safe_haven/pulumi/declarative_sre.py @@ -119,6 +119,7 @@ def run(self) -> None: networking_resource_group=networking.resource_group, pulumi_opts=self.pulumi_opts, sre_fqdn=networking.sre_fqdn, + subnet_configuration_data=networking.subnet_configuration_data, subnet_private_data=networking.subnet_private_data, subscription_id=self.cfg.azure.subscription_id, subscription_name=self.cfg.subscription_name, @@ -161,8 +162,8 @@ def run(self) -> None: location=self.cfg.azure.location, subnet_guacamole_containers=networking.subnet_guacamole_containers, subnet_guacamole_containers_support=networking.subnet_guacamole_containers_support, - storage_account_key=data.storage_account_state_key, - storage_account_name=data.storage_account_state_name, + storage_account_key=data.storage_account_configuration_data_key, + storage_account_name=data.storage_account_configuration_data_name, storage_account_resource_group_name=data.resource_group_name, virtual_network_resource_group_name=networking.resource_group.name, virtual_network=networking.virtual_network, @@ -230,8 +231,8 @@ def run(self) -> None: software_packages=self.cfg.sres[self.sre_name].software_packages, sre_fqdn=networking.sre_fqdn, sre_private_dns_zone_id=networking.sre_private_dns_zone_id, - storage_account_key=data.storage_account_state_key, - storage_account_name=data.storage_account_state_name, + storage_account_key=data.storage_account_configuration_data_key, + storage_account_name=data.storage_account_configuration_data_name, storage_account_resource_group_name=data.resource_group_name, subnet_containers=networking.subnet_user_services_containers, subnet_containers_support=networking.subnet_user_services_containers_support, From fb32f2783e785b8a912fd4262e1b8b0eef20b0ae Mon Sep 17 00:00:00 2001 From: James Robinson Date: Thu, 10 Aug 2023 16:04:49 +0100 Subject: [PATCH 17/38] :recycle: Refactor private data storage account naming conventions --- data_safe_haven/pulumi/common/enums.py | 7 +- .../pulumi/components/sre_backup.py | 20 +- data_safe_haven/pulumi/components/sre_data.py | 176 ++++++++++-------- .../pulumi/components/sre_networking.py | 70 +++---- .../pulumi/components/sre_workspace.py | 24 ++- data_safe_haven/pulumi/declarative_sre.py | 20 +- .../workspace.cloud_init.mustache.yaml | 8 +- 7 files changed, 173 insertions(+), 152 deletions(-) diff --git a/data_safe_haven/pulumi/common/enums.py b/data_safe_haven/pulumi/common/enums.py index 7946cb80f9..46769413c4 100644 --- a/data_safe_haven/pulumi/common/enums.py +++ b/data_safe_haven/pulumi/common/enums.py @@ -16,9 +16,10 @@ class NetworkingPriorities(int, Enum): INTERNAL_SHM_MONITORING_TOOLS = 1300 INTERNAL_SHM_UPDATE_SERVERS = 1400 INTERNAL_SRE_APPLICATION_GATEWAY = 1500 - INTERNAL_SRE_GUACAMOLE_CONTAINERS = 1600 - INTERNAL_SRE_GUACAMOLE_CONTAINERS_SUPPORT = 1650 - INTERNAL_SRE_PRIVATE_DATA = 1700 + INTERNAL_SRE_DATA_CONFIGURATION = 1600 + INTERNAL_SRE_DATA_PRIVATE = 1650 + INTERNAL_SRE_GUACAMOLE_CONTAINERS = 1700 + INTERNAL_SRE_GUACAMOLE_CONTAINERS_SUPPORT = 1750 INTERNAL_SRE_USER_SERVICES_CONTAINERS = 1800 INTERNAL_SRE_USER_SERVICES_CONTAINERS_SUPPORT = 1825 INTERNAL_SRE_USER_SERVICES_DATABASES = 1850 diff --git a/data_safe_haven/pulumi/components/sre_backup.py b/data_safe_haven/pulumi/components/sre_backup.py index e0785a86a9..888a1d2126 100644 --- a/data_safe_haven/pulumi/components/sre_backup.py +++ b/data_safe_haven/pulumi/components/sre_backup.py @@ -9,12 +9,16 @@ class SREBackupProps: def __init__( self, location: Input[str], - storage_account_securedata_id: Input[str], - storage_account_securedata_name: Input[str], + storage_account_data_private_sensitive_id: Input[str], + storage_account_data_private_sensitive_name: Input[str], ) -> None: self.location = location - self.storage_account_securedata_id = storage_account_securedata_id - self.storage_account_securedata_name = storage_account_securedata_name + self.storage_account_data_private_sensitive_id = ( + storage_account_data_private_sensitive_id + ) + self.storage_account_data_private_sensitive_name = ( + storage_account_data_private_sensitive_name + ) class SREBackupComponent(ComponentResource): @@ -164,19 +168,19 @@ def __init__( backup_instance_name="backup-instance-blobs", properties=dataprotection.BackupInstanceArgs( data_source_info=dataprotection.DatasourceArgs( - resource_id=props.storage_account_securedata_id, + resource_id=props.storage_account_data_private_sensitive_id, datasource_type="Microsoft.Storage/storageAccounts/blobServices", object_type="Datasource", resource_location=props.location, - resource_name=props.storage_account_securedata_name, + resource_name=props.storage_account_data_private_sensitive_name, resource_type="Microsoft.Storage/storageAccounts", - resource_uri=props.storage_account_securedata_id, + resource_uri=props.storage_account_data_private_sensitive_id, ), object_type="BackupInstance", policy_info=dataprotection.PolicyInfoArgs( policy_id=backup_policy_blobs.id, ), - friendly_name="BlobBackupSecureData", + friendly_name="BlobBackupSensitiveData", ), resource_group_name=resource_group.name, vault_name=backup_vault.name, diff --git a/data_safe_haven/pulumi/components/sre_data.py b/data_safe_haven/pulumi/components/sre_data.py index 77e6aa6cb1..89cb0f40ae 100644 --- a/data_safe_haven/pulumi/components/sre_data.py +++ b/data_safe_haven/pulumi/components/sre_data.py @@ -47,8 +47,8 @@ def __init__( networking_resource_group: Input[resources.ResourceGroup], pulumi_opts: Config, sre_fqdn: Input[str], - subnet_configuration_data: Input[network.GetSubnetResult], - subnet_private_data: Input[network.GetSubnetResult], + subnet_data_configuration: Input[network.GetSubnetResult], + subnet_data_private: Input[network.GetSubnetResult], subscription_id: Input[str], subscription_name: Input[str], tenant_id: Input[str], @@ -87,10 +87,10 @@ def __init__( pulumi_opts, "shm-networking-private_dns_zone_base_id" ) self.sre_fqdn = sre_fqdn - self.subnet_configuration_data_id = Output.from_input( - subnet_configuration_data + self.subnet_data_configuration_id = Output.from_input( + subnet_data_configuration ).apply(get_id_from_subnet) - self.subnet_private_data_id = Output.from_input(subnet_private_data).apply( + self.subnet_data_private_id = Output.from_input(subnet_data_private).apply( get_id_from_subnet ) self.subscription_id = subscription_id @@ -301,11 +301,11 @@ def __init__( ) # Deploy configuration data storage account - storage_account_configuration_data = storage.StorageAccount( - f"{self._name}_storage_account_configuration_data", + storage_account_data_configuration = storage.StorageAccount( + f"{self._name}_storage_account_data_configuration", # Note that account names have a maximum of 24 characters account_name=alphanumeric( - f"{''.join(truncate_tokens(stack_name.split('-'), 11))}configuration" + f"{''.join(truncate_tokens(stack_name.split('-'), 14))}configdata" )[:24], kind=storage.Kind.STORAGE_V2, resource_group_name=resource_group.name, @@ -313,8 +313,8 @@ def __init__( opts=child_opts, ) # Retrieve configuration data storage account keys - storage_account_configuration_data_keys = Output.all( - account_name=storage_account_configuration_data.name, + storage_account_data_configuration_keys = Output.all( + account_name=storage_account_data_configuration.name, resource_group_name=resource_group.name, ).apply( lambda kwargs: storage.list_storage_account_keys( @@ -323,30 +323,30 @@ def __init__( ) ) # Set up a private endpoint for the configuration data storage account - storage_account_configuration_data_private_endpoint = network.PrivateEndpoint( - f"{self._name}_storage_account_configuration_data_private_endpoint", + storage_account_data_configuration_private_endpoint = network.PrivateEndpoint( + f"{self._name}_storage_account_data_configuration_private_endpoint", location=props.location, - private_endpoint_name=f"{stack_name}-pep-storage-account-configuration-data", + private_endpoint_name=f"{stack_name}-pep-storage-account-data-configuration", private_link_service_connections=[ network.PrivateLinkServiceConnectionArgs( - group_ids=["blob"], - name=f"{stack_name}-cnxn-pep-storage-account-configuration-data", - private_link_service_id=storage_account_configuration_data.id, + group_ids=["file"], + name=f"{stack_name}-cnxn-pep-storage-account-data-configuration", + private_link_service_id=storage_account_data_configuration.id, ) ], resource_group_name=resource_group.name, - subnet=network.SubnetArgs(id=props.subnet_configuration_data_id), + subnet=network.SubnetArgs(id=props.subnet_data_configuration_id), opts=ResourceOptions.merge( - child_opts, ResourceOptions(parent=storage_account_configuration_data) + child_opts, ResourceOptions(parent=storage_account_data_configuration) ), ) - # Add a private DNS record for each configuration data custom DNS config + # Add a private DNS record for each configuration data endpoint custom DNS config network.PrivateDnsZoneGroup( - f"{self._name}_storage_account_configuration_data_private_dns_zone_group", + f"{self._name}_storage_account_data_configuration_private_dns_zone_group", private_dns_zone_configs=[ network.PrivateDnsZoneConfigArgs( name=replace_separators( - f"{stack_name}-storage-account-configuration-data-to-{dns_zone_name}", + f"{stack_name}-storage-account-data-configuration-to-{dns_zone_name}", "-", ), private_dns_zone_id=Output.concat( @@ -355,22 +355,22 @@ def __init__( ) for dns_zone_name in ordered_private_dns_zones("Storage account") ], - private_dns_zone_group_name=f"{stack_name}-dzg-storage-account-configuration-data", - private_endpoint_name=storage_account_configuration_data_private_endpoint.name, + private_dns_zone_group_name=f"{stack_name}-dzg-storage-account-data-configuration", + private_endpoint_name=storage_account_data_configuration_private_endpoint.name, resource_group_name=resource_group.name, opts=ResourceOptions.merge( - child_opts, ResourceOptions(parent=storage_account_configuration_data) + child_opts, ResourceOptions(parent=storage_account_data_configuration) ), ) - # Deploy secure data blob storage account + # Deploy sensitive data blob storage account # - Azure blobs have worse NFS support but can be accessed with Azure Storage Explorer # - Store the /data and /output folders here - storage_account_securedata = storage.StorageAccount( - f"{self._name}_storage_account_securedata", + storage_account_data_private_sensitive = storage.StorageAccount( + f"{self._name}_storage_account_data_private_sensitive", # Storage account names have a maximum of 24 characters account_name=alphanumeric( - f"{''.join(truncate_tokens(stack_name.split('-'), 14))}securedata{sha256hash(self._name)}" + f"{''.join(truncate_tokens(stack_name.split('-'), 11))}sensitivedata{sha256hash(self._name)}" )[:24], enable_https_traffic_only=True, enable_nfs_v3=True, @@ -403,7 +403,7 @@ def __init__( ), virtual_network_rules=[ storage.VirtualNetworkRuleArgs( - virtual_network_resource_id=props.subnet_private_data_id, + virtual_network_resource_id=props.subnet_data_private_id, ) ], ), @@ -413,7 +413,7 @@ def __init__( ) # Give the "Storage Blob Data Owner" role to the Azure admin group authorization.RoleAssignment( - f"{self._name}_storage_account_securedata_data_owner_role_assignment", + f"{self._name}_storage_account_data_private_sensitive_data_owner_role_assignment", principal_id=props.admin_group_id, principal_type=authorization.PrincipalType.GROUP, role_assignment_name="b7e6dc6d-f1e8-4753-8033-0f276bb0955b", # Storage Blob Data Owner @@ -422,34 +422,37 @@ def __init__( props.subscription_id, "/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b", ), - scope=storage_account_securedata.id, + scope=storage_account_data_private_sensitive.id, opts=ResourceOptions.merge( - child_opts, ResourceOptions(parent=storage_account_securedata) + child_opts, + ResourceOptions(parent=storage_account_data_private_sensitive), ), ) # Deploy storage containers storage_container_egress = storage.BlobContainer( f"{self._name}_storage_container_egress", - account_name=storage_account_securedata.name, + account_name=storage_account_data_private_sensitive.name, container_name="egress", default_encryption_scope="$account-encryption-key", deny_encryption_scope_override=False, public_access=storage.PublicAccess.NONE, resource_group_name=resource_group.name, opts=ResourceOptions.merge( - child_opts, ResourceOptions(parent=storage_account_securedata) + child_opts, + ResourceOptions(parent=storage_account_data_private_sensitive), ), ) storage_container_ingress = storage.BlobContainer( f"{self._name}_storage_container_ingress", - account_name=storage_account_securedata.name, + account_name=storage_account_data_private_sensitive.name, container_name="ingress", default_encryption_scope="$account-encryption-key", deny_encryption_scope_override=False, public_access=storage.PublicAccess.NONE, resource_group_name=resource_group.name, opts=ResourceOptions.merge( - child_opts, ResourceOptions(parent=storage_account_securedata) + child_opts, + ResourceOptions(parent=storage_account_data_private_sensitive), ), ) # Set storage container ACLs @@ -465,7 +468,7 @@ def __init__( apply_default_permissions=False, container_name=storage_container_egress.name, resource_group_name=resource_group.name, - storage_account_name=storage_account_securedata.name, + storage_account_name=storage_account_data_private_sensitive.name, subscription_name=props.subscription_name, ), opts=ResourceOptions.merge( @@ -483,38 +486,39 @@ def __init__( apply_default_permissions=True, container_name=storage_container_ingress.name, resource_group_name=resource_group.name, - storage_account_name=storage_account_securedata.name, + storage_account_name=storage_account_data_private_sensitive.name, subscription_name=props.subscription_name, ), opts=ResourceOptions.merge( child_opts, ResourceOptions(parent=storage_container_ingress) ), ) - # Set up a private endpoint for the securedata data account - storage_account_securedata_endpoint = network.PrivateEndpoint( - f"{self._name}_storage_account_securedata_private_endpoint", + # Set up a private endpoint for the sensitive data storage account + storage_account_data_private_sensitive_endpoint = network.PrivateEndpoint( + f"{self._name}_storage_account_data_private_sensitive_private_endpoint", location=props.location, - private_endpoint_name=f"{stack_name}-pep-storage-account-securedata", + private_endpoint_name=f"{stack_name}-pep-storage-account-data-private-sensitive", private_link_service_connections=[ network.PrivateLinkServiceConnectionArgs( group_ids=["blob"], - name=f"{stack_name}-cnxn-pep-storage-account-securedata", - private_link_service_id=storage_account_securedata.id, + name=f"{stack_name}-cnxn-pep-storage-account-data-private-sensitive", + private_link_service_id=storage_account_data_private_sensitive.id, ) ], resource_group_name=resource_group.name, - subnet=network.SubnetArgs(id=props.subnet_private_data_id), + subnet=network.SubnetArgs(id=props.subnet_data_private_id), opts=ResourceOptions.merge( - child_opts, ResourceOptions(parent=storage_account_securedata) + child_opts, + ResourceOptions(parent=storage_account_data_private_sensitive), ), ) - # Add a private DNS record for each securedata data custom DNS config + # Add a private DNS record for each sensitive data endpoint custom DNS config network.PrivateDnsZoneGroup( - f"{self._name}_storage_account_securedata_private_dns_zone_group", + f"{self._name}_storage_account_data_private_sensitive_private_dns_zone_group", private_dns_zone_configs=[ network.PrivateDnsZoneConfigArgs( name=replace_separators( - f"{stack_name}-storage-account-securedata-to-{dns_zone_name}", + f"{stack_name}-storage-account-data-private-sensitive-to-{dns_zone_name}", "-", ), private_dns_zone_id=Output.concat( @@ -523,20 +527,21 @@ def __init__( ) for dns_zone_name in ordered_private_dns_zones("Storage account") ], - private_dns_zone_group_name=f"{stack_name}-dzg-storage-account-securedata", - private_endpoint_name=storage_account_securedata_endpoint.name, + private_dns_zone_group_name=f"{stack_name}-dzg-storage-account-data-private-sensitive", + private_endpoint_name=storage_account_data_private_sensitive_endpoint.name, resource_group_name=resource_group.name, opts=ResourceOptions.merge( - child_opts, ResourceOptions(parent=storage_account_securedata) + child_opts, + ResourceOptions(parent=storage_account_data_private_sensitive), ), ) - # Deploy userdata files storage account + # Deploy data_private_user files storage account # - Azure Files has better NFS support and cannot be accessed with Azure Storage Explorer # - Allows root-squashing to be configured # - Store the /home and /shared folders here - storage_account_userdata = storage.StorageAccount( - f"{self._name}_storage_account_userdata", + storage_account_data_private_user = storage.StorageAccount( + f"{self._name}_storage_account_data_private_user", access_tier=storage.AccessTier.COOL, # Storage account names have a maximum of 24 characters account_name=alphanumeric( @@ -558,7 +563,7 @@ def __init__( default_action=storage.DefaultAction.DENY, virtual_network_rules=[ storage.VirtualNetworkRuleArgs( - virtual_network_resource_id=props.subnet_private_data_id, + virtual_network_resource_id=props.subnet_data_private_id, ) ], ), @@ -569,7 +574,7 @@ def __init__( storage.FileShare( f"{self._name}_storage_container_home", access_tier=storage.ShareAccessTier.PREMIUM, - account_name=storage_account_userdata.name, + account_name=storage_account_data_private_user.name, enabled_protocols=storage.EnabledProtocols.NFS, resource_group_name=resource_group.name, # Squashing prevents root from creating user home directories @@ -577,47 +582,48 @@ def __init__( share_name="home", share_quota=1024, opts=ResourceOptions.merge( - child_opts, ResourceOptions(parent=storage_account_userdata) + child_opts, ResourceOptions(parent=storage_account_data_private_user) ), ) storage.FileShare( f"{self._name}_storage_container_shared", access_tier=storage.ShareAccessTier.PREMIUM, - account_name=storage_account_userdata.name, + account_name=storage_account_data_private_user.name, enabled_protocols=storage.EnabledProtocols.NFS, resource_group_name=resource_group.name, root_squash=storage.RootSquashType.ROOT_SQUASH, share_name="shared", share_quota=1024, opts=ResourceOptions.merge( - child_opts, ResourceOptions(parent=storage_account_userdata) + child_opts, ResourceOptions(parent=storage_account_data_private_user) ), ) - # Set up a private endpoint for the userdata storage account - storage_account_userdata_endpoint = network.PrivateEndpoint( - f"{self._name}_storage_account_userdata_private_endpoint", + # Set up a private endpoint for the user data storage account + storage_account_data_private_user_endpoint = network.PrivateEndpoint( + f"{self._name}_storage_account_data_private_user_private_endpoint", location=props.location, - private_endpoint_name=f"{stack_name}-pep-storage-account-userdata", + private_endpoint_name=f"{stack_name}-pep-storage-account-data-private-user", private_link_service_connections=[ network.PrivateLinkServiceConnectionArgs( group_ids=["file"], - name=f"{stack_name}-cnxn-pep-storage-account-userdata", - private_link_service_id=storage_account_userdata.id, + name=f"{stack_name}-cnxn-pep-storage-account-data-private-user", + private_link_service_id=storage_account_data_private_user.id, ) ], resource_group_name=resource_group.name, - subnet=network.SubnetArgs(id=props.subnet_private_data_id), + subnet=network.SubnetArgs(id=props.subnet_data_private_id), opts=ResourceOptions.merge( - child_opts, ResourceOptions(parent=storage_account_userdata) + child_opts, ResourceOptions(parent=storage_account_data_private_user) ), ) - # Add a private DNS record for each userdata custom DNS config + # Add a private DNS record for each user data endpoint custom DNS config network.PrivateDnsZoneGroup( - f"{self._name}_storage_account_userdata_private_dns_zone_group", + f"{self._name}_storage_account_data_private_user_private_dns_zone_group", private_dns_zone_configs=[ network.PrivateDnsZoneConfigArgs( name=replace_separators( - f"{stack_name}-storage-account-userdata-to-{dns_zone_name}", "-" + f"{stack_name}-storage-account-data-private-user-to-{dns_zone_name}", + "-", ), private_dns_zone_id=Output.concat( props.private_dns_zone_base_id, dns_zone_name @@ -625,24 +631,30 @@ def __init__( ) for dns_zone_name in ordered_private_dns_zones("Storage account") ], - private_dns_zone_group_name=f"{stack_name}-dzg-storage-account-userdata", - private_endpoint_name=storage_account_userdata_endpoint.name, + private_dns_zone_group_name=f"{stack_name}-dzg-storage-account-data-private-user", + private_endpoint_name=storage_account_data_private_user_endpoint.name, resource_group_name=resource_group.name, opts=ResourceOptions.merge( - child_opts, ResourceOptions(parent=storage_account_userdata) + child_opts, ResourceOptions(parent=storage_account_data_private_user) ), ) # Register outputs self.sre_fqdn_certificate_secret_id = sre_fqdn_certificate.secret_id - self.storage_account_userdata_name = storage_account_userdata.name - self.storage_account_securedata_id = storage_account_securedata.id - self.storage_account_securedata_name = storage_account_securedata.name - self.storage_account_configuration_data_key = Output.secret( - storage_account_configuration_data_keys.keys[0].value - ) - self.storage_account_configuration_data_name = ( - storage_account_configuration_data.name + self.storage_account_data_private_user_name = ( + storage_account_data_private_user.name + ) + self.storage_account_data_private_sensitive_id = ( + storage_account_data_private_sensitive.id + ) + self.storage_account_data_private_sensitive_name = ( + storage_account_data_private_sensitive.name + ) + self.storage_account_data_configuration_key = Output.secret( + storage_account_data_configuration_keys.keys[0].value + ) + self.storage_account_data_configuration_name = ( + storage_account_data_configuration.name ) self.managed_identity = identity_key_vault_reader self.password_nexus_admin = Output.secret(props.password_nexus_admin) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 1daa15c303..23585d90ee 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -30,16 +30,16 @@ def __init__( self.subnet_application_gateway_iprange = self.vnet_iprange.apply( lambda r: r.next_subnet(256) ) - self.subnet_configuration_data_iprange = self.vnet_iprange.apply( + self.subnet_data_configuration_iprange = self.vnet_iprange.apply( lambda r: r.next_subnet(8) ) - self.subnet_guacamole_containers_iprange = self.vnet_iprange.apply( + self.subnet_data_private_iprange = self.vnet_iprange.apply( lambda r: r.next_subnet(8) ) - self.subnet_guacamole_containers_support_iprange = self.vnet_iprange.apply( + self.subnet_guacamole_containers_iprange = self.vnet_iprange.apply( lambda r: r.next_subnet(8) ) - self.subnet_private_data_iprange = self.vnet_iprange.apply( + self.subnet_guacamole_containers_support_iprange = self.vnet_iprange.apply( lambda r: r.next_subnet(8) ) self.subnet_user_services_containers_iprange = self.vnet_iprange.apply( @@ -101,11 +101,11 @@ def __init__( subnet_guacamole_containers_support_prefix = ( props.subnet_guacamole_containers_support_iprange.apply(lambda r: str(r)) ) - subnet_private_data_prefix = props.subnet_private_data_iprange.apply( + subnet_data_private_prefix = props.subnet_data_private_iprange.apply( lambda r: str(r) ) - subnet_configuration_data_prefix = ( - props.subnet_configuration_data_iprange.apply(lambda r: str(r)) + subnet_data_configuration_prefix = ( + props.subnet_data_configuration_iprange.apply(lambda r: str(r)) ) subnet_user_services_containers_prefix = ( props.subnet_user_services_containers_iprange.apply(lambda r: str(r)) @@ -222,8 +222,8 @@ def __init__( ], opts=child_opts, ) - nsg_configuration_data = network.NetworkSecurityGroup( - f"{self._name}_nsg_configuration_data", + nsg_data_configuration = network.NetworkSecurityGroup( + f"{self._name}_nsg_data_configuration", network_security_group_name=f"{stack_name}-nsg-configuration-data", resource_group_name=resource_group.name, security_rules=[ @@ -302,11 +302,11 @@ def __init__( network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow outbound connections to private data endpoints.", - destination_address_prefix=subnet_private_data_prefix, + destination_address_prefix=subnet_data_private_prefix, destination_port_range="*", direction=network.SecurityRuleDirection.OUTBOUND, name="AllowPrivateDataEndpointsOutbound", - priority=NetworkingPriorities.INTERNAL_SRE_PRIVATE_DATA, + priority=NetworkingPriorities.INTERNAL_SRE_DATA_PRIVATE, protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix=subnet_guacamole_containers_prefix, source_port_range="*", @@ -384,8 +384,8 @@ def __init__( ], opts=child_opts, ) - nsg_private_data = network.NetworkSecurityGroup( - f"{self._name}_nsg_private_data", + nsg_data_private = network.NetworkSecurityGroup( + f"{self._name}_nsg_data_private", network_security_group_name=f"{stack_name}-nsg-private-data", resource_group_name=resource_group.name, security_rules=[ @@ -405,7 +405,7 @@ def __init__( network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow inbound connections from Guacamole remote desktop gateway.", - destination_address_prefix=subnet_private_data_prefix, + destination_address_prefix=subnet_data_private_prefix, destination_port_range="*", direction=network.SecurityRuleDirection.INBOUND, name="AllowGuacamoleContainersInbound", @@ -417,7 +417,7 @@ def __init__( network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow inbound connections from user services containers.", - destination_address_prefix=subnet_private_data_prefix, + destination_address_prefix=subnet_data_private_prefix, destination_port_range="*", direction=network.SecurityRuleDirection.INBOUND, name="AllowUserServicesContainersInbound", @@ -429,7 +429,7 @@ def __init__( network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow inbound connections from user services software repositories.", - destination_address_prefix=subnet_private_data_prefix, + destination_address_prefix=subnet_data_private_prefix, destination_port_range="*", direction=network.SecurityRuleDirection.INBOUND, name="AllowUserServicesSoftwareRepositoriesInbound", @@ -441,7 +441,7 @@ def __init__( network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow inbound connections from SRE workspaces.", - destination_address_prefix=subnet_private_data_prefix, + destination_address_prefix=subnet_data_private_prefix, destination_port_range="*", direction=network.SecurityRuleDirection.INBOUND, name="AllowWorkspacesInbound", @@ -500,11 +500,11 @@ def __init__( network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow outbound connections to private data endpoints.", - destination_address_prefix=subnet_private_data_prefix, + destination_address_prefix=subnet_data_private_prefix, destination_port_range="*", direction=network.SecurityRuleDirection.OUTBOUND, name="AllowPrivateDataEndpointsOutbound", - priority=NetworkingPriorities.INTERNAL_SRE_PRIVATE_DATA, + priority=NetworkingPriorities.INTERNAL_SRE_DATA_PRIVATE, protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix=subnet_user_services_containers_prefix, source_port_range="*", @@ -616,11 +616,11 @@ def __init__( network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow outbound connections to private data endpoints.", - destination_address_prefix=subnet_private_data_prefix, + destination_address_prefix=subnet_data_private_prefix, destination_port_range="*", direction=network.SecurityRuleDirection.OUTBOUND, name="AllowPrivateDataEndpointsOutbound", - priority=NetworkingPriorities.INTERNAL_SRE_PRIVATE_DATA, + priority=NetworkingPriorities.INTERNAL_SRE_DATA_PRIVATE, protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix=subnet_user_services_software_repositories_prefix, source_port_range="*", @@ -732,11 +732,11 @@ def __init__( network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow outbound connections to private data endpoints.", - destination_address_prefix=subnet_private_data_prefix, + destination_address_prefix=subnet_data_private_prefix, destination_port_range="*", direction=network.SecurityRuleDirection.OUTBOUND, name="AllowPrivateDataEndpointsOutbound", - priority=NetworkingPriorities.INTERNAL_SRE_PRIVATE_DATA, + priority=NetworkingPriorities.INTERNAL_SRE_DATA_PRIVATE, protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix=subnet_workspaces_prefix, source_port_range="*", @@ -834,10 +834,10 @@ def __init__( # Define the virtual network and its subnets subnet_application_gateway_name = "ApplicationGatewaySubnet" - subnet_configuration_data_name = "ConfigurationDataSubnet" + subnet_data_configuration_name = "ConfigurationDataSubnet" subnet_guacamole_containers_name = "GuacamoleContainersSubnet" subnet_guacamole_containers_support_name = "GuacamoleContainersSupportSubnet" - subnet_private_data_name = "PrivateDataSubnet" + subnet_data_private_name = "PrivateDataSubnet" subnet_user_services_containers_name = "UserServicesContainersSubnet" subnet_user_services_containers_support_name = ( "UserServicesContainersSupportSubnet" @@ -864,10 +864,10 @@ def __init__( ), # Configuration data subnet network.SubnetArgs( - address_prefix=subnet_configuration_data_prefix, - name=subnet_configuration_data_name, + address_prefix=subnet_data_configuration_prefix, + name=subnet_data_configuration_name, network_security_group=network.NetworkSecurityGroupArgs( - id=nsg_configuration_data.id + id=nsg_data_configuration.id ), service_endpoints=[ network.ServiceEndpointPropertiesFormatArgs( @@ -902,10 +902,10 @@ def __init__( ), # Private data network.SubnetArgs( - address_prefix=subnet_private_data_prefix, - name=subnet_private_data_name, + address_prefix=subnet_data_private_prefix, + name=subnet_data_private_name, network_security_group=network.NetworkSecurityGroupArgs( - id=nsg_private_data.id + id=nsg_data_private.id ), service_endpoints=[ network.ServiceEndpointPropertiesFormatArgs( @@ -1123,8 +1123,8 @@ def __init__( resource_group_name=resource_group.name, virtual_network_name=sre_virtual_network.name, ) - self.subnet_configuration_data = network.get_subnet_output( - subnet_name=subnet_configuration_data_name, + self.subnet_data_configuration = network.get_subnet_output( + subnet_name=subnet_data_configuration_name, resource_group_name=resource_group.name, virtual_network_name=sre_virtual_network.name, ) @@ -1138,8 +1138,8 @@ def __init__( resource_group_name=resource_group.name, virtual_network_name=sre_virtual_network.name, ) - self.subnet_private_data = network.get_subnet_output( - subnet_name=subnet_private_data_name, + self.subnet_data_private = network.get_subnet_output( + subnet_name=subnet_data_private_name, resource_group_name=resource_group.name, virtual_network_name=sre_virtual_network.name, ) diff --git a/data_safe_haven/pulumi/components/sre_workspace.py b/data_safe_haven/pulumi/components/sre_workspace.py index 4b321bf768..b309f96bc3 100644 --- a/data_safe_haven/pulumi/components/sre_workspace.py +++ b/data_safe_haven/pulumi/components/sre_workspace.py @@ -37,8 +37,8 @@ def __init__( log_analytics_workspace_key: Input[str], sre_fqdn: Input[str], sre_name: Input[str], - storage_account_userdata_name: Input[str], - storage_account_securedata_name: Input[str], + storage_account_data_private_user_name: Input[str], + storage_account_data_private_sensitive_name: Input[str], subnet_workspaces: Input[network.GetSubnetResult], virtual_network_resource_group: Input[resources.ResourceGroup], virtual_network: Input[network.VirtualNetwork], @@ -60,8 +60,12 @@ def __init__( self.log_analytics_workspace_key = log_analytics_workspace_key self.sre_fqdn = sre_fqdn self.sre_name = sre_name - self.storage_account_userdata_name = storage_account_userdata_name - self.storage_account_securedata_name = storage_account_securedata_name + self.storage_account_data_private_user_name = ( + storage_account_data_private_user_name + ) + self.storage_account_data_private_sensitive_name = ( + storage_account_data_private_sensitive_name + ) self.virtual_network_name = Output.from_input(virtual_network).apply( get_name_from_vnet ) @@ -119,8 +123,8 @@ def __init__( ldap_user_search_base=props.ldap_user_search_base, linux_update_server_ip=props.linux_update_server_ip, sre_fqdn=props.sre_fqdn, - storage_account_userdata_name=props.storage_account_userdata_name, - storage_account_securedata_name=props.storage_account_securedata_name, + storage_account_data_private_user_name=props.storage_account_data_private_user_name, + storage_account_data_private_sensitive_name=props.storage_account_data_private_sensitive_name, ).apply(lambda kwargs: self.read_cloudinit(**kwargs)) # Deploy a variable number of VMs depending on the input parameters @@ -179,8 +183,8 @@ def read_cloudinit( ldap_user_search_base: str, linux_update_server_ip: str, sre_fqdn: str, - storage_account_userdata_name: str, - storage_account_securedata_name: str, + storage_account_data_private_user_name: str, + storage_account_data_private_sensitive_name: str, ) -> str: resources_path = ( pathlib.Path(__file__).parent.parent.parent / "resources" / "workspace" @@ -199,8 +203,8 @@ def read_cloudinit( "ldap_user_search_base": ldap_user_search_base, "linux_update_server_ip": linux_update_server_ip, "sre_fqdn": sre_fqdn, - "storage_account_userdata_name": storage_account_userdata_name, - "storage_account_securedata_name": storage_account_securedata_name, + "storage_account_data_private_user_name": storage_account_data_private_user_name, + "storage_account_data_private_sensitive_name": storage_account_data_private_sensitive_name, } cloudinit = chevron.render(f_cloudinit, mustache_values) return b64encode(cloudinit) diff --git a/data_safe_haven/pulumi/declarative_sre.py b/data_safe_haven/pulumi/declarative_sre.py index b08c8a8fbb..7ded84a99b 100644 --- a/data_safe_haven/pulumi/declarative_sre.py +++ b/data_safe_haven/pulumi/declarative_sre.py @@ -119,8 +119,8 @@ def run(self) -> None: networking_resource_group=networking.resource_group, pulumi_opts=self.pulumi_opts, sre_fqdn=networking.sre_fqdn, - subnet_configuration_data=networking.subnet_configuration_data, - subnet_private_data=networking.subnet_private_data, + subnet_data_configuration=networking.subnet_data_configuration, + subnet_data_private=networking.subnet_data_private, subscription_id=self.cfg.azure.subscription_id, subscription_name=self.cfg.subscription_name, tenant_id=self.cfg.azure.tenant_id, @@ -162,8 +162,8 @@ def run(self) -> None: location=self.cfg.azure.location, subnet_guacamole_containers=networking.subnet_guacamole_containers, subnet_guacamole_containers_support=networking.subnet_guacamole_containers_support, - storage_account_key=data.storage_account_configuration_data_key, - storage_account_name=data.storage_account_configuration_data_name, + storage_account_key=data.storage_account_data_configuration_key, + storage_account_name=data.storage_account_data_configuration_name, storage_account_resource_group_name=data.resource_group_name, virtual_network_resource_group_name=networking.resource_group.name, virtual_network=networking.virtual_network, @@ -198,8 +198,8 @@ def run(self) -> None: ), sre_fqdn=networking.sre_fqdn, sre_name=self.sre_name, - storage_account_userdata_name=data.storage_account_userdata_name, - storage_account_securedata_name=data.storage_account_securedata_name, + storage_account_data_private_user_name=data.storage_account_data_private_user_name, + storage_account_data_private_sensitive_name=data.storage_account_data_private_sensitive_name, subnet_workspaces=networking.subnet_workspaces, virtual_network_resource_group=networking.resource_group, virtual_network=networking.virtual_network, @@ -231,8 +231,8 @@ def run(self) -> None: software_packages=self.cfg.sres[self.sre_name].software_packages, sre_fqdn=networking.sre_fqdn, sre_private_dns_zone_id=networking.sre_private_dns_zone_id, - storage_account_key=data.storage_account_configuration_data_key, - storage_account_name=data.storage_account_configuration_data_name, + storage_account_key=data.storage_account_data_configuration_key, + storage_account_name=data.storage_account_data_configuration_name, storage_account_resource_group_name=data.resource_group_name, subnet_containers=networking.subnet_user_services_containers, subnet_containers_support=networking.subnet_user_services_containers_support, @@ -249,8 +249,8 @@ def run(self) -> None: self.stack_name, SREBackupProps( location=self.cfg.azure.location, - storage_account_securedata_id=data.storage_account_securedata_id, - storage_account_securedata_name=data.storage_account_securedata_name, + storage_account_data_private_sensitive_id=data.storage_account_data_private_sensitive_id, + storage_account_data_private_sensitive_name=data.storage_account_data_private_sensitive_name, ), ) diff --git a/data_safe_haven/resources/workspace/workspace.cloud_init.mustache.yaml b/data_safe_haven/resources/workspace/workspace.cloud_init.mustache.yaml index c1b93e2cda..c6a48dbbaa 100644 --- a/data_safe_haven/resources/workspace/workspace.cloud_init.mustache.yaml +++ b/data_safe_haven/resources/workspace/workspace.cloud_init.mustache.yaml @@ -57,11 +57,11 @@ write_files: mounts: # Secure data is in a blob container mounted as NFSv3 - - ["{{storage_account_securedata_name}}.blob.core.windows.net:/{{storage_account_securedata_name}}/ingress", /data, nfs, "ro,_netdev,sec=sys,vers=3,nolock,proto=tcp"] - - ["{{storage_account_securedata_name}}.blob.core.windows.net:/{{storage_account_securedata_name}}/egress", /output, nfs, "rw,_netdev,sec=sys,vers=3,nolock,proto=tcp"] + - ["{{storage_account_data_private_sensitive_name}}.blob.core.windows.net:/{{storage_account_data_private_sensitive_name}}/ingress", /data, nfs, "ro,_netdev,sec=sys,vers=3,nolock,proto=tcp"] + - ["{{storage_account_data_private_sensitive_name}}.blob.core.windows.net:/{{storage_account_data_private_sensitive_name}}/egress", /output, nfs, "rw,_netdev,sec=sys,vers=3,nolock,proto=tcp"] # User data is in a file share mounted as NFSv4 - - ["{{storage_account_userdata_name}}.file.core.windows.net:/{{storage_account_userdata_name}}/shared", /shared, nfs, "_netdev,sec=sys,nconnect=4"] - - ["{{storage_account_userdata_name}}.file.core.windows.net:/{{storage_account_userdata_name}}/home", /home, nfs, "_netdev,sec=sys,nconnect=4"] + - ["{{storage_account_data_private_user_name}}.file.core.windows.net:/{{storage_account_data_private_user_name}}/shared", /shared, nfs, "_netdev,sec=sys,nconnect=4"] + - ["{{storage_account_data_private_user_name}}.file.core.windows.net:/{{storage_account_data_private_user_name}}/home", /home, nfs, "_netdev,sec=sys,nconnect=4"] # Add additional apt repositories apt: From 95dd6fe18d904373fde3888c03b21888092ba15a Mon Sep 17 00:00:00 2001 From: James Robinson Date: Thu, 10 Aug 2023 16:09:14 +0100 Subject: [PATCH 18/38] :recycle: Separate networking access to private data and configuration data --- .../pulumi/components/sre_networking.py | 134 +++++++++--------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 23585d90ee..4fece55a23 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -101,12 +101,12 @@ def __init__( subnet_guacamole_containers_support_prefix = ( props.subnet_guacamole_containers_support_iprange.apply(lambda r: str(r)) ) - subnet_data_private_prefix = props.subnet_data_private_iprange.apply( - lambda r: str(r) - ) subnet_data_configuration_prefix = ( props.subnet_data_configuration_iprange.apply(lambda r: str(r)) ) + subnet_data_private_prefix = props.subnet_data_private_iprange.apply( + lambda r: str(r) + ) subnet_user_services_containers_prefix = ( props.subnet_user_services_containers_iprange.apply(lambda r: str(r)) ) @@ -301,12 +301,12 @@ def __init__( ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, - description="Allow outbound connections to private data endpoints.", - destination_address_prefix=subnet_data_private_prefix, + description="Allow outbound connections to configuration data endpoints.", + destination_address_prefix=subnet_data_configuration_prefix, destination_port_range="*", direction=network.SecurityRuleDirection.OUTBOUND, - name="AllowPrivateDataEndpointsOutbound", - priority=NetworkingPriorities.INTERNAL_SRE_DATA_PRIVATE, + name="AllowConfigurationDataEndpointsOutbound", + priority=NetworkingPriorities.INTERNAL_SRE_DATA_CONFIGURATION, protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix=subnet_guacamole_containers_prefix, source_port_range="*", @@ -347,7 +347,7 @@ def __init__( network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow inbound connections from Guacamole remote desktop gateway.", - destination_address_prefix=subnet_guacamole_containers_support_prefix, + destination_address_prefix=subnet_data_configuration_prefix, destination_port_ranges=["5432"], direction=network.SecurityRuleDirection.INBOUND, name="AllowGuacamoleContainersSupportInbound", @@ -356,6 +356,42 @@ def __init__( source_address_prefix=subnet_guacamole_containers_prefix, source_port_range="*", ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound connections from Guacamole remote desktop gateway.", + destination_address_prefix=subnet_data_configuration_prefix, + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="AllowGuacamoleContainersInbound", + priority=NetworkingPriorities.INTERNAL_SRE_GUACAMOLE_CONTAINERS, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix=subnet_guacamole_containers_prefix, + source_port_range="*", + ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound connections from user services containers.", + destination_address_prefix=subnet_data_configuration_prefix, + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="AllowUserServicesContainersInbound", + priority=NetworkingPriorities.INTERNAL_SRE_USER_SERVICES_CONTAINERS, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix=subnet_user_services_containers_prefix, + source_port_range="*", + ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound connections from user services software repositories.", + destination_address_prefix=subnet_data_configuration_prefix, + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="AllowUserServicesSoftwareRepositoriesInbound", + priority=NetworkingPriorities.INTERNAL_SRE_USER_SERVICES_SOFTWARE_REPOSITORIES, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix=subnet_user_services_software_repositories_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other inbound traffic.", @@ -402,42 +438,6 @@ def __init__( source_address_prefix="*", source_port_range="*", ), - network.SecurityRuleArgs( - access=network.SecurityRuleAccess.ALLOW, - description="Allow inbound connections from Guacamole remote desktop gateway.", - destination_address_prefix=subnet_data_private_prefix, - destination_port_range="*", - direction=network.SecurityRuleDirection.INBOUND, - name="AllowGuacamoleContainersInbound", - priority=NetworkingPriorities.INTERNAL_SRE_GUACAMOLE_CONTAINERS, - protocol=network.SecurityRuleProtocol.ASTERISK, - source_address_prefix=subnet_guacamole_containers_prefix, - source_port_range="*", - ), - network.SecurityRuleArgs( - access=network.SecurityRuleAccess.ALLOW, - description="Allow inbound connections from user services containers.", - destination_address_prefix=subnet_data_private_prefix, - destination_port_range="*", - direction=network.SecurityRuleDirection.INBOUND, - name="AllowUserServicesContainersInbound", - priority=NetworkingPriorities.INTERNAL_SRE_USER_SERVICES_CONTAINERS, - protocol=network.SecurityRuleProtocol.ASTERISK, - source_address_prefix=subnet_user_services_containers_prefix, - source_port_range="*", - ), - network.SecurityRuleArgs( - access=network.SecurityRuleAccess.ALLOW, - description="Allow inbound connections from user services software repositories.", - destination_address_prefix=subnet_data_private_prefix, - destination_port_range="*", - direction=network.SecurityRuleDirection.INBOUND, - name="AllowUserServicesSoftwareRepositoriesInbound", - priority=NetworkingPriorities.INTERNAL_SRE_USER_SERVICES_SOFTWARE_REPOSITORIES, - protocol=network.SecurityRuleProtocol.ASTERISK, - source_address_prefix=subnet_user_services_software_repositories_prefix, - source_port_range="*", - ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow inbound connections from SRE workspaces.", @@ -499,12 +499,12 @@ def __init__( # Outbound network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, - description="Allow outbound connections to private data endpoints.", - destination_address_prefix=subnet_data_private_prefix, + description="Allow outbound connections to configuration data endpoints.", + destination_address_prefix=subnet_data_configuration_prefix, destination_port_range="*", direction=network.SecurityRuleDirection.OUTBOUND, - name="AllowPrivateDataEndpointsOutbound", - priority=NetworkingPriorities.INTERNAL_SRE_DATA_PRIVATE, + name="AllowConfigurationDataEndpointsOutbound", + priority=NetworkingPriorities.INTERNAL_SRE_DATA_CONFIGURATION, protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix=subnet_user_services_containers_prefix, source_port_range="*", @@ -615,12 +615,12 @@ def __init__( # Outbound network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, - description="Allow outbound connections to private data endpoints.", - destination_address_prefix=subnet_data_private_prefix, + description="Allow outbound connections to configuration data endpoints.", + destination_address_prefix=subnet_data_configuration_prefix, destination_port_range="*", direction=network.SecurityRuleDirection.OUTBOUND, - name="AllowPrivateDataEndpointsOutbound", - priority=NetworkingPriorities.INTERNAL_SRE_DATA_PRIVATE, + name="AllowConfigurationDataEndpointsOutbound", + priority=NetworkingPriorities.INTERNAL_SRE_DATA_CONFIGURATION, protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix=subnet_user_services_software_repositories_prefix, source_port_range="*", @@ -835,9 +835,9 @@ def __init__( # Define the virtual network and its subnets subnet_application_gateway_name = "ApplicationGatewaySubnet" subnet_data_configuration_name = "ConfigurationDataSubnet" + subnet_data_private_name = "PrivateDataSubnet" subnet_guacamole_containers_name = "GuacamoleContainersSubnet" subnet_guacamole_containers_support_name = "GuacamoleContainersSupportSubnet" - subnet_data_private_name = "PrivateDataSubnet" subnet_user_services_containers_name = "UserServicesContainersSubnet" subnet_user_services_containers_support_name = ( "UserServicesContainersSupportSubnet" @@ -876,6 +876,20 @@ def __init__( ) ], ), + # Private data + network.SubnetArgs( + address_prefix=subnet_data_private_prefix, + name=subnet_data_private_name, + network_security_group=network.NetworkSecurityGroupArgs( + id=nsg_data_private.id + ), + service_endpoints=[ + network.ServiceEndpointPropertiesFormatArgs( + locations=[props.location], + service="Microsoft.Storage", + ) + ], + ), # Guacamole containers network.SubnetArgs( address_prefix=subnet_guacamole_containers_prefix, @@ -900,20 +914,6 @@ def __init__( ), private_endpoint_network_policies="Disabled", ), - # Private data - network.SubnetArgs( - address_prefix=subnet_data_private_prefix, - name=subnet_data_private_name, - network_security_group=network.NetworkSecurityGroupArgs( - id=nsg_data_private.id - ), - service_endpoints=[ - network.ServiceEndpointPropertiesFormatArgs( - locations=[props.location], - service="Microsoft.Storage", - ) - ], - ), # User services containers network.SubnetArgs( address_prefix=subnet_user_services_containers_prefix, From 56fc78d108e8d1947bba5ed3ec714f5ef15c57da Mon Sep 17 00:00:00 2001 From: James Robinson Date: Thu, 10 Aug 2023 17:56:35 +0100 Subject: [PATCH 19/38] :wrench: Allow AzureLoadBalancer and drop DenyAll rule for ApplicationGateway --- .../pulumi/components/sre_networking.py | 50 ++++++++----------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 4fece55a23..36148fd718 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -134,18 +134,6 @@ def __init__( resource_group_name=resource_group.name, security_rules=[ # Inbound - network.SecurityRuleArgs( - access=network.SecurityRuleAccess.ALLOW, - description="Allow inbound gateway management service traffic.", - destination_address_prefix="*", - destination_port_range="*", - direction=network.SecurityRuleDirection.INBOUND, - name="AllowGatewayManagerServiceInbound", - priority=NetworkingPriorities.AZURE_GATEWAY_MANAGER, - protocol=network.SecurityRuleProtocol.ASTERISK, - source_address_prefix="GatewayManager", - source_port_range="*", - ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow inbound connections from users over the internet.", @@ -160,14 +148,26 @@ def __init__( ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, - description="Allow inbound gateway management traffic over the internet.", - destination_address_prefix="*", # this must be '*' or Azure validation will fail + description="Allow inbound gateway management service traffic.", + destination_address_prefix="*", destination_port_range="65200-65535", direction=network.SecurityRuleDirection.INBOUND, - name="AllowGatewayManagerInternetInbound", - priority=NetworkingPriorities.EXTERNAL_INTERNET, + name="AllowGatewayManagerServiceInbound", + priority=NetworkingPriorities.AZURE_GATEWAY_MANAGER, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix="GatewayManager", + source_port_range="*", + ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound Azure load balancer traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="AllowAzureLoadBalancerServiceInbound", + priority=NetworkingPriorities.AZURE_LOAD_BALANCER, protocol=network.SecurityRuleProtocol.ASTERISK, - source_address_prefix="Internet", + source_address_prefix="AzureLoadBalancer", source_port_range="*", ), network.SecurityRuleArgs( @@ -199,26 +199,16 @@ def __init__( access=network.SecurityRuleAccess.ALLOW, description="Allow outbound gateway management traffic over the internet.", destination_address_prefix="Internet", - destination_port_range="65200-65535", + destination_port_range="*", direction=network.SecurityRuleDirection.OUTBOUND, name="AllowGatewayManagerInternetOutbound", priority=NetworkingPriorities.EXTERNAL_INTERNET, protocol=network.SecurityRuleProtocol.ASTERISK, - source_address_prefix="*", # this must be '*' or Azure validation will fail - source_port_range="*", - ), - network.SecurityRuleArgs( - access=network.SecurityRuleAccess.DENY, - description="Deny all other outbound traffic.", - destination_address_prefix="*", - destination_port_range="*", - direction=network.SecurityRuleDirection.OUTBOUND, - name="DenyAllOtherOutbound", - priority=NetworkingPriorities.ALL_OTHER, - protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix="*", source_port_range="*", ), + # Attempting to add our standard DenyAllOtherOutbound rule will cause + # this NSG to fail validation. See: https://learn.microsoft.com/en-us/azure/application-gateway/configuration-infrastructure#network-security-groups ], opts=child_opts, ) From aeff607760dd13352eb8850ebf575ae95ceb9f3e Mon Sep 17 00:00:00 2001 From: James Robinson Date: Thu, 10 Aug 2023 21:06:50 +0100 Subject: [PATCH 20/38] :bug: Move rules to correct NSG --- .../pulumi/components/sre_networking.py | 164 +++++++++--------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 36148fd718..6023352336 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -218,6 +218,88 @@ def __init__( resource_group_name=resource_group.name, security_rules=[ # Inbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound connections from Guacamole remote desktop gateway.", + destination_address_prefix=subnet_data_configuration_prefix, + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="AllowGuacamoleContainersInbound", + priority=NetworkingPriorities.INTERNAL_SRE_GUACAMOLE_CONTAINERS, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix=subnet_guacamole_containers_prefix, + source_port_range="*", + ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound connections from user services containers.", + destination_address_prefix=subnet_data_configuration_prefix, + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="AllowUserServicesContainersInbound", + priority=NetworkingPriorities.INTERNAL_SRE_USER_SERVICES_CONTAINERS, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix=subnet_user_services_containers_prefix, + source_port_range="*", + ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound connections from user services software repositories.", + destination_address_prefix=subnet_data_configuration_prefix, + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="AllowUserServicesSoftwareRepositoriesInbound", + priority=NetworkingPriorities.INTERNAL_SRE_USER_SERVICES_SOFTWARE_REPOSITORIES, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix=subnet_user_services_software_repositories_prefix, + source_port_range="*", + ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other inbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="DenyAllOtherInbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.DENY, + description="Deny all other outbound traffic.", + destination_address_prefix="*", + destination_port_range="*", + direction=network.SecurityRuleDirection.OUTBOUND, + name="DenyAllOtherOutbound", + priority=NetworkingPriorities.ALL_OTHER, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix="*", + source_port_range="*", + ), + ], + opts=child_opts, + ) + nsg_data_private = network.NetworkSecurityGroup( + f"{self._name}_nsg_data_private", + network_security_group_name=f"{stack_name}-nsg-private-data", + resource_group_name=resource_group.name, + security_rules=[ + # Inbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound connections from SRE workspaces.", + destination_address_prefix=subnet_data_private_prefix, + destination_port_range="*", + direction=network.SecurityRuleDirection.INBOUND, + name="AllowWorkspacesInbound", + priority=NetworkingPriorities.INTERNAL_SRE_WORKSPACES, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix=subnet_workspaces_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other inbound traffic.", @@ -334,18 +416,6 @@ def __init__( resource_group_name=resource_group.name, security_rules=[ # Inbound - network.SecurityRuleArgs( - access=network.SecurityRuleAccess.ALLOW, - description="Allow inbound connections from Guacamole remote desktop gateway.", - destination_address_prefix=subnet_data_configuration_prefix, - destination_port_ranges=["5432"], - direction=network.SecurityRuleDirection.INBOUND, - name="AllowGuacamoleContainersSupportInbound", - priority=NetworkingPriorities.INTERNAL_SRE_APPLICATION_GATEWAY, - protocol=network.SecurityRuleProtocol.TCP, - source_address_prefix=subnet_guacamole_containers_prefix, - source_port_range="*", - ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow inbound connections from Guacamole remote desktop gateway.", @@ -358,30 +428,6 @@ def __init__( source_address_prefix=subnet_guacamole_containers_prefix, source_port_range="*", ), - network.SecurityRuleArgs( - access=network.SecurityRuleAccess.ALLOW, - description="Allow inbound connections from user services containers.", - destination_address_prefix=subnet_data_configuration_prefix, - destination_port_range="*", - direction=network.SecurityRuleDirection.INBOUND, - name="AllowUserServicesContainersInbound", - priority=NetworkingPriorities.INTERNAL_SRE_USER_SERVICES_CONTAINERS, - protocol=network.SecurityRuleProtocol.ASTERISK, - source_address_prefix=subnet_user_services_containers_prefix, - source_port_range="*", - ), - network.SecurityRuleArgs( - access=network.SecurityRuleAccess.ALLOW, - description="Allow inbound connections from user services software repositories.", - destination_address_prefix=subnet_data_configuration_prefix, - destination_port_range="*", - direction=network.SecurityRuleDirection.INBOUND, - name="AllowUserServicesSoftwareRepositoriesInbound", - priority=NetworkingPriorities.INTERNAL_SRE_USER_SERVICES_SOFTWARE_REPOSITORIES, - protocol=network.SecurityRuleProtocol.ASTERISK, - source_address_prefix=subnet_user_services_software_repositories_prefix, - source_port_range="*", - ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other inbound traffic.", @@ -410,52 +456,6 @@ def __init__( ], opts=child_opts, ) - nsg_data_private = network.NetworkSecurityGroup( - f"{self._name}_nsg_data_private", - network_security_group_name=f"{stack_name}-nsg-private-data", - resource_group_name=resource_group.name, - security_rules=[ - # Inbound - network.SecurityRuleArgs( - access=network.SecurityRuleAccess.DENY, - description="Deny all other inbound traffic.", - destination_address_prefix="*", - destination_port_range="*", - direction=network.SecurityRuleDirection.INBOUND, - name="DenyAllOtherInbound", - priority=NetworkingPriorities.ALL_OTHER, - protocol=network.SecurityRuleProtocol.ASTERISK, - source_address_prefix="*", - source_port_range="*", - ), - network.SecurityRuleArgs( - access=network.SecurityRuleAccess.ALLOW, - description="Allow inbound connections from SRE workspaces.", - destination_address_prefix=subnet_data_private_prefix, - destination_port_range="*", - direction=network.SecurityRuleDirection.INBOUND, - name="AllowWorkspacesInbound", - priority=NetworkingPriorities.INTERNAL_SRE_WORKSPACES, - protocol=network.SecurityRuleProtocol.ASTERISK, - source_address_prefix=subnet_workspaces_prefix, - source_port_range="*", - ), - # Outbound - network.SecurityRuleArgs( - access=network.SecurityRuleAccess.DENY, - description="Deny all other outbound traffic.", - destination_address_prefix="*", - destination_port_range="*", - direction=network.SecurityRuleDirection.OUTBOUND, - name="DenyAllOtherOutbound", - priority=NetworkingPriorities.ALL_OTHER, - protocol=network.SecurityRuleProtocol.ASTERISK, - source_address_prefix="*", - source_port_range="*", - ), - ], - opts=child_opts, - ) nsg_user_services_containers = network.NetworkSecurityGroup( f"{self._name}_nsg_user_services_containers", network_security_group_name=f"{stack_name}-nsg-user-services-containers", From cd5f39782a24ee9a699e4c6e8baf431a4dcd0367 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Thu, 10 Aug 2023 21:21:30 +0100 Subject: [PATCH 21/38] :wrench: Use separate external IP address rules for config data and sensitive data --- data_safe_haven/pulumi/components/sre_data.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/data_safe_haven/pulumi/components/sre_data.py b/data_safe_haven/pulumi/components/sre_data.py index 89cb0f40ae..0f12c31ee8 100644 --- a/data_safe_haven/pulumi/components/sre_data.py +++ b/data_safe_haven/pulumi/components/sre_data.py @@ -55,7 +55,8 @@ def __init__( ) -> None: self.admin_email_address = admin_email_address self.admin_group_id = admin_group_id - self.approved_ip_addresses = Output.all( + self.data_configuration_ip_addresses = admin_ip_addresses + self.data_private_sensitive_ip_addresses = Output.all( admin_ip_addresses, data_provider_ip_addresses ).apply( lambda address_lists: { @@ -308,6 +309,7 @@ def __init__( f"{''.join(truncate_tokens(stack_name.split('-'), 14))}configdata" )[:24], kind=storage.Kind.STORAGE_V2, + location=props.location, resource_group_name=resource_group.name, sku=storage.SkuArgs(name=storage.SkuName.STANDARD_GRS), opts=child_opts, @@ -391,7 +393,9 @@ def __init__( network_rule_set=storage.NetworkRuleSetArgs( bypass=storage.Bypass.AZURE_SERVICES, default_action=storage.DefaultAction.DENY, - ip_rules=Output.from_input(props.approved_ip_addresses).apply( + ip_rules=Output.from_input( + props.data_private_sensitive_ip_addresses + ).apply( lambda ip_ranges: [ storage.IPRuleArgs( action=storage.Action.ALLOW, @@ -651,7 +655,9 @@ def __init__( storage_account_data_private_sensitive.name ) self.storage_account_data_configuration_key = Output.secret( - storage_account_data_configuration_keys.keys[0].value + storage_account_data_configuration_keys.apply( + lambda keys: keys.keys[0].value + ) ) self.storage_account_data_configuration_name = ( storage_account_data_configuration.name From 2c0da58e9257a65e60b472d5b99af6e47de5053a Mon Sep 17 00:00:00 2001 From: James Robinson Date: Thu, 10 Aug 2023 21:48:02 +0100 Subject: [PATCH 22/38] :wrench: Restrict access to configuration data storage account --- data_safe_haven/pulumi/components/sre_data.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/data_safe_haven/pulumi/components/sre_data.py b/data_safe_haven/pulumi/components/sre_data.py index 0f12c31ee8..900f2d3a10 100644 --- a/data_safe_haven/pulumi/components/sre_data.py +++ b/data_safe_haven/pulumi/components/sre_data.py @@ -310,6 +310,25 @@ def __init__( )[:24], kind=storage.Kind.STORAGE_V2, location=props.location, + network_rule_set=storage.NetworkRuleSetArgs( + bypass=storage.Bypass.AZURE_SERVICES, + default_action=storage.DefaultAction.DENY, + ip_rules=Output.from_input(props.data_configuration_ip_addresses).apply( + lambda ip_ranges: [ + storage.IPRuleArgs( + action=storage.Action.ALLOW, + i_p_address_or_range=str(ip_address), + ) + for ip_range in sorted(ip_ranges) + for ip_address in AzureIPv4Range.from_cidr(ip_range).all_ips() + ] + ), + virtual_network_rules=[ + storage.VirtualNetworkRuleArgs( + virtual_network_resource_id=props.subnet_data_configuration_id, + ) + ], + ), resource_group_name=resource_group.name, sku=storage.SkuArgs(name=storage.SkuName.STANDARD_GRS), opts=child_opts, From d8bf2ce392c09f90d3e6fb2668fd1f1aed64812a Mon Sep 17 00:00:00 2001 From: James Robinson Date: Thu, 10 Aug 2023 22:07:06 +0100 Subject: [PATCH 23/38] :wrench: Order NSG rules by priority --- .../pulumi/components/sre_networking.py | 110 +++++++++--------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 6023352336..93e535ff2b 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -134,18 +134,6 @@ def __init__( resource_group_name=resource_group.name, security_rules=[ # Inbound - network.SecurityRuleArgs( - access=network.SecurityRuleAccess.ALLOW, - description="Allow inbound connections from users over the internet.", - destination_address_prefix=subnet_application_gateway_prefix, - destination_port_ranges=["80", "443"], - direction=network.SecurityRuleDirection.INBOUND, - name="AllowUsersInternetInbound", - priority=NetworkingPriorities.AUTHORISED_EXTERNAL_USER_IPS, - protocol=network.SecurityRuleProtocol.TCP, - source_address_prefix=props.public_ip_range_users, - source_port_range="*", - ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow inbound gateway management service traffic.", @@ -170,6 +158,18 @@ def __init__( source_address_prefix="AzureLoadBalancer", source_port_range="*", ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow inbound connections from users over the internet.", + destination_address_prefix=subnet_application_gateway_prefix, + destination_port_ranges=["80", "443"], + direction=network.SecurityRuleDirection.INBOUND, + name="AllowUsersInternetInbound", + priority=NetworkingPriorities.AUTHORISED_EXTERNAL_USER_IPS, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix=props.public_ip_range_users, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other inbound traffic.", @@ -359,18 +359,6 @@ def __init__( source_port_range="*", ), # Outbound - network.SecurityRuleArgs( - access=network.SecurityRuleAccess.ALLOW, - description="Allow outbound connections to Guacamole support services.", - destination_address_prefix=subnet_guacamole_containers_support_prefix, - destination_port_ranges=["5432"], - direction=network.SecurityRuleDirection.OUTBOUND, - name="AllowGuacamoleContainersSupportOutbound", - priority=NetworkingPriorities.INTERNAL_SRE_GUACAMOLE_CONTAINERS_SUPPORT, - protocol=network.SecurityRuleProtocol.TCP, - source_address_prefix=subnet_guacamole_containers_prefix, - source_port_range="*", - ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow outbound connections to configuration data endpoints.", @@ -383,6 +371,18 @@ def __init__( source_address_prefix=subnet_guacamole_containers_prefix, source_port_range="*", ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow outbound connections to Guacamole support services.", + destination_address_prefix=subnet_guacamole_containers_support_prefix, + destination_port_ranges=["5432"], + direction=network.SecurityRuleDirection.OUTBOUND, + name="AllowGuacamoleContainersSupportOutbound", + priority=NetworkingPriorities.INTERNAL_SRE_GUACAMOLE_CONTAINERS_SUPPORT, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix=subnet_guacamole_containers_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow outbound connections to SRE workspaces.", @@ -709,64 +709,64 @@ def __init__( # Outbound network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, - description="Allow outbound connections to SHM monitoring tools.", - destination_address_prefix=str(props.shm_subnet_monitoring_prefix), - destination_port_ranges=["443"], + description=( + "Allow LDAP client requests over TCP. " + "See https://devopstales.github.io/linux/pfsense-ad-join/ for details." + ), + destination_address_prefix=props.shm_subnet_identity_servers_prefix, + destination_port_ranges=["389", "636"], direction=network.SecurityRuleDirection.OUTBOUND, - name="AllowMonitoringToolsOutbound", - priority=NetworkingPriorities.INTERNAL_SHM_MONITORING_TOOLS, + name="AllowLDAPClientTCPOutbound", + priority=NetworkingPriorities.INTERNAL_SHM_LDAP_TCP, protocol=network.SecurityRuleProtocol.TCP, source_address_prefix=subnet_workspaces_prefix, source_port_range="*", ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, - description="Allow outbound connections to private data endpoints.", - destination_address_prefix=subnet_data_private_prefix, - destination_port_range="*", + description="Allow LDAP client requests over UDP.", + destination_address_prefix=props.shm_subnet_identity_servers_prefix, + destination_port_ranges=["389", "636"], direction=network.SecurityRuleDirection.OUTBOUND, - name="AllowPrivateDataEndpointsOutbound", - priority=NetworkingPriorities.INTERNAL_SRE_DATA_PRIVATE, - protocol=network.SecurityRuleProtocol.ASTERISK, + name="AllowLDAPClientUDPOutbound", + priority=NetworkingPriorities.INTERNAL_SHM_LDAP_UDP, + protocol=network.SecurityRuleProtocol.UDP, source_address_prefix=subnet_workspaces_prefix, source_port_range="*", ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, - description="Allow outbound connections to Linux update servers.", - destination_address_prefix=props.shm_subnet_update_servers_prefix, - destination_port_ranges=["8000"], + description="Allow outbound connections to SHM monitoring tools.", + destination_address_prefix=str(props.shm_subnet_monitoring_prefix), + destination_port_ranges=["443"], direction=network.SecurityRuleDirection.OUTBOUND, - name="AllowLinuxUpdatesOutbound", - priority=NetworkingPriorities.INTERNAL_SHM_UPDATE_SERVERS, + name="AllowMonitoringToolsOutbound", + priority=NetworkingPriorities.INTERNAL_SHM_MONITORING_TOOLS, protocol=network.SecurityRuleProtocol.TCP, source_address_prefix=subnet_workspaces_prefix, source_port_range="*", ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, - description="Allow LDAP client requests over UDP.", - destination_address_prefix=props.shm_subnet_identity_servers_prefix, - destination_port_ranges=["389", "636"], + description="Allow outbound connections to Linux update servers.", + destination_address_prefix=props.shm_subnet_update_servers_prefix, + destination_port_ranges=["8000"], direction=network.SecurityRuleDirection.OUTBOUND, - name="AllowLDAPClientUDPOutbound", - priority=NetworkingPriorities.INTERNAL_SHM_LDAP_UDP, - protocol=network.SecurityRuleProtocol.UDP, + name="AllowLinuxUpdatesOutbound", + priority=NetworkingPriorities.INTERNAL_SHM_UPDATE_SERVERS, + protocol=network.SecurityRuleProtocol.TCP, source_address_prefix=subnet_workspaces_prefix, source_port_range="*", ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, - description=( - "Allow LDAP client requests over TCP. " - "See https://devopstales.github.io/linux/pfsense-ad-join/ for details." - ), - destination_address_prefix=props.shm_subnet_identity_servers_prefix, - destination_port_ranges=["389", "636"], + description="Allow outbound connections to private data endpoints.", + destination_address_prefix=subnet_data_private_prefix, + destination_port_range="*", direction=network.SecurityRuleDirection.OUTBOUND, - name="AllowLDAPClientTCPOutbound", - priority=NetworkingPriorities.INTERNAL_SHM_LDAP_TCP, - protocol=network.SecurityRuleProtocol.TCP, + name="AllowPrivateDataEndpointsOutbound", + priority=NetworkingPriorities.INTERNAL_SRE_DATA_PRIVATE, + protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix=subnet_workspaces_prefix, source_port_range="*", ), From 9cb93d29ecf208cd7d6c649ee02d5a04d03f0c57 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Thu, 10 Aug 2023 22:27:49 +0100 Subject: [PATCH 24/38] :bug: Add outbound connections to configuration data endpoints to software repositories NSG --- data_safe_haven/pulumi/components/sre_networking.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 93e535ff2b..06e1475a01 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -636,6 +636,18 @@ def __init__( resource_group_name=resource_group.name, security_rules=[ # Inbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow outbound connections to configuration data endpoints.", + destination_address_prefix=subnet_data_configuration_prefix, + destination_port_range="*", + direction=network.SecurityRuleDirection.OUTBOUND, + name="AllowConfigurationDataEndpointsOutbound", + priority=NetworkingPriorities.INTERNAL_SRE_DATA_CONFIGURATION, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix=subnet_guacamole_containers_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow inbound connections from SRE workspaces.", From 426c5ef88abe0a1f1f19138fdfe3a05f6c5e6075 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Thu, 10 Aug 2023 22:36:02 +0100 Subject: [PATCH 25/38] :wrench: Allow Guacamole to connect to SHM DC over LDAP --- data_safe_haven/pulumi/components/sre_networking.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 06e1475a01..91efc06919 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -359,6 +359,18 @@ def __init__( source_port_range="*", ), # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow LDAP client requests over TCP.", + destination_address_prefix=props.shm_subnet_identity_servers_prefix, + destination_port_ranges=["389", "636"], + direction=network.SecurityRuleDirection.OUTBOUND, + name="AllowLDAPClientTCPOutbound", + priority=NetworkingPriorities.INTERNAL_SHM_LDAP_TCP, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix=subnet_guacamole_containers_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow outbound connections to configuration data endpoints.", From 60fc7e2bd8d3bb9ed16cf744a65bb03cc0ddbe78 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Thu, 10 Aug 2023 22:54:06 +0100 Subject: [PATCH 26/38] :wrench: Allow Guacamole to connect to OAuth provider over the internet --- data_safe_haven/pulumi/components/sre_networking.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 91efc06919..49fbbf151e 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -407,6 +407,18 @@ def __init__( source_address_prefix=subnet_guacamole_containers_prefix, source_port_range="*", ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow outbound OAuth connections over the internet.", + destination_address_prefix="Internet", + destination_port_ranges=["80", "443"], + direction=network.SecurityRuleDirection.OUTBOUND, + name="AllowOAuthInternetOutbound", + priority=NetworkingPriorities.EXTERNAL_INTERNET, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix=subnet_guacamole_containers_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other outbound traffic.", From 0717d250220602a6c2f18c1e4161b97ffa2a305d Mon Sep 17 00:00:00 2001 From: James Robinson Date: Thu, 10 Aug 2023 22:55:07 +0100 Subject: [PATCH 27/38] :bug: Fix software repositories configuration NSG rule --- .../pulumi/components/sre_networking.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 49fbbf151e..8fed62f1b1 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -660,18 +660,6 @@ def __init__( resource_group_name=resource_group.name, security_rules=[ # Inbound - network.SecurityRuleArgs( - access=network.SecurityRuleAccess.ALLOW, - description="Allow outbound connections to configuration data endpoints.", - destination_address_prefix=subnet_data_configuration_prefix, - destination_port_range="*", - direction=network.SecurityRuleDirection.OUTBOUND, - name="AllowConfigurationDataEndpointsOutbound", - priority=NetworkingPriorities.INTERNAL_SRE_DATA_CONFIGURATION, - protocol=network.SecurityRuleProtocol.ASTERISK, - source_address_prefix=subnet_guacamole_containers_prefix, - source_port_range="*", - ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow inbound connections from SRE workspaces.", @@ -697,6 +685,18 @@ def __init__( source_port_range="*", ), # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow outbound connections to configuration data endpoints.", + destination_address_prefix=subnet_data_configuration_prefix, + destination_port_range="*", + direction=network.SecurityRuleDirection.OUTBOUND, + name="AllowConfigurationDataEndpointsOutbound", + priority=NetworkingPriorities.INTERNAL_SRE_DATA_CONFIGURATION, + protocol=network.SecurityRuleProtocol.ASTERISK, + source_address_prefix=subnet_user_services_software_repositories_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other outbound traffic.", From 5c601e1a0983b046830ffc4f67e52a0d7337dc3a Mon Sep 17 00:00:00 2001 From: James Robinson Date: Fri, 11 Aug 2023 10:04:03 +0100 Subject: [PATCH 28/38] :construction: Allow full internet access from Workspaces to allow access to Ubuntu keyserver during deployment. Needs to be locked down by routing. --- data_safe_haven/pulumi/components/sre_networking.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 8fed62f1b1..7779336af7 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -842,6 +842,18 @@ def __init__( source_address_prefix=subnet_workspaces_prefix, source_port_range="*", ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow outbound configuration traffic over the internet.", + destination_address_prefix="Internet", + destination_port_range="*", + direction=network.SecurityRuleDirection.OUTBOUND, + name="AllowConfigurationInternetOutbound", + priority=NetworkingPriorities.EXTERNAL_INTERNET, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix=subnet_workspaces_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other outbound traffic.", From 1103ee68edcb7cdc2cdcec18290f597b76f38aca Mon Sep 17 00:00:00 2001 From: James Robinson Date: Fri, 11 Aug 2023 10:16:50 +0100 Subject: [PATCH 29/38] :bug: Fix incorrect NSG rules --- data_safe_haven/pulumi/components/sre_networking.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 7779336af7..8c5f944540 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -443,7 +443,7 @@ def __init__( network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow inbound connections from Guacamole remote desktop gateway.", - destination_address_prefix=subnet_data_configuration_prefix, + destination_address_prefix=subnet_guacamole_containers_support_prefix, destination_port_range="*", direction=network.SecurityRuleDirection.INBOUND, name="AllowGuacamoleContainersInbound", @@ -559,13 +559,13 @@ def __init__( network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow inbound connections from user services containers.", - destination_address_prefix=subnet_guacamole_containers_support_prefix, + destination_address_prefix=subnet_user_services_containers_support_prefix, destination_port_ranges=["5432"], direction=network.SecurityRuleDirection.INBOUND, name="AllowUserServicesContainersInbound", priority=NetworkingPriorities.INTERNAL_SRE_USER_SERVICES_CONTAINERS, protocol=network.SecurityRuleProtocol.TCP, - source_address_prefix=subnet_guacamole_containers_prefix, + source_address_prefix=subnet_user_services_containers_prefix, source_port_range="*", ), network.SecurityRuleArgs( @@ -636,7 +636,7 @@ def __init__( name="AllowConfigurationDataEndpointsOutbound", priority=NetworkingPriorities.INTERNAL_SRE_DATA_CONFIGURATION, protocol=network.SecurityRuleProtocol.ASTERISK, - source_address_prefix=subnet_user_services_software_repositories_prefix, + source_address_prefix=subnet_user_services_databases_prefix, source_port_range="*", ), network.SecurityRuleArgs( From 783b577339bd1a81aa6b621fc7d1dfe662569e7d Mon Sep 17 00:00:00 2001 From: James Robinson Date: Fri, 11 Aug 2023 10:29:03 +0100 Subject: [PATCH 30/38] :wrench: Add rule to allow software repositories to access remote repositories --- data_safe_haven/pulumi/components/sre_networking.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 8c5f944540..9038aa0a55 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -697,6 +697,18 @@ def __init__( source_address_prefix=subnet_user_services_software_repositories_prefix, source_port_range="*", ), + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow outbound connections to external repositories over the internet.", + destination_address_prefix="Internet", + destination_port_ranges=["80", "443"], + direction=network.SecurityRuleDirection.OUTBOUND, + name="AllowPackagesInternetOutbound", + priority=NetworkingPriorities.EXTERNAL_INTERNET, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix=subnet_user_services_software_repositories_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.DENY, description="Deny all other outbound traffic.", From f581793e0265367b774b22b3cbda61fa5194b7cc Mon Sep 17 00:00:00 2001 From: James Robinson Date: Fri, 11 Aug 2023 14:00:14 +0100 Subject: [PATCH 31/38] :bug: Fix package name typo --- .../resources/software_repositories/allowlists/pypi.allowlist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_safe_haven/resources/software_repositories/allowlists/pypi.allowlist b/data_safe_haven/resources/software_repositories/allowlists/pypi.allowlist index d3f79b6448..59cd132ab7 100644 --- a/data_safe_haven/resources/software_repositories/allowlists/pypi.allowlist +++ b/data_safe_haven/resources/software_repositories/allowlists/pypi.allowlist @@ -7,7 +7,7 @@ numpy packaging pandas pillow -pscopyg2-binary +pscopg2-binary pyodbc pyparsing python-dateutil From 89bce7f8fa31ec2b3f92cf87ff67f8f6976b2ace Mon Sep 17 00:00:00 2001 From: James Robinson Date: Fri, 11 Aug 2023 17:27:20 +0100 Subject: [PATCH 32/38] :wrench: Allow user services to authenticate with LDAP --- data_safe_haven/pulumi/components/sre_networking.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 9038aa0a55..63d76e0b71 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -511,6 +511,18 @@ def __init__( source_port_range="*", ), # Outbound + network.SecurityRuleArgs( + access=network.SecurityRuleAccess.ALLOW, + description="Allow LDAP client requests over TCP.", + destination_address_prefix=props.shm_subnet_identity_servers_prefix, + destination_port_ranges=["389", "636"], + direction=network.SecurityRuleDirection.OUTBOUND, + name="AllowLDAPClientTCPOutbound", + priority=NetworkingPriorities.INTERNAL_SHM_LDAP_TCP, + protocol=network.SecurityRuleProtocol.TCP, + source_address_prefix=subnet_user_services_containers_prefix, + source_port_range="*", + ), network.SecurityRuleArgs( access=network.SecurityRuleAccess.ALLOW, description="Allow outbound connections to configuration data endpoints.", From 331be60a29d1a562851042abff7d7282f4720562 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Fri, 11 Aug 2023 20:36:54 +0100 Subject: [PATCH 33/38] :bug: Fix incorrect string concatenation --- .../pulumi/components/sre_hedgedoc_server.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/data_safe_haven/pulumi/components/sre_hedgedoc_server.py b/data_safe_haven/pulumi/components/sre_hedgedoc_server.py index 59bd01529c..0c2efec319 100644 --- a/data_safe_haven/pulumi/components/sre_hedgedoc_server.py +++ b/data_safe_haven/pulumi/components/sre_hedgedoc_server.py @@ -282,12 +282,13 @@ def __init__( ), containerinstance.EnvironmentVariableArgs( name="CMD_LDAP_SEARCHFILTER", - value=( - "(&" - "(objectClass=user)" - f"(memberOf=CN={props.ldap_user_security_group_cn})" - f"(sAMAccountName={{{{username}}}})" - ")" + value=Output.concat( + "(&", + "(objectClass=user)", + "(memberOf=CN=", + props.ldap_user_security_group_cn, + ")", + "(sAMAccountName={{username}}))", ), ), containerinstance.EnvironmentVariableArgs( From 61a000cebaaff8895dd90452ee276eccd2e3da82 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Mon, 14 Aug 2023 09:29:06 +0100 Subject: [PATCH 34/38] :truck: Move fqdn_nameservers output into networking category --- data_safe_haven/commands/deploy_shm.py | 3 ++- data_safe_haven/pulumi/components/shm_networking.py | 1 + data_safe_haven/pulumi/declarative_shm.py | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/data_safe_haven/commands/deploy_shm.py b/data_safe_haven/commands/deploy_shm.py index fd089682c4..929ca5e9cb 100644 --- a/data_safe_haven/commands/deploy_shm.py +++ b/data_safe_haven/commands/deploy_shm.py @@ -69,7 +69,8 @@ def deploy_shm( # Add the SHM domain as a custom domain in AzureAD graph_api.verify_custom_domain( - config.shm.fqdn, stack.output("fqdn_nameservers") + config.shm.fqdn, + stack.output("networking")["fqdn_nameservers"], ) # Add Pulumi infrastructure information to the config file diff --git a/data_safe_haven/pulumi/components/shm_networking.py b/data_safe_haven/pulumi/components/shm_networking.py index 05436ad702..0bb22584f7 100644 --- a/data_safe_haven/pulumi/components/shm_networking.py +++ b/data_safe_haven/pulumi/components/shm_networking.py @@ -502,6 +502,7 @@ def __init__( # Register exports self.exports = { + "fqdn_nameservers": self.dns_zone.name_servers, "private_dns_zone_base_id": self.private_dns_zone_base_id, "resource_group_name": resource_group.name, "subnet_bastion_prefix": self.subnet_bastion.apply( diff --git a/data_safe_haven/pulumi/declarative_shm.py b/data_safe_haven/pulumi/declarative_shm.py index 0aec8fb27a..a513ef086e 100644 --- a/data_safe_haven/pulumi/declarative_shm.py +++ b/data_safe_haven/pulumi/declarative_shm.py @@ -143,7 +143,6 @@ def run(self) -> None: # Export values for later use pulumi.export("domain_controllers", domain_controllers.exports) - pulumi.export("fqdn_nameservers", networking.dns_zone.name_servers) pulumi.export("monitoring", monitoring.exports) pulumi.export("networking", networking.exports) pulumi.export("update_servers", update_servers.exports) From 1de1c75dd5fabe0ea5b5c054c931315bcb3ca9e0 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Mon, 14 Aug 2023 09:40:20 +0100 Subject: [PATCH 35/38] :truck: Move AzureFirewallSubnet and IdentitySubnet to correct ordered position --- .../pulumi/components/shm_networking.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/data_safe_haven/pulumi/components/shm_networking.py b/data_safe_haven/pulumi/components/shm_networking.py index 0bb22584f7..4bf88375d5 100644 --- a/data_safe_haven/pulumi/components/shm_networking.py +++ b/data_safe_haven/pulumi/components/shm_networking.py @@ -21,14 +21,14 @@ def __init__( ) -> None: # Virtual network and subnet IP ranges self.vnet_iprange = AzureIPv4Range("10.0.0.0", "10.0.255.255") - # Firewall subnet must be at least /26 in size (64 addresses) - self.subnet_firewall_iprange = self.vnet_iprange.next_subnet(64) # Bastion subnet must be at least /26 in size (64 addresses) self.subnet_bastion_iprange = self.vnet_iprange.next_subnet(64) + # Firewall subnet must be at least /26 in size (64 addresses) + self.subnet_firewall_iprange = self.vnet_iprange.next_subnet(64) + self.subnet_identity_servers_iprange = self.vnet_iprange.next_subnet(8) # Monitoring subnet needs 2 IP addresses for automation and 13 for log analytics self.subnet_monitoring_iprange = self.vnet_iprange.next_subnet(32) self.subnet_update_servers_iprange = self.vnet_iprange.next_subnet(8) - self.subnet_identity_servers_iprange = self.vnet_iprange.next_subnet(8) # Other variables self.admin_ip_addresses = admin_ip_addresses self.fqdn = fqdn @@ -345,13 +345,6 @@ def __init__( ), resource_group_name=resource_group.name, subnets=[ # Note that we define subnets inline to avoid creation order issues - # AzureFirewall subnet - network.SubnetArgs( - address_prefix=str(props.subnet_firewall_iprange), - name=subnet_firewall_name, - network_security_group=None, # the firewall subnet must NOT have an NSG - route_table=None, # the firewall subnet must NOT be attached to the route table - ), # Bastion subnet network.SubnetArgs( address_prefix=str(props.subnet_bastion_iprange), @@ -361,6 +354,13 @@ def __init__( ), route_table=None, # the bastion subnet must NOT be attached to the route table ), + # AzureFirewall subnet + network.SubnetArgs( + address_prefix=str(props.subnet_firewall_iprange), + name=subnet_firewall_name, + network_security_group=None, # the firewall subnet must NOT have an NSG + route_table=None, # the firewall subnet must NOT be attached to the route table + ), # Identity servers subnet network.SubnetArgs( address_prefix=str(props.subnet_identity_servers_iprange), From 480551ea9ab953ee6fb0cb7a46b0aa5c3e671624 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Mon, 14 Aug 2023 16:03:43 +0100 Subject: [PATCH 36/38] :bug: Close file handles before deleting to avoid 'The specified resource may be in use by an SMB client.' error. --- data_safe_haven/pulumi/dynamic/file_share_file.py | 1 + 1 file changed, 1 insertion(+) diff --git a/data_safe_haven/pulumi/dynamic/file_share_file.py b/data_safe_haven/pulumi/dynamic/file_share_file.py index e4005da3b1..cd1a0b7a01 100644 --- a/data_safe_haven/pulumi/dynamic/file_share_file.py +++ b/data_safe_haven/pulumi/dynamic/file_share_file.py @@ -113,6 +113,7 @@ def delete(self, id_: str, props: dict[str, Any]) -> None: props["destination_path"], ) if self.file_exists(file_client): + file_client.close_all_handles() file_client.delete_file() except Exception as exc: file_name = file_client.file_name if file_client else "" From 34059950a93ceb26bb284dd790a0633675cbc631 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Mon, 14 Aug 2023 22:39:46 +0100 Subject: [PATCH 37/38] :truck: Renamed PrivateData and ConfigurationData subnets to DataPrivate and DataConfiguration --- .../pulumi/components/sre_networking.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 63d76e0b71..25de7bd34f 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -214,7 +214,7 @@ def __init__( ) nsg_data_configuration = network.NetworkSecurityGroup( f"{self._name}_nsg_data_configuration", - network_security_group_name=f"{stack_name}-nsg-configuration-data", + network_security_group_name=f"{stack_name}-nsg-data-configuration", resource_group_name=resource_group.name, security_rules=[ # Inbound @@ -284,7 +284,7 @@ def __init__( ) nsg_data_private = network.NetworkSecurityGroup( f"{self._name}_nsg_data_private", - network_security_group_name=f"{stack_name}-nsg-private-data", + network_security_group_name=f"{stack_name}-nsg-data-private", resource_group_name=resource_group.name, security_rules=[ # Inbound @@ -377,7 +377,7 @@ def __init__( destination_address_prefix=subnet_data_configuration_prefix, destination_port_range="*", direction=network.SecurityRuleDirection.OUTBOUND, - name="AllowConfigurationDataEndpointsOutbound", + name="AllowDataConfigurationEndpointsOutbound", priority=NetworkingPriorities.INTERNAL_SRE_DATA_CONFIGURATION, protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix=subnet_guacamole_containers_prefix, @@ -529,7 +529,7 @@ def __init__( destination_address_prefix=subnet_data_configuration_prefix, destination_port_range="*", direction=network.SecurityRuleDirection.OUTBOUND, - name="AllowConfigurationDataEndpointsOutbound", + name="AllowDataConfigurationEndpointsOutbound", priority=NetworkingPriorities.INTERNAL_SRE_DATA_CONFIGURATION, protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix=subnet_user_services_containers_prefix, @@ -645,7 +645,7 @@ def __init__( destination_address_prefix=subnet_data_configuration_prefix, destination_port_range="*", direction=network.SecurityRuleDirection.OUTBOUND, - name="AllowConfigurationDataEndpointsOutbound", + name="AllowDataConfigurationEndpointsOutbound", priority=NetworkingPriorities.INTERNAL_SRE_DATA_CONFIGURATION, protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix=subnet_user_services_databases_prefix, @@ -703,7 +703,7 @@ def __init__( destination_address_prefix=subnet_data_configuration_prefix, destination_port_range="*", direction=network.SecurityRuleDirection.OUTBOUND, - name="AllowConfigurationDataEndpointsOutbound", + name="AllowDataConfigurationEndpointsOutbound", priority=NetworkingPriorities.INTERNAL_SRE_DATA_CONFIGURATION, protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix=subnet_user_services_software_repositories_prefix, @@ -824,7 +824,7 @@ def __init__( destination_address_prefix=subnet_data_private_prefix, destination_port_range="*", direction=network.SecurityRuleDirection.OUTBOUND, - name="AllowPrivateDataEndpointsOutbound", + name="AllowDataPrivateEndpointsOutbound", priority=NetworkingPriorities.INTERNAL_SRE_DATA_PRIVATE, protocol=network.SecurityRuleProtocol.ASTERISK, source_address_prefix=subnet_workspaces_prefix, @@ -896,8 +896,8 @@ def __init__( # Define the virtual network and its subnets subnet_application_gateway_name = "ApplicationGatewaySubnet" - subnet_data_configuration_name = "ConfigurationDataSubnet" - subnet_data_private_name = "PrivateDataSubnet" + subnet_data_configuration_name = "DataConfigurationSubnet" + subnet_data_private_name = "DataPrivateSubnet" subnet_guacamole_containers_name = "GuacamoleContainersSubnet" subnet_guacamole_containers_support_name = "GuacamoleContainersSupportSubnet" subnet_user_services_containers_name = "UserServicesContainersSubnet" From 814290e9860ee1773f9d27f9d5d29b60bf980fba Mon Sep 17 00:00:00 2001 From: James Robinson Date: Mon, 14 Aug 2023 23:28:02 +0100 Subject: [PATCH 38/38] :bulb: Reorder SRE subnet address prefix calculations --- data_safe_haven/pulumi/components/sre_networking.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/data_safe_haven/pulumi/components/sre_networking.py b/data_safe_haven/pulumi/components/sre_networking.py index 25de7bd34f..0409a268ba 100644 --- a/data_safe_haven/pulumi/components/sre_networking.py +++ b/data_safe_haven/pulumi/components/sre_networking.py @@ -95,18 +95,18 @@ def __init__( subnet_application_gateway_prefix = ( props.subnet_application_gateway_iprange.apply(lambda r: str(r)) ) - subnet_guacamole_containers_prefix = ( - props.subnet_guacamole_containers_iprange.apply(lambda r: str(r)) - ) - subnet_guacamole_containers_support_prefix = ( - props.subnet_guacamole_containers_support_iprange.apply(lambda r: str(r)) - ) subnet_data_configuration_prefix = ( props.subnet_data_configuration_iprange.apply(lambda r: str(r)) ) subnet_data_private_prefix = props.subnet_data_private_iprange.apply( lambda r: str(r) ) + subnet_guacamole_containers_prefix = ( + props.subnet_guacamole_containers_iprange.apply(lambda r: str(r)) + ) + subnet_guacamole_containers_support_prefix = ( + props.subnet_guacamole_containers_support_iprange.apply(lambda r: str(r)) + ) subnet_user_services_containers_prefix = ( props.subnet_user_services_containers_iprange.apply(lambda r: str(r)) )