- refactor secrets
- add support for env in config - redact sensitive information in logs/responses
This commit is contained in:
@@ -1,12 +1,22 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/yaml"
|
||||
)
|
||||
|
||||
var (
|
||||
secretReplacer *strings.Replacer
|
||||
secretValues map[string]bool // Tracker für alle bekannten Secret-Werte
|
||||
secretMutex sync.RWMutex
|
||||
)
|
||||
|
||||
func QuoteSplit(s string) []string {
|
||||
@@ -40,6 +50,15 @@ func QuoteSplit(s string) []string {
|
||||
|
||||
// ReplaceEnvVars - support format ${CAMERA_PASSWORD} and ${RTSP_USER:admin}
|
||||
func ReplaceEnvVars(text string) string {
|
||||
var cfg struct {
|
||||
Env map[string]string `yaml:"env"`
|
||||
Secrets map[string]map[string]string `yaml:"secrets"`
|
||||
}
|
||||
|
||||
yaml.Unmarshal([]byte(text), &cfg)
|
||||
|
||||
buildSecretReplacer(cfg)
|
||||
|
||||
re := regexp.MustCompile(`\${([^}{]+)}`)
|
||||
return re.ReplaceAllStringFunc(text, func(match string) string {
|
||||
key := match[2 : len(match)-1]
|
||||
@@ -63,6 +82,23 @@ func ReplaceEnvVars(text string) string {
|
||||
return value
|
||||
}
|
||||
|
||||
if cfg.Env != nil {
|
||||
if value, ok := cfg.Env[key]; ok {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Secrets != nil {
|
||||
for secretName, secretValues := range cfg.Secrets {
|
||||
for k, v := range secretValues {
|
||||
name := fmt.Sprintf("%s_%s", secretName, k)
|
||||
if key == name {
|
||||
return v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if dok {
|
||||
return def
|
||||
}
|
||||
@@ -76,3 +112,84 @@ func RunUntilSignal() {
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
println("exit with signal:", (<-sigs).String())
|
||||
}
|
||||
|
||||
func Redact(text string) string {
|
||||
secretMutex.RLock()
|
||||
defer secretMutex.RUnlock()
|
||||
|
||||
if secretReplacer == nil {
|
||||
return text
|
||||
}
|
||||
|
||||
return secretReplacer.Replace(text)
|
||||
}
|
||||
|
||||
func buildSecretReplacer(cfg struct {
|
||||
Env map[string]string `yaml:"env"`
|
||||
Secrets map[string]map[string]string `yaml:"secrets"`
|
||||
}) {
|
||||
secretMutex.Lock()
|
||||
defer secretMutex.Unlock()
|
||||
|
||||
if secretValues == nil {
|
||||
secretValues = make(map[string]bool)
|
||||
}
|
||||
|
||||
var newSecrets []string
|
||||
|
||||
if dir, ok := os.LookupEnv("CREDENTIALS_DIRECTORY"); ok {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err == nil {
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
value, err := os.ReadFile(filepath.Join(dir, entry.Name()))
|
||||
if err == nil {
|
||||
cleanValue := strings.TrimSpace(string(value))
|
||||
if len(cleanValue) > 0 && !secretValues[cleanValue] {
|
||||
secretValues[cleanValue] = true
|
||||
newSecrets = append(newSecrets, cleanValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Env != nil {
|
||||
for _, value := range cfg.Env {
|
||||
if len(value) > 0 && !secretValues[value] {
|
||||
secretValues[value] = true
|
||||
newSecrets = append(newSecrets, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Secrets != nil {
|
||||
for _, secretMap := range cfg.Secrets {
|
||||
for _, value := range secretMap {
|
||||
if len(value) > 0 && !secretValues[value] {
|
||||
secretValues[value] = true
|
||||
newSecrets = append(newSecrets, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(newSecrets) > 0 {
|
||||
rebuildReplacer()
|
||||
}
|
||||
}
|
||||
|
||||
func rebuildReplacer() {
|
||||
var replacements []string
|
||||
|
||||
for secret := range secretValues {
|
||||
replacements = append(replacements, secret, "*****")
|
||||
}
|
||||
|
||||
if len(replacements) > 0 {
|
||||
secretReplacer = strings.NewReplacer(replacements...)
|
||||
} else {
|
||||
secretReplacer = nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user