Skip to content

Commit

Permalink
Merge pull request #1850 from bbockelm/drop_privs
Browse files Browse the repository at this point in the history
Drop privileges to user `pelican` if requested
  • Loading branch information
h2zh authored Feb 18, 2025
2 parents fd0e84a + 92357d5 commit 3ae700e
Show file tree
Hide file tree
Showing 24 changed files with 878 additions and 105 deletions.
56 changes: 54 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -1251,13 +1251,65 @@ func InitServer(ctx context.Context, currentServers server_structs.ServerType) e
if !param.Cache_RunLocation.IsSet() && !param.Origin_RunLocation.IsSet() && param.Xrootd_RunLocation.IsSet() {
return errors.New("Xrootd.RunLocation is set, but both modules are enabled. Please set Cache.RunLocation and Origin.RunLocation or disable Xrootd.RunLocation so the default location can be used.")
}
} else if param.Server_DropPrivileges.GetBool() {
puser, err := GetPelicanUser()
if err != nil {
return errors.Wrapf(err, "set to drop privileges but no target OS username found for %s", param.Server_UnprivilegedUser.GetString())
}
// Set up the directories for the server to run as a non-root user;
// for the most part, we need to recursively chown and chmod the directory
// so either root or pelican can access it.
pelicanLocations := []string{}
if currentServers.IsEnabled(server_structs.RegistryType) {
pelicanLocations = append(pelicanLocations, param.Registry_DbLocation.GetString())
}
if currentServers.IsEnabled(server_structs.OriginType) {
pelicanLocations = append(pelicanLocations, param.Origin_DbLocation.GetString())
}
if currentServers.IsEnabled(server_structs.DirectorType) {
pelicanLocations = append(pelicanLocations, param.Director_DbLocation.GetString(), param.Director_GeoIPLocation.GetString())
}
if err = setFileAndDirPerms(pelicanLocations, 0750, 0640, puser.Uid, 0, true); err != nil {
return errors.Wrap(err, "failure when setting up the file permissions for pelican")
}

pelicanLocationsNoRecursive := []string{}
if (currentServers.IsEnabled(server_structs.OriginType) || currentServers.IsEnabled(server_structs.CacheType)) && param.Shoveler_Enable.GetBool() {
pelicanLocationsNoRecursive = append(pelicanLocationsNoRecursive, param.Shoveler_AMQPTokenLocation.GetString())
}
if err = setFileAndDirPerms(pelicanLocationsNoRecursive, 0750, 0640, puser.Uid, 0, false); err != nil {
return errors.Wrap(err, "failure when setting up the file permissions for pelican")
}

pelicanDirs := []string{
param.Monitoring_DataLocation.GetString(),
}
if currentServers.IsEnabled(server_structs.LocalCacheType) {
pelicanDirs = append(pelicanDirs, param.LocalCache_RunLocation.GetString())
}
if currentServers.IsEnabled(server_structs.CacheType) && param.Cache_EnableLotman.GetBool() {
pelicanDirs = append(pelicanDirs, param.Lotman_LotHome.GetString(), param.Lotman_DbLocation.GetString())
}
if (currentServers.IsEnabled(server_structs.OriginType) || currentServers.IsEnabled(server_structs.CacheType)) && param.Shoveler_Enable.GetBool() {
pelicanDirs = append(pelicanDirs, param.Shoveler_QueueDirectory.GetString())
}
if currentServers.IsEnabled(server_structs.OriginType) {
pelicanDirs = append(pelicanDirs, param.Origin_GlobusConfigLocation.GetString())
}
if err = setDirPerms(pelicanDirs, 0750, 0640, puser.Uid, puser.Gid, true); err != nil {
return errors.Wrap(err, "failure when setting up the directory permissions for pelican")
}
}

if err := os.MkdirAll(param.Monitoring_DataLocation.GetString(), 0750); err != nil {
user, err := GetPelicanUser()
if err != nil {
return errors.Wrap(err, "no OS user found for pelican")
}
if err := MkdirAll(param.Monitoring_DataLocation.GetString(), 0750, user.Uid, user.Gid); err != nil {
return errors.Wrapf(err, "failure when creating a directory for the monitoring data")
}

if err := os.MkdirAll(param.Shoveler_QueueDirectory.GetString(), 0750); err != nil {
if err := MkdirAll(param.Shoveler_QueueDirectory.GetString(), 0750, user.Uid, user.Gid); err != nil {
return errors.Wrapf(err, "failure when creating a directory for the shoveler on-disk queue")
}
if currentServers.IsEnabled(server_structs.OriginType) {
Expand Down
92 changes: 25 additions & 67 deletions config/init_server_creds.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,20 +189,7 @@ func GeneratePrivateKey(keyLocation string, curve elliptic.Curve, allowRSA bool)
if keyLocation == "" {
return errors.New("failed to generate private key: key location is empty")
}
uid, err := GetDaemonUID()
if err != nil {
return err
}

gid, err := GetDaemonGID()
if err != nil {
return err
}
user, err := GetDaemonUser()
if err != nil {
return err
}
groupname, err := GetDaemonGroup()
user, err := GetPelicanUser()
if err != nil {
return err
}
Expand All @@ -222,7 +209,7 @@ func GeneratePrivateKey(keyLocation string, curve elliptic.Curve, allowRSA bool)
log.Warningf("Will generate a new private key at location: %v", keyLocation)

keyDir := filepath.Dir(keyLocation)
if err := MkdirAll(keyDir, 0750, -1, gid); err != nil {
if err := MkdirAll(keyDir, 0750, -1, user.Gid); err != nil {
return err
}
// In this case, the private key file doesn't exist.
Expand All @@ -235,16 +222,16 @@ func GeneratePrivateKey(keyLocation string, curve elliptic.Curve, allowRSA bool)
// Windows does not have "chown", has to work differently
currentOS := runtime.GOOS
if currentOS == "windows" {
cmd := exec.Command("icacls", keyLocation, "/grant", user+":F")
cmd := exec.Command("icacls", keyLocation, "/grant", user.Username+":F")
output, err := cmd.CombinedOutput()
if err != nil {
return errors.Wrapf(err, "Failed to chown generated key %v to daemon group %v: %s",
keyLocation, groupname, string(output))
keyLocation, user.Groupname, string(output))
}
} else { // Else we are running on linux/mac
if err = os.Chown(keyLocation, uid, gid); err != nil {
if err = os.Chown(keyLocation, user.Uid, user.Gid); err != nil {
return errors.Wrapf(err, "Failed to chown generated key %v to daemon group %v",
keyLocation, groupname)
keyLocation, user.Groupname)
}
}

Expand Down Expand Up @@ -273,15 +260,7 @@ func generatePrivateKeyToFile(file *os.File, curve elliptic.Curve) error {
// for non-production environment so that we can use the private key of the CA
// to sign the host certificate
func GenerateCACert() error {
gid, err := GetDaemonGID()
if err != nil {
return err
}
groupname, err := GetDaemonGroup()
if err != nil {
return err
}
user, err := GetDaemonUser()
user, err := GetPelicanUser()
if err != nil {
return err
}
Expand All @@ -306,7 +285,7 @@ func GenerateCACert() error {

// No existing CA cert present, generate a new CA root certificate and private key
tlsCertDir := filepath.Dir(tlsCACert)
if err := MkdirAll(tlsCertDir, 0755, -1, gid); err != nil {
if err := MkdirAll(tlsCertDir, 0755, user.Uid, user.Gid); err != nil {
return err
}

Expand Down Expand Up @@ -364,16 +343,16 @@ func GenerateCACert() error {
// Windows does not have "chown", has to work differently
currentOS := runtime.GOOS
if currentOS == "windows" {
cmd := exec.Command("icacls", tlsCACert, "/grant", user+":F")
cmd := exec.Command("icacls", tlsCACert, "/grant", user.Username+":F")
output, err := cmd.CombinedOutput()
if err != nil {
return errors.Wrapf(err, "Failed to chown generated key %v to daemon group %v: %s",
tlsCACert, groupname, string(output))
tlsCACert, user.Groupname, string(output))
}
} else { // Else we are running on linux/mac
if err = os.Chown(tlsCACert, -1, gid); err != nil {
if err = os.Chown(tlsCACert, user.Uid, user.Gid); err != nil {
return errors.Wrapf(err, "Failed to chown generated key %v to daemon group %v",
tlsCACert, groupname)
tlsCACert, user.Groupname)
}
}

Expand Down Expand Up @@ -408,23 +387,15 @@ func LoadCertificate(certFile string) (*x509.Certificate, error) {
}
}
if cert == nil {
return nil, fmt.Errorf("Certificate file, %v, contains no certificate", certFile)
return nil, fmt.Errorf("certificate file, %v, contains no certificate", certFile)
}
return cert, nil
}

// Generate a TLS certificate (host certificate) and its private key
// for non-production environment if the required TLS files are not present
func GenerateCert() error {
gid, err := GetDaemonGID()
if err != nil {
return err
}
groupname, err := GetDaemonGroup()
if err != nil {
return err
}
user, err := GetDaemonUser()
user, err := GetPelicanUser()
if err != nil {
return err
}
Expand Down Expand Up @@ -483,7 +454,7 @@ func GenerateCert() error {
}

tlsCertDir := filepath.Dir(tlsCert)
if err := MkdirAll(tlsCertDir, 0755, -1, gid); err != nil {
if err := MkdirAll(tlsCertDir, 0755, user.Uid, user.Gid); err != nil {
return err
}

Expand Down Expand Up @@ -554,16 +525,16 @@ func GenerateCert() error {
// Windows does not have "chown", has to work differently
currentOS := runtime.GOOS
if currentOS == "windows" {
cmd := exec.Command("icacls", tlsCert, "/grant", user+":F")
cmd := exec.Command("icacls", tlsCert, "/grant", user.Username+":F")
output, err := cmd.CombinedOutput()
if err != nil {
return errors.Wrapf(err, "Failed to chown generated key %v to daemon group %v: %s",
tlsCert, groupname, string(output))
tlsCert, user.Groupname, string(output))
}
} else { // Else we are running on linux/mac
if err = os.Chown(tlsCert, -1, gid); err != nil {
if err = os.Chown(tlsCert, user.Uid, user.Gid); err != nil {
return errors.Wrapf(err, "Failed to chown generated key %v to daemon group %v",
tlsCert, groupname)
tlsCert, user.Groupname)
}
}

Expand Down Expand Up @@ -857,20 +828,7 @@ func GenerateSessionSecret() error {
return errors.New("Empty filename for Server_SessionSecretFile")
}

uid, err := GetDaemonUID()
if err != nil {
return err
}

gid, err := GetDaemonGID()
if err != nil {
return err
}
user, err := GetDaemonUser()
if err != nil {
return err
}
groupname, err := GetDaemonGroup()
user, err := GetPelicanUser()
if err != nil {
return err
}
Expand All @@ -891,7 +849,7 @@ func GenerateSessionSecret() error {
return errors.Wrap(err, "Failed to load session secret due to I/O error")
}
keyDir := filepath.Dir(secretLocation)
if err := MkdirAll(keyDir, 0750, -1, gid); err != nil {
if err := MkdirAll(keyDir, 0750, user.Uid, user.Gid); err != nil {
return err
}

Expand All @@ -904,16 +862,16 @@ func GenerateSessionSecret() error {
// Windows does not have "chown", has to work differently
currentOS := runtime.GOOS
if currentOS == "windows" {
cmd := exec.Command("icacls", secretLocation, "/grant", user+":F")
cmd := exec.Command("icacls", secretLocation, "/grant", user.Username+":F")
output, err := cmd.CombinedOutput()
if err != nil {
return errors.Wrapf(err, "Failed to chown generated session secret %v to daemon group %v: %s",
secretLocation, groupname, string(output))
secretLocation, user.Groupname, string(output))
}
} else { // Else we are running on linux/mac
if err = os.Chown(secretLocation, uid, gid); err != nil {
if err = os.Chown(secretLocation, user.Uid, user.Gid); err != nil {
return errors.Wrapf(err, "Failed to chown generated session secret %v to daemon group %v",
secretLocation, groupname)
secretLocation, user.Groupname)
}
}

Expand Down
99 changes: 99 additions & 0 deletions config/mkdirall.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package config
import (
"os"
"os/exec"
"path/filepath"
"runtime"
"syscall"

Expand Down Expand Up @@ -101,3 +102,101 @@ func MkdirAll(path string, perm os.FileMode, uid int, gid int) error {
}
return nil
}

func setFileAndDirPerms(paths []string, dirPerm os.FileMode, perm os.FileMode, uid int, gid int, recursive bool) error {
dirs := map[string]bool{}
for _, path := range paths {
// Create the parent directory if it doesn't exist
dir := filepath.Dir(path)
err := MkdirAll(dir, dirPerm, uid, gid)
if err != nil {
return errors.Wrapf(err, "Failed to create directory %v", dir)
}
// Set the permissions on the parent directory
err = os.Chmod(dir, dirPerm)
if err != nil {
return errors.Wrapf(err, "Failed to set permissions on directory %v", dir)
}
if err = os.Chown(dir, uid, gid); err != nil {
return errors.Wrapf(err, "Failed to chown directory %v", dir)
}
if recursive {
dirs[dir] = true
}
// Skip the file if it doesn't exist
if _, err := os.Stat(path); os.IsNotExist(err) {
continue
}
// Set the permissions on the file
if err = os.Chmod(path, perm); err != nil {
return errors.Wrapf(err, "Failed to set permissions on file %v", path)
}
if err = os.Chown(path, uid, gid); err != nil {
return errors.Wrapf(err, "Failed to chown file %v", path)
}
}
// Set the permissions on all sub-directories, when recursive is set to true
for dir := range dirs {
if err := filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
}
itemPerm := perm
if d.IsDir() {
itemPerm = dirPerm
}
if err = os.Chmod(path, itemPerm); err != nil {
return errors.Wrapf(err, "Failed to set permissions on directory %v", path)
}
if err = os.Chown(path, uid, gid); err != nil {
return errors.Wrapf(err, "Failed to chown directory %v", path)
}
return nil
}); err != nil {
return errors.Wrapf(err, "Failed to walk directory %v", dir)
}
}
return nil
}

func setDirPerms(paths []string, dirPerm os.FileMode, perm os.FileMode, uid int, gid int, recursive bool) error {
dirs := map[string]bool{}
for _, path := range paths {
if path == "" {
continue
}
dirs[path] = true
}
for dir := range dirs {
err := MkdirAll(dir, dirPerm, uid, gid)
if err != nil {
return errors.Wrapf(err, "Failed to create directory %v", dir)
}
err = os.Chmod(dir, dirPerm)
if err != nil {
return errors.Wrapf(err, "Failed to set permissions on directory %v", dir)
}
if err = os.Chown(dir, uid, gid); err != nil {
return errors.Wrapf(err, "Failed to chown directory %v", dir)
}
if err := filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
}
itemPerm := perm
if d.IsDir() {
itemPerm = dirPerm
}
if err = os.Chmod(path, itemPerm); err != nil {
return errors.Wrapf(err, "Failed to set permissions on directory %v", path)
}
if err = os.Chown(path, uid, gid); err != nil {
return errors.Wrapf(err, "Failed to chown directory %v", path)
}
return nil
}); err != nil {
return errors.Wrapf(err, "Failed to walk directory %v", dir)
}
}
return nil
}
Loading

0 comments on commit 3ae700e

Please sign in to comment.