Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion internal/auth/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
38 changes: 38 additions & 0 deletions internal/protocols/tls/make_config.go
Original file line number Diff line number Diff line change
@@ -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 changes: 137 additions & 0 deletions internal/protocols/tls/make_config_test.go
Original file line number Diff line number Diff line change
@@ -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
}
35 changes: 0 additions & 35 deletions internal/protocols/tls/tls_config.go

This file was deleted.

16 changes: 11 additions & 5 deletions internal/staticsources/hls/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package hls

import (
"net/http"
"net/url"
"time"

"github.com/bluenviron/gohlslib/v2"
Expand Down Expand Up @@ -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()

Expand All @@ -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{
Expand All @@ -107,7 +113,7 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
},
}

err := c.Start()
err = c.Start()
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion internal/staticsources/rtmp/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion internal/staticsources/rtsp/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions internal/staticsources/webrtc/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion mediamtx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ pathDefaults:
# * $G1, $G2, ...: regular expression groups, if path name is
# a regular expression.
source: publisher
# If the source is a URL, and the source certificate is self-signed
# If the source is a URL, and the source TLS certificate is self-signed
# or invalid, you can provide the fingerprint of the certificate in order to
# validate it anyway. It can be obtained by running:
# openssl s_client -connect source_ip:source_port </dev/null 2>/dev/null | sed -n '/BEGIN/,/END/p' > server.crt
Expand Down