This is a comprehensive, phased implementation plan to refactor the Deployment Automation system. It addresses the critical security vulnerabilities from the previous review and integrates your new requirements for Configuration Management, mTLS, Key Vault / Windows Cert Store integration, and Azure Entra ID (JWT).
Implementation Plan: Secure Deployment Automation#
To ensure a smooth transition without breaking existing workflows, the implementation is broken down into four logical phases.
Phase 1: Core Security & Stability Fixes#
Address critical vulnerabilities related to input validation, resource exhaustion, and state integrity.
1.1 Path Traversal & Integrity Checks (Agent)#
- Update
DeploymentPayload.cs(Contracts): Add aPackageSha256property. The Orchestrator will calculate the file hash before uploading, and the Agent will verify it before extraction to ensure supply-chain integrity. - Update
FileExtractor.cs:- Validate
job.Config.Versionusing Regex (^[0-9a-zA-Z\.\-_]+$). - Implement
Path.GetFullPathboundary validation to guarantee extraction stays within the configuredBasePath. - Compute the SHA-256 hash of the uploaded
TempPackagePathand compare it againstPackageSha256. Throw aSecurityExceptionif they mismatch.
- Validate
1.2 Bounded Queue & Graceful Degradation (Agent)#
- Update
Program.cs: ReplaceChannel.CreateUnboundedwithChannel.CreateBounded<DeploymentJob>. Set capacity to a sensible limit (e.g.,10) and useBoundedChannelFullMode.Wait. - Update
DeployEndpoints.cs: Wrap thechannel.Writer.WriteAsyncin a cancellation token aware block. If the queue is full and times out, return503 Service Unavailable. - Temp File Cleanup: In
DeployEndpoints.cs, wrap the file upload stream in atry/catch. If anything fails before the job is queued, immediately delete the temporary file to prevent disk exhaustion.
1.3 Cancellation Token Propagation#
- Update
IFileExtractor,IIisConfigurator,IConfigFileUpdater, andIWindowsServiceManagerto accept aCancellationToken. - Pass the
stoppingTokenfromDeploymentWorkerServicedown the chain so that if the Agent service is stopped, local operations cancel gracefully rather than leaving orphaned processes.
Phase 2: Configuration Refactoring#
Eliminate hardcoded secrets and environment strings.
2.1 Refactor Orchestrator Connection Strings#
- Update
OrchestratorOptions.cs: Remove the hardcodedGetConnectionStringswitch statement. - Update
appsettings.json: Leverage standard nested connection strings mapped by environment:"ConnectionStrings": { "CommandCenter_Acceptatie": "Data Source=vmbenefitsdbap1;Initial Catalog=CommandCenter;Integrated Security=True;Encrypt=True;TrustServerCertificate=False", "CommandCenter_Productie": "Data Source=vmbenefitsdbp1;Initial Catalog=CommandCenter;Integrated Security=True;Encrypt=True;TrustServerCertificate=False" } - Update
SqlFinalizationService&SqlPreCheckService: InjectIConfigurationand dynamically resolve the connection string usingconfiguration.GetConnectionString($"CommandCenter_{environment}").
Phase 3: HTTPS, mTLS & Certificate Management#
Replace X-Api-Key with Mutual TLS. Support loading certificates from Windows Certificate Store and Azure Key Vault.
3.1 Agent Certificate Loading (Kestrel Server Cert)#
- Create a
CertificateLoaderServicein the Agent that reads fromappsettings.json: - If
Store: Load usingX509Store. - If
KeyVault: UseSecretClient(Azure.Security.KeyVault.Secrets) to download the PFX secret and instantiate anX509Certificate2. - Configure Kestrel in
Program.csto use this certificate for HTTPS binding.
3.2 Kestrel mTLS Configuration (Agent)#
- Install
Microsoft.AspNetCore.Authentication.Certificate. - In
Program.cs, configure Kestrel to allow client certificates (do not require them at the Kestrel level if JWT is also going to be supported): - Register Certificate Authentication:
builder.Services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme) .AddCertificate(options => { options.AllowedCertificateTypes = CertificateTypes.All; options.Events = new CertificateAuthenticationEvents { OnCertificateValidated = context => { // Validate Thumbprint or Subject against allowed list in config return Task.CompletedTask; } }; });
3.3 Orchestrator Client Certificate Management#
- Update the Orchestrator to load a Client Certificate. Create an
OrchestratorCertificateLoadersupportingFile(for Linux CI/CD runners),Store(for local Windows execution), andKeyVault. - Update
Program.cswhere theHttpClientis registered:services.AddHttpClient<IAgentClient, AgentClient>((sp, client) => { var opts = sp.GetRequiredService<IOptions<OrchestratorOptions>>().Value; client.BaseAddress = new Uri(opts.AgentBaseUrl); // Must be HTTPS now }) .ConfigurePrimaryHttpMessageHandler(sp => { var certLoader = sp.GetRequiredService<ICertificateLoader>(); var handler = new HttpClientHandler(); var clientCert = certLoader.GetClientCertificate(); if (clientCert != null) { handler.ClientCertificates.Add(clientCert); } return handler; });
Phase 4: Azure Entra ID (JWT) Integration#
Provide optional fallback/alternative authentication via Entra ID (OIDC/OAuth2).
4.1 Agent Multi-Scheme Authentication#
- Install
Microsoft.Identity.Web. - Update
Program.csin the Agent to support both schemes: - Crucial Step - Authorization Policy: Create a default authorization policy that accepts either scheme. If a request comes in with a valid Client Certificate, it passes. If it comes in with a valid
Authorization: Bearer <token>, it passes.
4.2 Orchestrator Entra ID Support#
- Install
Azure.Identity. - Update the Orchestrator's
AgentClient.cs(or via aDelegatingHandler) to attach a JWT if configured. - If the configuration specifies
AuthMode: EntraId, useDefaultAzureCredentialto acquire a token scoped to the Agent's App Registration Client ID, and attach it to theHttpClient:// Inside a DelegatingHandler or AgentClient var credential = new DefaultAzureCredential(); var token = await credential.GetTokenAsync( new TokenRequestContext(new[] { "api://<Agent-App-Client-Id>/.default" }), cancellationToken); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.Token);
Summary of Configuration Changes Required#
You will need to update the appsettings.json schemas for both projects.
Agent appsettings.json Additions:
{
"ServerCertificate": {
"Source": "Store",
"StoreLocation": "LocalMachine",
"StoreName": "My",
"Thumbprint": "YOUR_SERVER_CERT_THUMBPRINT"
},
"ClientAuthentication": {
"AllowedThumbprints": ["ALLOWED_CLIENT_CERT_THUMBPRINT"]
},
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "yourtenant.onmicrosoft.com",
"TenantId": "YOUR_TENANT_ID",
"ClientId": "AGENT_APP_REGISTRATION_CLIENT_ID"
}
}
Orchestrator appsettings.json Additions:
{
"ConnectionStrings": {
"CommandCenter_Acceptatie": "...",
"CommandCenter_Productie": "..."
},
"Orchestrator": {
"AgentBaseUrl": "https://vmbenefitsbmp1:5100",
"AuthMode": "mTLS", // or "EntraId"
"ClientCertificate": {
"Source": "KeyVault",
"KeyVaultUrl": "https://kv-orchestrator.vault.azure.net/",
"CertificateName": "OrchestratorClientCert"
},
"EntraId": {
"TargetScope": "api://<Agent-App-Client-Id>/.default"
}
}
}
Recommended Execution Order#
- Phase 1 & 2 can be done immediately. They remove critical vulnerabilities and fix technical debt (hardcoded strings).
- Phase 3 (mTLS) should be implemented and tested locally using Self-Signed certificates placed in the Windows Store before wiring up Azure Key Vault.
- Phase 4 (Entra ID) can be added as a final enhancement, giving you maximum flexibility for environments (like pure cloud CI/CD runners) where managing a Client Certificate file is less desirable than using Azure Managed Identities (which
DefaultAzureCredentialsupports out of the box).