Skip to content

feat: present client certs for outbound SIP TLS mTLS (#530) - #776

Open
lixuanqun wants to merge 1 commit into
livekit:mainfrom
lixuanqun:cursor/feat-outbound-mtls-33f3
Open

feat: present client certs for outbound SIP TLS mTLS (#530)#776
lixuanqun wants to merge 1 commit into
livekit:mainfrom
lixuanqun:cursor/feat-outbound-mtls-33f3

Conversation

@lixuanqun

Copy link
Copy Markdown

Outbound SIP/TLS dials share the UserAgent tls.Config with the inbound listener, but Go's default client-certificate selection can return no cert when the peer's AcceptableCAs don't match our leaf. Set an explicit GetClientCertificate hook so configured identities are always presented when the trunk requires mTLS.

Also allow optional tls.client_certs for a separate outbound identity, and document Redis TLS + SIP TLS/mTLS config keys in the README (redis.tls was already supported via livekit/protocol; see #470).

Fixes #530
Fixes #470

Outbound SIP/TLS dials share the UserAgent tls.Config with the inbound
listener, but Go's default client-certificate selection can return no
cert when the peer's AcceptableCAs don't match our leaf. Set an explicit
GetClientCertificate hook so configured identities are always presented
when the trunk requires mTLS.

Also allow optional tls.client_certs for a separate outbound identity,
and document Redis TLS + SIP TLS/mTLS config keys in the README
(redis.tls was already supported via livekit/protocol; see livekit#470).

Fixes livekit#530
Fixes livekit#470

Co-authored-by: li xuanqun <793005378@qq.com>
@lixuanqun
lixuanqun requested a review from a team as a code owner August 4, 2026 04:33

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View 1 additional finding in Devin Review.

Open in Devin Review

Comment thread pkg/sip/tls.go
Comment on lines +47 to +52
return func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
if len(certs) == 0 {
return nil, nil
}
return &certs[0], nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Outbound TLS connection can crash when no client identity is configured

An empty result is handed back to Go's TLS client (return nil, nil at pkg/sip/tls.go:49) when no client identity is configured, so a call placed over an encrypted trunk that asks for a client certificate can crash the service instead of connecting without one.
Impact: If this path is ever reached, the process panics during the handshake instead of gracefully continuing without a client certificate.

crypto/tls requires GetClientCertificate to return a non-nil Certificate

Go's documentation for tls.Config.GetClientCertificate states the hook must return a non-nil *Certificate; to send no certificate it must return an empty &tls.Certificate{}. Internally the client does certMsg.certificate = *cert (TLS 1.3) / certMsg.certificates = chainToSend.Certificate (TLS 1.2), which nil-dereferences on a nil return. The default implementation returns new(Certificate) for exactly this reason.

Today pkg/sip/service.go:244-254 rejects a TLS config with zero certs, so clientCerts is always non-empty in production, making the branch effectively unreachable — but the helper is exported to the package and pkg/sip/tls_test.go:13-16 asserts the nil behaviour, locking in the contract violation for any future caller.

Suggested change
return func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
if len(certs) == 0 {
return nil, nil
}
return &certs[0], nil
}
return func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
if len(certs) == 0 {
// crypto/tls requires a non-nil Certificate; an empty one means "send no cert".
return &tls.Certificate{}, nil
}
return &certs[0], nil
}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 21.73913% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.95%. Comparing base (0460b40) to head (7f8fec6).
⚠️ Report is 346 commits behind head on main.

Files with missing lines Patch % Lines
pkg/sip/service.go 0.00% 10 Missing ⚠️
pkg/sip/tls.go 38.46% 8 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #776      +/-   ##
==========================================
+ Coverage   65.25%   65.95%   +0.69%     
==========================================
  Files          51       41      -10     
  Lines        6588     7962    +1374     
==========================================
+ Hits         4299     5251     +952     
- Misses       1915     2226     +311     
- Partials      374      485     +111     

☔ View full report in Codecov by Harness.
📢 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants