[management] validate the domain for the flock in proxy (#7501)

This commit is contained in:
Pascal Fischer
2026-09-11 13:51:03 +02:00
committed by GitHub
parent 1047df5fa2
commit ad3f570e32
2 changed files with 42 additions and 4 deletions
+12 -4
View File
@@ -2,12 +2,14 @@ package acme
import (
"context"
"fmt"
"path/filepath"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/proxy/internal/flock"
"github.com/netbirdio/netbird/proxy/internal/k8s"
"github.com/netbirdio/netbird/shared/management/domain"
)
// certLocker provides distributed mutual exclusion for certificate operations.
@@ -74,9 +76,15 @@ func newFlockLocker(certDir string, logger *log.Logger) *flockLocker {
return &flockLocker{certDir: certDir, logger: logger}
}
// Lock acquires an advisory file lock for the given domain.
func (l *flockLocker) Lock(ctx context.Context, domain string) (func(), error) {
lockPath := filepath.Join(l.certDir, domain+".lock")
// Lock acquires an advisory file lock for the given domain. The domain must
// be a valid hostname so the lock file always resolves to a direct child of
// certDir; anything else is rejected before touching the filesystem.
func (l *flockLocker) Lock(ctx context.Context, name string) (func(), error) {
if !domain.IsValidDomainNoWildcard(name) {
return nil, fmt.Errorf("invalid domain %q for lock file", name)
}
lockPath := filepath.Join(l.certDir, name+".lock")
lockFile, err := flock.Lock(ctx, lockPath)
if err != nil {
return nil, err
@@ -89,7 +97,7 @@ func (l *flockLocker) Lock(ctx context.Context, domain string) (func(), error) {
return func() {
if err := flock.Unlock(lockFile); err != nil {
l.logger.Debugf("release cert lock for domain %q: %v", domain, err)
l.logger.Debugf("release cert lock for domain %q: %v", name, err)
}
}, nil
}
+30
View File
@@ -63,3 +63,33 @@ func TestNewCertLockerK8sFallsBackToFlock(t *testing.T) {
_, ok := locker.(*flockLocker)
assert.True(t, ok, "k8s-lease without SA should fall back to flockLocker")
}
func TestFlockLockerRejectsUnsafeDomain(t *testing.T) {
root := t.TempDir()
certDir := filepath.Join(root, "certs")
require.NoError(t, os.Mkdir(certDir, 0o700))
locker := newFlockLocker(certDir, nil)
for _, d := range []string{
"",
".",
"..",
"../escape",
"../../etc/cron.d/attacker",
"sub/dir.example.com",
`back\slash.example.com`,
"*.example.com",
} {
unlock, err := locker.Lock(context.Background(), d)
assert.Error(t, err, "domain %q", d)
assert.Nil(t, unlock, "domain %q", d)
}
assert.NoFileExists(t, filepath.Join(root, "escape.lock"))
certEntries, err := os.ReadDir(certDir)
require.NoError(t, err)
assert.Empty(t, certEntries)
rootEntries, err := os.ReadDir(root)
require.NoError(t, err)
assert.Len(t, rootEntries, 1)
}