feature: switch to simple user

This commit is contained in:
peio
2026-01-21 22:54:20 +00:00
parent 3269654f42
commit 41f6a63687
6 changed files with 125 additions and 10 deletions

View File

@@ -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
}

View File

@@ -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)

View 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
}

View File

@@ -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()