Merge pull request #9399 from influxdata/js-9266-unix-socket-permissions

Allow customizing the unix socket group and permissions created by the server
pull/9687/head
Jonathan A. Sternberg 2018-04-05 15:02:49 -05:00 committed by GitHub
commit b24afed528
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 188 additions and 39 deletions

View File

@ -1,6 +1,9 @@
package httpd
import "github.com/influxdata/influxdb/monitor/diagnostics"
import (
"github.com/influxdata/influxdb/monitor/diagnostics"
"github.com/influxdata/influxdb/toml"
)
const (
// DefaultBindAddress is the default address to bind to.
@ -18,39 +21,42 @@ const (
// Config represents a configuration for a HTTP service.
type Config struct {
Enabled bool `toml:"enabled"`
BindAddress string `toml:"bind-address"`
AuthEnabled bool `toml:"auth-enabled"`
LogEnabled bool `toml:"log-enabled"`
WriteTracing bool `toml:"write-tracing"`
PprofEnabled bool `toml:"pprof-enabled"`
HTTPSEnabled bool `toml:"https-enabled"`
HTTPSCertificate string `toml:"https-certificate"`
HTTPSPrivateKey string `toml:"https-private-key"`
MaxRowLimit int `toml:"max-row-limit"`
MaxConnectionLimit int `toml:"max-connection-limit"`
SharedSecret string `toml:"shared-secret"`
Realm string `toml:"realm"`
UnixSocketEnabled bool `toml:"unix-socket-enabled"`
BindSocket string `toml:"bind-socket"`
MaxBodySize int `toml:"max-body-size"`
AccessLogPath string `toml:"access-log-path"`
Enabled bool `toml:"enabled"`
BindAddress string `toml:"bind-address"`
AuthEnabled bool `toml:"auth-enabled"`
LogEnabled bool `toml:"log-enabled"`
WriteTracing bool `toml:"write-tracing"`
PprofEnabled bool `toml:"pprof-enabled"`
HTTPSEnabled bool `toml:"https-enabled"`
HTTPSCertificate string `toml:"https-certificate"`
HTTPSPrivateKey string `toml:"https-private-key"`
MaxRowLimit int `toml:"max-row-limit"`
MaxConnectionLimit int `toml:"max-connection-limit"`
SharedSecret string `toml:"shared-secret"`
Realm string `toml:"realm"`
UnixSocketEnabled bool `toml:"unix-socket-enabled"`
UnixSocketGroup *toml.Group `toml:"unix-socket-group"`
UnixSocketPermissions toml.FileMode `toml:"unix-socket-permissions"`
BindSocket string `toml:"bind-socket"`
MaxBodySize int `toml:"max-body-size"`
AccessLogPath string `toml:"access-log-path"`
}
// NewConfig returns a new Config with default settings.
func NewConfig() Config {
return Config{
Enabled: true,
BindAddress: DefaultBindAddress,
LogEnabled: true,
PprofEnabled: true,
HTTPSEnabled: false,
HTTPSCertificate: "/etc/ssl/influxdb.pem",
MaxRowLimit: 0,
Realm: DefaultRealm,
UnixSocketEnabled: false,
BindSocket: DefaultBindSocket,
MaxBodySize: DefaultMaxBodySize,
Enabled: true,
BindAddress: DefaultBindAddress,
LogEnabled: true,
PprofEnabled: true,
HTTPSEnabled: false,
HTTPSCertificate: "/etc/ssl/influxdb.pem",
MaxRowLimit: 0,
Realm: DefaultRealm,
UnixSocketEnabled: false,
UnixSocketPermissions: 0777,
BindSocket: DefaultBindSocket,
MaxBodySize: DefaultMaxBodySize,
}
}

View File

@ -55,6 +55,8 @@ type Service struct {
err chan error
unixSocket bool
unixSocketPerm uint32
unixSocketGroup int
bindSocket string
unixSocketListener net.Listener
@ -66,20 +68,24 @@ type Service struct {
// NewService returns a new instance of Service.
func NewService(c Config) *Service {
s := &Service{
addr: c.BindAddress,
https: c.HTTPSEnabled,
cert: c.HTTPSCertificate,
key: c.HTTPSPrivateKey,
limit: c.MaxConnectionLimit,
err: make(chan error),
unixSocket: c.UnixSocketEnabled,
bindSocket: c.BindSocket,
Handler: NewHandler(c),
Logger: zap.NewNop(),
addr: c.BindAddress,
https: c.HTTPSEnabled,
cert: c.HTTPSCertificate,
key: c.HTTPSPrivateKey,
limit: c.MaxConnectionLimit,
err: make(chan error),
unixSocket: c.UnixSocketEnabled,
unixSocketPerm: uint32(c.UnixSocketPermissions),
bindSocket: c.BindSocket,
Handler: NewHandler(c),
Logger: zap.NewNop(),
}
if s.key == "" {
s.key = s.cert
}
if c.UnixSocketGroup != nil {
s.unixSocketGroup = int(*c.UnixSocketGroup)
}
s.Handler.Logger = s.Logger
return s
}
@ -133,6 +139,16 @@ func (s *Service) Open() error {
if err != nil {
return err
}
if s.unixSocketPerm != 0 {
if err := os.Chmod(s.bindSocket, os.FileMode(s.unixSocketPerm)); err != nil {
return err
}
}
if s.unixSocketGroup != 0 {
if err := os.Chown(s.bindSocket, -1, s.unixSocketGroup); err != nil {
return err
}
}
s.Logger.Info("Listening on unix socket",
zap.Stringer("addr", listener.Addr()))

View File

@ -3,9 +3,11 @@ package toml // import "github.com/influxdata/influxdb/toml"
import (
"encoding"
"errors"
"fmt"
"math"
"os"
"os/user"
"reflect"
"strconv"
"strings"
@ -94,6 +96,53 @@ func (s *Size) UnmarshalText(text []byte) error {
return nil
}
type FileMode uint32
func (m *FileMode) UnmarshalText(text []byte) error {
// Ignore if there is no value set.
if len(text) == 0 {
return nil
}
mode, err := strconv.ParseUint(string(text), 8, 32)
if err != nil {
return err
} else if mode == 0 {
return errors.New("file mode cannot be zero")
}
*m = FileMode(mode)
return nil
}
func (m FileMode) MarshalText() (text []byte, err error) {
if m != 0 {
return []byte(fmt.Sprintf("%04o", m)), nil
}
return nil, nil
}
type Group int
func (g *Group) UnmarshalTOML(data interface{}) error {
if grpName, ok := data.(string); ok {
group, err := user.LookupGroup(grpName)
if err != nil {
return err
}
gid, err := strconv.Atoi(group.Gid)
if err != nil {
return err
}
*g = Group(gid)
return nil
} else if gid, ok := data.(int64); ok {
*g = Group(gid)
return nil
}
return errors.New("group must be a name (string) or id (int)")
}
func ApplyEnvOverrides(getenv func(string) string, prefix string, val interface{}) error {
if getenv == nil {
getenv = os.Getenv

View File

@ -4,6 +4,9 @@ import (
"bytes"
"fmt"
"math"
"os/user"
"runtime"
"strconv"
"strings"
"testing"
"time"
@ -62,6 +65,81 @@ func TestSize_UnmarshalText(t *testing.T) {
}
}
func TestFileMode_MarshalText(t *testing.T) {
for _, test := range []struct {
mode int
want string
}{
{mode: 0755, want: `0755`},
{mode: 0777, want: `0777`},
{mode: 01777, want: `1777`},
} {
mode := itoml.FileMode(test.mode)
if got, err := mode.MarshalText(); err != nil {
t.Errorf("unexpected error: %s", err)
} else if test.want != string(got) {
t.Errorf("wanted: %v got: %v", test.want, string(got))
}
}
}
func TestFileMode_UnmarshalText(t *testing.T) {
for _, test := range []struct {
str string
want uint32
}{
{str: ``, want: 0},
{str: `0777`, want: 0777},
{str: `777`, want: 0777},
{str: `1777`, want: 01777},
{str: `0755`, want: 0755},
} {
var mode itoml.FileMode
if err := mode.UnmarshalText([]byte(test.str)); err != nil {
t.Errorf("unexpected error: %s", err)
} else if mode != itoml.FileMode(test.want) {
t.Errorf("wanted: %04o got: %04o", test.want, mode)
}
}
}
func TestGroup_UnmarshalTOML(t *testing.T) {
// Skip this test on windows since it does not support setting the group anyway.
if runtime.GOOS == "windows" {
t.Skip("unsupported on windows")
}
// Find the current user ID so we can use that group name.
u, err := user.Current()
if err != nil {
t.Skipf("unable to find the current user: %s", err)
}
// Lookup the group by the group id.
gr, err := user.LookupGroupId(u.Gid)
if err == nil {
var group itoml.Group
if err := group.UnmarshalTOML(gr.Name); err != nil {
t.Fatalf("unexpected error: %s", err)
} else if got, want := u.Gid, strconv.Itoa(int(group)); got != want {
t.Fatalf("unexpected group id: %s != %s", got, want)
}
}
// Attempt to convert the group to an integer so we can test reading an integer.
gid, err := strconv.Atoi(u.Gid)
if err != nil {
t.Fatalf("group id is not an integer: %s", err)
}
var group itoml.Group
if err := group.UnmarshalTOML(int64(gid)); err != nil {
t.Fatalf("unexpected error: %s", err)
} else if int(group) != gid {
t.Fatalf("unexpected group id: %d != %d", gid, int(group))
}
}
func TestConfig_Encode(t *testing.T) {
var c run.Config
c.Coordinator.WriteTimeout = itoml.Duration(time.Minute)