Skip to content

sm: implement post-CER/CEA TLS upgrade (RFC 6733 §6.2)#243

Merged
fiorix merged 3 commits into
fiorix:mainfrom
tridentsx:fix/238-post-cer-tls-upgrade
May 2, 2026
Merged

sm: implement post-CER/CEA TLS upgrade (RFC 6733 §6.2)#243
fiorix merged 3 commits into
fiorix:mainfrom
tridentsx:fix/238-post-cer-tls-upgrade

Conversation

@tridentsx

Copy link
Copy Markdown
Contributor

Add support for upgrading a plain TCP connection to TLS after the CER/CEA exchange, as specified in RFC 6733 §6.2.

Changes

  • Add TLSUpgrader interface to diam package
  • Add StartTLS(*tls.Config) error method to the server connection
  • Add TLSConfig *tls.Config field to sm.Settings
  • Update handleCER: when peer sends Inband-Security-Id=1 and server has TLSConfig, accept the CER, send success CEA, then upgrade to TLS
  • Add ParseWithSecurity() and RequestedSecurity() to CER parser
  • Add test verifying server accepts Inband-Security-Id=1 when TLSConfig is set

RFC Reference

RFC 6733 §6.2:

If TLS is to be used with a Diameter connection, the TLS handshake MUST begin immediately following the CER/CEA exchange for connections that are not pre-configured to use TLS.

Backwards Compatibility

Without TLSConfig set in Settings, existing behavior is preserved (Inband-Security-Id=1 is rejected with NO_COMMON_SECURITY on plain connections).

All existing tests pass. The SCTP close test failure is pre-existing on upstream main.

Fixes #238
Related: #236, #239

@fiorix

fiorix commented May 1, 2026

Copy link
Copy Markdown
Owner

Useful direction but a few things to resolve before merging:

  1. Server-only upgrade. StartTLS lands on *response but there's no client-side counterpart. Without one, no in-tree peer can complete the upgrade — which is why the new test acknowledges it can't actually drive the handshake. RFC 6733 §6.2 requires both sides to upgrade.

  2. Concurrency. StartTLS swaps w.conn.rwc, w.conn.sr.r, and w.conn.buf. The serve loop reads via w.conn.buf without holding w.mu. As long as the upgrade happens inside handleCER before the loop reads the next message it's probably safe in practice, but the invariant should be documented and asserted (e.g. only callable from a handler, before the next read).

  3. Failed upgrade after success CEA. On StartTLS error the conn is closed, but the peer already received a success CEA. Either send a failure CEA on upgrade error, or document that an immediate close after success CEA means upgrade failure. (Doing the upgrade before the CEA contradicts §6.2.)

  4. Silent downgrade for non-upgradeable conns. canTLS := tlsActive || sm.cfg.TLSConfig != nil accepts Inband-Security-Id=1 even when c.(diam.TLSUpgrader) fails the type assertion (e.g. SCTP, third-party transports). The upgrade is silently skipped and the conn stays plaintext after success CEA. Should reject the CER in that case.

Suggestion: split into (a) TLSUpgrader interface + server-side StartTLS as infra (this PR, scoped down), (b) client-side StartTLS, (c) handleCER orchestration with the safeguards above. Or hold this PR until (b) lands so we can ship them together with an end-to-end test.

#240, #241, #242 can land independently of this — they're useful on their own and don't block the design discussion here.

@fiorix

fiorix commented May 1, 2026

Copy link
Copy Markdown
Owner

Thanks — the new commit addresses three of the four concerns nicely:

  • Client side: StartTLSClient + Client.RequestInbandTLS + handleCEA upgrade closes the loop. Both peers can now complete the handshake.
  • Concurrency invariant: documented on the interface ("must be called from within a handler before the next read"). Good.
  • No more silent downgrade: canTLS now requires c.(diam.TLSUpgrader) to succeed, and the upgrade path also rejects defensively.

Two things left:

  1. Dictionary for Credit-Control Applications #3 from the original review still open: on StartTLS failure the conn is closed after a success CEA was sent — peer sees success-then-RST. Consider sending a failure CEA instead, or at minimum document that an immediate close after success CEA means upgrade failure.

  2. Rebase against main: this PR is currently mergeable=dirty (sm: accept Inband-Security-Id=1 when TLS is already established #242 landed and conflicts with the parser changes here). Once rebased, CI will rerun and we can validate end-to-end with the new client-side path.

Also worth adding now that both sides are wired up: an integration test that drives a full plain→CER/CEA→TLS handshake using the new StartTLSClient. That would have been impossible before this revision and is the strongest proof the feature works.

tridentsx added 3 commits May 2, 2026 02:10
Add TLSUpgrader interface and StartTLS method to support upgrading a
plain TCP connection to TLS after the CER/CEA exchange, as specified
in RFC 6733 §6.2.

Changes:
- Add TLSUpgrader interface to diam package
- Add StartTLS() method to the server connection (response type)
- Add TLSConfig field to sm.Settings for server-side TLS upgrade
- Update handleCER to accept Inband-Security-Id=1 when TLSConfig is
  set, and perform TLS upgrade after sending success CEA
- Add ParseWithSecurity() to CER parser (accepts tlsActive flag)
- Add RequestedSecurity() accessor to CER struct

When a peer sends CER with Inband-Security-Id=1 and the server has
TLSConfig configured, the server sends a success CEA then upgrades
the connection to TLS. Without TLSConfig, the existing behavior
(reject with NO_COMMON_SECURITY) is preserved.

Fixes fiorix#238
- Add client-side StartTLSClient method (tls.Client) to TLSUpgrader
- Add RequestInbandTLS field to Client; sends Inband-Security-Id=1
- Perform client-side TLS upgrade in handleCEA after success CEA
- Fix silent downgrade: type-assert TLSUpgrader before accepting
  Inband-Security-Id=1 in handleCER (rejects non-upgradeable conns)
- Document concurrency safety constraints on StartTLS methods
- Defensive close if upgrade fails after success CEA was sent
- Document that TLS handshake failure after success CEA results in
  connection close (both peers observe the failure directly per
  RFC 6733 §6.2; sending a second CEA would violate the protocol)
- Add TestInbandTLSUpgrade_EndToEnd: drives a full plain TCP →
  CER/CEA with Inband-Security-Id=1 → TLS upgrade flow, then
  verifies DWR/DWA exchange over the encrypted connection
@tridentsx tridentsx force-pushed the fix/238-post-cer-tls-upgrade branch from 85637f5 to 254c8d2 Compare May 1, 2026 19:18
@tridentsx

Copy link
Copy Markdown
Contributor Author

I tried to address those last things please check if taths ok !

@fiorix fiorix merged commit 64dabe9 into fiorix:main May 2, 2026
1 check passed
fiorix added a commit that referenced this pull request May 10, 2026
arberkatellari pushed a commit to arberkatellari/go-diameter that referenced this pull request May 12, 2026
* sm: implement post-CER/CEA TLS upgrade (RFC 6733 §6.2)

Add TLSUpgrader interface and StartTLS method to support upgrading a
plain TCP connection to TLS after the CER/CEA exchange, as specified
in RFC 6733 §6.2.

Changes:
- Add TLSUpgrader interface to diam package
- Add StartTLS() method to the server connection (response type)
- Add TLSConfig field to sm.Settings for server-side TLS upgrade
- Update handleCER to accept Inband-Security-Id=1 when TLSConfig is
  set, and perform TLS upgrade after sending success CEA
- Add ParseWithSecurity() to CER parser (accepts tlsActive flag)
- Add RequestedSecurity() accessor to CER struct

When a peer sends CER with Inband-Security-Id=1 and the server has
TLSConfig configured, the server sends a success CEA then upgrades
the connection to TLS. Without TLSConfig, the existing behavior
(reject with NO_COMMON_SECURITY) is preserved.

Fixes fiorix#238

* sm: address review comments on TLS upgrade PR

- Add client-side StartTLSClient method (tls.Client) to TLSUpgrader
- Add RequestInbandTLS field to Client; sends Inband-Security-Id=1
- Perform client-side TLS upgrade in handleCEA after success CEA
- Fix silent downgrade: type-assert TLSUpgrader before accepting
  Inband-Security-Id=1 in handleCER (rejects non-upgradeable conns)
- Document concurrency safety constraints on StartTLS methods
- Defensive close if upgrade fails after success CEA was sent

* sm: document failure semantics and add end-to-end TLS upgrade test

- Document that TLS handshake failure after success CEA results in
  connection close (both peers observe the failure directly per
  RFC 6733 §6.2; sending a second CEA would violate the protocol)
- Add TestInbandTLSUpgrade_EndToEnd: drives a full plain TCP →
  CER/CEA with Inband-Security-Id=1 → TLS upgrade flow, then
  verifies DWR/DWA exchange over the encrypted connection
arberkatellari pushed a commit to arberkatellari/go-diameter that referenced this pull request May 12, 2026
danbogos pushed a commit to cgrates/go-diameter that referenced this pull request May 12, 2026
* sm: implement post-CER/CEA TLS upgrade (RFC 6733 §6.2)

Add TLSUpgrader interface and StartTLS method to support upgrading a
plain TCP connection to TLS after the CER/CEA exchange, as specified
in RFC 6733 §6.2.

Changes:
- Add TLSUpgrader interface to diam package
- Add StartTLS() method to the server connection (response type)
- Add TLSConfig field to sm.Settings for server-side TLS upgrade
- Update handleCER to accept Inband-Security-Id=1 when TLSConfig is
  set, and perform TLS upgrade after sending success CEA
- Add ParseWithSecurity() to CER parser (accepts tlsActive flag)
- Add RequestedSecurity() accessor to CER struct

When a peer sends CER with Inband-Security-Id=1 and the server has
TLSConfig configured, the server sends a success CEA then upgrades
the connection to TLS. Without TLSConfig, the existing behavior
(reject with NO_COMMON_SECURITY) is preserved.

Fixes fiorix#238

* sm: address review comments on TLS upgrade PR

- Add client-side StartTLSClient method (tls.Client) to TLSUpgrader
- Add RequestInbandTLS field to Client; sends Inband-Security-Id=1
- Perform client-side TLS upgrade in handleCEA after success CEA
- Fix silent downgrade: type-assert TLSUpgrader before accepting
  Inband-Security-Id=1 in handleCER (rejects non-upgradeable conns)
- Document concurrency safety constraints on StartTLS methods
- Defensive close if upgrade fails after success CEA was sent

* sm: document failure semantics and add end-to-end TLS upgrade test

- Document that TLS handshake failure after success CEA results in
  connection close (both peers observe the failure directly per
  RFC 6733 §6.2; sending a second CEA would violate the protocol)
- Add TestInbandTLSUpgrade_EndToEnd: drives a full plain TCP →
  CER/CEA with Inband-Security-Id=1 → TLS upgrade flow, then
  verifies DWR/DWA exchange over the encrypted connection
danbogos pushed a commit to cgrates/go-diameter that referenced this pull request May 12, 2026
kulcsartibor pushed a commit to kulcsartibor/go-diameter that referenced this pull request Jun 10, 2026
* sm: implement post-CER/CEA TLS upgrade (RFC 6733 §6.2)

Add TLSUpgrader interface and StartTLS method to support upgrading a
plain TCP connection to TLS after the CER/CEA exchange, as specified
in RFC 6733 §6.2.

Changes:
- Add TLSUpgrader interface to diam package
- Add StartTLS() method to the server connection (response type)
- Add TLSConfig field to sm.Settings for server-side TLS upgrade
- Update handleCER to accept Inband-Security-Id=1 when TLSConfig is
  set, and perform TLS upgrade after sending success CEA
- Add ParseWithSecurity() to CER parser (accepts tlsActive flag)
- Add RequestedSecurity() accessor to CER struct

When a peer sends CER with Inband-Security-Id=1 and the server has
TLSConfig configured, the server sends a success CEA then upgrades
the connection to TLS. Without TLSConfig, the existing behavior
(reject with NO_COMMON_SECURITY) is preserved.

Fixes fiorix#238

* sm: address review comments on TLS upgrade PR

- Add client-side StartTLSClient method (tls.Client) to TLSUpgrader
- Add RequestInbandTLS field to Client; sends Inband-Security-Id=1
- Perform client-side TLS upgrade in handleCEA after success CEA
- Fix silent downgrade: type-assert TLSUpgrader before accepting
  Inband-Security-Id=1 in handleCER (rejects non-upgradeable conns)
- Document concurrency safety constraints on StartTLS methods
- Defensive close if upgrade fails after success CEA was sent

* sm: document failure semantics and add end-to-end TLS upgrade test

- Document that TLS handshake failure after success CEA results in
  connection close (both peers observe the failure directly per
  RFC 6733 §6.2; sending a second CEA would violate the protocol)
- Add TestInbandTLSUpgrade_EndToEnd: drives a full plain TCP →
  CER/CEA with Inband-Security-Id=1 → TLS upgrade flow, then
  verifies DWR/DWA exchange over the encrypted connection
kulcsartibor pushed a commit to kulcsartibor/go-diameter that referenced this pull request Jun 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No support for post-CER/CEA TLS upgrade (RFC 6733 §6.2)

2 participants