Skip to content

Fix SPN using instance name instead of resolved port for Protocol.None and Protocol.Admin#4180

Draft
paulmedynski wants to merge 3 commits intomainfrom
dev/automation/fix-spn-ssrp-protocol-none
Draft

Fix SPN using instance name instead of resolved port for Protocol.None and Protocol.Admin#4180
paulmedynski wants to merge 3 commits intomainfrom
dev/automation/fix-spn-ssrp-protocol-none

Conversation

@paulmedynski
Copy link
Copy Markdown
Contributor

@paulmedynski paulmedynski commented Apr 10, 2026

Fix

Fixes #3566.

Depends on #4252.

Problem

When connecting to a named instance without a tcp: prefix (for example, Data Source=server\instance), DataSource.ResolvedProtocol is Protocol.None. SPN generation only used resolved port for Protocol.TCP, so Protocol.None and Protocol.Admin could incorrectly use instance name in the SPN:

MSSQLSvc/server.fqdn:instancename

instead of using the SSRP-resolved port:

MSSQLSvc/server.fqdn:12345

Why this addresses the review concern

@cheenamalhotra correctly noted that port and instance name can both map to the same SQL Server instance. This change does not claim they are always semantically different targets.

The driver behavior here is protocol-specific for SPN construction:

  • Named Pipes (NP): keep instance-name postfix
  • TCP-like paths (TCP, None, Admin): use port postfix (resolved via SSRP for named instances)

This gives deterministic SPN formatting for integrated auth on Unix/Linux SSRP flows while preserving NP behavior and explicit ServerSPN overrides.

Code changes

  • SniProxy.netcore.cs
    • SPN postfix selection now uses instance name only for Protocol.NP
    • Protocol.TCP, Protocol.None, and Protocol.Admin use ResolvedPort
    • Added resolved protocol/port in trace logging to improve diagnostics
  • SniProxyGetSqlServerSPNsTest.cs
    • Recreated and finalized regression tests for:
      • Protocol.None uses resolved port
      • Protocol.TCP uses resolved port
      • Protocol.Admin uses resolved port
      • Protocol.NP uses instance name
      • Custom ServerSPN passthrough

Testing performed

  • Targeted SPN unit tests:
    • dotnet test src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft.Data.SqlClient.UnitTests.csproj --filter FullyQualifiedName~SniProxyGetSqlServerSPNsTest
    • Result: Passed on net8.0, net9.0, net10.0 (15/15)
  • Kerberos-adjacent SSPI unit coverage:
    • dotnet test src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft.Data.SqlClient.UnitTests.csproj --filter FullyQualifiedName~SspiTests
    • Result: Passed on net8.0, net9.0, net10.0 (15/15)

Kerberos environment validation status

I will run our existing suite of Kerberos tests and provide results here.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes SPN generation for named-instance connections when the connection string omits a protocol prefix (Protocol.None) or uses DAC (Protocol.Admin), ensuring TCP-like protocols use the SSRP-resolved port rather than the instance name in the SPN.

Changes:

  • Updated SniProxy.GetSqlServerSPNs(DataSource, string) to use the instance name only for Named Pipes (NP); all other protocols use the SSRP-resolved port.
  • Changed GetSqlServerSPNs(DataSource, string) visibility from private to internal to enable direct unit testing.
  • Added unit tests covering Protocol.None/TCP/Admin and custom SPN passthrough behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ManagedSni/SniProxy.netcore.cs Adjusts SPN postfix selection logic so TCP-like protocols use SSRP-resolved port; exposes method for unit tests.
src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/ManagedSni/SniProxyGetSqlServerSPNsTest.cs Adds regression tests for SPN formatting across protocols and custom SPN passthrough.
Comments suppressed due to low confidence (1)

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ManagedSni/SniProxy.netcore.cs:144

  • The trace event logs dataSource.Port but the postfix/SPN decision for named instances now depends on dataSource.ResolvedPort. Consider logging ResolvedPort as well (or instead) so diagnostics reflect the value actually used to build the SPN.
                postfix = dataSource.ResolvedProtocol == DataSource.Protocol.NP ? dataSource.InstanceName : dataSource.ResolvedPort.ToString();
            }

            SqlClientEventSource.Log.TryTraceEvent("SNIProxy.GetSqlServerSPN | Info | ServerName {0}, InstanceName {1}, Port {2}, postfix {3}", dataSource?.ServerName, dataSource?.InstanceName, dataSource?.Port, postfix);
            return GetSqlServerSPNs(hostName, postfix, dataSource.ResolvedProtocol);

@paulmedynski paulmedynski force-pushed the dev/automation/fix-spn-ssrp-protocol-none branch from da6afed to f027453 Compare April 10, 2026 19:27
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 10, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 64.36%. Comparing base (f5942fd) to head (c2710f0).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4180      +/-   ##
==========================================
- Coverage   65.99%   64.36%   -1.63%     
==========================================
  Files         277      272       -5     
  Lines       42988    65785   +22797     
==========================================
+ Hits        28370    42345   +13975     
- Misses      14618    23440    +8822     
Flag Coverage Δ
CI-SqlClient ?
PR-SqlClient-Project 64.36% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

{
postfix = dataSource.ResolvedProtocol == DataSource.Protocol.TCP ? dataSource.ResolvedPort.ToString() : dataSource.InstanceName;
// Named Pipes use the instance name in the SPN (MSSQLSvc/host:instance).
// All other protocols (TCP, None, Admin) use the port resolved by SSRP
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessarily, both Port and Instance Name can be used interchangeably. Refer to Microsoft Docs They both point to the same SQL Server instance.

Protocol.None points the driver to use Shared Memory protocol when establishing connection - which is supported on Windows, but not on Unix.

// (MSSQLSvc/host:port). Protocol.None is the default when no prefix is
// specified in the data source (e.g. "server\instance"), and it is treated
// as TCP for connection purposes. See GitHub issue #3566.
postfix = dataSource.ResolvedProtocol == DataSource.Protocol.NP ? dataSource.InstanceName : dataSource.ResolvedPort.ToString();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has this been tested in Kerberos test suite to verify it doesn't cause any regression in any environment?

Copy link
Copy Markdown
Member

@cheenamalhotra cheenamalhotra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plz confirm if testing has been performed on Kerberos environment.

@paulmedynski paulmedynski force-pushed the dev/automation/fix-spn-ssrp-protocol-none branch from f027453 to f5942fd Compare April 29, 2026 16:05
Copilot AI review requested due to automatic review settings April 29, 2026 16:05
@github-project-automation github-project-automation Bot moved this from In progress to Done in SqlClient Board Apr 29, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review any files in this pull request.

@paulmedynski paulmedynski reopened this Apr 29, 2026
@github-project-automation github-project-automation Bot moved this from Done to Backlog in SqlClient Board Apr 29, 2026
Copilot AI review requested due to automatic review settings April 29, 2026 18:05
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.

Comment thread src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/KerberosTests/KerberosTest.cs Outdated
Comment thread src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/KerberosTests/KerberosTest.cs Outdated
Comment thread src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/KerberosTests/KerberosTest.cs Outdated
Comment thread src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/KerberosTests/KerberosTest.cs Outdated
Comment thread src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/KerberosTests/KerberosTest.cs Outdated
@paulmedynski paulmedynski moved this from Backlog to In progress in SqlClient Board Apr 30, 2026
…ecific SPN handling

- Add XML class and method documentation to SniProxyGetSqlServerSPNsTest explaining:
  * Purpose: regression tests for SPN protocol-specific behavior (issue #3566)
  * Protocol semantics: TCP-like protocols use port, Named Pipes uses instance name
  * Reference to official Microsoft Learn SPN format documentation
  * Specific test intentions and assertions

- Add inline comments in unit tests clarifying:
  * DataSource parsing and Protocol.None/TCP/NP/Admin differentiation
  * SSRP port simulation behavior
  * Why each assertion validates the correct SPN format per protocol

- Add 5 integration tests to KerberosTest.cs for end-to-end Kerberos validation:
  * ProtocolNone_NamedInstanceWithSsrpResolution - Tests issue #3566 (SSRP-resolved port in SPN)
  * ProtocolTcp_NamedInstanceWithExplicitPort - Tests TCP protocol with named instances
  * CustomServerSPN_BypassesAutoGeneration - Tests explicit SPN overrides for custom environments
  * ProtocolAdmin_DedicatedAdminConnection - Tests DAC protocol SPN behavior
  * Plus enhanced documentation explaining environment setup requirements

Tests verify that Kerberos authentication succeeds by checking auth_scheme='KERBEROS'
in sys.dm_exec_connections, confirming that protocol-specific SPN generation enables
successful authentication across all protocol types.

Addresses Cheena's review concerns about protocol semantics and Kerberos regression testing.
…robustness

SniProxy.netcore.cs:
- Guard against ResolvedPort <= 0 (e.g. -1 before SSRP resolves) to avoid
  producing malformed SPNs like ':-1' in error paths. Fall back to instance
  name when port is not yet available.

SniProxyGetSqlServerSPNsTest.cs:
- Replace generic 'server' hostnames with 'localhost' in all ParseServerName
  calls and the NP direct-call test. This avoids real DNS lookups that could
  cause flakiness in restricted-DNS environments.

KerberosTest.cs:
- Protocol.None test: build connection string from SqlConnectionStringBuilder(tcpConnStr)
  instead of from scratch to preserve Encrypt, TrustServerCertificate, etc.
- TCP test: skip if no named instance; omit port fallback to 1433 (which
  disables SSRP and can produce invalid 'host\,port' strings).
- Custom SPN test: require explicit port and use TCP-format SPN (host:port)
  rather than instance name, which is wrong for port-based SPN registrations.
- Admin test: build from tcpConnStr base; require named instance; remove fragile
  catch-by-message-substring that could hide failures. DAC unavailability now
  correctly surfaces as a test failure instead of silent pass.
Copilot AI review requested due to automatic review settings May 7, 2026 14:15
@paulmedynski paulmedynski force-pushed the dev/automation/fix-spn-ssrp-protocol-none branch from c2710f0 to d485228 Compare May 7, 2026 14:15
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment on lines +213 to +217
// settings. Do NOT fall back to port 1433 — the DAC port is separate from the regular
// SQL Server port and must be discovered via SSRP if not explicitly known.
string newDataSource = port > 0
? $"admin:{hostname}\\{instanceName},{port}"
: $"admin:{hostname}\\{instanceName}";
Comment on lines +96 to +100
/// Environment: Requires a named instance with an explicitly specified or SSRP-resolvable port.
/// </summary>
[PlatformSpecific(TestPlatforms.Linux)]
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsKerberosTest))]
public void KerberosTest_ProtocolTcp_NamedInstanceWithExplicitPort()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

Service principal name on linux uses instance name instead of port when using SSRP

3 participants