Add Argon2 credential hash support (#2888)
* Add argon2 credential hash support * update README, tests and documentation --------- Co-authored-by: aler9 <46489434+aler9@users.noreply.github.com>
This commit is contained in:
parent
20bb9b90cd
commit
397c58a882
24
README.md
24
README.md
|
@ -1035,14 +1035,30 @@ It's possible to setup authentication for readers too:
|
|||
|
||||
```yml
|
||||
pathDefaults:
|
||||
readUser: user
|
||||
readPass: userpass
|
||||
readUser: myuser
|
||||
readPass: mypass
|
||||
```
|
||||
|
||||
If storing plain credentials in the configuration file is a security problem, username and passwords can be stored as sha256-hashed strings; a string must be hashed with sha256 and encoded with base64:
|
||||
If storing plain credentials in the configuration file is a security problem, username and passwords can be stored as hashed strings. The Argon2 and SHA256 hashing algorithms are supported.
|
||||
|
||||
To use Argon2, the string must be hashed using Argon2id (recommended) or Argon2i:
|
||||
|
||||
```
|
||||
echo -n "userpass" | openssl dgst -binary -sha256 | openssl base64
|
||||
echo -n "mypass" | argon2 saltItWithSalt -id -l 32 -e
|
||||
```
|
||||
|
||||
Then stored with the `argon2:` prefix:
|
||||
|
||||
```yml
|
||||
pathDefaults:
|
||||
readUser: argon2:$argon2id$v=19$m=4096,t=3,p=1$MTIzNDU2Nzg$OGGO0eCMN0ievb4YGSzvS/H+Vajx1pcbUmtLp2tRqRU
|
||||
readPass: argon2:$argon2i$v=19$m=4096,t=3,p=1$MTIzNDU2Nzg$oct3kOiFywTdDdt19kT07hdvmsPTvt9zxAUho2DLqZw
|
||||
```
|
||||
|
||||
To use SHA256, the string must be hashed with SHA256 and encoded with base64:
|
||||
|
||||
```
|
||||
echo -n "mypass" | openssl dgst -binary -sha256 | openssl base64
|
||||
```
|
||||
|
||||
Then stored with the `sha256:` prefix:
|
||||
|
|
1
go.mod
1
go.mod
|
@ -17,6 +17,7 @@ require (
|
|||
github.com/gookit/color v1.5.4
|
||||
github.com/gorilla/websocket v1.5.1
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/matthewhartstonge/argon2 v1.0.0
|
||||
github.com/notedit/rtmp v0.0.2
|
||||
github.com/pion/ice/v2 v2.3.11
|
||||
github.com/pion/interceptor v0.1.25
|
||||
|
|
2
go.sum
2
go.sum
|
@ -102,6 +102,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/matthewhartstonge/argon2 v1.0.0 h1:e65fkae6O8Na6YTy2HAccUbXR+GQHOnpQxeWGqWCRIw=
|
||||
github.com/matthewhartstonge/argon2 v1.0.0/go.mod h1:Fm4FHZxdxCM6hg21Jkz3YZVKnU7VnTlqDQ3ghS/Myok=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
|
|
@ -1,22 +1,31 @@
|
|||
package conf
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/matthewhartstonge/argon2"
|
||||
)
|
||||
|
||||
var reCredential = regexp.MustCompile(`^[a-zA-Z0-9!\$\(\)\*\+\.;<=>\[\]\^_\-\{\}@#&]+$`)
|
||||
var (
|
||||
rePlainCredential = regexp.MustCompile(`^[a-zA-Z0-9!\$\(\)\*\+\.;<=>\[\]\^_\-\{\}@#&]+$`)
|
||||
reBase64 = regexp.MustCompile(`^sha256:[a-zA-Z0-9\+/=]+$`)
|
||||
)
|
||||
|
||||
const credentialSupportedChars = "A-Z,0-9,!,$,(,),*,+,.,;,<,=,>,[,],^,_,-,\",\",@,#,&"
|
||||
const plainCredentialSupportedChars = "A-Z,0-9,!,$,(,),*,+,.,;,<,=,>,[,],^,_,-,\",\",@,#,&"
|
||||
|
||||
// Credential is a parameter that is used as username or password.
|
||||
type Credential string
|
||||
type Credential struct {
|
||||
value string
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (d Credential) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(string(d))
|
||||
return json.Marshal(d.value)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
|
@ -26,17 +35,89 @@ func (d *Credential) UnmarshalJSON(b []byte) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if in != "" &&
|
||||
!strings.HasPrefix(in, "sha256:") &&
|
||||
!reCredential.MatchString(in) {
|
||||
return fmt.Errorf("credential contains unsupported characters. Supported are: %s", credentialSupportedChars)
|
||||
*d = Credential{
|
||||
value: in,
|
||||
}
|
||||
|
||||
*d = Credential(in)
|
||||
return nil
|
||||
return d.validateConfig()
|
||||
}
|
||||
|
||||
// UnmarshalEnv implements env.Unmarshaler.
|
||||
func (d *Credential) UnmarshalEnv(_ string, v string) error {
|
||||
return d.UnmarshalJSON([]byte(`"` + v + `"`))
|
||||
}
|
||||
|
||||
// GetValue returns the value of the credential.
|
||||
func (d *Credential) GetValue() string {
|
||||
return d.value
|
||||
}
|
||||
|
||||
// IsEmpty returns true if the credential is not configured.
|
||||
func (d *Credential) IsEmpty() bool {
|
||||
return d.value == ""
|
||||
}
|
||||
|
||||
// IsSha256 returns true if the credential is a sha256 hash.
|
||||
func (d *Credential) IsSha256() bool {
|
||||
return d.value != "" && strings.HasPrefix(d.value, "sha256:")
|
||||
}
|
||||
|
||||
// IsArgon2 returns true if the credential is an argon2 hash.
|
||||
func (d *Credential) IsArgon2() bool {
|
||||
return d.value != "" && strings.HasPrefix(d.value, "argon2:")
|
||||
}
|
||||
|
||||
// IsHashed returns true if the credential is a sha256 or argon2 hash.
|
||||
func (d *Credential) IsHashed() bool {
|
||||
return d.IsSha256() || d.IsArgon2()
|
||||
}
|
||||
|
||||
func sha256Base64(in string) string {
|
||||
h := sha256.New()
|
||||
h.Write([]byte(in))
|
||||
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// Check returns true if the given value matches the credential.
|
||||
func (d *Credential) Check(guess string) bool {
|
||||
if d.IsSha256() {
|
||||
return d.value[len("sha256:"):] == sha256Base64(guess)
|
||||
}
|
||||
if d.IsArgon2() {
|
||||
// TODO: remove matthewhartstonge/argon2 when this PR gets merged into mainline Go:
|
||||
// https://go-review.googlesource.com/c/crypto/+/502515
|
||||
ok, err := argon2.VerifyEncoded([]byte(guess), []byte(d.value[len("argon2:"):]))
|
||||
return ok && err == nil
|
||||
}
|
||||
if d.IsEmpty() {
|
||||
// when no credential is set, any value is valid
|
||||
return true
|
||||
}
|
||||
|
||||
return d.value == guess
|
||||
}
|
||||
|
||||
func (d *Credential) validateConfig() error {
|
||||
if d.IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch {
|
||||
case d.IsSha256():
|
||||
if !reBase64.MatchString(d.value) {
|
||||
return fmt.Errorf("credential contains unsupported characters, sha256 hash must be base64 encoded")
|
||||
}
|
||||
case d.IsArgon2():
|
||||
// TODO: remove matthewhartstonge/argon2 when this PR gets merged into mainline Go:
|
||||
// https://go-review.googlesource.com/c/crypto/+/502515
|
||||
_, err := argon2.Decode([]byte(d.value[len("argon2:"):]))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid argon2 hash: %w", err)
|
||||
}
|
||||
default:
|
||||
if !rePlainCredential.MatchString(d.value) {
|
||||
return fmt.Errorf("credential contains unsupported characters. Supported are: %s", plainCredentialSupportedChars)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
package conf
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCredential(t *testing.T) {
|
||||
t.Run("MarshalJSON", func(t *testing.T) {
|
||||
cred := Credential{value: "password"}
|
||||
expectedJSON := []byte(`"password"`)
|
||||
actualJSON, err := cred.MarshalJSON()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedJSON, actualJSON)
|
||||
})
|
||||
|
||||
t.Run("UnmarshalJSON", func(t *testing.T) {
|
||||
expectedCred := Credential{value: "password"}
|
||||
jsonData := []byte(`"password"`)
|
||||
var actualCred Credential
|
||||
err := actualCred.UnmarshalJSON(jsonData)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedCred, actualCred)
|
||||
})
|
||||
|
||||
t.Run("UnmarshalEnv", func(t *testing.T) {
|
||||
cred := Credential{}
|
||||
err := cred.UnmarshalEnv("", "password")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "password", cred.value)
|
||||
})
|
||||
|
||||
t.Run("GetValue", func(t *testing.T) {
|
||||
cred := Credential{value: "password"}
|
||||
actualValue := cred.GetValue()
|
||||
assert.Equal(t, "password", actualValue)
|
||||
})
|
||||
|
||||
t.Run("IsEmpty", func(t *testing.T) {
|
||||
cred := Credential{}
|
||||
assert.True(t, cred.IsEmpty())
|
||||
assert.False(t, cred.IsHashed())
|
||||
|
||||
cred.value = "password"
|
||||
assert.False(t, cred.IsEmpty())
|
||||
assert.False(t, cred.IsHashed())
|
||||
})
|
||||
|
||||
t.Run("IsSha256", func(t *testing.T) {
|
||||
cred := Credential{}
|
||||
assert.False(t, cred.IsSha256())
|
||||
assert.False(t, cred.IsHashed())
|
||||
|
||||
cred.value = "sha256:j1tsRqDEw9xvq/D7/9tMx6Jh/jMhk3UfjwIB2f1zgMo="
|
||||
assert.True(t, cred.IsSha256())
|
||||
assert.True(t, cred.IsHashed())
|
||||
|
||||
cred.value = "argon2:$argon2id$v=19$m=65536,t=1," +
|
||||
"p=4$WXJGqwIB2qd+pRmxMOw9Dg$X4gvR0ZB2DtQoN8vOnJPR2SeFdUhH9TyVzfV98sfWeE"
|
||||
assert.False(t, cred.IsSha256())
|
||||
assert.True(t, cred.IsHashed())
|
||||
})
|
||||
|
||||
t.Run("IsArgon2", func(t *testing.T) {
|
||||
cred := Credential{}
|
||||
assert.False(t, cred.IsArgon2())
|
||||
assert.False(t, cred.IsHashed())
|
||||
|
||||
cred.value = "sha256:j1tsRqDEw9xvq/D7/9tMx6Jh/jMhk3UfjwIB2f1zgMo="
|
||||
assert.False(t, cred.IsArgon2())
|
||||
assert.True(t, cred.IsHashed())
|
||||
|
||||
cred.value = "argon2:$argon2id$v=19$m=65536,t=1," +
|
||||
"p=4$WXJGqwIB2qd+pRmxMOw9Dg$X4gvR0ZB2DtQoN8vOnJPR2SeFdUhH9TyVzfV98sfWeE"
|
||||
assert.True(t, cred.IsArgon2())
|
||||
assert.True(t, cred.IsHashed())
|
||||
})
|
||||
|
||||
t.Run("Check-plain", func(t *testing.T) {
|
||||
cred := Credential{value: "password"}
|
||||
assert.True(t, cred.Check("password"))
|
||||
assert.False(t, cred.Check("wrongpassword"))
|
||||
})
|
||||
|
||||
t.Run("Check-sha256", func(t *testing.T) {
|
||||
cred := Credential{value: "password"}
|
||||
assert.True(t, cred.Check("password"))
|
||||
assert.False(t, cred.Check("wrongpassword"))
|
||||
})
|
||||
|
||||
t.Run("Check-sha256", func(t *testing.T) {
|
||||
cred := Credential{value: "sha256:rl3rgi4NcZkpAEcacZnQ2VuOfJ0FxAqCRaKB/SwdZoQ="}
|
||||
assert.True(t, cred.Check("testuser"))
|
||||
assert.False(t, cred.Check("notestuser"))
|
||||
})
|
||||
|
||||
t.Run("Check-argon2", func(t *testing.T) {
|
||||
cred := Credential{value: "argon2:$argon2id$v=19$m=4096,t=3," +
|
||||
"p=1$MTIzNDU2Nzg$Ux/LWeTgJQPyfMMJo1myR64+o8rALHoPmlE1i/TR+58"}
|
||||
assert.True(t, cred.Check("testuser"))
|
||||
assert.False(t, cred.Check("notestuser"))
|
||||
})
|
||||
|
||||
t.Run("validateConfig", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
cred *Credential
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Empty credential",
|
||||
cred: &Credential{value: ""},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Valid plain credential",
|
||||
cred: &Credential{value: "validPlain123"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid plain credential",
|
||||
cred: &Credential{value: "invalid/Plain"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Valid sha256 credential",
|
||||
cred: &Credential{value: "sha256:validBase64EncodedHash=="},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid sha256 credential",
|
||||
cred: &Credential{value: "sha256:inval*idBase64"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Valid Argon2 credential",
|
||||
cred: &Credential{value: "argon2:$argon2id$v=19$m=4096," +
|
||||
"t=3,p=1$MTIzNDU2Nzg$zarsL19s86GzUWlAkvwt4gJBFuU/A9CVuCjNI4fksow"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid Argon2 credential",
|
||||
cred: &Credential{value: "argon2:invalid"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid Argon2 credential",
|
||||
// testing argon2d errors, because it's not supported
|
||||
cred: &Credential{value: "$argon2d$v=19$m=4096,t=3," +
|
||||
"p=1$MTIzNDU2Nzg$Xqyd4R7LzXvvAEHaVU12+Nzf5OkHoYcwIEIIYJUDpz0"},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.cred.validateConfig()
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
|
@ -339,11 +339,11 @@ func (pconf *Path) check(conf *Conf, name string) error {
|
|||
|
||||
// Authentication
|
||||
|
||||
if (pconf.PublishUser != "" && pconf.PublishPass == "") ||
|
||||
(pconf.PublishUser == "" && pconf.PublishPass != "") {
|
||||
if (!pconf.PublishUser.IsEmpty() && pconf.PublishPass.IsEmpty()) ||
|
||||
(pconf.PublishUser.IsEmpty() && !pconf.PublishPass.IsEmpty()) {
|
||||
return fmt.Errorf("read username and password must be both filled")
|
||||
}
|
||||
if pconf.PublishUser != "" && pconf.Source != "publisher" {
|
||||
if !pconf.PublishUser.IsEmpty() && pconf.Source != "publisher" {
|
||||
return fmt.Errorf("'publishUser' is useless when source is not 'publisher', since " +
|
||||
"the stream is not provided by a publisher, but by a fixed source")
|
||||
}
|
||||
|
@ -351,22 +351,22 @@ func (pconf *Path) check(conf *Conf, name string) error {
|
|||
return fmt.Errorf("'publishIPs' is useless when source is not 'publisher', since " +
|
||||
"the stream is not provided by a publisher, but by a fixed source")
|
||||
}
|
||||
if (pconf.ReadUser != "" && pconf.ReadPass == "") ||
|
||||
(pconf.ReadUser == "" && pconf.ReadPass != "") {
|
||||
if (!pconf.ReadUser.IsEmpty() && pconf.ReadPass.IsEmpty()) ||
|
||||
(pconf.ReadUser.IsEmpty() && !pconf.ReadPass.IsEmpty()) {
|
||||
return fmt.Errorf("read username and password must be both filled")
|
||||
}
|
||||
if contains(conf.AuthMethods, headers.AuthDigest) {
|
||||
if strings.HasPrefix(string(pconf.PublishUser), "sha256:") ||
|
||||
strings.HasPrefix(string(pconf.PublishPass), "sha256:") ||
|
||||
strings.HasPrefix(string(pconf.ReadUser), "sha256:") ||
|
||||
strings.HasPrefix(string(pconf.ReadPass), "sha256:") {
|
||||
if pconf.PublishUser.IsHashed() ||
|
||||
pconf.PublishPass.IsHashed() ||
|
||||
pconf.ReadUser.IsHashed() ||
|
||||
pconf.ReadPass.IsHashed() {
|
||||
return fmt.Errorf("hashed credentials can't be used when the digest auth method is available")
|
||||
}
|
||||
}
|
||||
if conf.ExternalAuthenticationURL != "" {
|
||||
if pconf.PublishUser != "" ||
|
||||
if !pconf.PublishUser.IsEmpty() ||
|
||||
len(pconf.PublishIPs) > 0 ||
|
||||
pconf.ReadUser != "" ||
|
||||
!pconf.ReadUser.IsEmpty() ||
|
||||
len(pconf.ReadIPs) > 0 {
|
||||
return fmt.Errorf("credentials or IPs can't be used together with 'externalAuthenticationURL'")
|
||||
}
|
||||
|
|
|
@ -2,13 +2,10 @@ package core
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/auth"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
||||
|
@ -18,20 +15,6 @@ import (
|
|||
"github.com/bluenviron/mediamtx/internal/defs"
|
||||
)
|
||||
|
||||
func sha256Base64(in string) string {
|
||||
h := sha256.New()
|
||||
h.Write([]byte(in))
|
||||
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func checkCredential(right string, guess string) bool {
|
||||
if strings.HasPrefix(right, "sha256:") {
|
||||
return right[len("sha256:"):] == sha256Base64(guess)
|
||||
}
|
||||
|
||||
return right == guess
|
||||
}
|
||||
|
||||
func doExternalAuthentication(
|
||||
ur string,
|
||||
accessRequest defs.PathAccessRequest,
|
||||
|
@ -102,17 +85,17 @@ func doAuthentication(
|
|||
}
|
||||
|
||||
var pathIPs conf.IPsOrCIDRs
|
||||
var pathUser string
|
||||
var pathPass string
|
||||
var pathUser conf.Credential
|
||||
var pathPass conf.Credential
|
||||
|
||||
if accessRequest.Publish {
|
||||
pathIPs = pathConf.PublishIPs
|
||||
pathUser = string(pathConf.PublishUser)
|
||||
pathPass = string(pathConf.PublishPass)
|
||||
pathUser = pathConf.PublishUser
|
||||
pathPass = pathConf.PublishPass
|
||||
} else {
|
||||
pathIPs = pathConf.ReadIPs
|
||||
pathUser = string(pathConf.ReadUser)
|
||||
pathPass = string(pathConf.ReadPass)
|
||||
pathUser = pathConf.ReadUser
|
||||
pathPass = pathConf.ReadPass
|
||||
}
|
||||
|
||||
if pathIPs != nil {
|
||||
|
@ -121,12 +104,12 @@ func doAuthentication(
|
|||
}
|
||||
}
|
||||
|
||||
if pathUser != "" {
|
||||
if !pathUser.IsEmpty() {
|
||||
if accessRequest.RTSPRequest != nil && rtspAuth.Method == headers.AuthDigest {
|
||||
err := auth.Validate(
|
||||
accessRequest.RTSPRequest,
|
||||
pathUser,
|
||||
pathPass,
|
||||
pathUser.GetValue(),
|
||||
pathPass.GetValue(),
|
||||
accessRequest.RTSPBaseURL,
|
||||
rtspAuthMethods,
|
||||
"IPCAM",
|
||||
|
@ -134,8 +117,7 @@ func doAuthentication(
|
|||
if err != nil {
|
||||
return defs.AuthenticationError{Message: err.Error()}
|
||||
}
|
||||
} else if !checkCredential(pathUser, accessRequest.User) ||
|
||||
!checkCredential(pathPass, accessRequest.Pass) {
|
||||
} else if !pathUser.Check(accessRequest.User) || !pathPass.Check(accessRequest.Pass) {
|
||||
return defs.AuthenticationError{Message: "invalid credentials"}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ func TestRTSPServer(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRTSPServerAuthHashed(t *testing.T) {
|
||||
func TestRTSPServerAuthHashedSHA256(t *testing.T) {
|
||||
p, ok := newInstance(
|
||||
"rtmp: no\n" +
|
||||
"hls: no\n" +
|
||||
|
@ -112,6 +112,29 @@ func TestRTSPServerAuthHashed(t *testing.T) {
|
|||
defer source.Close()
|
||||
}
|
||||
|
||||
func TestRTSPServerAuthHashedArgon2(t *testing.T) {
|
||||
p, ok := newInstance(
|
||||
"rtmp: no\n" +
|
||||
"hls: no\n" +
|
||||
"webrtc: no\n" +
|
||||
"paths:\n" +
|
||||
" all_others:\n" +
|
||||
" publishUser: argon2:$argon2id$v=19$m=4096,t=3,p=1$MTIzNDU2Nzg$Ux/LWeTgJQPyfMMJo1myR64+o8rALHoPmlE1i/TR+58\n" +
|
||||
" publishPass: argon2:$argon2i$v=19$m=4096,t=3,p=1$MTIzNDU2Nzg$/mrZ42TiTv1mcPnpMUera5oi0SFYbbyueAbdx5sUvWo\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.Close()
|
||||
|
||||
medi := testMediaH264
|
||||
|
||||
source := gortsplib.Client{}
|
||||
|
||||
err := source.StartRecording(
|
||||
"rtsp://testuser:testpass@127.0.0.1:8554/test/stream",
|
||||
&description.Session{Medias: []*description.Media{medi}})
|
||||
require.NoError(t, err)
|
||||
defer source.Close()
|
||||
}
|
||||
|
||||
func TestRTSPServerAuthFail(t *testing.T) {
|
||||
for _, ca := range []struct {
|
||||
name string
|
||||
|
|
|
@ -318,19 +318,19 @@ pathDefaults:
|
|||
# Default path settings -> Authentication
|
||||
|
||||
# Username required to publish.
|
||||
# SHA256-hashed values can be inserted with the "sha256:" prefix.
|
||||
# Hashed values can be inserted with the "argon2:" or "sha256:" prefix.
|
||||
publishUser:
|
||||
# Password required to publish.
|
||||
# SHA256-hashed values can be inserted with the "sha256:" prefix.
|
||||
# Hashed values can be inserted with the "argon2:" or "sha256:" prefix.
|
||||
publishPass:
|
||||
# IPs or networks (x.x.x.x/24) allowed to publish.
|
||||
publishIPs: []
|
||||
|
||||
# Username required to read.
|
||||
# SHA256-hashed values can be inserted with the "sha256:" prefix.
|
||||
# Hashed values can be inserted with the "argon2:" or "sha256:" prefix.
|
||||
readUser:
|
||||
# password required to read.
|
||||
# SHA256-hashed values can be inserted with the "sha256:" prefix.
|
||||
# Hashed values can be inserted with the "argon2:" or "sha256:" prefix.
|
||||
readPass:
|
||||
# IPs or networks (x.x.x.x/24) allowed to read.
|
||||
readIPs: []
|
||||
|
|
Loading…
Reference in New Issue