mirror of
https://github.com/bluenviron/mediamtx.git
synced 2025-12-26 04:52:00 -08:00
support encrypting the configuration file
This commit is contained in:
parent
903842484e
commit
6b1643940e
7 changed files with 152 additions and 60 deletions
21
README.md
21
README.md
|
|
@ -30,6 +30,7 @@ Features:
|
|||
* [Configuration](#configuration)
|
||||
* [Encryption](#encryption)
|
||||
* [Authentication](#authentication)
|
||||
* [Encrypt the configuration](#encrypt-the-configuration)
|
||||
* [RTSP proxy mode](#rtsp-proxy-mode)
|
||||
* [Publish a webcam](#publish-a-webcam)
|
||||
* [Publish a Raspberry Pi Camera](#publish-a-raspberry-pi-camera)
|
||||
|
|
@ -217,6 +218,26 @@ paths:
|
|||
|
||||
**WARNING**: enable encryption or use a VPN to ensure that no one is intercepting the credentials.
|
||||
|
||||
### Encrypt the configuration
|
||||
|
||||
The configuration file can be entirely encrypted for security purposes.
|
||||
|
||||
An online encryption tool is [available here](https://play.golang.org/p/rX29jwObNe4).
|
||||
|
||||
The encryption procedure is the following:
|
||||
|
||||
1. NaCL's `crypto_secretbox` function is applied to the content of the configuration. NaCL is a cryptographic library available for [C/C++](https://nacl.cr.yp.to/secretbox.html), [Go](https://pkg.go.dev/golang.org/x/crypto/nacl/secretbox), [C#](https://github.com/somdoron/NaCl.net) and many other languages;
|
||||
|
||||
2. The string is prefixed with the nonce;
|
||||
|
||||
3. The string is encoded with base64.
|
||||
|
||||
After performing the encryption, it's enough to put the base64-encoded result into the configuration file, and launch the server with the `RTSP_CONFKEY` variable:
|
||||
|
||||
```
|
||||
RTSP_CONFKEY=mykey ./rtsp-simple-server
|
||||
```
|
||||
|
||||
### RTSP proxy mode
|
||||
|
||||
_rtsp-simple-server_ is also a RTSP proxy, that is usually deployed in one of these scenarios:
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -11,6 +11,7 @@ require (
|
|||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/notedit/rtmp v0.0.2
|
||||
github.com/stretchr/testify v1.6.1
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
)
|
||||
|
|
|
|||
9
go.sum
9
go.sum
|
|
@ -29,8 +29,17 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
|
|||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
package conf
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/aler9/gortsplib"
|
||||
"github.com/aler9/gortsplib/pkg/headers"
|
||||
"golang.org/x/crypto/nacl/secretbox"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/aler9/rtsp-simple-server/internal/confenv"
|
||||
|
|
@ -23,6 +26,25 @@ const (
|
|||
EncryptionStrict
|
||||
)
|
||||
|
||||
func decrypt(key string, byts []byte) ([]byte, error) {
|
||||
enc, err := base64.StdEncoding.DecodeString(string(byts))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var secretKey [32]byte
|
||||
copy(secretKey[:], key)
|
||||
|
||||
var decryptNonce [24]byte
|
||||
copy(decryptNonce[:], enc[:24])
|
||||
decrypted, ok := secretbox.Open(nil, enc[24:], &decryptNonce, &secretKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("decryption error")
|
||||
}
|
||||
|
||||
return decrypted, nil
|
||||
}
|
||||
|
||||
// Conf is the main program configuration.
|
||||
type Conf struct {
|
||||
LogLevel string `yaml:"logLevel"`
|
||||
|
|
@ -228,15 +250,21 @@ func Load(fpath string) (*Conf, bool, error) {
|
|||
}
|
||||
}
|
||||
|
||||
f, err := os.Open(fpath)
|
||||
byts, err := ioutil.ReadFile(fpath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return true, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
err = yaml.NewDecoder(f).Decode(conf)
|
||||
if key, ok := os.LookupEnv("RTSP_CONFKEY"); ok {
|
||||
byts, err = decrypt(key, byts)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(byts, conf)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return true, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package conf
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
|
@ -12,9 +13,56 @@ import (
|
|||
"github.com/aler9/gortsplib/pkg/base"
|
||||
)
|
||||
|
||||
const userPassSupportedChars = "A-Z,0-9,!,$,(,),*,+,.,;,<,=,>,[,],^,_,-,{,}"
|
||||
|
||||
var reUserPass = regexp.MustCompile(`^[a-zA-Z0-9!\$\(\)\*\+\.;<=>\[\]\^_\-\{\}]+$`)
|
||||
|
||||
const userPassSupportedChars = "A-Z,0-9,!,$,(,),*,+,.,;,<,=,>,[,],^,_,-,{,}"
|
||||
var rePathName = regexp.MustCompile(`^[0-9a-zA-Z_\-/]+$`)
|
||||
|
||||
func parseIPCidrList(in []string) ([]interface{}, error) {
|
||||
if len(in) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var ret []interface{}
|
||||
for _, t := range in {
|
||||
_, ipnet, err := net.ParseCIDR(t)
|
||||
if err == nil {
|
||||
ret = append(ret, ipnet)
|
||||
continue
|
||||
}
|
||||
|
||||
ip := net.ParseIP(t)
|
||||
if ip != nil {
|
||||
ret = append(ret, ip)
|
||||
continue
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unable to parse ip/network '%s'", t)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// CheckPathName checks if a path name is valid.
|
||||
func CheckPathName(name string) error {
|
||||
if name == "" {
|
||||
return fmt.Errorf("cannot be empty")
|
||||
}
|
||||
|
||||
if name[0] == '/' {
|
||||
return fmt.Errorf("can't begin with a slash")
|
||||
}
|
||||
|
||||
if name[len(name)-1] == '/' {
|
||||
return fmt.Errorf("can't end with a slash")
|
||||
}
|
||||
|
||||
if !rePathName.MatchString(name) {
|
||||
return fmt.Errorf("can contain only alfanumeric characters, underscore, minus or slash")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PathConf is a path configuration.
|
||||
type PathConf struct {
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
package conf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var rePathName = regexp.MustCompile(`^[0-9a-zA-Z_\-/]+$`)
|
||||
|
||||
// CheckPathName check if a path name is valid.
|
||||
func CheckPathName(name string) error {
|
||||
if name == "" {
|
||||
return fmt.Errorf("cannot be empty")
|
||||
}
|
||||
|
||||
if name[0] == '/' {
|
||||
return fmt.Errorf("can't begin with a slash")
|
||||
}
|
||||
|
||||
if name[len(name)-1] == '/' {
|
||||
return fmt.Errorf("can't end with a slash")
|
||||
}
|
||||
|
||||
if !rePathName.MatchString(name) {
|
||||
return fmt.Errorf("can contain only alfanumeric characters, underscore, minus or slash")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseIPCidrList(in []string) ([]interface{}, error) {
|
||||
if len(in) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var ret []interface{}
|
||||
for _, t := range in {
|
||||
_, ipnet, err := net.ParseCIDR(t)
|
||||
if err == nil {
|
||||
ret = append(ret, ipnet)
|
||||
continue
|
||||
}
|
||||
|
||||
ip := net.ParseIP(t)
|
||||
if ip != nil {
|
||||
ret = append(ret, ip)
|
||||
continue
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unable to parse ip/network '%s'", t)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
39
main_test.go
39
main_test.go
|
|
@ -1,6 +1,9 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
|
|
@ -13,6 +16,7 @@ import (
|
|||
|
||||
"github.com/aler9/gortsplib"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/crypto/nacl/secretbox"
|
||||
|
||||
"github.com/aler9/rtsp-simple-server/internal/conf"
|
||||
)
|
||||
|
|
@ -245,6 +249,41 @@ func TestEnvironmentNoFile(t *testing.T) {
|
|||
}, pa)
|
||||
}
|
||||
|
||||
func TestEncryptedConf(t *testing.T) {
|
||||
key := "testing123testin"
|
||||
plaintext := `
|
||||
paths:
|
||||
path1:
|
||||
path2:
|
||||
`
|
||||
|
||||
encryptedConf := func() string {
|
||||
var secretKey [32]byte
|
||||
copy(secretKey[:], key[:])
|
||||
|
||||
var nonce [24]byte
|
||||
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
encrypted := secretbox.Seal(nonce[:], []byte(plaintext), &nonce, &secretKey)
|
||||
return base64.StdEncoding.EncodeToString(encrypted)
|
||||
}()
|
||||
|
||||
os.Setenv("RTSP_CONFKEY", key)
|
||||
defer os.Unsetenv("RTSP_CONFKEY")
|
||||
|
||||
p, ok := testProgram(encryptedConf)
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
|
||||
_, ok = p.conf.Paths["path1"]
|
||||
require.Equal(t, true, ok)
|
||||
|
||||
_, ok = p.conf.Paths["path2"]
|
||||
require.Equal(t, true, ok)
|
||||
}
|
||||
|
||||
var serverCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDazCCAlOgAwIBAgIUXw1hEC3LFpTsllv7D3ARJyEq7sIwDQYJKoZIhvcNAQEL
|
||||
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue