Connect to Azure SQL with certificate validation instead of disabling it - #32
Conversation
…sabling it The connection string no longer carries TrustServerCertificate. Its default is already "no" in ODBC Driver 18, so the driver validates the server certificate, which is what Microsoft recommends: "Install a certificate on the server that the client trusts. This option is the recommended one, and it's the only one that keeps both encryption and validation" (https://learn.microsoft.com/sql/connect/odbc/major-version-differences#encryption-changes). Keeping the keyword at its default value only obscured that the sample was opting out of validation. This works against LocalStack for Azure because the emulator now serves a publicly trusted certificate for the host name it returns in fullyQualifiedDomainName, so the pod needs no local exception to reach the database. Verified on the emulator with the sample otherwise unchanged: the cluster script and all four deployment scripts exit 0, the three pods run, and a port-forward to the vacation-planner-sql service returns the app's page with 9 cells rendered from the database. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
The updated comment still frames TrustServerCertificate=yes as the primary mitigation for LocalStack CA scenarios instead of explicitly recommending CA installation first and treating disabling validation as a last resort.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This PR hardens the Azure SQL connection behavior in the Python web-app sample by removing TrustServerCertificate=yes from the constructed ODBC connection string, ensuring certificate validation remains enabled while keeping encryption explicit.
Changes:
- Removed
TrustServerCertificate=yesfrom the ODBC connection string builder. - Updated the in-code comment to explain why the keyword is absent and when it might be needed for LocalStack.
File summaries
| File | Description |
|---|---|
| samples/web-app-sql-database/python/src/database.py | Removes TrustServerCertificate=yes from the built connection string and updates explanatory comments about certificate validation behavior. |
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Raised by the review on aks-samples#32: the comment presented TrustServerCertificate=yes as the answer when LocalStack serves a certificate from its own root authority, which contradicts the reasoning for the change. Installing that authority in the client's trust store keeps both encryption and validation; TrustServerCertificate=yes is the last resort, and the comment now says why. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
That summary was produced against the first version of the branch; the point it makes was raised as an inline comment and is already fixed in 2d68ead, which is the head of this PR. The comment on So CA installation is the recommendation and disabling validation is the fallback, which is the order Microsoft's guidance uses. The inline thread is resolved, and the same wording is applied to the App Service counterpart in localstack/localstack-azure-samples#122. |
Motivation
The sample connected to Azure SQL with
TrustServerCertificate=yes, which keeps the connection encrypted but turns certificate validation off. That was a workaround for LocalStack for Azure, where the SQL endpoint used to present a self-signed certificate that no CA chain could validate, and the sample's own comment said so: "tells the ODBC driver to accept self-signed certificates without verification".Two things make it unnecessary now. LocalStack for Azure serves a publicly trusted certificate for the host name it returns in
fullyQualifiedDomainName, so a pod with default driver settings validates it, and Microsoft's guidance is explicit that disabling validation is the option of last resort: "Install a certificate on the server that the client trusts. This option is the recommended one, and it's the only one that keeps both encryption and validation" (ODBC driver version differences).Leaving the keyword in place also taught the wrong habit: a reader copying this sample into an application would carry an adversary-in-the-middle exposure into production, for no benefit.
Changes
TrustServerCertificateis removed from the connection string the application builds, rather than set tono. Its default is alreadynoin ODBC Driver 18, so writing the default value only adds noise; the keyword belongs in a connection string when you deviate from the default.Encrypt=yesstays explicit, because that default does differ between driver 17 and 18.The code comment now says what the absence means, and when you would still need the keyword: if LocalStack cannot download its public certificate (for example with
SKIP_SSL_CERT_DOWNLOAD=1), the server presents a certificate issued by the LocalStack root certificate authority, and a client then needs that authority installed orTrustServerCertificate=yes.Fixes SMF-897
Tests
The sample is otherwise unchanged.
LocalStack for Azure (emulator started with
RUNTIME_COMPONENTS=azure, cluster created byscripts/01-user-assigned-managed-identity.shwith one system and one user node):vacation-planner-sqlpods Running.vacation-planner-sqlservice returns HTTP 200,<title>Vacation Planner</title>, and 9 table cells rendered from the SQL database.Real Azure (a real AKS cluster in
italynorth, invented prefix, resource group deleted afterwards):Succeededand both nodesReady.01-deploy-resources.sh,02-build-docker-image.sh,04-push-docker-image.shand05-deploy-app.shall exit 0.vacation-planner-sqlpods reach the Ready condition, and a port-forward to the service returns HTTP 200,<title>Vacation Planner</title>, and 27 table cells rendered from the Azure SQL database.One note on how these runs authenticated, which does not affect the sample. The cluster script creates the cluster with
--enable-aad --enable-azure-rbac --aad-admin-group-object-ids, so Kubernetes API calls are authorized through Microsoft Entra: a member of that admin group runskubectlwith a plainaz aks get-credentials. These runs authenticate as a service principal that belongs to no such group and holds no AKS RBAC role, and subscription Owner does not help, becauseOwnercarriesdataActions: []while in-cluster access lives in thedataActionsof roles such as Azure Kubernetes Service RBAC Cluster Admin. Thekubectlsteps therefore usedaz aks get-credentials --adminfor that identity only.The other thing worth knowing before a run: the cluster needs 4 vCPU of the
standardDDSv5Familyquota in the region, which a leftover cluster can exhaust.🤖 Generated with Claude Code