feature: switch to simple user
This commit is contained in:
@@ -30,6 +30,23 @@ func main() {
|
||||
log.Fatalf("config: %v", err)
|
||||
}
|
||||
|
||||
policyListener, err := mailcloak.OpenPolicyListener(cfg)
|
||||
if err != nil {
|
||||
log.Fatalf("policy listener: %v", err)
|
||||
}
|
||||
|
||||
socketmapListener, err := mailcloak.OpenSocketmapListener(cfg)
|
||||
if err != nil {
|
||||
_ = policyListener.Close()
|
||||
log.Fatalf("socketmap listener: %v", err)
|
||||
}
|
||||
|
||||
if err := mailcloak.DropPrivileges(cfg); err != nil {
|
||||
_ = policyListener.Close()
|
||||
_ = socketmapListener.Close()
|
||||
log.Fatalf("privileges: %v", err)
|
||||
}
|
||||
|
||||
db, err := mailcloak.OpenAliasDB(cfg.SQLite.Path)
|
||||
if err != nil {
|
||||
log.Fatalf("sqlite: %v", err)
|
||||
@@ -44,14 +61,14 @@ func main() {
|
||||
|
||||
// Start socketmap server
|
||||
go func() {
|
||||
if err := mailcloak.RunSocketmap(ctx, cfg, db); err != nil {
|
||||
if err := mailcloak.ServeSocketmap(ctx, cfg, db, socketmapListener); err != nil {
|
||||
log.Fatalf("socketmap: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Start policy server
|
||||
go func() {
|
||||
if err := mailcloak.RunPolicy(ctx, cfg, db, kc, cache); err != nil {
|
||||
if err := mailcloak.ServePolicy(ctx, cfg, db, kc, cache, policyListener); err != nil {
|
||||
log.Fatalf("policy: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -24,3 +24,6 @@ sockets:
|
||||
socket_owner_user: "postfix"
|
||||
socket_owner_group: "postfix"
|
||||
socket_mode: "0660"
|
||||
|
||||
daemon:
|
||||
user: "mailcloak"
|
||||
|
||||
@@ -8,6 +8,10 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Daemon struct {
|
||||
User string `yaml:"user"`
|
||||
} `yaml:"daemon"`
|
||||
|
||||
Keycloak struct {
|
||||
BaseURL string `yaml:"base_url"`
|
||||
Realm string `yaml:"realm"`
|
||||
@@ -58,5 +62,8 @@ func LoadConfig(path string) (*Config, error) {
|
||||
if cfg.Policy.KeycloakFailureMode == "" {
|
||||
cfg.Policy.KeycloakFailureMode = "tempfail"
|
||||
}
|
||||
if cfg.Daemon.User == "" {
|
||||
cfg.Daemon.User = "mailcloak"
|
||||
}
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
@@ -38,20 +38,26 @@ func (c *Cache) Put(key, val string, ok bool) {
|
||||
c.m[key] = cacheItem{val: val, ok: ok, expires: time.Now().Add(c.ttl)}
|
||||
}
|
||||
|
||||
func RunPolicy(ctx context.Context, cfg *Config, db *AliasDB, kc *Keycloak, cache *Cache) error {
|
||||
func OpenPolicyListener(cfg *Config) (net.Listener, error) {
|
||||
sock := cfg.Sockets.PolicySocket
|
||||
_ = os.Remove(sock)
|
||||
|
||||
l, err := net.Listen("unix", sock)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
if err := ChownChmodSocket(sock, cfg); err != nil {
|
||||
return err
|
||||
_ = l.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func ServePolicy(ctx context.Context, cfg *Config, db *AliasDB, kc *Keycloak, cache *Cache, l net.Listener) error {
|
||||
defer l.Close()
|
||||
|
||||
for {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
@@ -66,6 +72,14 @@ func RunPolicy(ctx context.Context, cfg *Config, db *AliasDB, kc *Keycloak, cach
|
||||
}
|
||||
}
|
||||
|
||||
func RunPolicy(ctx context.Context, cfg *Config, db *AliasDB, kc *Keycloak, cache *Cache) error {
|
||||
l, err := OpenPolicyListener(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ServePolicy(ctx, cfg, db, kc, cache, l)
|
||||
}
|
||||
|
||||
func handlePolicyConn(conn net.Conn, cfg *Config, db *AliasDB, kc *Keycloak, cache *Cache) {
|
||||
defer conn.Close()
|
||||
r := bufio.NewReader(conn)
|
||||
|
||||
60
internal/mailcloak/privileges.go
Normal file
60
internal/mailcloak/privileges.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package mailcloak
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func DropPrivileges(cfg *Config) error {
|
||||
userName := cfg.Daemon.User
|
||||
if userName == "" {
|
||||
userName = "mailcloak"
|
||||
}
|
||||
|
||||
u, err := user.Lookup(userName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("lookup user %s: %w", userName, err)
|
||||
}
|
||||
uid, err := strconv.Atoi(u.Uid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("bad uid for %s: %w", userName, err)
|
||||
}
|
||||
gid, err := strconv.Atoi(u.Gid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("bad gid for %s: %w", userName, err)
|
||||
}
|
||||
|
||||
euid := os.Geteuid()
|
||||
if euid == uid {
|
||||
return nil
|
||||
}
|
||||
if euid != 0 {
|
||||
return fmt.Errorf("must run as root to switch user to %s", userName)
|
||||
}
|
||||
|
||||
if groups, err := u.GroupIds(); err == nil {
|
||||
gids := make([]int, 0, len(groups))
|
||||
for _, g := range groups {
|
||||
if id, err := strconv.Atoi(g); err == nil {
|
||||
gids = append(gids, id)
|
||||
}
|
||||
}
|
||||
if len(gids) > 0 {
|
||||
if err := syscall.Setgroups(gids); err != nil {
|
||||
return fmt.Errorf("setgroups: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := syscall.Setgid(gid); err != nil {
|
||||
return fmt.Errorf("setgid: %w", err)
|
||||
}
|
||||
if err := syscall.Setuid(uid); err != nil {
|
||||
return fmt.Errorf("setuid: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -11,20 +11,26 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func RunSocketmap(ctx context.Context, cfg *Config, db *AliasDB) error {
|
||||
func OpenSocketmapListener(cfg *Config) (net.Listener, error) {
|
||||
sock := cfg.Sockets.SocketmapSocket
|
||||
_ = os.Remove(sock)
|
||||
|
||||
l, err := net.Listen("unix", sock)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
if err := ChownChmodSocket(sock, cfg); err != nil {
|
||||
return err
|
||||
_ = l.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func ServeSocketmap(ctx context.Context, cfg *Config, db *AliasDB, l net.Listener) error {
|
||||
defer l.Close()
|
||||
|
||||
for {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
@@ -39,6 +45,14 @@ func RunSocketmap(ctx context.Context, cfg *Config, db *AliasDB) error {
|
||||
}
|
||||
}
|
||||
|
||||
func RunSocketmap(ctx context.Context, cfg *Config, db *AliasDB) error {
|
||||
l, err := OpenSocketmapListener(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ServeSocketmap(ctx, cfg, db, l)
|
||||
}
|
||||
|
||||
// Postfix socketmap framing: "<len>:<payload>,"
|
||||
func handleSocketmapConn(conn net.Conn, cfg *Config, db *AliasDB) {
|
||||
defer conn.Close()
|
||||
|
||||
Reference in New Issue
Block a user