diff --git a/.gitignore b/.gitignore index a98e257..4f2bd7e 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ # ignore configuration file /config.yml +/config*.yml # Ignore Vagrant stuff /.vagrant diff --git a/cmd/config_finder.go b/cmd/config_finder.go deleted file mode 100644 index 3e9f42a..0000000 --- a/cmd/config_finder.go +++ /dev/null @@ -1,61 +0,0 @@ -package cmd - -import ( - "os" - "path/filepath" - - "github.com/pterodactyl/wings/config" -) - -// We've gone through a couple of iterations of where the configuration is stored. This -// helpful little function will look through the three areas it might have ended up, and -// return it. -// -// We only run this if the configuration flag for the instance is not actually passed in -// via the command line. Once found, the configuration is moved into the expected default -// location. Only errors are returned from this function, you can safely assume that after -// running this the configuration can be found in the correct default location. -func RelocateConfiguration() error { - var match string - check := []string{ - config.DefaultLocation, - "/var/lib/pterodactyl/config.yml", - "/etc/wings/config.yml", - } - - // Loop over all of the configuration paths, and return which one we found, if - // any. - for _, p := range check { - if s, err := os.Stat(p); err != nil { - if !os.IsNotExist(err) { - return err - } - } else if !s.IsDir() { - match = p - break - } - } - - // Just return a generic not exist error at this point if we didn't have a match, this - // will allow the caller to handle displaying a more friendly error to the user. If we - // did match in the default location, go ahead and return successfully. - if match == "" { - return os.ErrNotExist - } else if match == config.DefaultLocation { - return nil - } - - // The rest of this function simply creates the new default location and moves the - // old configuration file over to the new location, then sets the permissions on the - // file correctly so that only the user running this process can read it. - p, _ := filepath.Split(config.DefaultLocation) - if err := os.MkdirAll(p, 0755); err != nil { - return err - } - - if err := os.Rename(match, config.DefaultLocation); err != nil { - return err - } - - return os.Chmod(config.DefaultLocation, 0600) -} diff --git a/cmd/root.go b/cmd/root.go index 0414c79..c1080b3 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -26,12 +26,13 @@ import ( "github.com/pterodactyl/wings/sftp" "github.com/pterodactyl/wings/system" "github.com/spf13/cobra" + "github.com/spf13/viper" "golang.org/x/crypto/acme" "golang.org/x/crypto/acme/autocert" ) var ( - configPath = config.DefaultLocation + configPath = "" debug = false ) @@ -64,7 +65,9 @@ func Execute() { } func init() { - rootCommand.PersistentFlags().StringVar(&configPath, "config", config.DefaultLocation, "set the location for the configuration file") + cobra.OnInitialize(initConfig, initLogging) + + rootCommand.PersistentFlags().StringVar(&configPath, "config", "", "set the location for the configuration file") rootCommand.PersistentFlags().BoolVar(&debug, "debug", false, "pass in order to run wings in debug mode") // Flags specifically used when running the API. @@ -119,17 +122,6 @@ func rootCmdRun(cmd *cobra.Command, _ []string) { defer profile.Start(profile.BlockProfile).Stop() } - // Only attempt configuration file relocation if a custom location has not - // been specified in the command startup. - if configPath == config.DefaultLocation { - if err := RelocateConfiguration(); err != nil { - if errors.Is(err, os.ErrNotExist) { - exitWithConfigurationNotice() - } - panic(err) - } - } - c, err := readConfiguration() if err != nil { panic(err) @@ -140,14 +132,8 @@ func rootCmdRun(cmd *cobra.Command, _ []string) { } printLogo() - if err := configureLogging(c.System.LogDirectory, c.Debug); err != nil { - panic(err) - } - - log.WithField("path", c.GetPath()).Info("loading configuration from path") - if c.Debug { - log.Debug("running in debug mode") - } + log.WithField("path", viper.ConfigFileUsed()).Info("loading configuration from file") + log.Debug("running in debug mode") if ok, _ := cmd.Flags().GetBool("ignore-certificate-errors"); ok { log.Warn("running with --ignore-certificate-errors: TLS certificate host chains and name will not be verified") @@ -158,47 +144,42 @@ func rootCmdRun(cmd *cobra.Command, _ []string) { config.Set(c) config.SetDebugViaFlag(debug) - - if err := c.System.ConfigureTimezone(); err != nil { + if err := config.ConfigureTimezone(); err != nil { log.WithField("error", err).Fatal("failed to detect system timezone or use supplied configuration value") - return } - log.WithField("timezone", c.System.Timezone).Info("configured wings with system timezone") - if err := c.System.ConfigureDirectories(); err != nil { + if err := config.ConfigureDirectories(); err != nil { log.WithField("error", err).Fatal("failed to configure system directories for pterodactyl") return } - if err := c.System.EnableLogRotation(); err != nil { + if err := config.EnableLogRotation(); err != nil { log.WithField("error", err).Fatal("failed to configure log rotation on the system") return } log.WithField("username", c.System.Username).Info("checking for pterodactyl system user") - if su, err := c.EnsurePterodactylUser(); err != nil { + if err := config.EnsurePterodactylUser(); err != nil { log.WithField("error", err).Fatal("failed to create pterodactyl system user") - return - } else { - log.WithFields(log.Fields{ - "username": su.Username, - "uid": su.Uid, - "gid": su.Gid, - }).Info("configured system user successfully") } + log.WithFields(log.Fields{ + "username": viper.GetString("system.username"), + "uid": viper.GetInt("system.user.uid"), + "gid": viper.GetInt("system.user.gid"), + }).Info("configured system user successfully") if err := server.LoadDirectory(); err != nil { log.WithField("error", err).Fatal("failed to load server configurations") return } - if err := environment.ConfigureDocker(&c.Docker); err != nil { + if err := environment.ConfigureDocker(cmd.Context()); err != nil { log.WithField("error", err).Fatal("failed to configure docker environment") return } - if err := c.WriteToDisk(); err != nil { + if err := viper.WriteConfig(); err != nil { log.WithField("error", err).Error("failed to save configuration to disk") } @@ -379,28 +360,44 @@ func rootCmdRun(cmd *cobra.Command, _ []string) { } } +func initConfig() { + if configPath != "" { + viper.SetConfigName("config") + viper.SetConfigType("yaml") + viper.AddConfigPath("/etc/pterodactyl") + viper.AddConfigPath("$HOME/.pterodactyl") + viper.AddConfigPath(".") + } else { + viper.SetConfigFile(configPath) + } + if err := viper.ReadInConfig(); err != nil { + if _, ok := err.(*viper.ConfigFileNotFoundError); ok { + exitWithConfigurationNotice() + } + log2.Fatalf("cmd/root: failed to read configuration: %s", err) + } +} + // Configures the global logger for Zap so that we can call it from any location // in the code without having to pass around a logger instance. -func configureLogging(logDir string, debug bool) error { - if err := os.MkdirAll(path.Join(logDir, "/install"), 0700); err != nil { - return err +func initLogging() { + dir := viper.GetString("system.log_directory") + if err := os.MkdirAll(path.Join(dir, "/install"), 0700); err != nil { + log2.Fatalf("cmd/root: failed to create install directory path: %s", err) } - - p := filepath.Join(logDir, "/wings.log") + p := filepath.Join(dir, "/wings.log") w, err := logrotate.NewFile(p) if err != nil { - return err + log2.Fatalf("cmd/root: failed to create wings log: %s", err) } log.SetLevel(log.InfoLevel) - if debug { + if viper.GetBool("debug") { log.SetLevel(log.DebugLevel) } log.SetHandler(multi.New(cli.Default, cli.New(w.File, false))) log.WithField("path", p).Info("writing log files to disk") - - return nil } // Prints the wings logo, nothing special here! @@ -429,11 +426,8 @@ func exitWithConfigurationNotice() { [_red_][white][bold]Error: Configuration File Not Found[reset] Wings was not able to locate your configuration file, and therefore is not -able to complete its boot process. - -Please ensure you have copied your instance configuration file into -the default location, or have provided the --config flag to use a -custom location. +able to complete its boot process. Please ensure you have copied your instance +configuration file into the default location below. Default Location: /etc/pterodactyl/config.yml diff --git a/config/config.go b/config/config.go index 0af323f..8d2d3d9 100644 --- a/config/config.go +++ b/config/config.go @@ -6,7 +6,6 @@ import ( "os" "os/exec" "os/user" - "strconv" "strings" "sync" @@ -14,6 +13,8 @@ import ( "github.com/cobaugh/osrelease" "github.com/creasty/defaults" "github.com/gbrlsnchs/jwt/v3" + "github.com/pterodactyl/wings/system" + "github.com/spf13/viper" "gopkg.in/yaml.v2" ) @@ -223,98 +224,64 @@ func (c *Configuration) GetPath() string { return c.path } -// Ensures that the Pterodactyl core user exists on the system. This user will be the -// owner of all data in the root data directory and is used as the user within containers. +// EnsurePterodactylUser ensures that the Pterodactyl core user exists on the +// system. This user will be the owner of all data in the root data directory +// and is used as the user within containers. // -// If files are not owned by this user there will be issues with permissions on Docker -// mount points. -func (c *Configuration) EnsurePterodactylUser() (*user.User, error) { +// If files are not owned by this user there will be issues with permissions on +// Docker mount points. +func EnsurePterodactylUser() error { sysName, err := getSystemName() if err != nil { - return nil, err + return err } // Our way of detecting if wings is running inside of Docker. if sysName == "busybox" { - uid := os.Getenv("WINGS_UID") - if uid == "" { - uid = "988" - } - - gid := os.Getenv("WINGS_GID") - if gid == "" { - gid = "988" - } - - username := os.Getenv("WINGS_USERNAME") - if username == "" { - username = "pterodactyl" - } - - u := &user.User{ - Uid: uid, - Gid: gid, - Username: username, - } - return u, c.setSystemUser(u) + viper.Set("system.username", system.FirstNotEmpty(os.Getenv("WINGS_USERNAME"), "pterodactyl")) + viper.Set("system.user.uid", system.MustInt(system.FirstNotEmpty(os.Getenv("WINGS_UID"), "988"))) + viper.Set("system.user.gid", system.MustInt(system.FirstNotEmpty(os.Getenv("WINGS_GID"), "988"))) + return nil } - u, err := user.Lookup(c.System.Username) - + username := viper.GetString("system.username") + u, err := user.Lookup(username) // If an error is returned but it isn't the unknown user error just abort // the process entirely. If we did find a user, return it immediately. - if err == nil { - return u, c.setSystemUser(u) - } else if _, ok := err.(user.UnknownUserError); !ok { - return nil, err + if err != nil { + if _, ok := err.(user.UnknownUserError); !ok { + return err + } + } else { + viper.Set("system.user.uid", system.MustInt(u.Uid)) + viper.Set("system.user.gid", system.MustInt(u.Gid)) + return nil } - command := fmt.Sprintf("useradd --system --no-create-home --shell /usr/sbin/nologin %s", c.System.Username) - - // Alpine Linux is the only OS we currently support that doesn't work with the useradd command, so - // in those cases we just modify the command a bit to work as expected. + command := fmt.Sprintf("useradd --system --no-create-home --shell /usr/sbin/nologin %s", username) + // Alpine Linux is the only OS we currently support that doesn't work with the useradd + // command, so in those cases we just modify the command a bit to work as expected. if strings.HasPrefix(sysName, "alpine") { - command = fmt.Sprintf("adduser -S -D -H -G %[1]s -s /sbin/nologin %[1]s", c.System.Username) - + command = fmt.Sprintf("adduser -S -D -H -G %[1]s -s /sbin/nologin %[1]s", username) // We have to create the group first on Alpine, so do that here before continuing on // to the user creation process. - if _, err := exec.Command("addgroup", "-S", c.System.Username).Output(); err != nil { - return nil, err + if _, err := exec.Command("addgroup", "-S", username).Output(); err != nil { + return err } } split := strings.Split(command, " ") if _, err := exec.Command(split[0], split[1:]...).Output(); err != nil { - return nil, err - } - - if u, err := user.Lookup(c.System.Username); err != nil { - return nil, err - } else { - return u, c.setSystemUser(u) - } -} - -// Set the system user into the configuration and then write it to the disk so that -// it is persisted on boot. -func (c *Configuration) setSystemUser(u *user.User) error { - uid, err := strconv.Atoi(u.Uid) - if err != nil { return err } - gid, err := strconv.Atoi(u.Gid) + u, err = user.Lookup(username) if err != nil { return err } - - c.Lock() - c.System.Username = u.Username - c.System.User.Uid = uid - c.System.User.Gid = gid - c.Unlock() - - return c.WriteToDisk() + viper.Set("system.user.uid", system.MustInt(u.Uid)) + viper.Set("system.user.gid", system.MustInt(u.Gid)) + return nil } // Writes the configuration to the disk as a blocking operation by obtaining an exclusive diff --git a/config/config_system.go b/config/config_system.go index c87dd21..0b2fd9a 100644 --- a/config/config_system.go +++ b/config/config_system.go @@ -14,6 +14,7 @@ import ( "emperror.dev/errors" "github.com/apex/log" + "github.com/spf13/viper" ) // Defines basic system configuration settings. @@ -116,11 +117,13 @@ type Transfers struct { DownloadLimit int `default:"0" yaml:"download_limit"` } -// Ensures that all of the system directories exist on the system. These directories are -// created so that only the owner can read the data, and no other users. -func (sc *SystemConfiguration) ConfigureDirectories() error { - log.WithField("path", sc.RootDirectory).Debug("ensuring root data directory exists") - if err := os.MkdirAll(sc.RootDirectory, 0700); err != nil { +// ConfigureDirectories ensures that all of the system directories exist on the +// system. These directories are created so that only the owner can read the data, +// and no other users. +func ConfigureDirectories() error { + root := viper.GetString("system.root_directory") + log.WithField("path", root).Debug("ensuring root data directory exists") + if err := os.MkdirAll(root, 0700); err != nil { return err } @@ -132,40 +135,42 @@ func (sc *SystemConfiguration) ConfigureDirectories() error { // For the sake of automating away as much of this as possible, see if the data directory is a // symlink, and if so resolve to its final real path, and then update the configuration to use // that. - if d, err := filepath.EvalSymlinks(sc.Data); err != nil { + data := viper.GetString("system.data") + if d, err := filepath.EvalSymlinks(data); err != nil { if !os.IsNotExist(err) { return err } - } else if d != sc.Data { - sc.Data = d + } else if d != data { + data = d + viper.Set("system.data", d) } - log.WithField("path", sc.Data).Debug("ensuring server data directory exists") - if err := os.MkdirAll(sc.Data, 0700); err != nil { + log.WithField("path", data).Debug("ensuring server data directory exists") + if err := os.MkdirAll(data, 0700); err != nil { return err } - log.WithField("path", sc.ArchiveDirectory).Debug("ensuring archive data directory exists") - if err := os.MkdirAll(sc.ArchiveDirectory, 0700); err != nil { + log.WithField("path", viper.GetString("system.archive_directory")).Debug("ensuring archive data directory exists") + if err := os.MkdirAll(viper.GetString("system.archive_directory"), 0700); err != nil { return err } - log.WithField("path", sc.BackupDirectory).Debug("ensuring backup data directory exists") - if err := os.MkdirAll(sc.BackupDirectory, 0700); err != nil { + log.WithField("path", viper.GetString("system.backup_directory")).Debug("ensuring backup data directory exists") + if err := os.MkdirAll(viper.GetString("system.backup_directory"), 0700); err != nil { return err } return nil } -// Writes a logrotate file for wings to the system logrotate configuration directory if one -// exists and a logrotate file is not found. This allows us to basically automate away the log -// rotation for most installs, but also enable users to make modifications on their own. -func (sc *SystemConfiguration) EnableLogRotation() error { +// EnableLogRotation writes a logrotate file for wings to the system logrotate +// configuration directory if one exists and a logrotate file is not found. This +// allows us to basically automate away the log rotation for most installs, but +// also enable users to make modifications on their own. +func EnableLogRotation() error { // Do nothing if not enabled. - if sc.EnableLogRotate == false { + if !viper.GetBool("system.enable_log_rotate") { log.Info("skipping log rotate configuration, disabled in wings config file") - return nil } @@ -174,14 +179,11 @@ func (sc *SystemConfiguration) EnableLogRotation() error { } else if (err != nil && os.IsNotExist(err)) || !st.IsDir() { return nil } - - if _, err := os.Stat("/etc/logrotate.d/wings"); err != nil && !os.IsNotExist(err) { + if _, err := os.Stat("/etc/logrotate.d/wings"); err == nil || !os.IsNotExist(err) { return err - } else if err == nil { - return nil } - log.Info("no log rotation configuration found, system is configured to support it, adding file now") + log.Info("no log rotation configuration found: adding file now") // If we've gotten to this point it means the logrotate directory exists on the system // but there is not a file for wings already. In that case, let us write a new file to // it so files can be rotated easily. @@ -191,8 +193,14 @@ func (sc *SystemConfiguration) EnableLogRotation() error { } defer f.Close() + type logrotateConfig struct { + Directory string + UserID int + GroupID int + } + t, err := template.New("logrotate").Parse(` -{{.LogDirectory}}/wings.log { +{{.Directory}}/wings.log { size 10M compress delaycompress @@ -200,17 +208,21 @@ func (sc *SystemConfiguration) EnableLogRotation() error { maxage 7 missingok notifempty - create 0640 {{.User.Uid}} {{.User.Gid}} + create 0640 {{.UserID}} {{.GroupID}} postrotate killall -SIGHUP wings endscript }`) - if err != nil { return err } - return errors.WithMessage(t.Execute(f, sc), "failed to write logrotate file to disk") + err = t.Execute(f, logrotateConfig{ + Directory: viper.GetString("system.log_directory"), + UserID: viper.GetInt("system.user.uid"), + GroupID: viper.GetInt("system.user.gid"), + }) + return errors.Wrap(err, "config: failed to write logrotate to disk") } // Returns the location of the JSON file that tracks server states. @@ -223,25 +235,28 @@ func (sc *SystemConfiguration) GetInstallLogPath() string { return path.Join(sc.LogDirectory, "install/") } -// Configures the timezone data for the configuration if it is currently missing. If -// a value has been set, this functionality will only run to validate that the timezone -// being used is valid. -func (sc *SystemConfiguration) ConfigureTimezone() error { - if sc.Timezone == "" { - if b, err := ioutil.ReadFile("/etc/timezone"); err != nil { +// ConfigureTimezone sets the timezone data for the configuration if it is +// currently missing. If a value has been set, this functionality will only run +// to validate that the timezone being used is valid. +func ConfigureTimezone() error { + tz := viper.GetString("system.timezone") + defer viper.Set("system.timezone", tz) + if tz == "" { + b, err := ioutil.ReadFile("/etc/timezone") + if err != nil { if !os.IsNotExist(err) { - return errors.WithMessage(err, "failed to open /etc/timezone for automatic server timezone calibration") + return errors.WithMessage(err, "config: failed to open timezone file") } - ctx, _ := context.WithTimeout(context.Background(), time.Second*5) + tz = "UTC" + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() // Okay, file isn't found on this OS, we will try using timedatectl to handle this. If this // command fails, exit, but if it returns a value use that. If no value is returned we will // fall through to UTC to get Wings booted at least. out, err := exec.CommandContext(ctx, "timedatectl").Output() if err != nil { log.WithField("error", err).Warn("failed to execute \"timedatectl\" to determine system timezone, falling back to UTC") - - sc.Timezone = "UTC" return nil } @@ -249,20 +264,16 @@ func (sc *SystemConfiguration) ConfigureTimezone() error { matches := r.FindSubmatch(out) if len(matches) != 2 || string(matches[1]) == "" { log.Warn("failed to parse timezone from \"timedatectl\" output, falling back to UTC") - - sc.Timezone = "UTC" return nil } - - sc.Timezone = string(matches[1]) + tz = string(matches[1]) } else { - sc.Timezone = string(b) + tz = string(b) } } - sc.Timezone = regexp.MustCompile(`(?i)[^a-z_/]+`).ReplaceAllString(sc.Timezone, "") + tz = regexp.MustCompile(`(?i)[^a-z_/]+`).ReplaceAllString(tz, "") + _, err := time.LoadLocation(tz) - _, err := time.LoadLocation(sc.Timezone) - - return errors.WithMessage(err, fmt.Sprintf("the supplied timezone %s is invalid", sc.Timezone)) + return errors.WithMessage(err, fmt.Sprintf("the supplied timezone %s is invalid", tz)) } diff --git a/environment/docker.go b/environment/docker.go index fb59b92..8b8b9c3 100644 --- a/environment/docker.go +++ b/environment/docker.go @@ -6,112 +6,95 @@ import ( "sync" "github.com/apex/log" - "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" - "github.com/pterodactyl/wings/config" + "github.com/spf13/viper" ) -var _cmu sync.Mutex +var _conce sync.Once var _client *client.Client -// Return a Docker client to be used throughout the codebase. Once a client has been created it -// will be returned for all subsequent calls to this function. +// DockerClient returns a Docker client to be used throughout the codebase. Once +// a client has been created it will be returned for all subsequent calls to this +// function. func DockerClient() (*client.Client, error) { - _cmu.Lock() - defer _cmu.Unlock() - - if _client != nil { - return _client, nil - } - - _client, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) - + var err error + _conce.Do(func() { + _client, err = client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + }) return _client, err } -// Configures the required network for the docker environment. -func ConfigureDocker(c *config.DockerConfiguration) error { +// ConfigureDocker configures the required network for the docker environment. +func ConfigureDocker(ctx context.Context) error { // Ensure the required docker network exists on the system. cli, err := DockerClient() if err != nil { return err } - resource, err := cli.NetworkInspect(context.Background(), c.Network.Name, types.NetworkInspectOptions{}) - if err != nil && client.IsErrNotFound(err) { - log.Info("creating missing pterodactyl0 interface, this could take a few seconds...") - return createDockerNetwork(cli, c) - } else if err != nil { - log.WithField("error", err).Fatal("failed to create required docker network for containers") + nw := viper.Sub("docker.network") + resource, err := cli.NetworkInspect(ctx, nw.GetString("name"), types.NetworkInspectOptions{}) + if err != nil { + if client.IsErrNotFound(err) { + log.Info("creating missing pterodactyl0 interface, this could take a few seconds...") + if err := createDockerNetwork(ctx, cli); err != nil { + return err + } + } + return err + } else { + nw.Set("driver", resource.Driver) } - switch resource.Driver { + switch nw.GetString("driver") { case "host": - c.Network.Interface = "127.0.0.1" - c.Network.ISPN = false - return nil + nw.Set("interface", "127.0.0.1") + nw.Set("ispn", false) case "overlay": + fallthrough case "weavemesh": - c.Network.Interface = "" - c.Network.ISPN = true - return nil + nw.Set("interface", "") + nw.Set("ispn", true) default: - c.Network.ISPN = false + nw.Set("ispn", false) } - return nil } // Creates a new network on the machine if one does not exist already. -func createDockerNetwork(cli *client.Client, c *config.DockerConfiguration) error { - _, err := cli.NetworkCreate(context.Background(), c.Network.Name, types.NetworkCreate{ - Driver: c.Network.Driver, +func createDockerNetwork(ctx context.Context, cli *client.Client) error { + nw := viper.Sub("docker.network") + _, err := cli.NetworkCreate(ctx, nw.GetString("name"), types.NetworkCreate{ + Driver: nw.GetString("driver"), EnableIPv6: true, - Internal: c.Network.IsInternal, + Internal: nw.GetBool("is_internal"), IPAM: &network.IPAM{ Config: []network.IPAMConfig{ { - Subnet: c.Network.Interfaces.V4.Subnet, - Gateway: c.Network.Interfaces.V4.Gateway, + Subnet: nw.GetString("interfaces.v4.subnet"), + Gateway: nw.GetString("interfaces.v4.gateway"), }, { - Subnet: c.Network.Interfaces.V6.Subnet, - Gateway: c.Network.Interfaces.V6.Gateway, + Subnet: nw.GetString("interfaces.v6.subnet"), + Gateway: nw.GetString("interfaces.v6.gateway"), }, }, }, Options: map[string]string{ "encryption": "false", "com.docker.network.bridge.default_bridge": "false", - "com.docker.network.bridge.enable_icc": strconv.FormatBool(c.Network.EnableICC), + "com.docker.network.bridge.enable_icc": strconv.FormatBool(nw.GetBool("enable_icc")), "com.docker.network.bridge.enable_ip_masquerade": "true", "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0", "com.docker.network.bridge.name": "pterodactyl0", "com.docker.network.driver.mtu": "1500", }, }) - - if err != nil { - return err + driver := nw.GetString("driver") + if driver != "host" && driver != "overlay" && driver != "weavemesh" { + nw.Set("interface", nw.GetString("interfaces.v4.gateway")) } - - switch c.Network.Driver { - case "host": - c.Network.Interface = "127.0.0.1" - c.Network.ISPN = false - break - case "overlay": - case "weavemesh": - c.Network.Interface = "" - c.Network.ISPN = true - break - default: - c.Network.Interface = c.Network.Interfaces.V4.Gateway - c.Network.ISPN = false - break - } - - return nil + return err } diff --git a/go.mod b/go.mod index 079a014..b25e755 100644 --- a/go.mod +++ b/go.mod @@ -64,6 +64,7 @@ require ( github.com/sirupsen/logrus v1.7.0 // indirect github.com/spf13/cobra v1.1.1 github.com/spf13/pflag v1.0.5 + github.com/spf13/viper v1.7.1 github.com/ugorji/go v1.2.2 // indirect github.com/ulikunitz/xz v0.5.9 // indirect golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad diff --git a/go.sum b/go.sum index 9e9b174..ca88e19 100644 --- a/go.sum +++ b/go.sum @@ -285,6 +285,7 @@ github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -401,6 +402,7 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= @@ -454,6 +456,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= @@ -550,11 +553,14 @@ github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4S github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= @@ -562,6 +568,8 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= +github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -575,6 +583,7 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tj/assert v0.0.0-20171129193455-018094318fb0 h1:Rw8kxzWo1mr6FSaYXjQELRe88y2KdfynXdnK72rdjtA= github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= diff --git a/system/utils.go b/system/utils.go index e0e8d72..681ac2a 100644 --- a/system/utils.go +++ b/system/utils.go @@ -7,14 +7,35 @@ import ( "encoding/json" "fmt" "io" + "strconv" "strings" "sync" "time" + + "emperror.dev/errors" ) var cr = []byte(" \r") var crr = []byte("\r\n") +// FirstNotEmpty returns the first string passed in that is not an empty value. +func FirstNotEmpty(v ...string) string { + for _, val := range v { + if val != "" { + return val + } + } + return "" +} + +func MustInt(v string) int { + i, err := strconv.Atoi(v) + if err != nil { + panic(errors.Wrap(err, "system/utils: could not parse int")) + } + return i +} + func ScanReader(r io.Reader, callback func(line string)) error { br := bufio.NewReader(r) // Avoid constantly re-allocating memory when we're flooding lines through this