From ac39ba51bdfd4e1b8e2c5addf3ba33bfeee179e2 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Tue, 16 Apr 2024 12:45:09 +0100 Subject: [PATCH 1/5] :sparkles: Add a local DNS record to the SRE identity server --- .../infrastructure/stacks/declarative_sre.py | 3 +++ .../infrastructure/stacks/sre/identity.py | 26 ++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/data_safe_haven/infrastructure/stacks/declarative_sre.py b/data_safe_haven/infrastructure/stacks/declarative_sre.py index ea4f937dc9..eb1544ff3f 100644 --- a/data_safe_haven/infrastructure/stacks/declarative_sre.py +++ b/data_safe_haven/infrastructure/stacks/declarative_sre.py @@ -216,8 +216,11 @@ def run(self) -> None: aad_application_name=f"sre-{self.sre_name}-apricot", aad_auth_token=self.graph_api_token, aad_tenant_id=self.cfg.shm.aad_tenant_id, + dns_resource_group_name=dns.resource_group.name, location=self.cfg.azure.location, + networking_resource_group_name=networking.resource_group.name, shm_fqdn=self.cfg.shm.fqdn, + sre_fqdn=networking.sre_fqdn, 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, diff --git a/data_safe_haven/infrastructure/stacks/sre/identity.py b/data_safe_haven/infrastructure/stacks/sre/identity.py index 8ee3980c2e..08661caed0 100644 --- a/data_safe_haven/infrastructure/stacks/sre/identity.py +++ b/data_safe_haven/infrastructure/stacks/sre/identity.py @@ -11,6 +11,8 @@ from data_safe_haven.infrastructure.components import ( AzureADApplication, AzureADApplicationProps, + LocalDnsRecordComponent, + LocalDnsRecordProps, ) @@ -22,8 +24,11 @@ def __init__( aad_application_name: Input[str], aad_auth_token: Input[str], aad_tenant_id: Input[str], + dns_resource_group_name: Input[str], location: Input[str], + networking_resource_group_name: Input[str], shm_fqdn: Input[str], + sre_fqdn: Input[str], storage_account_key: Input[str], storage_account_name: Input[str], storage_account_resource_group_name: Input[str], @@ -32,8 +37,11 @@ def __init__( self.aad_application_name = aad_application_name self.aad_auth_token = aad_auth_token self.aad_tenant_id = aad_tenant_id + self.dns_resource_group_name = dns_resource_group_name self.location = location + self.networking_resource_group_name = networking_resource_group_name self.shm_fqdn = shm_fqdn + self.sre_fqdn = sre_fqdn self.storage_account_key = storage_account_key self.storage_account_name = storage_account_name self.storage_account_resource_group_name = storage_account_resource_group_name @@ -219,6 +227,22 @@ def __init__( ), tags=child_tags, ) + private_ip_address = get_ip_address_from_container_group(container_group) + + # Register the container group in the SRE DNS zone + LocalDnsRecordComponent( + f"{self._name}_dns_record_set", + LocalDnsRecordProps( + base_fqdn=props.sre_fqdn, + public_dns_resource_group_name=props.networking_resource_group_name, + private_dns_resource_group_name=props.dns_resource_group_name, + private_ip_address=private_ip_address, + record_name="identity", + ), + opts=ResourceOptions.merge( + child_opts, ResourceOptions(parent=container_group) + ), + ) # Register outputs - self.ip_address = get_ip_address_from_container_group(container_group) + self.ip_address = private_ip_address From 47ea811c5e2c92ffa4148a34600ab1e8bb492950 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Tue, 16 Apr 2024 12:51:33 +0100 Subject: [PATCH 2/5] :sparkles: Expose hostname from local DNS record --- .../infrastructure/components/composite/local_dns_record.py | 4 ++++ data_safe_haven/infrastructure/stacks/sre/identity.py | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/data_safe_haven/infrastructure/components/composite/local_dns_record.py b/data_safe_haven/infrastructure/components/composite/local_dns_record.py index 03c7f3f712..3c8b214074 100644 --- a/data_safe_haven/infrastructure/components/composite/local_dns_record.py +++ b/data_safe_haven/infrastructure/components/composite/local_dns_record.py @@ -47,6 +47,7 @@ def __init__( ttl=3600, opts=child_opts, ) + # Redirect the public DNS to private DNS network.RecordSet( f"{self._name}_public_record_set", @@ -62,3 +63,6 @@ def __init__( child_opts, ResourceOptions(parent=private_dns_record_set) ), ) + + # Register outputs + self.hostname = Output.concat(props.record_name, ".", props.base_fqdn) diff --git a/data_safe_haven/infrastructure/stacks/sre/identity.py b/data_safe_haven/infrastructure/stacks/sre/identity.py index 08661caed0..6994e18ed2 100644 --- a/data_safe_haven/infrastructure/stacks/sre/identity.py +++ b/data_safe_haven/infrastructure/stacks/sre/identity.py @@ -230,7 +230,7 @@ def __init__( private_ip_address = get_ip_address_from_container_group(container_group) # Register the container group in the SRE DNS zone - LocalDnsRecordComponent( + local_dns = LocalDnsRecordComponent( f"{self._name}_dns_record_set", LocalDnsRecordProps( base_fqdn=props.sre_fqdn, @@ -245,4 +245,5 @@ def __init__( ) # Register outputs + self.hostname = local_dns.hostname self.ip_address = private_ip_address From b691f0c1236723bb2b343bc867a6ce95e2cb8c5b Mon Sep 17 00:00:00 2001 From: James Robinson Date: Tue, 16 Apr 2024 12:56:13 +0100 Subject: [PATCH 3/5] :recycle: Switch from using ldap_server_ip to ldap_server_hostname --- .../infrastructure/stacks/declarative_sre.py | 6 +++--- .../infrastructure/stacks/sre/gitea_server.py | 6 +++--- .../infrastructure/stacks/sre/hedgedoc_server.py | 6 +++--- .../infrastructure/stacks/sre/remote_desktop.py | 6 +++--- .../infrastructure/stacks/sre/user_services.py | 8 ++++---- .../infrastructure/stacks/sre/workspaces.py | 10 +++++----- .../resources/gitea/gitea/configure.mustache.sh | 2 +- .../workspace/workspace.cloud_init.mustache.yaml | 2 +- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/data_safe_haven/infrastructure/stacks/declarative_sre.py b/data_safe_haven/infrastructure/stacks/declarative_sre.py index eb1544ff3f..c682a2014d 100644 --- a/data_safe_haven/infrastructure/stacks/declarative_sre.py +++ b/data_safe_haven/infrastructure/stacks/declarative_sre.py @@ -258,7 +258,7 @@ def run(self) -> None: dns_server_ip=dns.ip_address, ldap_group_filter=ldap_group_filter, ldap_group_search_base=ldap_group_search_base, - ldap_server_ip=identity.ip_address, + ldap_server_hostname=identity.hostname, ldap_server_port=identity.server_port, ldap_user_filter=ldap_user_filter, ldap_user_search_base=ldap_user_search_base, @@ -280,7 +280,7 @@ def run(self) -> None: admin_password=data.password_workspace_admin, ldap_group_filter=ldap_group_filter, ldap_group_search_base=ldap_group_search_base, - ldap_server_ip=identity.ip_address, + ldap_server_hostname=identity.hostname, ldap_server_port=identity.server_port, ldap_user_filter=ldap_user_filter, ldap_user_search_base=ldap_user_search_base, @@ -318,7 +318,7 @@ def run(self) -> None: dns_server_ip=dns.ip_address, gitea_database_password=data.password_gitea_database_admin, hedgedoc_database_password=data.password_hedgedoc_database_admin, - ldap_server_ip=identity.ip_address, + ldap_server_hostname=identity.hostname, ldap_server_port=identity.server_port, ldap_user_filter=ldap_user_filter, ldap_username_attribute=ldap_username_attribute, diff --git a/data_safe_haven/infrastructure/stacks/sre/gitea_server.py b/data_safe_haven/infrastructure/stacks/sre/gitea_server.py index 542a89eb9a..7ab494f682 100644 --- a/data_safe_haven/infrastructure/stacks/sre/gitea_server.py +++ b/data_safe_haven/infrastructure/stacks/sre/gitea_server.py @@ -28,7 +28,7 @@ def __init__( database_subnet_id: Input[str], dns_resource_group_name: Input[str], dns_server_ip: Input[str], - ldap_server_ip: Input[str], + ldap_server_hostname: Input[str], ldap_server_port: Input[int], ldap_username_attribute: Input[str], ldap_user_filter: Input[str], @@ -51,7 +51,7 @@ def __init__( ) self.dns_resource_group_name = dns_resource_group_name self.dns_server_ip = dns_server_ip - self.ldap_server_ip = ldap_server_ip + self.ldap_server_hostname = ldap_server_hostname self.ldap_server_port = ldap_server_port self.ldap_username_attribute = ldap_username_attribute self.ldap_user_filter = ldap_user_filter @@ -130,7 +130,7 @@ def __init__( admin_username="dshadmin", ldap_username_attribute=props.ldap_username_attribute, ldap_user_filter=props.ldap_user_filter, - ldap_server_ip=props.ldap_server_ip, + ldap_server_hostname=props.ldap_server_hostname, ldap_server_port=props.ldap_server_port, ldap_user_search_base=props.ldap_user_search_base, ).apply( diff --git a/data_safe_haven/infrastructure/stacks/sre/hedgedoc_server.py b/data_safe_haven/infrastructure/stacks/sre/hedgedoc_server.py index 5fa8cb239f..5be8195725 100644 --- a/data_safe_haven/infrastructure/stacks/sre/hedgedoc_server.py +++ b/data_safe_haven/infrastructure/stacks/sre/hedgedoc_server.py @@ -29,7 +29,7 @@ def __init__( database_subnet_id: Input[str], dns_resource_group_name: Input[str], dns_server_ip: Input[str], - ldap_server_ip: Input[str], + ldap_server_hostname: Input[str], ldap_server_port: Input[int], ldap_user_filter: Input[str], ldap_user_search_base: Input[str], @@ -52,7 +52,7 @@ def __init__( ) self.dns_resource_group_name = dns_resource_group_name self.dns_server_ip = dns_server_ip - self.ldap_server_ip = ldap_server_ip + self.ldap_server_hostname = ldap_server_hostname self.ldap_server_port = Output.from_input(ldap_server_port).apply(str) self.ldap_user_filter = ldap_user_filter self.ldap_user_search_base = ldap_user_search_base @@ -225,7 +225,7 @@ def __init__( name="CMD_LDAP_URL", value=Output.concat( "ldap://", - props.ldap_server_ip, + props.ldap_server_hostname, ":", props.ldap_server_port, ), diff --git a/data_safe_haven/infrastructure/stacks/sre/remote_desktop.py b/data_safe_haven/infrastructure/stacks/sre/remote_desktop.py index d77c0a9c49..3d7cd87b0e 100644 --- a/data_safe_haven/infrastructure/stacks/sre/remote_desktop.py +++ b/data_safe_haven/infrastructure/stacks/sre/remote_desktop.py @@ -42,7 +42,7 @@ def __init__( dns_server_ip: Input[str], ldap_group_filter: Input[str], ldap_group_search_base: Input[str], - ldap_server_ip: Input[str], + ldap_server_hostname: Input[str], ldap_server_port: Input[int], ldap_user_filter: Input[str], ldap_user_search_base: Input[str], @@ -67,7 +67,7 @@ def __init__( self.dns_server_ip = dns_server_ip self.ldap_group_filter = ldap_group_filter self.ldap_group_search_base = ldap_group_search_base - self.ldap_server_ip = ldap_server_ip + self.ldap_server_hostname = ldap_server_hostname self.ldap_server_port = ldap_server_port self.ldap_user_filter = ldap_user_filter self.ldap_user_search_base = ldap_user_search_base @@ -324,7 +324,7 @@ def __init__( ), containerinstance.EnvironmentVariableArgs( name="LDAP_HOST", - value=props.ldap_server_ip, + value=props.ldap_server_hostname, ), containerinstance.EnvironmentVariableArgs( name="LDAP_PORT", diff --git a/data_safe_haven/infrastructure/stacks/sre/user_services.py b/data_safe_haven/infrastructure/stacks/sre/user_services.py index d54adf65b3..1b51b44b82 100644 --- a/data_safe_haven/infrastructure/stacks/sre/user_services.py +++ b/data_safe_haven/infrastructure/stacks/sre/user_services.py @@ -26,7 +26,7 @@ def __init__( dns_server_ip: Input[str], gitea_database_password: Input[str], hedgedoc_database_password: Input[str], - ldap_server_ip: Input[str], + ldap_server_hostname: Input[str], ldap_server_port: Input[int], ldap_username_attribute: Input[str], ldap_user_filter: Input[str], @@ -51,7 +51,7 @@ def __init__( self.dns_server_ip = dns_server_ip self.gitea_database_password = gitea_database_password self.hedgedoc_database_password = hedgedoc_database_password - self.ldap_server_ip = ldap_server_ip + self.ldap_server_hostname = ldap_server_hostname self.ldap_server_port = ldap_server_port self.ldap_username_attribute = ldap_username_attribute self.ldap_user_filter = ldap_user_filter @@ -113,7 +113,7 @@ def __init__( database_password=props.gitea_database_password, dns_resource_group_name=props.dns_resource_group_name, dns_server_ip=props.dns_server_ip, - ldap_server_ip=props.ldap_server_ip, + ldap_server_hostname=props.ldap_server_hostname, ldap_server_port=props.ldap_server_port, ldap_username_attribute=props.ldap_username_attribute, ldap_user_filter=props.ldap_user_filter, @@ -141,7 +141,7 @@ def __init__( database_subnet_id=props.subnet_containers_support_id, dns_resource_group_name=props.dns_resource_group_name, dns_server_ip=props.dns_server_ip, - ldap_server_ip=props.ldap_server_ip, + ldap_server_hostname=props.ldap_server_hostname, ldap_server_port=props.ldap_server_port, ldap_username_attribute=props.ldap_username_attribute, ldap_user_filter=props.ldap_user_filter, diff --git a/data_safe_haven/infrastructure/stacks/sre/workspaces.py b/data_safe_haven/infrastructure/stacks/sre/workspaces.py index 51e64ba988..623bbca05c 100644 --- a/data_safe_haven/infrastructure/stacks/sre/workspaces.py +++ b/data_safe_haven/infrastructure/stacks/sre/workspaces.py @@ -32,7 +32,7 @@ def __init__( admin_password: Input[str], ldap_group_filter: Input[str], ldap_group_search_base: Input[str], - ldap_server_ip: Input[str], + ldap_server_hostname: Input[str], ldap_server_port: Input[int], ldap_user_filter: Input[str], ldap_user_search_base: Input[str], @@ -54,7 +54,7 @@ def __init__( self.admin_username = "dshadmin" self.ldap_group_filter = ldap_group_filter self.ldap_group_search_base = ldap_group_search_base - self.ldap_server_ip = ldap_server_ip + self.ldap_server_hostname = ldap_server_hostname self.ldap_server_port = Output.from_input(ldap_server_port).apply(str) self.ldap_user_filter = ldap_user_filter self.ldap_user_search_base = ldap_user_search_base @@ -123,7 +123,7 @@ def __init__( b64cloudinit = Output.all( ldap_group_filter=props.ldap_group_filter, ldap_group_search_base=props.ldap_group_search_base, - ldap_server_ip=props.ldap_server_ip, + ldap_server_hostname=props.ldap_server_hostname, ldap_server_port=props.ldap_server_port, ldap_user_filter=props.ldap_user_filter, ldap_user_search_base=props.ldap_user_search_base, @@ -212,7 +212,7 @@ def read_cloudinit( self, ldap_group_filter: str, ldap_group_search_base: str, - ldap_server_ip: str, + ldap_server_hostname: str, ldap_server_port: str, ldap_user_filter: str, ldap_user_search_base: str, @@ -228,7 +228,7 @@ def read_cloudinit( mustache_values = { "ldap_group_filter": ldap_group_filter, "ldap_group_search_base": ldap_group_search_base, - "ldap_server_ip": ldap_server_ip, + "ldap_server_hostname": ldap_server_hostname, "ldap_server_port": ldap_server_port, "ldap_user_filter": ldap_user_filter, "ldap_user_search_base": ldap_user_search_base, diff --git a/data_safe_haven/resources/gitea/gitea/configure.mustache.sh b/data_safe_haven/resources/gitea/gitea/configure.mustache.sh index 7f4f2a404f..4108c5c9dd 100644 --- a/data_safe_haven/resources/gitea/gitea/configure.mustache.sh +++ b/data_safe_haven/resources/gitea/gitea/configure.mustache.sh @@ -13,7 +13,7 @@ until su-exec "$USER" /usr/local/bin/gitea admin auth list | grep "DataSafeHaven su-exec "$USER" /usr/local/bin/gitea admin auth add-ldap \ --name DataSafeHavenLDAP \ --security-protocol "unencrypted" \ - --host "{{ldap_server_ip}}" \ + --host "{{ldap_server_hostname}}" \ --port "{{ldap_server_port}}" \ --user-search-base "{{ldap_user_search_base}}" \ --user-filter "(&{{{ldap_user_filter}}}({{ldap_username_attribute}}=%[1]s))" \ 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 d67913e63a..1bdb57fca4 100644 --- a/data_safe_haven/resources/workspace/workspace.cloud_init.mustache.yaml +++ b/data_safe_haven/resources/workspace/workspace.cloud_init.mustache.yaml @@ -22,7 +22,7 @@ write_files: nss_min_uid 2000 # General connection options - uri ldap://{{ldap_server_ip}}:{{ldap_server_port}} + uri ldap://{{ldap_server_hostname}}:{{ldap_server_port}} # Search/mapping options base {{ldap_user_search_base}} From 6fbb8898563673f661567b7b1fc72c3583c6bc20 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Tue, 16 Apr 2024 12:57:15 +0100 Subject: [PATCH 4/5] :coffin: Drop unused identity.ip_address --- data_safe_haven/infrastructure/stacks/sre/identity.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/data_safe_haven/infrastructure/stacks/sre/identity.py b/data_safe_haven/infrastructure/stacks/sre/identity.py index 6994e18ed2..9a725c8d99 100644 --- a/data_safe_haven/infrastructure/stacks/sre/identity.py +++ b/data_safe_haven/infrastructure/stacks/sre/identity.py @@ -227,7 +227,6 @@ def __init__( ), tags=child_tags, ) - private_ip_address = get_ip_address_from_container_group(container_group) # Register the container group in the SRE DNS zone local_dns = LocalDnsRecordComponent( @@ -236,7 +235,7 @@ def __init__( base_fqdn=props.sre_fqdn, public_dns_resource_group_name=props.networking_resource_group_name, private_dns_resource_group_name=props.dns_resource_group_name, - private_ip_address=private_ip_address, + private_ip_address=get_ip_address_from_container_group(container_group), record_name="identity", ), opts=ResourceOptions.merge( @@ -246,4 +245,3 @@ def __init__( # Register outputs self.hostname = local_dns.hostname - self.ip_address = private_ip_address From 308b145d65dddadd30df59a41e6bdb22737a6622 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Tue, 16 Apr 2024 13:04:10 +0100 Subject: [PATCH 5/5] :coffin: Do not expose Guacamole container private IP address as traffic can be routed to any available IP by the ApplicationGateway backend pool --- data_safe_haven/infrastructure/stacks/sre/remote_desktop.py | 4 ---- data_safe_haven/provisioning/sre_provisioning_manager.py | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/data_safe_haven/infrastructure/stacks/sre/remote_desktop.py b/data_safe_haven/infrastructure/stacks/sre/remote_desktop.py index 3d7cd87b0e..f4883fdf80 100644 --- a/data_safe_haven/infrastructure/stacks/sre/remote_desktop.py +++ b/data_safe_haven/infrastructure/stacks/sre/remote_desktop.py @@ -13,7 +13,6 @@ from data_safe_haven.external import AzureIPv4Range from data_safe_haven.infrastructure.common import ( get_id_from_subnet, - get_ip_address_from_container_group, ) from data_safe_haven.infrastructure.components import ( AzureADApplication, @@ -421,9 +420,6 @@ def __init__( "connection_db_name": db_guacamole_connections, "connection_db_server_name": db_server_guacamole.db_server.name, "container_group_name": container_group.name, - "container_ip_address": get_ip_address_from_container_group( - container_group - ), "disable_copy": props.disable_copy, "disable_paste": props.disable_paste, "resource_group_name": resource_group.name, diff --git a/data_safe_haven/provisioning/sre_provisioning_manager.py b/data_safe_haven/provisioning/sre_provisioning_manager.py index 68884808fa..63655458df 100644 --- a/data_safe_haven/provisioning/sre_provisioning_manager.py +++ b/data_safe_haven/provisioning/sre_provisioning_manager.py @@ -84,9 +84,7 @@ def restart_remote_desktop_containers(self) -> None: self.remote_desktop_params["resource_group_name"], self.subscription_name, ) - guacamole_provisioner.restart( - self.remote_desktop_params["container_ip_address"] - ) + guacamole_provisioner.restart() def update_remote_desktop_connections(self) -> None: """Update connection information on the Guacamole PostgreSQL server"""