feat: add --socket-perm option for UNIX socket file permissions

Add a --socket-perm flag (VGW_SOCKET_PERM env var) to control the
file-mode permissions on file-backed UNIX domain sockets. This allows
operators to limit access permission without relying on process umask.
The option applies to S3, admin, and WebUI sockets and has no effect
on TCP/IP addresses or Linux abstract namespace sockets.

Fixes #2010
This commit is contained in:
Ben McClelland
2026-04-20 19:07:08 -07:00
parent 7b65d744e6
commit 0dc074acbf
7 changed files with 97 additions and 10 deletions
+29 -2
View File
@@ -272,6 +272,15 @@ func resolveHostnameAddrs(address string) ([]string, error) {
return addrs, nil
}
// ListenerOptions configures optional behaviour for NewMultiAddrListener and
// NewMultiAddrTLSListener.
type ListenerOptions struct {
// SocketPerm, when non-zero, sets the file-mode permissions on file-backed
// UNIX sockets after binding. It is ignored for TCP/IP addresses and
// abstract namespace sockets.
SocketPerm os.FileMode
}
// NewMultiAddrListener creates listeners for all IP addresses that the hostname
// in the address resolves to. If the address is already an IP, it creates a
// single listener. Returns a MultiListener if multiple addresses are resolved,
@@ -281,7 +290,10 @@ func resolveHostnameAddrs(address string) ([]string, error) {
// - "/path/to/socket" or "./rel/socket" — file-backed socket; any stale
// socket file is removed before binding.
// - "@name" — Linux abstract namespace socket; no file is created or removed.
func NewMultiAddrListener(network, address string) (net.Listener, error) {
//
// opts.SocketPerm, when non-zero, sets the file-mode permissions on file-backed
// sockets after binding. It is ignored for TCP/IP addresses and abstract sockets.
func NewMultiAddrListener(network, address string, opts ListenerOptions) (net.Listener, error) {
if IsUnixSocketPath(address) {
// For file-backed sockets, remove any stale socket file so re-binding works cleanly.
// Abstract sockets (@name) have no filesystem entry; skip removal for them.
@@ -294,6 +306,12 @@ func NewMultiAddrListener(network, address string) (net.Listener, error) {
if err != nil {
return nil, fmt.Errorf("failed to bind unix socket listener %s: %w", address, err)
}
if opts.SocketPerm != 0 && !isAbstractSocket(address) {
if err := os.Chmod(address, opts.SocketPerm); err != nil {
ln.Close()
return nil, fmt.Errorf("failed to set permissions on socket %s: %w", address, err)
}
}
return NewMultiListener(ln), nil
}
@@ -328,7 +346,10 @@ func NewMultiAddrListener(network, address string) (net.Listener, error) {
// - "/path/to/socket" or "./rel/socket" — file-backed socket; any stale
// socket file is removed before binding.
// - "@name" — Linux abstract namespace socket; no file is created or removed.
func NewMultiAddrTLSListener(network, address string, getCertificateFunc func(*tls.ClientHelloInfo) (*tls.Certificate, error)) (net.Listener, error) {
//
// opts.SocketPerm, when non-zero, sets the file-mode permissions on file-backed
// sockets after binding. It is ignored for TCP/IP addresses and abstract sockets.
func NewMultiAddrTLSListener(network, address string, getCertificateFunc func(*tls.ClientHelloInfo) (*tls.Certificate, error), opts ListenerOptions) (net.Listener, error) {
config := &tls.Config{
MinVersion: tls.VersionTLS12,
GetCertificate: getCertificateFunc,
@@ -344,6 +365,12 @@ func NewMultiAddrTLSListener(network, address string, getCertificateFunc func(*t
if err != nil {
return nil, fmt.Errorf("failed to bind unix TLS socket listener %s: %w", address, err)
}
if opts.SocketPerm != 0 && !isAbstractSocket(address) {
if err := os.Chmod(address, opts.SocketPerm); err != nil {
ln.Close()
return nil, fmt.Errorf("failed to set permissions on socket %s: %w", address, err)
}
}
return NewMultiListener(tls.NewListener(ln, config)), nil
}
+2 -2
View File
@@ -301,7 +301,7 @@ func TestNewMultiAddrListener(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ln, err := NewMultiAddrListener("tcp", tt.address)
ln, err := NewMultiAddrListener("tcp", tt.address, ListenerOptions{})
if (err != nil) != tt.wantErr {
t.Errorf("NewMultiAddrListener() error = %v, wantErr %v", err, tt.wantErr)
return
@@ -353,7 +353,7 @@ func TestNewMultiAddrTLSListener(t *testing.T) {
return &cert, err
}
ln, err := NewMultiAddrTLSListener("tcp", "127.0.0.1:0", getCertFunc)
ln, err := NewMultiAddrTLSListener("tcp", "127.0.0.1:0", getCertFunc, ListenerOptions{})
if err != nil {
t.Fatalf("NewMultiAddrTLSListener() error = %v", err)
}