mirror of
https://github.com/versity/versitygw.git
synced 2026-08-17 12:46:23 +00:00
Merge pull request #2068 from versity/ben/socket-permissions
This commit is contained in:
@@ -105,6 +105,7 @@ var (
|
||||
disableACLs bool
|
||||
mpMaxParts int
|
||||
copyObjectThreshold int64
|
||||
socketPerm string
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -772,6 +773,12 @@ func initFlags() []cli.Flag {
|
||||
Value: 5 * 1024 * 1024 * 1024,
|
||||
Destination: ©ObjectThreshold,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "socket-perm",
|
||||
Usage: "file permissions for file-backed UNIX domain sockets (octal, e.g. '0660'); ignored for TCP/IP and abstract namespace sockets",
|
||||
EnvVars: []string{"VGW_SOCKET_PERM"},
|
||||
Destination: &socketPerm,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -855,6 +862,15 @@ func runGateway(ctx context.Context, be backend.Backend) error {
|
||||
|
||||
utils.SetBucketNameValidationStrict(!disableStrictBucketNames)
|
||||
|
||||
var parsedSocketPerm os.FileMode
|
||||
if socketPerm != "" {
|
||||
perm, err := strconv.ParseUint(socketPerm, 8, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid --socket-perm value %q: must be an octal integer (e.g. '0660'): %w", socketPerm, err)
|
||||
}
|
||||
parsedSocketPerm = os.FileMode(perm)
|
||||
}
|
||||
|
||||
if pprof != "" {
|
||||
// listen on specified port for pprof debug
|
||||
// point browser to http://<ip:port>/debug/pprof/
|
||||
@@ -867,6 +883,9 @@ func runGateway(ctx context.Context, be backend.Backend) error {
|
||||
s3api.WithConcurrencyLimiter(maxConnections, maxRequests),
|
||||
s3api.WithMpMaxParts(mpMaxParts),
|
||||
}
|
||||
if socketPerm != "" {
|
||||
opts = append(opts, s3api.WithSocketPerm(parsedSocketPerm))
|
||||
}
|
||||
if corsAllowOrigin != "" {
|
||||
opts = append(opts, s3api.WithCORSAllowOrigin(corsAllowOrigin))
|
||||
}
|
||||
@@ -1103,6 +1122,9 @@ func runGateway(ctx context.Context, be backend.Backend) error {
|
||||
if debug {
|
||||
opts = append(opts, s3api.WithAdminDebug())
|
||||
}
|
||||
if socketPerm != "" {
|
||||
opts = append(opts, s3api.WithAdminSocketPerm(parsedSocketPerm))
|
||||
}
|
||||
|
||||
admSrv = s3api.NewAdminServer(be, middlewares.RootUserConfig{Access: rootUserAccess, Secret: rootUserSecret}, region, iam, loggers.AdminLogger, srv.Router.Ctrl, opts...)
|
||||
}
|
||||
@@ -1215,6 +1237,9 @@ func runGateway(ctx context.Context, be backend.Backend) error {
|
||||
if webuiPathPrefix != "" {
|
||||
webOpts = append(webOpts, webui.WithPathPrefix(webuiPathPrefix))
|
||||
}
|
||||
if socketPerm != "" {
|
||||
webOpts = append(webOpts, webui.WithSocketPerm(parsedSocketPerm))
|
||||
}
|
||||
|
||||
webSrv = webui.NewServer(&webui.ServerConfig{
|
||||
Gateways: gateways,
|
||||
|
||||
@@ -102,6 +102,14 @@ ROOT_SECRET_ACCESS_KEY=
|
||||
#VGW_ADMIN_CERT=
|
||||
#VGW_ADMIN_CERT_KEY=
|
||||
|
||||
# The VGW_SOCKET_PERM option sets the file-mode permissions for file-backed
|
||||
# UNIX domain sockets created by the S3, admin, and WebUI servers. The value
|
||||
# must be an octal integer (e.g. 0660 to allow owner and group read/write
|
||||
# access). This option has no effect on TCP/IP addresses or Linux abstract
|
||||
# namespace sockets (those prefixed with "@"). When not set, the socket file
|
||||
# permissions are determined by the process umask.
|
||||
#VGW_SOCKET_PERM=
|
||||
|
||||
# The VGW_QUIET option when set will supress the S3 server request summary
|
||||
# logging to stdout.
|
||||
#VGW_QUIET=false
|
||||
|
||||
+11
-2
@@ -17,6 +17,7 @@ package s3api
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||
@@ -40,6 +41,7 @@ type S3AdminServer struct {
|
||||
corsAllowOrigin string
|
||||
maxConnections int
|
||||
maxRequests int
|
||||
socketPerm os.FileMode
|
||||
}
|
||||
|
||||
func NewAdminServer(be backend.Backend, root middlewares.RootUserConfig, region string, iam auth.IAMService, l s3log.AuditLogger, ctrl controllers.S3ApiController, opts ...AdminOpt) *S3AdminServer {
|
||||
@@ -124,6 +126,13 @@ func WithAdminConcurrencyLimiter(maxConnections, maxRequests int) AdminOpt {
|
||||
}
|
||||
}
|
||||
|
||||
// WithAdminSocketPerm sets the file-mode permissions applied to file-backed
|
||||
// UNIX domain sockets after binding. It has no effect on TCP/IP or abstract
|
||||
// namespace sockets.
|
||||
func WithAdminSocketPerm(perm os.FileMode) AdminOpt {
|
||||
return func(s *S3AdminServer) { s.socketPerm = perm }
|
||||
}
|
||||
|
||||
// ServeMultiPort creates listeners for multiple port specifications and serves
|
||||
// on all of them simultaneously. This supports listening on multiple ports and/or
|
||||
// addresses (e.g., [":8080", "localhost:8081"]).
|
||||
@@ -140,9 +149,9 @@ func (sa *S3AdminServer) ServeMultiPort(ports []string) error {
|
||||
var err error
|
||||
|
||||
if sa.CertStorage != nil {
|
||||
ln, err = utils.NewMultiAddrTLSListener(sa.app.Config().Network, portSpec, sa.CertStorage.GetCertificate)
|
||||
ln, err = utils.NewMultiAddrTLSListener(sa.app.Config().Network, portSpec, sa.CertStorage.GetCertificate, utils.ListenerOptions{SocketPerm: sa.socketPerm})
|
||||
} else {
|
||||
ln, err = utils.NewMultiAddrListener(sa.app.Config().Network, portSpec)
|
||||
ln, err = utils.NewMultiAddrListener(sa.app.Config().Network, portSpec, utils.ListenerOptions{SocketPerm: sa.socketPerm})
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
+11
-2
@@ -19,6 +19,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -54,6 +55,7 @@ type S3ApiServer struct {
|
||||
maxRequests int
|
||||
webuiMountPrefix string
|
||||
webuiSrvCfg *webui.ServerConfig
|
||||
socketPerm os.FileMode
|
||||
}
|
||||
|
||||
func New(
|
||||
@@ -217,6 +219,13 @@ func WithConcurrencyLimiter(maxConnections, maxRequests int) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithSocketPerm sets the file-mode permissions applied to file-backed UNIX
|
||||
// domain sockets after binding. It has no effect on TCP/IP or abstract
|
||||
// namespace sockets.
|
||||
func WithSocketPerm(perm os.FileMode) Option {
|
||||
return func(s *S3ApiServer) { s.socketPerm = perm }
|
||||
}
|
||||
|
||||
// WithDisableACL disables the s3 api server ACLs, by ignoring all
|
||||
// bucket/object ACL headers
|
||||
func WithDisableACL() Option {
|
||||
@@ -239,9 +248,9 @@ func (sa *S3ApiServer) ServeMultiPort(ports []string) error {
|
||||
var err error
|
||||
|
||||
if sa.CertStorage != nil {
|
||||
ln, err = utils.NewMultiAddrTLSListener(sa.app.Config().Network, portSpec, sa.CertStorage.GetCertificate)
|
||||
ln, err = utils.NewMultiAddrTLSListener(sa.app.Config().Network, portSpec, sa.CertStorage.GetCertificate, utils.ListenerOptions{SocketPerm: sa.socketPerm})
|
||||
} else {
|
||||
ln, err = utils.NewMultiAddrListener(sa.app.Config().Network, portSpec)
|
||||
ln, err = utils.NewMultiAddrListener(sa.app.Config().Network, portSpec, utils.ListenerOptions{SocketPerm: sa.socketPerm})
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to bind s3 listener %s: %w", portSpec, err)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
+11
-2
@@ -19,6 +19,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
@@ -43,6 +44,7 @@ type Server struct {
|
||||
config *ServerConfig
|
||||
pathPrefix string
|
||||
quiet bool
|
||||
socketPerm os.FileMode
|
||||
}
|
||||
|
||||
// Option sets various options for NewServer()
|
||||
@@ -63,6 +65,13 @@ func WithPathPrefix(prefix string) Option {
|
||||
return func(s *Server) { s.pathPrefix = prefix }
|
||||
}
|
||||
|
||||
// WithSocketPerm sets the file-mode permissions applied to file-backed UNIX
|
||||
// domain sockets after binding. It has no effect on TCP/IP or abstract
|
||||
// namespace sockets.
|
||||
func WithSocketPerm(perm os.FileMode) Option {
|
||||
return func(s *Server) { s.socketPerm = perm }
|
||||
}
|
||||
|
||||
// NewServer creates a new GUI server instance
|
||||
func NewServer(cfg *ServerConfig, opts ...Option) *Server {
|
||||
app := fiber.New(fiber.Config{
|
||||
@@ -175,9 +184,9 @@ func (s *Server) ServeMultiPort(ports []string) error {
|
||||
var err error
|
||||
|
||||
if s.CertStorage != nil {
|
||||
ln, err = utils.NewMultiAddrTLSListener(s.app.Config().Network, addrSpec, s.CertStorage.GetCertificate)
|
||||
ln, err = utils.NewMultiAddrTLSListener(s.app.Config().Network, addrSpec, s.CertStorage.GetCertificate, utils.ListenerOptions{SocketPerm: s.socketPerm})
|
||||
} else {
|
||||
ln, err = utils.NewMultiAddrListener(s.app.Config().Network, addrSpec)
|
||||
ln, err = utils.NewMultiAddrListener(s.app.Config().Network, addrSpec, utils.ListenerOptions{SocketPerm: s.socketPerm})
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user