From 9155bffefbd5b6e97133dc898ca0ec90de767229 Mon Sep 17 00:00:00 2001 From: aler9 <46489434+aler9@users.noreply.github.com> Date: Mon, 25 Oct 2021 21:13:02 +0200 Subject: [PATCH] allow disabling HTTPS validation by using sourceFingerprint (#665) --- internal/core/hls_source.go | 20 ++++++++++------- internal/core/path.go | 1 + internal/hls/client.go | 45 ++++++++++++++++++++++++++++++------- internal/hls/client_test.go | 1 + rtsp-simple-server.yml | 4 ++-- 5 files changed, 53 insertions(+), 18 deletions(-) diff --git a/internal/core/hls_source.go b/internal/core/hls_source.go index d3386221..a6611569 100644 --- a/internal/core/hls_source.go +++ b/internal/core/hls_source.go @@ -23,9 +23,10 @@ type hlsSourceParent interface { } type hlsSource struct { - ur string - wg *sync.WaitGroup - parent hlsSourceParent + ur string + fingerprint string + wg *sync.WaitGroup + parent hlsSourceParent ctx context.Context ctxCancel func() @@ -34,16 +35,18 @@ type hlsSource struct { func newHLSSource( parentCtx context.Context, ur string, + fingerprint string, wg *sync.WaitGroup, parent hlsSourceParent) *hlsSource { ctx, ctxCancel := context.WithCancel(parentCtx) s := &hlsSource{ - ur: ur, - wg: wg, - parent: parent, - ctx: ctx, - ctxCancel: ctxCancel, + ur: ur, + fingerprint: fingerprint, + wg: wg, + parent: parent, + ctx: ctx, + ctxCancel: ctxCancel, } s.Log(logger.Info, "started") @@ -141,6 +144,7 @@ func (s *hlsSource) runInner() bool { c := hls.NewClient( s.ur, + s.fingerprint, onTracks, onFrame, s, diff --git a/internal/core/path.go b/internal/core/path.go index f64022c8..64473518 100644 --- a/internal/core/path.go +++ b/internal/core/path.go @@ -624,6 +624,7 @@ func (pa *path) staticSourceCreate() { pa.source = newHLSSource( pa.ctx, pa.conf.Source, + pa.conf.SourceFingerprint, &pa.sourceStaticWg, pa) } diff --git a/internal/hls/client.go b/internal/hls/client.go index e8e283f1..7e8421f7 100644 --- a/internal/hls/client.go +++ b/internal/hls/client.go @@ -3,6 +3,9 @@ package hls import ( "bytes" "context" + "crypto/sha256" + "crypto/tls" + "encoding/hex" "fmt" "io/ioutil" "net/http" @@ -428,6 +431,7 @@ type Client struct { ctx context.Context ctxCancel func() + httpClient *http.Client urlParsed *url.URL lastDownloadTime time.Time downloadedSegmentURIs []string @@ -455,19 +459,44 @@ type Client struct { // NewClient allocates a Client. func NewClient( ur string, + fingerprint string, onTracks func(*gortsplib.Track, *gortsplib.Track) error, onFrame func(bool, []byte), parent ClientParent, ) *Client { ctx, ctxCancel := context.WithCancel(context.Background()) + tlsConfig := &tls.Config{} + + if fingerprint != "" { + tlsConfig.InsecureSkipVerify = true + tlsConfig.VerifyConnection = func(cs tls.ConnectionState) error { + h := sha256.New() + h.Write(cs.PeerCertificates[0].Raw) + hstr := hex.EncodeToString(h.Sum(nil)) + fingerprintLower := strings.ToLower(fingerprint) + + if hstr != fingerprintLower { + return fmt.Errorf("server fingerprint do not match: expected %s, got %s", + fingerprintLower, hstr) + } + + return nil + } + } + c := &Client{ - ur: ur, - onTracks: onTracks, - onFrame: onFrame, - parent: parent, - ctx: ctx, - ctxCancel: ctxCancel, + ur: ur, + onTracks: onTracks, + onFrame: onFrame, + parent: parent, + ctx: ctx, + ctxCancel: ctxCancel, + httpClient: &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: tlsConfig, + }, + }, segmentQueue: newClientSegmentQueue(), allocateProcs: make(chan clientAllocateProcsReq), outErr: make(chan error, 1), @@ -690,7 +719,7 @@ func (c *Client) downloadPlaylist(innerCtx context.Context) (m3u8.Playlist, erro return nil, err } - res, err := http.DefaultClient.Do(req) + res, err := c.httpClient.Do(req) if err != nil { return nil, err } @@ -720,7 +749,7 @@ func (c *Client) downloadSegment(innerCtx context.Context, segmentURI string) ([ return nil, err } - res, err := http.DefaultClient.Do(req) + res, err := c.httpClient.Do(req) if err != nil { return nil, err } diff --git a/internal/hls/client_test.go b/internal/hls/client_test.go index 08047885..543fb5a7 100644 --- a/internal/hls/client_test.go +++ b/internal/hls/client_test.go @@ -116,6 +116,7 @@ func TestClient(t *testing.T) { c := NewClient( "http://localhost:5780/stream.m3u8", + "", onTracks, onFrame, testClientParent{}, diff --git a/rtsp-simple-server.yml b/rtsp-simple-server.yml index 555e8f0e..f4c283d1 100644 --- a/rtsp-simple-server.yml +++ b/rtsp-simple-server.yml @@ -144,9 +144,9 @@ paths: # when interacting with old cameras that require it. sourceAnyPortEnable: no - # if the source is a RTSPS URL, and the source certificate is self-signed + # if the source is a RTSPS or HTTPS URL, and the source certificate is self-signed # or invalid, you can provide the fingerprint of the certificate in order to - # validate it anyway, and at the same time prevent man-in-the-middle attacks. + # validate it anyway. # the fingerprint can be obtained by running: # openssl s_client -connect source_ip:source_port /dev/null | sed -n '/BEGIN/,/END/p' > server.crt # openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':'