Skip to content

Commit

Permalink
feat: add ability to mount generated /etc/passwd file to containers
Browse files Browse the repository at this point in the history
Signed-off-by: Matthew Penner <[email protected]>
  • Loading branch information
matthewpi committed Jul 24, 2024
1 parent ac260bd commit 5a4b29f
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 2 deletions.
3 changes: 3 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
if err := config.EnsurePterodactylUser(); err != nil {
log.WithField("error", err).Fatal("failed to create pterodactyl system user")
}
if err := config.ConfigurePasswd(); err != nil {
log.WithField("error", err).Fatal("failed to configure container passwd file")
}
log.WithFields(log.Fields{
"username": config.Get().System.Username,
"uid": config.Get().System.User.Uid,
Expand Down
48 changes: 48 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,25 @@ type SystemConfiguration struct {
Gid int `yaml:"gid"`
} `yaml:"user"`

// Passwd controls the mounting of a generated `/etc/passwd` into containers started by Wings.
Passwd struct {
// Enable controls whether a generated `/etc/passwd` should be mounted into containers.
//
// By default this option is disabled and containers will use the `/etc/passwd` from the
// container image itself.
Enable bool `yaml:"enabled" default:"false"`

// Path is the path on disk where the generated `/etc/passwd` should be stored.
// This directory may be temporary as the file will be re-created whenever Wings is started.
//
// This path **WILL** be both written to by Wings and mounted into containers created by
// Wings. If you are running Wings itself in a container, this path will need to be mounted
// into the Wings container as the exact path on the host, which should match the value
// specified here. If you are using SELinux, you will need to make sure this file has the
// correct SELinux context in order for containers to use it.
Path string `yaml:"path" default:"/run/wings/passwd"`
} `yaml:"passwd"`

// The amount of time in seconds that can elapse before a server's disk space calculation is
// considered stale and a re-check should occur. DANGER: setting this value too low can seriously
// impact system performance and cause massive I/O bottlenecks and high CPU usage for the Wings
Expand Down Expand Up @@ -497,6 +516,27 @@ func EnsurePterodactylUser() error {
return nil
}

// ConfigurePasswd generates a `/etc/passwd` file for use with containers started by Wings.
func ConfigurePasswd() error {
passwd := _config.System.Passwd
if !passwd.Enable {
return nil
}

v := []byte(fmt.Sprintf(
`root:x:0:0::/root:/bin/sh
container:x:%d:%d::/home/container:/bin/sh
nobody:x:65534:65534::/var/empty:/bin/sh
`,
_config.System.User.Uid,
_config.System.User.Gid,
))
if err := os.WriteFile(passwd.Path, v, 0o644); err != nil {

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
return fmt.Errorf("failed to write custom passwd file to %s: %v", passwd.Path, err)
}
return nil
}

// FromFile reads the configuration from the provided file and stores it in the
// global singleton for this instance.
func FromFile(path string) error {
Expand Down Expand Up @@ -561,6 +601,14 @@ func ConfigureDirectories() error {
return err
}

if _config.System.Passwd.Enable {
p := filepath.Dir(_config.System.Passwd.Path)
log.WithField("path", p).Debug("ensuring passwd directory exists")
if err := os.MkdirAll(p, 0o755); err != nil {

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
return err
}
}

return nil
}

Expand Down
13 changes: 11 additions & 2 deletions server/mounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ func (s *Server) Mounts() []environment.Mount {
},
}

// Handle mounting a generated `/etc/passwd` if the feature is enabled.
if passwd := config.Get().System.Passwd; passwd.Enable {
path := passwd.Path
s.Log().WithFields(log.Fields{"source_path": path}).Info("mouting generated /etc/passwd to workaround UID/GID issues")
m = append(m, environment.Mount{
Source: path,
Target: "/etc/passwd",
ReadOnly: true,
})
}

// Also include any of this server's custom mounts when returning them.
return append(m, s.customMounts()...)
}
Expand Down Expand Up @@ -56,14 +67,12 @@ func (s *Server) customMounts() []environment.Mount {
if !strings.HasPrefix(source, filepath.Clean(allowed)) {
continue
}

mounted = true
mounts = append(mounts, environment.Mount{
Source: source,
Target: target,
ReadOnly: m.ReadOnly,
})

break
}

Expand Down

0 comments on commit 5a4b29f

Please sign in to comment.