mirror of
https://github.com/aler9/rtsp-simple-server
synced 2025-09-26 19:51:26 +08:00
send server name (SNI) when opening TLS connections (#4973)
This commit is contained in:
@@ -273,8 +273,13 @@ func (m *Manager) pullJWTJWKS() (jwt.Keyfunc, error) {
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
if now.Sub(m.jwksLastRefresh) >= jwksRefreshPeriod {
|
||||
u, err := url.Parse(m.JWTJWKS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: tls.ConfigForFingerprint(m.JWTJWKSFingerprint),
|
||||
TLSClientConfig: tls.MakeConfig(u.Hostname(), m.JWTJWKSFingerprint),
|
||||
}
|
||||
defer tr.CloseIdleConnections()
|
||||
|
||||
|
38
internal/protocols/tls/make_config.go
Normal file
38
internal/protocols/tls/make_config.go
Normal file
@@ -0,0 +1,38 @@
|
||||
// Package tls contains TLS utilities.
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MakeConfig returns a tls.Config with:
|
||||
// - server name indicator (SNI) support
|
||||
// - fingerprint support
|
||||
func MakeConfig(serverName string, fingerprint string) *tls.Config {
|
||||
conf := &tls.Config{
|
||||
ServerName: serverName,
|
||||
}
|
||||
|
||||
if fingerprint != "" {
|
||||
fingerprintLower := strings.ToLower(fingerprint)
|
||||
conf.InsecureSkipVerify = true
|
||||
conf.VerifyConnection = func(cs tls.ConnectionState) error {
|
||||
h := sha256.New()
|
||||
h.Write(cs.PeerCertificates[0].Raw)
|
||||
hstr := hex.EncodeToString(h.Sum(nil))
|
||||
|
||||
if hstr != fingerprintLower {
|
||||
return fmt.Errorf("source fingerprint does not match: expected %s, got %s",
|
||||
fingerprintLower, hstr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return conf
|
||||
}
|
137
internal/protocols/tls/make_config_test.go
Normal file
137
internal/protocols/tls/make_config_test.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var testTLSCertPub = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDazCCAlOgAwIBAgIUXw1hEC3LFpTsllv7D3ARJyEq7sIwDQYJKoZIhvcNAQEL
|
||||
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDEyMTMxNzQ0NThaFw0zMDEy
|
||||
MTExNzQ0NThaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
|
||||
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
|
||||
AQUAA4IBDwAwggEKAoIBAQDG8DyyS51810GsGwgWr5rjJK7OE1kTTLSNEEKax8Bj
|
||||
zOyiaz8rA2JGl2VUEpi2UjDr9Cm7nd+YIEVs91IIBOb7LGqObBh1kGF3u5aZxLkv
|
||||
NJE+HrLVvUhaDobK2NU+Wibqc/EI3DfUkt1rSINvv9flwTFu1qHeuLWhoySzDKEp
|
||||
OzYxpFhwjVSokZIjT4Red3OtFz7gl2E6OAWe2qoh5CwLYVdMWtKR0Xuw3BkDPk9I
|
||||
qkQKx3fqv97LPEzhyZYjDT5WvGrgZ1WDAN3booxXF3oA1H3GHQc4m/vcLatOtb8e
|
||||
nI59gMQLEbnp08cl873bAuNuM95EZieXTHNbwUnq5iybAgMBAAGjUzBRMB0GA1Ud
|
||||
DgQWBBQBKhJh8eWu0a4au9X/2fKhkFX2vjAfBgNVHSMEGDAWgBQBKhJh8eWu0a4a
|
||||
u9X/2fKhkFX2vjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBj
|
||||
3aCW0YPKukYgVK9cwN0IbVy/D0C1UPT4nupJcy/E0iC7MXPZ9D/SZxYQoAkdptdO
|
||||
xfI+RXkpQZLdODNx9uvV+cHyZHZyjtE5ENu/i5Rer2cWI/mSLZm5lUQyx+0KZ2Yu
|
||||
tEI1bsebDK30msa8QSTn0WidW9XhFnl3gRi4wRdimcQapOWYVs7ih+nAlSvng7NI
|
||||
XpAyRs8PIEbpDDBMWnldrX4TP6EWYUi49gCp8OUDRREKX3l6Ls1vZ02F34yHIt/7
|
||||
7IV/XSKG096bhW+icKBWV0IpcEsgTzPK1J1hMxgjhzIMxGboAeUU+kidthOob6Sd
|
||||
XQxaORfgM//NzX9LhUPk
|
||||
-----END CERTIFICATE-----
|
||||
`)
|
||||
|
||||
var testTLSCertKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAxvA8skudfNdBrBsIFq+a4ySuzhNZE0y0jRBCmsfAY8zsoms/
|
||||
KwNiRpdlVBKYtlIw6/Qpu53fmCBFbPdSCATm+yxqjmwYdZBhd7uWmcS5LzSRPh6y
|
||||
1b1IWg6GytjVPlom6nPxCNw31JLda0iDb7/X5cExbtah3ri1oaMkswyhKTs2MaRY
|
||||
cI1UqJGSI0+EXndzrRc+4JdhOjgFntqqIeQsC2FXTFrSkdF7sNwZAz5PSKpECsd3
|
||||
6r/eyzxM4cmWIw0+Vrxq4GdVgwDd26KMVxd6ANR9xh0HOJv73C2rTrW/HpyOfYDE
|
||||
CxG56dPHJfO92wLjbjPeRGYnl0xzW8FJ6uYsmwIDAQABAoIBACi0BKcyQ3HElSJC
|
||||
kaAao+Uvnzh4yvPg8Nwf5JDIp/uDdTMyIEWLtrLczRWrjGVZYbsVROinP5VfnPTT
|
||||
kYwkfKINj2u+gC6lsNuPnRuvHXikF8eO/mYvCTur1zZvsQnF5kp4GGwIqr+qoPUP
|
||||
bB0UMndG1PdpoMryHe+JcrvTrLHDmCeH10TqOwMsQMLHYLkowvxwJWsmTY7/Qr5S
|
||||
Wm3PPpOcW2i0uyPVuyuv4yD1368fqnqJ8QFsQp1K6QtYsNnJ71Hut1/IoxK/e6hj
|
||||
5Z+byKtHVtmcLnABuoOT7BhleJNFBksX9sh83jid4tMBgci+zXNeGmgqo2EmaWAb
|
||||
agQslkECgYEA8B1rzjOHVQx/vwSzDa4XOrpoHQRfyElrGNz9JVBvnoC7AorezBXQ
|
||||
M9WTHQIFTGMjzD8pb+YJGi3gj93VN51r0SmJRxBaBRh1ZZI9kFiFzngYev8POgD3
|
||||
ygmlS3kTHCNxCK/CJkB+/jMBgtPj5ygDpCWVcTSuWlQFphePkW7jaaECgYEA1Blz
|
||||
ulqgAyJHZaqgcbcCsI2q6m527hVr9pjzNjIVmkwu38yS9RTCgdlbEVVDnS0hoifl
|
||||
+jVMEGXjF3xjyMvL50BKbQUH+KAa+V4n1WGlnZOxX9TMny8MBjEuSX2+362vQ3BX
|
||||
4vOlX00gvoc+sY+lrzvfx/OdPCHQGVYzoKCxhLsCgYA07HcviuIAV/HsO2/vyvhp
|
||||
xF5gTu+BqNUHNOZDDDid+ge+Jre2yfQLCL8VPLXIQW3Jff53IH/PGl+NtjphuLvj
|
||||
7UDJvgvpZZuymIojP6+2c3gJ3CASC9aR3JBnUzdoE1O9s2eaoMqc4scpe+SWtZYf
|
||||
3vzSZ+cqF6zrD/Rf/M35IQKBgHTU4E6ShPm09CcoaeC5sp2WK8OevZw/6IyZi78a
|
||||
r5Oiy18zzO97U/k6xVMy6F+38ILl/2Rn31JZDVJujniY6eSkIVsUHmPxrWoXV1HO
|
||||
y++U32uuSFiXDcSLarfIsE992MEJLSAynbF1Rsgsr3gXbGiuToJRyxbIeVy7gwzD
|
||||
94TpAoGAY4/PejWQj9psZfAhyk5dRGra++gYRQ/gK1IIc1g+Dd2/BxbT/RHr05GK
|
||||
6vwrfjsoRyMWteC1SsNs/CurjfQ/jqCfHNP5XPvxgd5Ec8sRJIiV7V5RTuWJsPu1
|
||||
+3K6cnKEyg+0ekYmLertRFIY6SwWmY1fyKgTvxudMcsBY7dC4xs=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`)
|
||||
|
||||
func TestMakeConfigSNI(t *testing.T) {
|
||||
l, err := net.Listen("tcp", "localhost:8556")
|
||||
require.NoError(t, err)
|
||||
defer l.Close()
|
||||
|
||||
serverDone := make(chan struct{})
|
||||
defer func() { <-serverDone }()
|
||||
|
||||
go func() {
|
||||
defer close(serverDone)
|
||||
|
||||
nconn, err2 := l.Accept()
|
||||
require.NoError(t, err2)
|
||||
defer nconn.Close()
|
||||
|
||||
cert, err2 := tls.X509KeyPair(testTLSCertPub, testTLSCertKey)
|
||||
require.NoError(t, err2)
|
||||
|
||||
tnconn := tls.Server(nconn, &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
InsecureSkipVerify: true,
|
||||
VerifyConnection: func(cs tls.ConnectionState) error {
|
||||
require.Equal(t, "myhost", cs.ServerName)
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
err2 = tnconn.Handshake()
|
||||
require.EqualError(t, err2, "remote error: tls: bad certificate")
|
||||
}()
|
||||
|
||||
conf := MakeConfig("myhost", "")
|
||||
|
||||
_, err = tls.Dial("tcp", "localhost:8556", conf)
|
||||
require.EqualError(t, err, "tls: failed to verify certificate: x509: "+
|
||||
"certificate is not valid for any names, but wanted to match myhost")
|
||||
}
|
||||
|
||||
func TestMakeConfigFingerprint(t *testing.T) {
|
||||
l, err := net.Listen("tcp", "localhost:8556")
|
||||
require.NoError(t, err)
|
||||
defer l.Close()
|
||||
|
||||
serverDone := make(chan struct{})
|
||||
defer func() { <-serverDone }()
|
||||
|
||||
go func() {
|
||||
defer close(serverDone)
|
||||
|
||||
nconn, err2 := l.Accept()
|
||||
require.NoError(t, err2)
|
||||
defer nconn.Close()
|
||||
|
||||
cert, err2 := tls.X509KeyPair(testTLSCertPub, testTLSCertKey)
|
||||
require.NoError(t, err2)
|
||||
|
||||
tnconn := tls.Server(nconn, &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
InsecureSkipVerify: true,
|
||||
VerifyConnection: func(cs tls.ConnectionState) error {
|
||||
require.Equal(t, "myhost", cs.ServerName)
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
err2 = tnconn.Handshake()
|
||||
require.NoError(t, err2)
|
||||
}()
|
||||
|
||||
conf := MakeConfig("myhost", "33949e05fffb5ff3e8aa16f8213a6251b4d9363804ba53233c4da9a46d6f2739")
|
||||
|
||||
conn, err := tls.Dial("tcp", "localhost:8556", conf)
|
||||
require.NoError(t, err)
|
||||
defer conn.Close() //nolint:errcheck
|
||||
}
|
@@ -1,35 +0,0 @@
|
||||
// Package tls contains TLS utilities.
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ConfigForFingerprint returns a tls.Config that supports given fingerprint.
|
||||
func ConfigForFingerprint(fingerprint string) *tls.Config {
|
||||
if fingerprint == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
fingerprintLower := strings.ToLower(fingerprint)
|
||||
|
||||
return &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
VerifyConnection: func(cs tls.ConnectionState) error {
|
||||
h := sha256.New()
|
||||
h.Write(cs.PeerCertificates[0].Raw)
|
||||
hstr := hex.EncodeToString(h.Sum(nil))
|
||||
|
||||
if hstr != fingerprintLower {
|
||||
return fmt.Errorf("source fingerprint does not match: expected %s, got %s",
|
||||
fingerprintLower, hstr)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
@@ -3,6 +3,7 @@ package hls
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/bluenviron/gohlslib/v2"
|
||||
@@ -60,8 +61,13 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
|
||||
decodeErrors.Start()
|
||||
defer decodeErrors.Stop()
|
||||
|
||||
u, err := url.Parse(params.ResolvedSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: tls.ConfigForFingerprint(params.Conf.SourceFingerprint),
|
||||
TLSClientConfig: tls.MakeConfig(u.Hostname(), params.Conf.SourceFingerprint),
|
||||
}
|
||||
defer tr.CloseIdleConnections()
|
||||
|
||||
@@ -88,9 +94,9 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
|
||||
decodeErrors.Increase()
|
||||
},
|
||||
OnTracks: func(tracks []*gohlslib.Track) error {
|
||||
medias, err := hls.ToStream(c, tracks, &stream)
|
||||
if err != nil {
|
||||
return err
|
||||
medias, err2 := hls.ToStream(c, tracks, &stream)
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
|
||||
res := s.Parent.SetReady(defs.PathSourceStaticSetReadyReq{
|
||||
@@ -107,7 +113,7 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
|
||||
},
|
||||
}
|
||||
|
||||
err := c.Start()
|
||||
err = c.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -83,7 +83,7 @@ func (s *Source) runReader(ctx context.Context, u *url.URL, fingerprint string)
|
||||
connectCtx, connectCtxCancel := context.WithTimeout(ctx, time.Duration(s.ReadTimeout))
|
||||
conn := &gortmplib.Client{
|
||||
URL: u,
|
||||
TLSConfig: tls.ConfigForFingerprint(fingerprint),
|
||||
TLSConfig: tls.MakeConfig(u.Hostname(), fingerprint),
|
||||
Publish: false,
|
||||
}
|
||||
err := conn.Initialize(connectCtx)
|
||||
|
@@ -125,7 +125,7 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
|
||||
Scheme: u.Scheme,
|
||||
Host: u.Host,
|
||||
Transport: params.Conf.RTSPTransport.Transport,
|
||||
TLSConfig: tls.ConfigForFingerprint(params.Conf.SourceFingerprint),
|
||||
TLSConfig: tls.MakeConfig(u.Hostname(), params.Conf.SourceFingerprint),
|
||||
ReadTimeout: time.Duration(s.ReadTimeout),
|
||||
WriteTimeout: time.Duration(s.WriteTimeout),
|
||||
WriteQueueSize: s.WriteQueueSize,
|
||||
|
@@ -48,17 +48,17 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
|
||||
u.Scheme = strings.ReplaceAll(u.Scheme, "whep", "http")
|
||||
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: tls.ConfigForFingerprint(params.Conf.SourceFingerprint),
|
||||
TLSClientConfig: tls.MakeConfig(u.Hostname(), params.Conf.SourceFingerprint),
|
||||
}
|
||||
defer tr.CloseIdleConnections()
|
||||
|
||||
client := whip.Client{
|
||||
URL: u,
|
||||
HTTPClient: &http.Client{
|
||||
Timeout: time.Duration(s.ReadTimeout),
|
||||
Transport: tr,
|
||||
},
|
||||
UseAbsoluteTimestamp: params.Conf.UseAbsoluteTimestamp,
|
||||
URL: u,
|
||||
Log: s,
|
||||
}
|
||||
err = client.Initialize(params.Context)
|
||||
|
Reference in New Issue
Block a user