Skip to content

Commit de3ea74

Browse files
authored
Implement pterodactyl 292 changes (#158)
* Implement pterodactyl 292 changes Add the same change as pterodactyl/wings#292 This adds configuration for the `machone-id` file that is required by hytale Creates and manages machine-id files on a per-server basis Adds code to remove machine-id files when a server is deleted as well. It also adds a group file for use along with the passwd file Updated config for passwd Updated mounts to not set default except for the the correct default. * Update machine-id generation Moved machine-id generation code outside of server create only called during initial server creation Create machine-id file for servers that already exists if the file is missing. Makes sure tempdir is created on wings start * remove append removes the append where not needed
1 parent a4161a2 commit de3ea74

File tree

6 files changed

+123
-29
lines changed

6 files changed

+123
-29
lines changed

cmd/root.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
139139
log.WithField("error", err).Fatal("failed to create pelican system user")
140140
return
141141
}
142+
if err := config.ConfigurePasswd(); err != nil {
143+
log.WithField("error", err).Fatal("failed to create passwd files for pelican")
144+
}
142145
log.WithFields(log.Fields{
143146
"username": config.Get().System.Username,
144147
"uid": config.Get().System.User.Uid,

config/config.go

Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,11 @@ import (
1717
"text/template"
1818
"time"
1919

20-
"github.com/gbrlsnchs/jwt/v3"
21-
2220
"emperror.dev/errors"
2321
"github.com/acobaugh/osrelease"
2422
"github.com/apex/log"
2523
"github.com/creasty/defaults"
24+
"github.com/gbrlsnchs/jwt/v3"
2625
"golang.org/x/sys/unix"
2726
"gopkg.in/yaml.v2"
2827

@@ -129,9 +128,9 @@ type RemoteQueryConfiguration struct {
129128
// be less likely to cause performance issues on the Panel.
130129
BootServersPerPage int `default:"50" yaml:"boot_servers_per_page"`
131130

132-
//When using services like Cloudflare Access to manage access to
133-
//a specific system via an external authentication system,
134-
//it is possible to add special headers to bypass authentication.
131+
//When using services like Cloudflare Access to manage access to
132+
//a specific system via an external authentication system,
133+
//it is possible to add special headers to bypass authentication.
135134
//The mentioned headers can be appended to queries sent from Wings to the panel.
136135
CustomHeaders map[string]string `yaml:"custom_headers"`
137136
}
@@ -187,11 +186,23 @@ type SystemConfiguration struct {
187186
Uid int `yaml:"uid"`
188187
Gid int `yaml:"gid"`
189188

190-
// Passwd controls weather a passwd file is mounted in the container
191-
// at /etc/passwd to resolve missing user issues
192-
Passwd bool `json:"mount_passwd" yaml:"mount_passwd" default:"true"`
193-
PasswdFile string `json:"passwd_file" yaml:"passwd_file" default:"/etc/pelican/passwd"`
194-
} `yaml:"user"`
189+
// Passwd controls weather a passwd and group file is mounted in the container
190+
// at /etc/passwd to resolve missing user/group issues inside the container
191+
Passwd struct {
192+
Enable bool `json:"enable" yaml:"enable" default:"true"`
193+
Directory string `json:"directory" yaml:"directory" default:"/etc/pelican"`
194+
} `json:"passwd" yaml:"passwd"`
195+
} `json:"user" yaml:"user"`
196+
197+
// MachineID manages the mounting of a 'machine-id' file for containers as required for
198+
// some game servers. I.E. Hytale
199+
MachineID struct {
200+
// Enable controls if the machine-id file is generated and mounted into the server container
201+
// This is enabled by default
202+
Enable bool `json:"enable" yaml:"enable" default:"true"`
203+
// FilePath is the full path to the machine-id file that will be generated and mounted
204+
Directory string `json:"directory" yaml:"directory" default:"/etc/pelican/machine-id"`
205+
} `json:"machine_id" yaml:"machine_id"`
195206

196207
// The amount of time in seconds that can elapse before a server's disk space calculation is
197208
// considered stale and a re-check should occur. DANGER: setting this value too low can seriously
@@ -604,19 +615,6 @@ func ConfigureDirectories() error {
604615
return err
605616
}
606617

607-
log.WithField("filepath", _config.System.User.PasswdFile).Debug("ensuring passwd file exists")
608-
if passwd, err := os.Create(_config.System.User.PasswdFile); err != nil {
609-
return err
610-
} else {
611-
// the WriteFile method returns an error if unsuccessful
612-
err := os.WriteFile(passwd.Name(), []byte(fmt.Sprintf("container:x:%d:%d::/home/container:/usr/sbin/nologin", _config.System.User.Uid, _config.System.User.Gid)), 0644)
613-
// handle this error
614-
if err != nil {
615-
// print it out
616-
fmt.Println(err)
617-
}
618-
}
619-
620618
// There are a non-trivial number of users out there whose data directories are actually a
621619
// symlink to another location on the disk. If we do not resolve that final destination at this
622620
// point things will appear to work, but endless errors will be encountered when we try to
@@ -638,6 +636,11 @@ func ConfigureDirectories() error {
638636
return err
639637
}
640638

639+
log.WithField("path", _config.System.TmpDirectory).Debug("ensuring temporary data directory exists")
640+
if err := os.MkdirAll(_config.System.TmpDirectory, 0o700); err != nil {
641+
return err
642+
}
643+
641644
log.WithField("path", _config.System.ArchiveDirectory).Debug("ensuring archive data directory exists")
642645
if err := os.MkdirAll(_config.System.ArchiveDirectory, 0o700); err != nil {
643646
return err
@@ -648,9 +651,42 @@ func ConfigureDirectories() error {
648651
return err
649652
}
650653

654+
log.WithField("path", _config.System.User.Passwd.Directory).Debug("ensuring passwd directory exists")
655+
if err := os.MkdirAll(_config.System.User.Passwd.Directory, 0o700); err != nil {
656+
return err
657+
}
658+
659+
log.WithField("path", _config.System.MachineID.Directory).Debug("ensuring machine-id directory exists")
660+
if err := os.MkdirAll(_config.System.MachineID.Directory, 0o700); err != nil {
661+
return err
662+
}
651663
return nil
652664
}
653665

666+
// ConfigurePasswd generates the passwd and group files to be used by
667+
// this looks cleaner than the previous way and is similar to pterodactyl
668+
func ConfigurePasswd() (err error) {
669+
if !_config.System.User.Passwd.Enable {
670+
return
671+
}
672+
log.WithField("filepath", filepath.Join(_config.System.User.Passwd.Directory, "passwd")).
673+
Debug("ensuring passwd file exists")
674+
if err = os.WriteFile(filepath.Join(_config.System.User.Passwd.Directory, "passwd"),
675+
[]byte(fmt.Sprintf("container:x:%d:%d::/home/container:/usr/sbin/nologin",
676+
_config.System.User.Uid, _config.System.User.Gid)), 0644); err != nil {
677+
return fmt.Errorf("could not write passwd file: %w", err)
678+
}
679+
680+
log.WithField("filepath", filepath.Join(_config.System.User.Passwd.Directory, "group")).
681+
Debug("ensuring group file exists")
682+
if err = os.WriteFile(filepath.Join(_config.System.User.Passwd.Directory, "group"),
683+
[]byte(fmt.Sprintf("container:x:%d:container",
684+
_config.System.User.Gid)), 0644); err != nil {
685+
return fmt.Errorf("could not write group file: %w", err)
686+
}
687+
return
688+
}
689+
654690
// EnableLogRotation writes a logrotate file for wings to the system logrotate
655691
// configuration directory if one exists and a logrotate file is not found. This
656692
// allows us to basically automate away the log rotation for most installs, but

router/router_server.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ func getServerInstallLogs(c *gin.Context) {
7575
c.JSON(http.StatusOK, gin.H{"data": output})
7676
}
7777

78-
7978
// Handles a request to control the power state of a server. If the action being passed
8079
// through is invalid a 404 is returned. Otherwise, a HTTP/202 Accepted response is returned
8180
// and the actual power action is run asynchronously so that we don't have to block the
@@ -281,7 +280,16 @@ func deleteServer(c *gin.Context) {
281280
p := fs.Path()
282281
_ = fs.UnixFS().Close()
283282
if err := os.RemoveAll(p); err != nil {
284-
log.WithFields(log.Fields{"path": p, "error": err}).Warn("failed to remove server files during deletion process")
283+
log.WithFields(log.Fields{"path": p, "error": err}).
284+
Warn("failed to remove server files during deletion process")
285+
}
286+
}(s)
287+
288+
// remove hanging machine-id file for the server when removing
289+
go func(s *server.Server) {
290+
if err := os.Remove(filepath.Join(config.Get().System.MachineID.Directory, s.ID())); err != nil {
291+
log.WithFields(log.Fields{"server_id": s.ID(), "error": err}).
292+
Warn("failed to remove machine-id file for server")
285293
}
286294
}(s)
287295

server/mounts.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,32 @@ func (s *Server) Mounts() []environment.Mount {
3030
},
3131
}
3232

33-
if config.Get().System.User.Passwd {
33+
if config.Get().System.User.Passwd.Enable {
3434
passwdMount := environment.Mount{
35-
Default: true,
3635
Target: "/etc/passwd",
37-
Source: config.Get().System.User.PasswdFile,
36+
Source: filepath.Join(config.Get().System.User.Passwd.Directory, "passwd"),
3837
ReadOnly: true,
3938
}
4039

4140
m = append(m, passwdMount)
41+
42+
groupMount := environment.Mount{
43+
Target: "/etc/group",
44+
Source: filepath.Join(config.Get().System.User.Passwd.Directory, "group"),
45+
ReadOnly: true,
46+
}
47+
48+
m = append(m, groupMount)
49+
}
50+
51+
if config.Get().System.MachineID.Enable {
52+
machineIDMount := environment.Mount{
53+
Target: "/etc/machine-id",
54+
Source: filepath.Join(config.Get().System.MachineID.Directory, s.ID()),
55+
ReadOnly: true,
56+
}
57+
58+
m = append(m, machineIDMount)
4259
}
4360
// Also include any of this server's custom mounts when returning them.
4461
return append(m, s.customMounts()...)

server/power.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,13 @@ func (s *Server) onBeforeStart() error {
214214
}
215215
}
216216

217+
// create the machine-id file on start in case it's missing
218+
if config.Get().System.MachineID.Enable {
219+
if err := s.CreateMachineID(); err != nil {
220+
return err
221+
}
222+
}
223+
217224
s.Log().Info("completed server preflight, starting boot process...")
218225
return nil
219226
}

server/server.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"net/http"
77
"os"
88
"path"
9+
"path/filepath"
910
"regexp"
1011
"strconv"
1112
"strings"
@@ -166,7 +167,6 @@ func DetermineServerTimezone(envvars map[string]interface{}, defaultTimezone str
166167
return defaultTimezone
167168
}
168169

169-
170170
// parseInvocation parses the start command in the same way we already do in the entrypoint
171171
// We can use this to set the container command with all variables replaced.
172172
func parseInvocation(invocation string, envvars map[string]interface{}, memory int64, port int, ip string) (parsed string) {
@@ -312,9 +312,32 @@ func (s *Server) CreateEnvironment() error {
312312
return err
313313
}
314314

315+
// create the machine-id file on install
316+
if config.Get().System.MachineID.Enable {
317+
if err := s.CreateMachineID(); err != nil {
318+
return err
319+
}
320+
}
321+
315322
return s.Environment.Create()
316323
}
317324

325+
// CreateMachineID generates the machine-id file for the server
326+
func (s *Server) CreateMachineID() error {
327+
// Hytale wants a machine-id in order to encrypt tokens for the server. So
328+
// write a machine-id file for the server that contains the server's UUID
329+
// without any dashes.
330+
p := filepath.Join(config.Get().System.MachineID.Directory, s.ID())
331+
s.Log().WithFields(log.Fields{
332+
"path": p}).Debug("creating machine-id file")
333+
machineID := []byte(strings.ReplaceAll(s.ID(), "-", ""))
334+
if err := os.WriteFile(p, machineID, 0o644); err != nil {
335+
return fmt.Errorf("failed to write machine-id (at '%s') for server '%s': %w", p, s.ID(), err)
336+
}
337+
338+
return nil
339+
}
340+
318341
// Checks if the server is marked as being suspended or not on the system.
319342
func (s *Server) IsSuspended() bool {
320343
return s.Config().Suspended

0 commit comments

Comments
 (0)