Make server unix socket file permission configurable via flag UnixSocketPerm (#2763)

* feat(any): Add flag unixsocketperm with default 0017 - #2625

Signed-off-by: johannesengl <hello@johannesengl.com>

* feat(server): Update unix socket file perm based on config - #2625

Signed-off-by: johannesengl <hello@johannesengl.com>

* Fix default value of socket.

* Refactor unix socket file creation.

* Remove misplaced comment

---------

Signed-off-by: johannesengl <hello@johannesengl.com>
Co-authored-by: Caio Cotts <caio@cotts.com.br>
Co-authored-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Johannes Engl 2024-01-20 20:50:30 +01:00 committed by GitHub
parent 8570773b90
commit 8f03454312
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 78 additions and 6 deletions

View File

@ -183,6 +183,7 @@ func init() {
rootCmd.Flags().IntP("port", "p", viper.GetInt("port"), "HTTP port Navidrome will listen to")
rootCmd.Flags().String("baseurl", viper.GetString("baseurl"), "base URL to configure Navidrome behind a proxy (ex: /music or http://my.server.com)")
rootCmd.Flags().String("tlscert", viper.GetString("tlscert"), "optional path to a TLS cert file (enables HTTPS listening)")
rootCmd.Flags().String("unixsocketperm", viper.GetString("unixsocketperm"), "optional file permission for the unix socket")
rootCmd.Flags().String("tlskey", viper.GetString("tlskey"), "optional path to a TLS key file (enables HTTPS listening)")
rootCmd.Flags().Duration("sessiontimeout", viper.GetDuration("sessiontimeout"), "how long Navidrome will wait before closing web ui idle sessions")
@ -199,6 +200,7 @@ func init() {
_ = viper.BindPFlag("address", rootCmd.Flags().Lookup("address"))
_ = viper.BindPFlag("port", rootCmd.Flags().Lookup("port"))
_ = viper.BindPFlag("tlscert", rootCmd.Flags().Lookup("tlscert"))
_ = viper.BindPFlag("unixsocketperm", rootCmd.Flags().Lookup("unixsocketperm"))
_ = viper.BindPFlag("tlskey", rootCmd.Flags().Lookup("tlskey"))
_ = viper.BindPFlag("baseurl", rootCmd.Flags().Lookup("baseurl"))

View File

@ -21,6 +21,7 @@ type configOptions struct {
ConfigFile string
Address string
Port int
UnixSocketPerm string
MusicFolder string
DataFolder string
CacheFolder string
@ -275,6 +276,7 @@ func init() {
viper.SetDefault("loglevel", "info")
viper.SetDefault("address", "0.0.0.0")
viper.SetDefault("port", 4533)
viper.SetDefault("unixsocketperm", "0660")
viper.SetDefault("sessiontimeout", consts.DefaultSessionTimeout)
viper.SetDefault("scaninterval", -1)
viper.SetDefault("scanschedule", "@every 1m")

View File

@ -9,6 +9,7 @@ import (
"net/url"
"os"
"path"
"strconv"
"strings"
"time"
@ -71,13 +72,9 @@ func (s *Server) Run(ctx context.Context, addr string, port int, tlsCert string,
var err error
if strings.HasPrefix(addr, "unix:") {
socketPath := strings.TrimPrefix(addr, "unix:")
// Remove the socket file if it already exists
if err := os.Remove(socketPath); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("error removing previous unix socket file: %w", err)
}
listener, err = net.Listen("unix", socketPath)
listener, err = createUnixSocketFile(socketPath, conf.Server.UnixSocketPerm)
if err != nil {
return fmt.Errorf("error creating unix socket listener: %w", err)
return err
}
} else {
addr = fmt.Sprintf("%s:%d", addr, port)
@ -136,6 +133,28 @@ func (s *Server) Run(ctx context.Context, addr string, port int, tlsCert string,
return nil
}
func createUnixSocketFile(socketPath string, socketPerm string) (net.Listener, error) {
// Remove the socket file if it already exists
if err := os.Remove(socketPath); err != nil && !os.IsNotExist(err) {
return nil, fmt.Errorf("error removing previous unix socket file: %w", err)
}
// Create listener
listener, err := net.Listen("unix", socketPath)
if err != nil {
return nil, fmt.Errorf("error creating unix socket listener: %w", err)
}
// Converts the socketPerm to uint and updates the permission of the unix socket file
perm, err := strconv.ParseUint(socketPerm, 8, 32)
if err != nil {
return nil, fmt.Errorf("error parsing unix socket file permissions: %w", err)
}
err = os.Chmod(socketPath, os.FileMode(perm))
if err != nil {
return nil, fmt.Errorf("error updating permission of unix socket file: %w", err)
}
return listener, nil
}
func (s *Server) initRoutes() {
s.appRoot = path.Join(conf.Server.BasePath, consts.URLPathUI)

View File

@ -1,8 +1,11 @@
package server
import (
"io/fs"
"net/http"
"net/url"
"os"
"path/filepath"
"github.com/navidrome/navidrome/conf"
. "github.com/onsi/ginkgo/v2"
@ -58,3 +61,49 @@ var _ = Describe("AbsoluteURL", func() {
})
})
})
var _ = Describe("createUnixSocketFile", func() {
var socketPath string
BeforeEach(func() {
tempDir, _ := os.MkdirTemp("", "create_unix_socket_file_test")
socketPath = filepath.Join(tempDir, "test.sock")
DeferCleanup(func() {
_ = os.RemoveAll(tempDir)
})
})
When("unixSocketPerm is valid", func() {
It("updates the permission of the unix socket file and returns nil", func() {
_, err := createUnixSocketFile(socketPath, "0777")
fileInfo, _ := os.Stat(socketPath)
actualPermission := fileInfo.Mode().Perm()
Expect(actualPermission).To(Equal(os.FileMode(0777)))
Expect(err).ToNot(HaveOccurred())
})
})
When("unixSocketPerm is invalid", func() {
It("returns an error", func() {
_, err := createUnixSocketFile(socketPath, "invalid")
Expect(err).To(HaveOccurred())
})
})
When("file already exists", func() {
It("recreates the file as a socket with the right permissions", func() {
_, err := os.Create(socketPath)
Expect(err).ToNot(HaveOccurred())
Expect(os.Chmod(socketPath, os.FileMode(0777))).To(Succeed())
_, err = createUnixSocketFile(socketPath, "0600")
Expect(err).ToNot(HaveOccurred())
fileInfo, _ := os.Stat(socketPath)
Expect(fileInfo.Mode().Perm()).To(Equal(os.FileMode(0600)))
Expect(fileInfo.Mode().Type()).To(Equal(fs.ModeSocket))
})
})
})