vmauth ip filters (refactoring) (#4059)

Added ip filters (allow_list and deny_list) for enterprise-version of vmauth (#3491)

---------

Signed-off-by: Alexander Marshalov <_@marshalov.org>
This commit is contained in:
Alexander Marshalov 2023-04-20 19:08:27 +02:00 committed by Aliaksandr Valialkin
parent 624e13a1d6
commit f5981c1447
No known key found for this signature in database
GPG Key ID: A72BEC6CD3D0DED1
3 changed files with 43 additions and 19 deletions

View File

@ -289,11 +289,11 @@ func initAuthConfig() {
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1240 // See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1240
sighupCh := procutil.NewSighupChan() sighupCh := procutil.NewSighupChan()
m, err := readAuthConfig(*authConfigPath) err := loadAuthConfig()
if err != nil { if err != nil {
logger.Fatalf("cannot load auth config from `-auth.config=%s`: %s", *authConfigPath, err) logger.Fatalf("cannot load auth config from `-auth.config=%s`: %s", *authConfigPath, err)
} }
authConfig.Store(m)
stopCh = make(chan struct{}) stopCh = make(chan struct{})
authConfigWG.Add(1) authConfigWG.Add(1)
go func() { go func() {
@ -324,44 +324,60 @@ func authConfigReloader(sighupCh <-chan os.Signal) {
procutil.SelfSIGHUP() procutil.SelfSIGHUP()
case <-sighupCh: case <-sighupCh:
logger.Infof("SIGHUP received; loading -auth.config=%q", *authConfigPath) logger.Infof("SIGHUP received; loading -auth.config=%q", *authConfigPath)
m, err := readAuthConfig(*authConfigPath) err := loadAuthConfig()
if err != nil { if err != nil {
logger.Errorf("failed to load -auth.config=%q; using the last successfully loaded config; error: %s", *authConfigPath, err) logger.Errorf("failed to load -auth.config=%q; using the last successfully loaded config; error: %s", *authConfigPath, err)
continue continue
} }
authConfig.Store(m)
logger.Infof("Successfully reloaded -auth.config=%q", *authConfigPath) logger.Infof("Successfully reloaded -auth.config=%q", *authConfigPath)
} }
} }
} }
var authConfig atomic.Value var authConfig atomic.Pointer[AuthConfig]
var authUsers atomic.Pointer[map[string]*UserInfo]
var authConfigWG sync.WaitGroup var authConfigWG sync.WaitGroup
var stopCh chan struct{} var stopCh chan struct{}
func readAuthConfig(path string) (map[string]*UserInfo, error) { func loadAuthConfig() error {
ac, err := readAuthConfig(*authConfigPath)
if err != nil {
return fmt.Errorf("failed to load `-auth.config=%q`: %s", *authConfigPath, err)
}
m, err := parseAuthConfigUsers(ac)
if err != nil {
return fmt.Errorf("failed to parse users from `-auth.config=%q`: %s", *authConfigPath, err)
}
logger.Infof("Loaded information about %d users from %q", len(m), *authConfigPath)
authConfig.Store(ac)
authUsers.Store(&m)
return nil
}
func readAuthConfig(path string) (*AuthConfig, error) {
data, err := fs.ReadFileOrHTTP(path) data, err := fs.ReadFileOrHTTP(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
m, err := parseAuthConfig(data) return parseAuthConfig(data)
if err != nil {
return nil, fmt.Errorf("cannot parse %q: %w", path, err)
}
logger.Infof("Loaded information about %d users from %q", len(m), path)
return m, nil
} }
func parseAuthConfig(data []byte) (map[string]*UserInfo, error) { func parseAuthConfig(data []byte) (*AuthConfig, error) {
var err error data, err := envtemplate.ReplaceBytes(data)
data, err = envtemplate.ReplaceBytes(data)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot expand environment vars: %w", err) return nil, fmt.Errorf("cannot expand environment vars: %w", err)
} }
var ac AuthConfig var ac AuthConfig
if err := yaml.UnmarshalStrict(data, &ac); err != nil { if err = yaml.UnmarshalStrict(data, &ac); err != nil {
return nil, fmt.Errorf("cannot unmarshal AuthConfig data: %w", err) return nil, fmt.Errorf("cannot unmarshal AuthConfig data: %w", err)
} }
return &ac, nil
}
func parseAuthConfigUsers(ac *AuthConfig) (map[string]*UserInfo, error) {
uis := ac.Users uis := ac.Users
if len(uis) == 0 { if len(uis) == 0 {
return nil, fmt.Errorf("`users` section cannot be empty in AuthConfig") return nil, fmt.Errorf("`users` section cannot be empty in AuthConfig")

View File

@ -13,7 +13,11 @@ import (
func TestParseAuthConfigFailure(t *testing.T) { func TestParseAuthConfigFailure(t *testing.T) {
f := func(s string) { f := func(s string) {
t.Helper() t.Helper()
_, err := parseAuthConfig([]byte(s)) ac, err := parseAuthConfig([]byte(s))
if err != nil {
return
}
_, err = parseAuthConfigUsers(ac)
if err == nil { if err == nil {
t.Fatalf("expecting non-nil error") t.Fatalf("expecting non-nil error")
} }
@ -202,7 +206,11 @@ users:
func TestParseAuthConfigSuccess(t *testing.T) { func TestParseAuthConfigSuccess(t *testing.T) {
f := func(s string, expectedAuthConfig map[string]*UserInfo) { f := func(s string, expectedAuthConfig map[string]*UserInfo) {
t.Helper() t.Helper()
m, err := parseAuthConfig([]byte(s)) ac, err := parseAuthConfig([]byte(s))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
m, err := parseAuthConfigUsers(ac)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }

View File

@ -94,7 +94,7 @@ func requestHandler(w http.ResponseWriter, r *http.Request) bool {
authToken = strings.Replace(authToken, "Token", "Bearer", 1) authToken = strings.Replace(authToken, "Token", "Bearer", 1)
} }
ac := authConfig.Load().(map[string]*UserInfo) ac := *authUsers.Load()
ui := ac[authToken] ui := ac[authToken]
if ui == nil { if ui == nil {
invalidAuthTokenRequests.Inc() invalidAuthTokenRequests.Inc()