From 5f65757d1a760e746c8376cc142a362a6fd9b497 Mon Sep 17 00:00:00 2001 From: Paolo Salvatori Date: Fri, 18 Sep 2026 12:31:13 +0200 Subject: [PATCH 1/2] Connect to SQL with the driver's certificate validation instead of disabling 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) --- .../web-app-sql-database/python/src/database.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/samples/web-app-sql-database/python/src/database.py b/samples/web-app-sql-database/python/src/database.py index a5259ea..c28be10 100644 --- a/samples/web-app-sql-database/python/src/database.py +++ b/samples/web-app-sql-database/python/src/database.py @@ -132,15 +132,17 @@ def _build_connection_string(self) -> str: f"Server=tcp:{self.server},1433;" f"Database={self.database};" f"Encrypt=yes;" - f"TrustServerCertificate=yes;" f"Connection Timeout={self.connection_timeout};" ) - # TrustServerCertificate=yes tells the ODBC driver to accept self-signed certificates without verification - # This is appropriate for: - # - Local development with Docker containers - # - Testing environments with self-signed certificates - # - Internal networks where you control the SQL Server + # TrustServerCertificate is deliberately absent: its default is already "no" in ODBC Driver 18, + # so the driver validates the server certificate, which is what Microsoft recommends + # (https://learn.microsoft.com/sql/connect/odbc/major-version-differences#encryption-changes). + # That works against Azure SQL Database and against LocalStack for Azure alike, because the + # emulator serves a publicly trusted certificate for the host name it returns in + # fullyQualifiedDomainName. Add TrustServerCertificate=yes only if LocalStack could not + # download that certificate (for example with SKIP_SSL_CERT_DOWNLOAD=1), in which case the + # server presents a certificate issued by the LocalStack root certificate authority. if not self.use_azure_credential: # Traditional SQL authentication From 2d68ead5489fd1838be03cb6ed158052b8c1105d Mon Sep 17 00:00:00 2001 From: Paolo Salvatori Date: Fri, 18 Sep 2026 14:15:59 +0200 Subject: [PATCH 2/2] Prefer installing the LocalStack root CA over turning validation off 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) --- samples/web-app-sql-database/python/src/database.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/samples/web-app-sql-database/python/src/database.py b/samples/web-app-sql-database/python/src/database.py index c28be10..6d0b4fc 100644 --- a/samples/web-app-sql-database/python/src/database.py +++ b/samples/web-app-sql-database/python/src/database.py @@ -140,9 +140,12 @@ def _build_connection_string(self) -> str: # (https://learn.microsoft.com/sql/connect/odbc/major-version-differences#encryption-changes). # That works against Azure SQL Database and against LocalStack for Azure alike, because the # emulator serves a publicly trusted certificate for the host name it returns in - # fullyQualifiedDomainName. Add TrustServerCertificate=yes only if LocalStack could not - # download that certificate (for example with SKIP_SSL_CERT_DOWNLOAD=1), in which case the - # server presents a certificate issued by the LocalStack root certificate authority. + # fullyQualifiedDomainName. If LocalStack could not download that certificate (for example + # with SKIP_SSL_CERT_DOWNLOAD=1) it serves one issued by the LocalStack root certificate + # authority instead: install that authority in the client's trust store, which keeps both + # encryption and validation. TrustServerCertificate=yes is a last resort, because it stops + # the driver validating the certificate at all and exposes the connection to an + # adversary-in-the-middle. if not self.use_azure_credential: # Traditional SQL authentication