From a68d10cfb8a0acd278a4dc9008b18ae22fbdb55f Mon Sep 17 00:00:00 2001 From: LAKIN Date: Tue, 25 Aug 2026 11:35:37 +0100 Subject: [PATCH 1/8] Added local dev functionality for containers etc --- .gitignore | 1 - Dockerfile.dev | 40 ++++++++++++++++++ docker-compose.yaml | 41 +++++++++++++++++++ script/dev-api-docker-entrypoint.sh | 7 ++++ .../Directory.Build.targets | 4 ++ .../Directory.Build.targets | 4 ++ 6 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 Dockerfile.dev create mode 100644 docker-compose.yaml create mode 100644 script/dev-api-docker-entrypoint.sh create mode 100644 src/GovUK.Dfe.FlexForms.Api.Client/Directory.Build.targets create mode 100644 src/GovUK.Dfe.FlexForms.Api/Directory.Build.targets diff --git a/.gitignore b/.gitignore index d6bcfc6b..0bb801ba 100644 --- a/.gitignore +++ b/.gitignore @@ -184,5 +184,4 @@ backend.vars uploads/ Directory.Build.targets.user -/src/LocalPackages .cursor \ No newline at end of file diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 00000000..7380cba3 --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,40 @@ +# See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +# This stage is used when running from VS in fast mode (Default for Debug configuration) +FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base +USER root +WORKDIR /app +EXPOSE 8080 +EXPOSE 8081 + +# This stage is used to build the service project +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build +ENV CI=true +ARG BUILD_CONFIGURATION=Debug +WORKDIR /src +COPY ["Directory.Build.props", "."] +COPY ["src/GovUK.Dfe.FlexForms.Api/GovUK.Dfe.FlexForms.Api.csproj", "src/GovUK.Dfe.FlexForms.Api/"] +COPY ["src/GovUK.Dfe.FlexForms.Application/GovUK.Dfe.FlexForms.Application.csproj", "src/GovUK.Dfe.FlexForms.Application/"] +COPY ["src/GovUK.Dfe.FlexForms.Domain/GovUK.Dfe.FlexForms.Domain.csproj", "src/GovUK.Dfe.FlexForms.Domain/"] +COPY ["src/GovUK.Dfe.FlexForms.Utils/GovUK.Dfe.FlexForms.Utils.csproj", "src/GovUK.Dfe.FlexForms.Utils/"] +COPY ["src/GovUK.Dfe.FlexForms.Infrastructure/GovUK.Dfe.FlexForms.Infrastructure.csproj", "src/GovUK.Dfe.FlexForms.Infrastructure/"] +RUN dotnet restore "./src/GovUK.Dfe.FlexForms.Api/GovUK.Dfe.FlexForms.Api.csproj" +COPY . . +WORKDIR "/src/src/GovUK.Dfe.FlexForms.Api" +RUN dotnet build "./GovUK.Dfe.FlexForms.Api.csproj" -c $BUILD_CONFIGURATION -p:SkipDockerCompose=true -o /app/build + +# This stage is used to publish the service project to be copied to the final stage +FROM build AS publish +ARG BUILD_CONFIGURATION=Debug +RUN dotnet publish "./GovUK.Dfe.FlexForms.Api.csproj" -c $BUILD_CONFIGURATION -p:SkipDockerCompose=true -o /app/publish /p:UseAppHost=false + +# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration) +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . + +COPY script/dev-api-docker-entrypoint.sh /app/docker-entrypoint.sh +RUN sed -i 's/\r$//' /app/docker-entrypoint.sh +RUN chmod +x /app/docker-entrypoint.sh + +ENTRYPOINT ["/app/docker-entrypoint.sh"] \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 00000000..0db9af47 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,41 @@ +name: dfe-flexforms + +networks: + dfe-flexforms-network: + name: dfe-flexforms-network + driver: bridge + external: true + +services: + ea_api: + container_name: ea-flexforms-api + cap_add: + - SYS_PTRACE + security_opt: + - seccomp:unconfined + build: + context: ${REPO_PATH} + dockerfile: Dockerfile.dev + ports: + - 5277:8080 + - 7089:8081 + restart: unless-stopped + environment: + - ConnectionStrings__DefaultConnection=Server=sql,1433;Database=ExternalApplications;User Id=SA;Password=Pa55w0rd!;TrustServerCertificate=True; + - ConnectionStrings__TenantConfigDatabase=Server=sql,1433;Database=TenantConfig;User Id=SA;Password=Pa55w0rd!;TrustServerCertificate=True; + - ConnectionStrings__Redis=redis:6379 + - CacheSettings__Redis__ConnectionString=redis:6379 + - DataProtection__UseAzure=false + - DataProtection__UseStorageSas=false + - Tenants__CodeGeneration__ConnectionStrings__Redis=redis:6379 + - ASPNETCORE_URLS=https://+:8081;http://+:8080 + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_Kestrel__Certificates__Default__Password=Pa55w0rd + - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/ea_cert.pfx + #- CI=true + volumes: + - ${APPDATA}/dev-certs/ea_cert.pfx:/https/ea_cert.pfx:ro + - ${APPDATA}/Microsoft/UserSecrets/f714ca7a-fc08-46ff-b0cc-373d6f04cf4c:/home/app/.microsoft/usersecrets/f714ca7a-fc08-46ff-b0cc-373d6f04cf4c:ro + - ${APPDATA}/mkcert/rootCA.pem:/usr/local/share/ca-certificates/mkcert-rootCA.crt:ro + networks: + - dfe-flexforms-network \ No newline at end of file diff --git a/script/dev-api-docker-entrypoint.sh b/script/dev-api-docker-entrypoint.sh new file mode 100644 index 00000000..f389c8fc --- /dev/null +++ b/script/dev-api-docker-entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -e + +#apt-get update +#update-ca-certificates + +exec su app -s /bin/sh -c "dotnet GovUK.Dfe.FlexForms.Api.dll" \ No newline at end of file diff --git a/src/GovUK.Dfe.FlexForms.Api.Client/Directory.Build.targets b/src/GovUK.Dfe.FlexForms.Api.Client/Directory.Build.targets new file mode 100644 index 00000000..86048979 --- /dev/null +++ b/src/GovUK.Dfe.FlexForms.Api.Client/Directory.Build.targets @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/src/GovUK.Dfe.FlexForms.Api/Directory.Build.targets b/src/GovUK.Dfe.FlexForms.Api/Directory.Build.targets new file mode 100644 index 00000000..86048979 --- /dev/null +++ b/src/GovUK.Dfe.FlexForms.Api/Directory.Build.targets @@ -0,0 +1,4 @@ + + + \ No newline at end of file From 3bd43f96c59d89e5d1f150a0f5cf7c1806a3a472 Mon Sep 17 00:00:00 2001 From: LAKIN Date: Fri, 28 Aug 2026 16:13:39 +0100 Subject: [PATCH 2/8] Local development key decryption setup for use with a container --- .gitignore | 3 ++- docker-compose.yaml | 4 +++- .../Security/DataProtectionSettings.cs | 10 ++++++++++ .../TenantSettingsDataProtectionExtensions.cs | 12 ++++++++++-- .../appsettings.CodeGeneration.json | 3 ++- src/GovUK.Dfe.FlexForms.Api/appsettings.json | 3 ++- 6 files changed, 29 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 0bb801ba..6eb47faa 100644 --- a/.gitignore +++ b/.gitignore @@ -184,4 +184,5 @@ backend.vars uploads/ Directory.Build.targets.user -.cursor \ No newline at end of file +.cursor +/encryption-keys \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 0db9af47..71ebbde4 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -27,15 +27,17 @@ services: - CacheSettings__Redis__ConnectionString=redis:6379 - DataProtection__UseAzure=false - DataProtection__UseStorageSas=false + - DataProtection__ApplicationName=GovUK.Dfe.FlexForms.Api + - DataProtection__LocalKeysPath=/home/app/.aspnet/DataProtection-Keys - Tenants__CodeGeneration__ConnectionStrings__Redis=redis:6379 - ASPNETCORE_URLS=https://+:8081;http://+:8080 - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_Kestrel__Certificates__Default__Password=Pa55w0rd - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/ea_cert.pfx - #- CI=true volumes: - ${APPDATA}/dev-certs/ea_cert.pfx:/https/ea_cert.pfx:ro - ${APPDATA}/Microsoft/UserSecrets/f714ca7a-fc08-46ff-b0cc-373d6f04cf4c:/home/app/.microsoft/usersecrets/f714ca7a-fc08-46ff-b0cc-373d6f04cf4c:ro - ${APPDATA}/mkcert/rootCA.pem:/usr/local/share/ca-certificates/mkcert-rootCA.crt:ro + - ${APPDATA}/DataProtection-Keys:/home/app/.aspnet/DataProtection-Keys:rw networks: - dfe-flexforms-network \ No newline at end of file diff --git a/src/GovUK.Dfe.FlexForms.Api/Security/DataProtectionSettings.cs b/src/GovUK.Dfe.FlexForms.Api/Security/DataProtectionSettings.cs index 89d54d25..dc27e22b 100644 --- a/src/GovUK.Dfe.FlexForms.Api/Security/DataProtectionSettings.cs +++ b/src/GovUK.Dfe.FlexForms.Api/Security/DataProtectionSettings.cs @@ -42,4 +42,14 @@ public sealed class DataProtectionSettings /// Always accessed with managed identity / DefaultAzureCredential. /// public string? KeyVaultKeyId { get; set; } + + /// + /// Directory for the local file-system key ring (for example + /// /home/app/.aspnet/DataProtection-Keys in the API container). + /// Bind-mount the host key directory to this path (read-only is fine). + /// When the path is not writable, XML keys are copied to a temp directory + /// and automatic key generation is disabled so the container only decrypts. + /// Leave empty to use the ASP.NET default key location. + /// + public string LocalKeysPath { get; set; } = "/home/app/.aspnet/DataProtection-Keys"; } diff --git a/src/GovUK.Dfe.FlexForms.Api/Security/TenantSettingsDataProtectionExtensions.cs b/src/GovUK.Dfe.FlexForms.Api/Security/TenantSettingsDataProtectionExtensions.cs index 029d71bc..6f937b55 100644 --- a/src/GovUK.Dfe.FlexForms.Api/Security/TenantSettingsDataProtectionExtensions.cs +++ b/src/GovUK.Dfe.FlexForms.Api/Security/TenantSettingsDataProtectionExtensions.cs @@ -27,10 +27,18 @@ public static IDataProtectionBuilder AddTenantSettingsDataProtection( // Local/Development without Azure opt-in: default key ring only (no SetApplicationName) // so existing locally encrypted TenantSettings remain decryptable. - var builder = services.AddDataProtection(); - + IDataProtectionBuilder builder; if (ShouldUseLocalKeyRing(environment, settings)) + { + Console.WriteLine("Using local key ring for Data Protection (no Azure)."); + Console.WriteLine(settings.LocalKeysPath); + builder = services.AddDataProtection().PersistKeysToFileSystem(new DirectoryInfo(settings.LocalKeysPath)).SetApplicationName(settings.ApplicationName); return builder; + } + else + { + builder = services.AddDataProtection(); + } var applicationName = string.IsNullOrWhiteSpace(settings.ApplicationName) ? "GovUK.Dfe.FlexForms.Api" diff --git a/src/GovUK.Dfe.FlexForms.Api/appsettings.CodeGeneration.json b/src/GovUK.Dfe.FlexForms.Api/appsettings.CodeGeneration.json index 24b93a41..3c131f7c 100644 --- a/src/GovUK.Dfe.FlexForms.Api/appsettings.CodeGeneration.json +++ b/src/GovUK.Dfe.FlexForms.Api/appsettings.CodeGeneration.json @@ -7,7 +7,8 @@ "UseStorageSas": false, "ApplicationName": "GovUK.Dfe.FlexForms.Api", "BlobUri": "", - "KeyVaultKeyId": "" + "KeyVaultKeyId": "", + "LocalKeysPath": "/home/app/.aspnet/DataProtection-Keys" }, "Tenants": { "CodeGeneration": { diff --git a/src/GovUK.Dfe.FlexForms.Api/appsettings.json b/src/GovUK.Dfe.FlexForms.Api/appsettings.json index 8b25ba0b..4c2b03b3 100644 --- a/src/GovUK.Dfe.FlexForms.Api/appsettings.json +++ b/src/GovUK.Dfe.FlexForms.Api/appsettings.json @@ -59,7 +59,8 @@ "UseStorageSas": false, "ApplicationName": "GovUK.Dfe.FlexForms.Api", "BlobUri": "", - "KeyVaultKeyId": "" + "KeyVaultKeyId": "", + "LocalKeysPath": "/home/app/.aspnet/DataProtection-Keys" }, "GlobalConfiguration": { "ApplicationInsights": { From c6592d9b01bf467d358aa7fa2e6f76b43976897e Mon Sep 17 00:00:00 2001 From: LAKIN Date: Fri, 11 Sep 2026 10:50:38 +0100 Subject: [PATCH 3/8] Updates to the docker-compose file --- docker-compose.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 71ebbde4..7cdf4d21 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -5,6 +5,9 @@ networks: name: dfe-flexforms-network driver: bridge external: true + dfe-shared-network: + name: dfe-shared-network + external: true services: ea_api: @@ -33,11 +36,12 @@ services: - ASPNETCORE_URLS=https://+:8081;http://+:8080 - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_Kestrel__Certificates__Default__Password=Pa55w0rd - - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/ea_cert.pfx + - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/docker_dev_cert.pfx volumes: - - ${APPDATA}/dev-certs/ea_cert.pfx:/https/ea_cert.pfx:ro + - ${APPDATA}/dev-certs/docker_dev_cert.pfx:/https/docker_dev_cert.pfx:ro - ${APPDATA}/Microsoft/UserSecrets/f714ca7a-fc08-46ff-b0cc-373d6f04cf4c:/home/app/.microsoft/usersecrets/f714ca7a-fc08-46ff-b0cc-373d6f04cf4c:ro - ${APPDATA}/mkcert/rootCA.pem:/usr/local/share/ca-certificates/mkcert-rootCA.crt:ro - ${APPDATA}/DataProtection-Keys:/home/app/.aspnet/DataProtection-Keys:rw networks: - - dfe-flexforms-network \ No newline at end of file + - dfe-flexforms-network + - dfe-shared-network \ No newline at end of file From 25e479bd022628b9436e52486068ed83741c1946 Mon Sep 17 00:00:00 2001 From: LAKIN Date: Fri, 11 Sep 2026 11:10:54 +0100 Subject: [PATCH 4/8] Added line that was removed in error --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6eb47faa..5e5a611a 100644 --- a/.gitignore +++ b/.gitignore @@ -184,5 +184,6 @@ backend.vars uploads/ Directory.Build.targets.user +/src/LocalPackages .cursor /encryption-keys \ No newline at end of file From 40fa5ba75823ac86d965ea6f24f96710c9b9d4c4 Mon Sep 17 00:00:00 2001 From: LAKIN Date: Fri, 11 Sep 2026 11:42:16 +0100 Subject: [PATCH 5/8] Updated the dockercompose for the dataprotection keys --- docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 7cdf4d21..2dbbdb28 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -41,7 +41,7 @@ services: - ${APPDATA}/dev-certs/docker_dev_cert.pfx:/https/docker_dev_cert.pfx:ro - ${APPDATA}/Microsoft/UserSecrets/f714ca7a-fc08-46ff-b0cc-373d6f04cf4c:/home/app/.microsoft/usersecrets/f714ca7a-fc08-46ff-b0cc-373d6f04cf4c:ro - ${APPDATA}/mkcert/rootCA.pem:/usr/local/share/ca-certificates/mkcert-rootCA.crt:ro - - ${APPDATA}/DataProtection-Keys:/home/app/.aspnet/DataProtection-Keys:rw + - ${APPDATA}/DataProtection-Keys:/home/app/.aspnet/DataProtection-Keys:ro networks: - dfe-flexforms-network - dfe-shared-network \ No newline at end of file From 6424749f1737b080042fde9d1f798be6ea8eb9a3 Mon Sep 17 00:00:00 2001 From: LAKIN Date: Fri, 11 Sep 2026 14:37:52 +0100 Subject: [PATCH 6/8] Added co pilot suggestion for CI changes --- .github/workflows/test-solution.yml | 1 + docker-compose.yaml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-solution.yml b/.github/workflows/test-solution.yml index 62ab6f42..dd93f1fc 100644 --- a/.github/workflows/test-solution.yml +++ b/.github/workflows/test-solution.yml @@ -76,6 +76,7 @@ jobs: - name: Run Tests env: CI: true + ASPNETCORE_DATAPROTECTION_PROVIDER: NONE run: dotnet test GovUK.Dfe.FlexForms.Api.sln --no-build --verbosity normal --collect:"XPlat Code Coverage" - name: Generate Code Coverage Report diff --git a/docker-compose.yaml b/docker-compose.yaml index 2dbbdb28..7cdf4d21 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -41,7 +41,7 @@ services: - ${APPDATA}/dev-certs/docker_dev_cert.pfx:/https/docker_dev_cert.pfx:ro - ${APPDATA}/Microsoft/UserSecrets/f714ca7a-fc08-46ff-b0cc-373d6f04cf4c:/home/app/.microsoft/usersecrets/f714ca7a-fc08-46ff-b0cc-373d6f04cf4c:ro - ${APPDATA}/mkcert/rootCA.pem:/usr/local/share/ca-certificates/mkcert-rootCA.crt:ro - - ${APPDATA}/DataProtection-Keys:/home/app/.aspnet/DataProtection-Keys:ro + - ${APPDATA}/DataProtection-Keys:/home/app/.aspnet/DataProtection-Keys:rw networks: - dfe-flexforms-network - dfe-shared-network \ No newline at end of file From 2673b7c43c91da965c0aae17424c2476e0b3fca9 Mon Sep 17 00:00:00 2001 From: LAKIN Date: Fri, 11 Sep 2026 14:47:40 +0100 Subject: [PATCH 7/8] Added new CI updates to allow access to the key ring folder --- .github/workflows/test-solution.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/test-solution.yml b/.github/workflows/test-solution.yml index dd93f1fc..36ac01e0 100644 --- a/.github/workflows/test-solution.yml +++ b/.github/workflows/test-solution.yml @@ -73,6 +73,11 @@ jobs: dotnet-sonarscanner begin /k:"DFE-Digital_flexforms-api" /o:"dfe-digital" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.coverageReportPaths=CoverageReport/SonarQube.xml dotnet build GovUK.Dfe.FlexForms.Api.sln --no-restore -p:CI=${CI} + - name: Prepare Test Environment + run: | + mkdir -p /home/app/.aspnet/DataProtection-Keys + chmod 777 /home/app/.aspnet/DataProtection-Keys + - name: Run Tests env: CI: true From e7309bae995aa31f5cef270654054fff7110c444 Mon Sep 17 00:00:00 2001 From: LAKIN Date: Fri, 11 Sep 2026 14:52:55 +0100 Subject: [PATCH 8/8] Co pilot updates --- .github/workflows/test-solution.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test-solution.yml b/.github/workflows/test-solution.yml index 36ac01e0..7f03ed16 100644 --- a/.github/workflows/test-solution.yml +++ b/.github/workflows/test-solution.yml @@ -73,15 +73,11 @@ jobs: dotnet-sonarscanner begin /k:"DFE-Digital_flexforms-api" /o:"dfe-digital" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.coverageReportPaths=CoverageReport/SonarQube.xml dotnet build GovUK.Dfe.FlexForms.Api.sln --no-restore -p:CI=${CI} - - name: Prepare Test Environment - run: | - mkdir -p /home/app/.aspnet/DataProtection-Keys - chmod 777 /home/app/.aspnet/DataProtection-Keys - - - name: Run Tests + - name: Run Tests env: CI: true ASPNETCORE_DATAPROTECTION_PROVIDER: NONE + DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: 1 run: dotnet test GovUK.Dfe.FlexForms.Api.sln --no-build --verbosity normal --collect:"XPlat Code Coverage" - name: Generate Code Coverage Report