Compare commits

...

33 Commits

Author SHA1 Message Date
Matthew Penner
d739948989
feat: add ability to mount generated passwd files to containers (#197)
This PR will add an option to mount:
- `/etc/group`
- `/etc/passwd`

Signed-off-by: Matthew Penner <me@matthewp.io>
2024-07-24 15:12:42 -06:00
Matthew Penner
ac260bd5ee
ci: update to go1.22.5, drop go1.21
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-07-24 13:14:25 -06:00
Matthew Penner
2f4a0d7262
nix: update flake.lock
Flake lock file updates:

• Updated input 'flake-parts':
    'github:hercules-ci/flake-parts/b253292d9c0a5ead9bc98c4e9a26c6312e27d69f?narHash=sha256-a0NYyp%2Bh9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg%3D' (2024-02-01)
  → 'github:hercules-ci/flake-parts/9227223f6d922fee3c7b190b2cc238a99527bbb7?narHash=sha256-pQMhCCHyQGRzdfAkdJ4cIWiw%2BJNuWsTX7f0ZYSyz0VY%3D' (2024-07-03)
• Updated input 'flake-parts/nixpkgs-lib':
    'github:NixOS/nixpkgs/97b17f32362e475016f942bbdfda4a4a72a8a652?dir=lib&narHash=sha256-UcsnCG6wx%2B%2B23yeER4Hg18CXWbgNpqNXcHIo5/1Y%2Bhc%3D' (2024-01-29)
  → '5daf051448.tar.gz?narHash=sha256-Fm2rDDs86sHy0/1jxTOKB1118Q0O3Uc7EC0iXvXKpbI%3D' (2024-07-01)
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/a4d4fe8c5002202493e87ec8dbc91335ff55552c?narHash=sha256-ZL2TrjVsiFNKOYwYQozpbvQSwvtV/3Me7Zwhmdsfyu4%3D' (2024-02-15)
  → 'github:NixOS/nixpkgs/68c9ed8bbed9dfce253cc91560bf9043297ef2fe?narHash=sha256-Tybxt65eyOARf285hMHIJ2uul8SULjFZbT9ZaEeUnP8%3D' (2024-07-21)
• Updated input 'treefmt-nix':
    'github:numtide/treefmt-nix/ac599dab59a66304eb511af07b3883114f061b9d?narHash=sha256-qQF0fEkHlnxHcrKIMRzOETnRBksUK048MXkX0SOmxvA%3D' (2024-02-07)
  → 'github:numtide/treefmt-nix/8db8970be1fb8be9c845af7ebec53b699fe7e009?narHash=sha256-6Pqa0bi5nV74IZcENKYRToRNM5obo1EQ%2B3ihtunJ014%3D' (2024-07-23)
2024-07-24 13:10:35 -06:00
EpicPlayerA10
1d8b383682
fix: only count hard-links once when calculating filesystem usage (#181) 2024-06-29 12:56:51 -06:00
Arnaud Lier
934bf2493d
fix: properly use base2 (1024, *bibyte) when calculating memory limits (#190) 2024-06-29 12:34:20 -06:00
Daniel Barton
29e4425e21
fix: overhaul docker container termination signals (#192)
Fixes https://github.com/pterodactyl/panel/issues/4783

Requires https://github.com/pterodactyl/panel/pull/5132 to work
2024-06-29 12:31:36 -06:00
Daniel Barton
5a15612754
chore: show the actual location in "config not found" error (#179) 2024-06-29 12:26:59 -06:00
Daniel Barton
ad1ae862a9
fix: user-defined labels not being passed to environment (#191) 2024-06-29 12:25:47 -06:00
Matthew Penner
3114a3b82e
Update README.md
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-06-22 19:21:54 -06:00
Matthew Penner
500f217514
Update CHANGELOG.md 2024-05-13 16:01:33 -06:00
Daniel Barton
9ffbcdcdb1
cmd: handle relative paths to the config file (#180) 2024-05-13 15:53:33 -06:00
Daniel Barton
9b341db2db
server(filesystem): fix sort position of directories (#188) 2024-05-13 15:52:10 -06:00
Matthew Penner
71c5338549
Update CHANGELOG.md 2024-05-07 22:12:59 -06:00
Matthew Penner
326f115f5b
Update README.md 2024-05-07 22:12:55 -06:00
Geri
06614de99d
server(filesystem): handle individual compressed files (#184) 2024-05-07 22:06:15 -06:00
Matthew Penner
2b0e35360b
cmd(configure): fix panel url not being set
Fixes https://github.com/pterodactyl/panel/issues/5087
2024-05-07 22:01:12 -06:00
Matthew Penner
202f2229a9
Update README.md 2024-05-03 10:09:21 -06:00
Matthew Penner
baf1f0b5cd
Update CHANGELOG.md 2024-05-03 10:09:18 -06:00
Daniel Barton
ec54371b86
cmd(diagnostics): fix Content-Type and accept more status codes (#186) 2024-04-16 15:15:19 -06:00
Matthew Penner
1d5090957b
ci: use go 1.21.9 and 1.22.2 2024-04-10 17:16:28 -06:00
Matthew Penner
5415f8ae07
config: prevent programmatic updates to specific fields 2024-04-10 15:27:54 -06:00
Matthew Penner
617fbcbf27
router(download): validate that backup_uuid is actually a uuid 2024-04-10 15:22:39 -06:00
Matthew Penner
c152e36101
downloader: move internal subnet validation into http Transport 2024-04-10 15:22:09 -06:00
Matthew Penner
5b0422d756
go: update dependencies 2024-04-10 15:21:37 -06:00
Matthew Penner
f1c5bbd42d
server(filesystem): allow decompressing individual files
Implements logic for handling a file that is compressed but isn't an archive.

Fixes https://github.com/pterodactyl/panel/issues/5034

Signed-off-by: Matthew Penner <me@matthewp.io>
2024-03-17 14:58:30 -06:00
Matthew Penner
1c5ddcd20c
server(filesystem): handle writing empty files
Fixes https://github.com/pterodactyl/panel/issues/5038

Signed-off-by: Matthew Penner <me@matthewp.io>
2024-03-17 14:58:13 -06:00
Matthew Penner
a877305202
Update README.md 2024-03-16 14:02:13 -06:00
Matthew Penner
1f77d2256b
Update CHANGELOG.md
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-03-14 14:01:13 -06:00
Matthew Penner
ac9bd1d95e
server(filesystem): fix archives in subdirectories
When creating an archive starting from a subdirectory instead of the
root of a server's filesystem, the walker would treat the paths as if
they where relative to the parent of the subdirectory, instead of as
descendants of the subdirectory.

Fixes https://github.com/pterodactyl/panel/issues/5030
2024-03-14 13:46:36 -06:00
Matthew Penner
979df34392
server(filesystem): fix archive creation with ignore
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-03-13 20:29:09 -06:00
Matthew Penner
8f129931d5
Update CHANGELOG.md
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-03-13 15:19:24 -06:00
Matthew Penner
2931430eb8
server(filesystem): fix archive skipping directories
Fixes https://github.com/pterodactyl/panel/issues/5027

Signed-off-by: Matthew Penner <me@matthewp.io>
2024-03-13 15:09:24 -06:00
Matthew Penner
99b9924a4a
router(server): fix panic on server deletion
Fixes https://github.com/pterodactyl/panel/issues/5028

Signed-off-by: Matthew Penner <me@matthewp.io>
2024-03-13 14:46:17 -06:00
30 changed files with 734 additions and 253 deletions

1
.envrc Normal file
View File

@ -0,0 +1 @@
use flake

View File

@ -16,7 +16,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-22.04]
go: ["1.21.8", "1.22.1"]
go: ["1.22.5"]
goos: [linux]
goarch: [amd64, arm64]
@ -62,14 +62,14 @@ jobs:
- name: Upload Release Artifact
uses: actions/upload-artifact@v4
if: ${{ (github.ref == 'refs/heads/develop' || github.event_name == 'pull_request') && matrix.go == '1.21.8' }}
if: ${{ (github.ref == 'refs/heads/develop' || github.event_name == 'pull_request') && matrix.go == '1.22.5' }}
with:
name: wings_linux_${{ matrix.goarch }}
path: dist/wings
- name: Upload Debug Artifact
uses: actions/upload-artifact@v4
if: ${{ (github.ref == 'refs/heads/develop' || github.event_name == 'pull_request') && matrix.go == '1.21.8' }}
if: ${{ (github.ref == 'refs/heads/develop' || github.event_name == 'pull_request') && matrix.go == '1.22.5' }}
with:
name: wings_linux_${{ matrix.goarch }}_debug
path: dist/wings_debug

View File

@ -17,7 +17,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.21.8"
go-version: "1.22.5"
- name: Build release binaries
env:

View File

@ -1,5 +1,39 @@
# Changelog
## v1.11.14
### Added
* Support relative file paths for the Wings config ([#180](https://github.com/pterodactyl/wings/pull/180))
### Fixed
* Folders not being sorted before files properly ([#5078](https://github.com/pterodactyl/panel/issues/5078)
## v1.11.13
### Fixed
* Auto-configure not working ([#5087](https://github.com/pterodactyl/panel/issues/5087))
* Individual files unable to be decompressed ([#5034](https://github.com/pterodactyl/panel/issues/5034))
## v1.11.12
### Fixed
* Arbitrary File Write/Read ([GHSA-gqmf-jqgv-v8fw](https://github.com/pterodactyl/wings/security/advisories/GHSA-gqmf-jqgv-v8fw))
* Server-side Request Forgery (SSRF) during remote file pull ([GHSA-qq22-jj8x-4wwv](https://github.com/pterodactyl/wings/security/advisories/GHSA-qq22-jj8x-4wwv))
* Invalid `Content-Type` being used with the `wings diagnostics` command ([#186](https://github.com/pterodactyl/wings/pull/186))
## v1.11.11
### Fixed
* Backups missing content when a `.pteroignore` file is used
* Archives originating from a subdirectory not containing any files ([#5030](https://github.com/pterodactyl/panel/issues/5030))
## v1.11.10
### Fixed
* Archives randomly ignoring files and directories ([#5027](https://github.com/pterodactyl/panel/issues/5027))
* Crash when deleting or transferring a server ([#5028](https://github.com/pterodactyl/panel/issues/5028))
## v1.11.9
### Changed
* Release binaries are now built with Go 1.21.8

View File

@ -1,5 +1,5 @@
# Stage 1 (Build)
FROM golang:1.21.8-alpine AS builder
FROM golang:1.22.5-alpine AS builder
ARG VERSION
RUN apk add --update --no-cache git make

View File

@ -19,12 +19,14 @@ I would like to extend my sincere thanks to the following sponsors for helping f
[Interested in becoming a sponsor?](https://github.com/sponsors/matthewpi)
| Company | About |
|-----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|--------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [**Aussie Server Hosts**](https://aussieserverhosts.com/) | No frills Australian Owned and operated High Performance Server hosting for some of the most demanding games serving Australia and New Zealand. |
| [**CodeNode LLC**](https://codenode.gg/) | Looking for simplicity? Well, look no further! CodeNode has got you covered with everything you need at the rock-bottom price of $1.75 per GB, including dedicated IPs in Dallas, Texas, and Amsterdam, Netherlands. We're not just good, we're the best in the game! |
| [**BisectHosting**](https://www.bisecthosting.com/) | BisectHosting provides Minecraft, Valheim and other server hosting services with the highest reliability and lightning fast support since 2012. |
| [**MineStrator**](https://minestrator.com/) | Looking for the most highend French hosting company for your minecraft server? More than 24,000 members on our discord trust us. Give us a try! |
| [**VibeGAMES**](https://vibegames.net/) | VibeGAMES is a game server provider that specializes in DDOS protection for the games we offer. We have multiple locations in the US, Brazil, France, Germany, Singapore, Australia and South Africa. |
| [**HostEZ**](https://hostez.io) | US & EU Rust & Minecraft Hosting. DDoS Protected bare metal, VPS and colocation with low latency, high uptime and maximum availability. EZ! |
| [**Blueprint**](https://blueprint.zip/?pterodactyl=true) | Create and install Pterodactyl addons and themes with the growing Blueprint framework - the package-manager for Pterodactyl. Use multiple modifications at once without worrying about conflicts and make use of the large extension ecosystem. |
| [**indifferent broccoli**](https://indifferentbroccoli.com/) | indifferent broccoli is a game server hosting and rental company. With us, you get top-notch computer power for your gaming sessions. We destroy lag, latency, and complexity--letting you focus on the fun stuff. |
## Documentation

View File

@ -155,6 +155,9 @@ func configureCmdRun(cmd *cobra.Command, args []string) {
panic(err)
}
// Manually specify the Panel URL as it won't be decoded from JSON.
cfg.PanelLocation = configureArgs.PanelURL
if err = config.WriteToDisk(cfg); err != nil {
panic(err)
}

View File

@ -229,8 +229,8 @@ func uploadToHastebin(hbUrl, content string) (string, error) {
return "", err
}
u.Path = path.Join(u.Path, "documents")
res, err := http.Post(u.String(), "plain/text", r)
if err != nil || res.StatusCode != 200 {
res, err := http.Post(u.String(), "text/plain", r)
if err != nil || res.StatusCode < 200 || res.StatusCode >= 300 {
fmt.Println("Failed to upload report to ", u.String(), err)
return "", err
}

View File

@ -13,7 +13,6 @@ import (
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
"github.com/NYTimes/logrotate"
@ -113,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,
@ -379,13 +381,14 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
// Reads the configuration from the disk and then sets up the global singleton
// with all the configuration values.
func initConfig() {
if !strings.HasPrefix(configPath, "/") {
d, err := os.Getwd()
if !filepath.IsAbs(configPath) {
d, err := filepath.Abs(configPath)
if err != nil {
log2.Fatalf("cmd/root: could not determine directory: %s", err)
log2.Fatalf("cmd/root: failed to get path to config file: %s", err)
}
configPath = path.Clean(path.Join(d, configPath))
configPath = d
}
err := config.FromFile(configPath)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
@ -440,18 +443,18 @@ in all copies or substantial portions of the Software.%s`), system.Version, time
}
func exitWithConfigurationNotice() {
fmt.Print(colorstring.Color(`
fmt.Printf(colorstring.Color(`
[_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 below.
Default Location: /etc/pterodactyl/config.yml
Default Location: %s
[yellow]This is not a bug with this software. Please do not make a bug report
for this issue, it will be closed.[reset]
`))
`), config.DefaultLocation)
os.Exit(1)
}

View File

@ -89,7 +89,7 @@ type ApiConfiguration struct {
// Determines if functionality for allowing remote download of files into server directories
// is enabled on this instance. If set to "true" remote downloads will not be possible for
// servers.
DisableRemoteDownload bool `json:"disable_remote_download" yaml:"disable_remote_download"`
DisableRemoteDownload bool `json:"-" yaml:"disable_remote_download"`
// The maximum size for files uploaded through the Panel in MB.
UploadLimit int64 `default:"100" json:"upload_limit" yaml:"upload_limit"`
@ -123,23 +123,23 @@ type RemoteQueryConfiguration struct {
// SystemConfiguration defines basic system configuration settings.
type SystemConfiguration struct {
// The root directory where all of the pterodactyl data is stored at.
RootDirectory string `default:"/var/lib/pterodactyl" yaml:"root_directory"`
RootDirectory string `default:"/var/lib/pterodactyl" json:"-" yaml:"root_directory"`
// Directory where logs for server installations and other wings events are logged.
LogDirectory string `default:"/var/log/pterodactyl" yaml:"log_directory"`
LogDirectory string `default:"/var/log/pterodactyl" json:"-" yaml:"log_directory"`
// Directory where the server data is stored at.
Data string `default:"/var/lib/pterodactyl/volumes" yaml:"data"`
Data string `default:"/var/lib/pterodactyl/volumes" json:"-" yaml:"data"`
// Directory where server archives for transferring will be stored.
ArchiveDirectory string `default:"/var/lib/pterodactyl/archives" yaml:"archive_directory"`
ArchiveDirectory string `default:"/var/lib/pterodactyl/archives" json:"-" yaml:"archive_directory"`
// Directory where local backups will be stored on the machine.
BackupDirectory string `default:"/var/lib/pterodactyl/backups" yaml:"backup_directory"`
BackupDirectory string `default:"/var/lib/pterodactyl/backups" json:"-" yaml:"backup_directory"`
// TmpDirectory specifies where temporary files for Pterodactyl installation processes
// should be created. This supports environments running docker-in-docker.
TmpDirectory string `default:"/tmp/pterodactyl" yaml:"tmp_directory"`
TmpDirectory string `default:"/tmp/pterodactyl" json:"-" yaml:"tmp_directory"`
// The user that should own all of the server files, and be used for containers.
Username string `default:"pterodactyl" yaml:"username"`
@ -172,6 +172,25 @@ type SystemConfiguration struct {
Gid int `yaml:"gid"`
} `yaml:"user"`
// Passwd controls the mounting of a generated passwd files into containers started by Wings.
Passwd struct {
// Enable controls whether generated passwd files should be mounted into containers.
//
// By default this option is disabled and Wings will not mount any additional passwd
// files into containers.
Enable bool `yaml:"enabled" default:"false"`
// Directory is the directory on disk where the generated files will be stored.
// This directory may be temporary as it 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.
Directory string `yaml:"directory" default:"/run/wings/etc"`
} `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
@ -306,7 +325,7 @@ type Configuration struct {
// The location where the panel is running that this daemon should connect to
// to collect data and send events.
PanelLocation string `json:"remote" yaml:"remote"`
PanelLocation string `json:"-" yaml:"remote"`
RemoteQuery RemoteQueryConfiguration `json:"remote_query" yaml:"remote_query"`
// AllowedMounts is a list of allowed host-system mount points.
@ -497,6 +516,37 @@ func EnsurePterodactylUser() error {
return nil
}
// ConfigurePasswd generates required passwd files 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:
container:x:%d:
nogroup:x:65534:`,
_config.System.User.Gid,
))
if err := os.WriteFile(filepath.Join(passwd.Directory, "group"), v, 0o644); err != nil {
return fmt.Errorf("failed to write file to %s/group: %v", passwd.Directory, err)
}
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(filepath.Join(passwd.Directory, "passwd"), v, 0o644); err != nil {
return fmt.Errorf("failed to write file to %s/passwd: %v", passwd.Directory, 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 {
@ -561,6 +611,13 @@ func ConfigureDirectories() error {
return err
}
if _config.System.Passwd.Enable {
log.WithField("path", _config.System.Passwd.Directory).Debug("ensuring passwd directory exists")
if err := os.MkdirAll(_config.System.Passwd.Directory, 0o755); err != nil {
return err
}
}
return nil
}
@ -676,8 +733,10 @@ func getSystemName() (string, error) {
return release["ID"], nil
}
var openat2 atomic.Bool
var openat2Set atomic.Bool
var (
openat2 atomic.Bool
openat2Set atomic.Bool
)
func UseOpenat2() bool {
if openat2Set.Load() {

View File

@ -4,7 +4,6 @@ import (
"context"
"os"
"strings"
"syscall"
"time"
"emperror.dev/errors"
@ -143,42 +142,49 @@ func (e *Environment) Stop(ctx context.Context) error {
s := e.meta.Stop
e.mu.RUnlock()
// A native "stop" as the Type field value will just skip over all of this
// logic and end up only executing the container stop command (which may or
// may not work as expected).
if s.Type == "" || s.Type == remote.ProcessStopSignal {
if s.Type == "" {
log.WithField("container_id", e.Id).Warn("no stop configuration detected for environment, using termination procedure")
}
signal := os.Kill
// Handle a few common cases, otherwise just fall through and just pass along
// the os.Kill signal to the process.
switch strings.ToUpper(s.Value) {
case "SIGABRT":
signal = syscall.SIGABRT
case "SIGINT":
signal = syscall.SIGINT
case "SIGTERM":
signal = syscall.SIGTERM
}
return e.Terminate(ctx, signal)
}
// If the process is already offline don't switch it back to stopping. Just leave it how
// it is and continue through to the stop handling for the process.
if e.st.Load() != environment.ProcessOfflineState {
e.SetState(environment.ProcessStoppingState)
}
// Handle signal based actions
if s.Type == remote.ProcessStopSignal {
log.WithField("signal_value", s.Value).Debug("stopping server using signal")
// Handle some common signals - Default to SIGKILL
signal := "SIGKILL"
switch strings.ToUpper(s.Value) {
case "SIGABRT":
signal = "SIGABRT"
case "SIGINT", "C":
signal = "SIGINT"
case "SIGTERM":
signal = "SIGTERM"
case "SIGKILL":
signal = "SIGKILL"
default:
log.Info("Unrecognised signal requested, defaulting to SIGKILL")
}
return e.SignalContainer(ctx, signal)
}
// Handle command based stops
// Only attempt to send the stop command to the instance if we are actually attached to
// the instance. If we are not for some reason, just send the container stop event.
if e.IsAttached() && s.Type == remote.ProcessStopCommand {
return e.SendCommand(s.Value)
}
// Allow the stop action to run for however long it takes, similar to executing a command
// and using a different logic pathway to wait for the container to stop successfully.
if s.Type == "" {
log.WithField("container_id", e.Id).Warn("no stop configuration detected for environment, using native docker stop")
}
// Fallback to a native docker stop. As we aren't passing a signal to ContainerStop docker will
// attempt to stop the container using the default stop signal, SIGTERM, unless
// another signal was specified in the Dockerfile
//
// Using a negative timeout here will allow the container to stop gracefully,
// rather than forcefully terminating it. Value is in seconds, but -1 is
@ -224,7 +230,7 @@ func (e *Environment) WaitForStop(ctx context.Context, duration time.Duration, t
doTermination := func(s string) error {
e.log().WithField("step", s).WithField("duration", duration).Warn("container stop did not complete in time, terminating process...")
return e.Terminate(ctx, os.Kill)
return e.Terminate(ctx, "SIGKILL")
}
// We pass through the timed context for this stop action so that if one of the
@ -268,8 +274,8 @@ func (e *Environment) WaitForStop(ctx context.Context, duration time.Duration, t
return nil
}
// Terminate forcefully terminates the container using the signal provided.
func (e *Environment) Terminate(ctx context.Context, signal os.Signal) error {
// Sends the specified signal to the container in an attempt to stop it.
func (e *Environment) SignalContainer(ctx context.Context, signal string) error {
c, err := e.ContainerInspect(ctx)
if err != nil {
// Treat missing containers as an okay error state, means it is obviously
@ -294,11 +300,27 @@ func (e *Environment) Terminate(ctx context.Context, signal os.Signal) error {
// We set it to stopping than offline to prevent crash detection from being triggered.
e.SetState(environment.ProcessStoppingState)
sig := strings.TrimSuffix(strings.TrimPrefix(signal.String(), "signal "), "ed")
if err := e.client.ContainerKill(ctx, e.Id, sig); err != nil && !client.IsErrNotFound(err) {
if err := e.client.ContainerKill(ctx, e.Id, signal); err != nil && !client.IsErrNotFound(err) {
return errors.WithStack(err)
}
return nil
}
// Terminate forcefully terminates the container using the signal provided.
// then sets its state to stopped.
func (e *Environment) Terminate(ctx context.Context, signal string) error {
// Send the signal to the container to kill it
if err := e.SignalContainer(ctx, signal); err != nil {
return errors.WithStack(err)
}
// We expect Terminate to instantly kill the container
// so go ahead and mark it as dead and clean up
e.SetState(environment.ProcessOfflineState)
return nil
}

View File

@ -2,7 +2,6 @@ package environment
import (
"context"
"os"
"time"
"github.com/pterodactyl/wings/events"
@ -72,7 +71,7 @@ type ProcessEnvironment interface {
// Terminate stops a running server instance using the provided signal. This function
// is a no-op if the server is already stopped.
Terminate(ctx context.Context, signal os.Signal) error
Terminate(ctx context.Context, signal string) error
// Destroys the environment removing any containers that were created (in Docker
// environments at least).

View File

@ -34,7 +34,7 @@ type Mount struct {
// Limits is the build settings for a given server that impact docker container
// creation and resource limits for a server instance.
type Limits struct {
// The total amount of memory in megabytes that this server is allowed to
// The total amount of memory in mebibytes that this server is allowed to
// use on the host system.
MemoryLimit int64 `json:"memory_limit"`
@ -79,7 +79,7 @@ func (l Limits) MemoryOverheadMultiplier() float64 {
}
func (l Limits) BoundedMemoryLimit() int64 {
return int64(math.Round(float64(l.MemoryLimit) * l.MemoryOverheadMultiplier() * 1_000_000))
return int64(math.Round(float64(l.MemoryLimit) * l.MemoryOverheadMultiplier() * 1024 * 1024))
}
// ConvertedSwap returns the amount of swap available as a total in bytes. This
@ -90,7 +90,7 @@ func (l Limits) ConvertedSwap() int64 {
return -1
}
return (l.Swap * 1_000_000) + l.BoundedMemoryLimit()
return (l.Swap * 1024 * 1024) + l.BoundedMemoryLimit()
}
// ProcessLimit returns the process limit for a container. This is currently
@ -105,7 +105,7 @@ func (l Limits) AsContainerResources() container.Resources {
pids := l.ProcessLimit()
resources := container.Resources{
Memory: l.BoundedMemoryLimit(),
MemoryReservation: l.MemoryLimit * 1_000_000,
MemoryReservation: l.MemoryLimit * 1024 * 1024,
MemorySwap: l.ConvertedSwap(),
BlkioWeight: l.IoWeight,
OomKillDisable: &l.OOMDisabled,

79
flake.lock Normal file
View File

@ -0,0 +1,79 @@
{
"nodes": {
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1719994518,
"narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1721562059,
"narHash": "sha256-Tybxt65eyOARf285hMHIJ2uul8SULjFZbT9ZaEeUnP8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "68c9ed8bbed9dfce253cc91560bf9043297ef2fe",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1719876945,
"narHash": "sha256-Fm2rDDs86sHy0/1jxTOKB1118Q0O3Uc7EC0iXvXKpbI=",
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz"
}
},
"root": {
"inputs": {
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs",
"treefmt-nix": "treefmt-nix"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1721769617,
"narHash": "sha256-6Pqa0bi5nV74IZcENKYRToRNM5obo1EQ+3ihtunJ014=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "8db8970be1fb8be9c845af7ebec53b699fe7e009",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

54
flake.nix Normal file
View File

@ -0,0 +1,54 @@
{
description = "Wings";
inputs = {
flake-parts.url = "github:hercules-ci/flake-parts";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
treefmt-nix = {
url = "github:numtide/treefmt-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = {...} @ inputs:
inputs.flake-parts.lib.mkFlake {inherit inputs;} {
systems = ["aarch64-darwin" "aarch64-linux" "x86_64-darwin" "x86_64-linux"];
imports = [
inputs.treefmt-nix.flakeModule
];
perSystem = {system, ...}: let
pkgs = import inputs.nixpkgs {inherit system;};
in {
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
go_1_22
gofumpt
golangci-lint
gotools
];
};
treefmt = {
projectRootFile = "flake.nix";
programs = {
alejandra.enable = true;
deadnix.enable = true;
gofumpt = {
enable = true;
extra = true;
};
shellcheck.enable = true;
shfmt = {
enable = true;
indent_size = 0; # 0 causes shfmt to use tabs
};
yamlfmt.enable = true;
};
};
};
};
}

47
go.mod
View File

@ -12,7 +12,7 @@ require (
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
github.com/beevik/etree v1.3.0
github.com/buger/jsonparser v1.1.1
github.com/cenkalti/backoff/v4 v4.2.1
github.com/cenkalti/backoff/v4 v4.3.0
github.com/creasty/defaults v1.7.0
github.com/docker/docker v25.0.4+incompatible
github.com/docker/go-connections v0.5.0
@ -22,7 +22,7 @@ require (
github.com/gammazero/workerpool v1.1.3
github.com/gbrlsnchs/jwt/v3 v3.0.1
github.com/gin-gonic/gin v1.9.1
github.com/glebarez/sqlite v1.10.0
github.com/glebarez/sqlite v1.11.0
github.com/go-co-op/gocron v1.37.0
github.com/goccy/go-json v0.10.2
github.com/google/uuid v1.6.0
@ -30,7 +30,7 @@ require (
github.com/iancoleman/strcase v0.3.0
github.com/icza/dyno v0.0.0-20230330125955-09f820a8d9c0
github.com/juju/ratelimit v1.0.2
github.com/klauspost/compress v1.17.7
github.com/klauspost/compress v1.17.8
github.com/klauspost/pgzip v1.2.6
github.com/magiconair/properties v1.8.7
github.com/mattn/go-colorable v0.1.13
@ -41,28 +41,28 @@ require (
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
github.com/spf13/cobra v1.8.0
github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.21.0
golang.org/x/sync v0.6.0
golang.org/x/sys v0.18.0
golang.org/x/crypto v0.22.0
golang.org/x/sync v0.7.0
golang.org/x/sys v0.19.0
gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
gorm.io/gorm v1.25.7
gorm.io/gorm v1.25.9
)
require (
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Microsoft/hcsshim v0.12.0 // indirect
github.com/Microsoft/hcsshim v0.12.2 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/bodgit/plumbing v1.3.0 // indirect
github.com/bodgit/sevenzip v1.5.0 // indirect
github.com/bodgit/sevenzip v1.5.1 // indirect
github.com/bodgit/windows v1.0.1 // indirect
github.com/bytedance/sonic v1.11.3 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
@ -79,6 +79,7 @@ require (
github.com/golang/snappy v0.0.4 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
@ -98,7 +99,7 @@ require (
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.0 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
@ -109,28 +110,28 @@ require (
github.com/therootcompany/xz v1.0.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/ulikunitz/xz v0.5.11 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 // indirect
go.opentelemetry.io/otel v1.25.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.25.0 // indirect
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.25.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/arch v0.7.0 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/term v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
golang.org/x/tools v0.19.0 // indirect
golang.org/x/tools v0.20.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gotest.tools/v3 v3.0.2 // indirect
modernc.org/libc v1.44.0 // indirect
modernc.org/libc v1.49.3 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.7.2 // indirect
modernc.org/sqlite v1.29.4 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/sqlite v1.29.6 // indirect
)

103
go.sum
View File

@ -27,8 +27,8 @@ github.com/Jeffail/gabs/v2 v2.7.0 h1:Y2edYaTcE8ZpRsR2AtmPu5xQdFDIthFG0jYhu5PY8kg
github.com/Jeffail/gabs/v2 v2.7.0/go.mod h1:dp5ocw1FvBBQYssgHsG7I1WYsiLRtkUaB1FEtSwvNUw=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Microsoft/hcsshim v0.12.0 h1:rbICA+XZFwrBef2Odk++0LjFvClNCJGRK+fsrP254Ts=
github.com/Microsoft/hcsshim v0.12.0/go.mod h1:RZV12pcHCXQ42XnlQ3pz6FZfmrC1C+R4gaOHhRNML1g=
github.com/Microsoft/hcsshim v0.12.2 h1:AcXy+yfRvrx20g9v7qYaJv5Rh+8GaHOS6b8G6Wx/nKs=
github.com/Microsoft/hcsshim v0.12.2/go.mod h1:RZV12pcHCXQ42XnlQ3pz6FZfmrC1C+R4gaOHhRNML1g=
github.com/NYTimes/logrotate v1.0.0 h1:6jFGbon6jOtpy3t3kwZZKS4Gdmf1C/Wv5J4ll4Xn5yk=
github.com/NYTimes/logrotate v1.0.0/go.mod h1:GxNz1cSw1c6t99PXoZlw+nm90H6cyQyrH66pjVv7x88=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
@ -50,8 +50,8 @@ github.com/beevik/etree v1.3.0 h1:hQTc+pylzIKDb23yYprodCWWTt+ojFfUZyzU09a/hmU=
github.com/beevik/etree v1.3.0/go.mod h1:aiPf89g/1k3AShMVAzriilpcE4R/Vuor90y83zVZWFc=
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
github.com/bodgit/sevenzip v1.5.0 h1:QESwnPUnhqftOgbi6wIiWm1WEkrT4puHukt5a2psEcw=
github.com/bodgit/sevenzip v1.5.0/go.mod h1:+E74G6pfBX8IMaVybsKMgGTTTBcbHU8ssPTJ9mLUr38=
github.com/bodgit/sevenzip v1.5.1 h1:rVj0baZsooZFy64DJN0zQogPzhPrT8BQ8TTRd1H4WHw=
github.com/bodgit/sevenzip v1.5.1/go.mod h1:Q3YMySuVWq6pyGEolyIE98828lOfEoeWg5zeH6x22rc=
github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4=
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
@ -60,8 +60,8 @@ github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.11.3 h1:jRN+yEjakWh8aK5FzrciUHG8OFXK+4/KrAX/ysEtHAA=
github.com/bytedance/sonic v1.11.3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
@ -86,8 +86,8 @@ github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbD
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v25.0.4+incompatible h1:XITZTrq+52tZyZxUOtFIahUf3aH367FLxJzt9vZeAF8=
github.com/docker/docker v25.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
@ -123,8 +123,8 @@ github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
github.com/glebarez/sqlite v1.10.0 h1:u4gt8y7OND/cCei/NMHmfbLxF6xP2wgKcT/BJf2pYkc=
github.com/glebarez/sqlite v1.10.0/go.mod h1:IJ+lfSOmiekhQsFTJRx/lHtGYmCdtAiTaf5wI9u5uHA=
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0=
github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@ -197,6 +197,8 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
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/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@ -224,8 +226,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
@ -289,8 +291,8 @@ github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQ
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
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/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo=
github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@ -329,6 +331,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@ -354,8 +357,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@ -363,20 +366,20 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 h1:cEPbyTSEHlQR89XVlyo78gqluF8Y3oMeBkXGWzQsfXY=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0/go.mod h1:DKdbWcT4GH1D0Y3Sqt/PFXt2naRKDWtU+eE6oLdFNA8=
go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k=
go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA=
go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s=
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM=
go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I=
go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI=
go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@ -402,8 +405,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -431,8 +434,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -453,8 +456,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -468,8 +471,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -495,14 +498,14 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -546,8 +549,8 @@ golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -614,8 +617,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8=
gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gotest.tools/v3 v3.0.2 h1:kG1BFyqVHuQoVQiR1bWGnfz/fmHvvuiSPIV7rvl360E=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@ -623,26 +626,26 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
modernc.org/cc/v4 v4.19.3 h1:vE9kmJqUcyvNOf8F2Hn8od14SOMq34BiqcZ2tMzLk5c=
modernc.org/cc/v4 v4.19.3/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.10.1 h1:qi+3luLv0LR5UkLmZyKXZxIC4K/136vcAUoYYeGSS+g=
modernc.org/ccgo/v4 v4.10.1/go.mod h1:9YDnb1IIvHymh899K5a++jza0JIWygZPTc5dlh7xvhQ=
modernc.org/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk=
modernc.org/cc/v4 v4.20.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.16.0 h1:ofwORa6vx2FMm0916/CkZjpFPSR70VwTjUCe2Eg5BnA=
modernc.org/ccgo/v4 v4.16.0/go.mod h1:dkNyWIjFrVIZ68DTo36vHK+6/ShBn4ysU61So6PIqCI=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/libc v1.44.0 h1:71bbnKgb0mCg7GOOI/PHlzz7Bv6obELGNKnIEeowX8c=
modernc.org/libc v1.44.0/go.mod h1:RRqfGVjvILF5AdNP3RPCiihj7+Dn2pIBrdlU60lA9vs=
modernc.org/libc v1.49.3 h1:j2MRCRdwJI2ls/sGbeSk0t2bypOG/uvPZUsGQFDulqg=
modernc.org/libc v1.49.3/go.mod h1:yMZuGkn7pXbKfoT/M35gFJOAEdSKdxL0q64sF7KqCDo=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/sqlite v1.29.4 h1:mbvQTJ3Tl5Vz+wLA6z8hdBFSeNQ0XXQ+KVwn8NkUliw=
modernc.org/sqlite v1.29.4/go.mod h1:MjUIBKZ+tU/lqjNLbVAAMjsQPdWdA/ciwdhsT9kBwk8=
modernc.org/sqlite v1.29.6 h1:0lOXGrycJPptfHDuohfYgNqoe4hu+gYuN/pKgY5XjS4=
modernc.org/sqlite v1.29.6/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=

View File

@ -66,6 +66,13 @@ func (fs *UnixFS) walkDir(b []byte, parentfd int, name, relative string, d DirEn
}
for _, d1 := range dirs {
// TODO: the path.Join on this line may actually be partially incorrect.
// If we are not walking starting at the root, relative will contain the
// name of the directory we are starting the walk from, which will be
// relative to the root of the filesystem instead of from where the walk
// was initiated from.
//
// ref; https://github.com/pterodactyl/panel/issues/5030
if err := fs.walkDir(b, dirfd, d1.Name(), path.Join(relative, d1.Name()), d1, walkDirFn); err != nil {
if err == SkipDir {
break

View File

@ -20,8 +20,45 @@ import (
"github.com/pterodactyl/wings/server"
)
var client = &http.Client{
var client *http.Client
func init() {
dialer := &net.Dialer{
LocalAddr: nil,
}
trnspt := http.DefaultTransport.(*http.Transport).Clone()
trnspt.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
c, err := dialer.DialContext(ctx, network, addr)
if err != nil {
return nil, errors.WithStack(err)
}
ipStr, _, err := net.SplitHostPort(c.RemoteAddr().String())
if err != nil {
return c, errors.WithStack(err)
}
ip := net.ParseIP(ipStr)
if ip == nil {
return c, errors.WithStack(ErrInvalidIPAddress)
}
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() || ip.IsInterfaceLocalMulticast() {
return c, errors.WithStack(ErrInternalResolution)
}
for _, block := range internalRanges {
if !block.Contains(ip) {
continue
}
return c, errors.WithStack(ErrInternalResolution)
}
return c, nil
}
client = &http.Client{
Timeout: time.Hour * 12,
Transport: trnspt,
// Disallow any redirect on an HTTP call. This is a security requirement: do not modify
// this logic without first ensuring that the new target location IS NOT within the current
// instance's local network.
@ -34,6 +71,7 @@ var client = &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
}
var instance = &Downloader{
@ -143,12 +181,6 @@ func (dl *Download) Execute() error {
dl.cancelFunc = &cancel
defer dl.Cancel()
// Always ensure that we're checking the destination for the download to avoid a malicious
// user from accessing internal network resources.
if err := dl.isExternalNetwork(ctx); err != nil {
return err
}
// At this point we have verified the destination is not within the local network, so we can
// now make a request to that URL and pull down the file, saving it to the server's data
// directory.
@ -243,59 +275,6 @@ func (dl *Download) counter(contentLength int64) *Counter {
}
}
// Verifies that a given download resolves to a location not within the current local
// network for the machine. If the final destination of a resource is within the local
// network an ErrInternalResolution error is returned.
func (dl *Download) isExternalNetwork(ctx context.Context) error {
dialer := &net.Dialer{
LocalAddr: nil,
}
host := dl.req.URL.Host
// This cluster-fuck of math and integer shit converts an integer IP into a proper IPv4.
// For example: 16843009 would become 1.1.1.1
//if i, err := strconv.ParseInt(host, 10, 64); err == nil {
// host = strconv.FormatInt((i>>24)&0xFF, 10) + "." + strconv.FormatInt((i>>16)&0xFF, 10) + "." + strconv.FormatInt((i>>8)&0xFF, 10) + "." + strconv.FormatInt(i&0xFF, 10)
//}
if _, _, err := net.SplitHostPort(host); err != nil {
if !strings.Contains(err.Error(), "missing port in address") {
return errors.WithStack(err)
}
switch dl.req.URL.Scheme {
case "http":
host += ":80"
case "https":
host += ":443"
}
}
c, err := dialer.DialContext(ctx, "tcp", host)
if err != nil {
return errors.WithStack(err)
}
_ = c.Close()
ipStr, _, err := net.SplitHostPort(c.RemoteAddr().String())
if err != nil {
return errors.WithStack(err)
}
ip := net.ParseIP(ipStr)
if ip == nil {
return errors.WithStack(ErrInvalidIPAddress)
}
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() || ip.IsInterfaceLocalMulticast() {
return errors.WithStack(ErrInternalResolution)
}
for _, block := range internalRanges {
if block.Contains(ip) {
return errors.WithStack(ErrInternalResolution)
}
}
return nil
}
// Downloader represents a global downloader that keeps track of all currently processing downloads
// for the machine.
type Downloader struct {

View File

@ -8,6 +8,7 @@ import (
"strconv"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/pterodactyl/wings/router/middleware"
"github.com/pterodactyl/wings/router/tokens"
@ -19,12 +20,14 @@ func getDownloadBackup(c *gin.Context) {
client := middleware.ExtractApiClient(c)
manager := middleware.ExtractManager(c)
// Get the payload from the token.
token := tokens.BackupPayload{}
if err := tokens.ParseToken([]byte(c.Query("token")), &token); err != nil {
middleware.CaptureAndAbort(c, err)
return
}
// Get the server using the UUID from the token.
if _, ok := manager.Get(token.ServerUuid); !ok || !token.IsUniqueRequest() {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
"error": "The requested resource was not found on this server.",
@ -32,6 +35,14 @@ func getDownloadBackup(c *gin.Context) {
return
}
// Validate that the BackupUuid field is actually a UUID and not some random characters or a
// file path.
if _, err := uuid.Parse(token.BackupUuid); err != nil {
middleware.CaptureAndAbort(c, err)
return
}
// Locate the backup on the local disk.
b, st, err := backup.LocateLocal(client, token.BackupUuid)
if err != nil {
if errors.Is(err, os.ErrNotExist) {

View File

@ -227,20 +227,19 @@ func deleteServer(c *gin.Context) {
//
// In addition, servers with large amounts of files can take some time to finish deleting,
// so we don't want to block the HTTP call while waiting on this.
go func(p string) {
_ = s.Filesystem().UnixFS().Close()
go func(s *server.Server) {
fs := s.Filesystem()
p := fs.Path()
_ = fs.UnixFS().Close()
if err := os.RemoveAll(p); err != nil {
log.WithFields(log.Fields{"path": p, "error": err}).Warn("failed to remove server files during deletion process")
}
}(s.Filesystem().Path())
}(s)
middleware.ExtractManager(c).Remove(func(server *server.Server) bool {
return server.ID() == s.ID()
})
// Deallocate the reference to this server.
s = nil
c.Status(http.StatusNoContent)
}

View File

@ -239,7 +239,8 @@ func postServerWriteFile(c *gin.Context) {
return
}
if c.Request.ContentLength < 1 {
// A content length of -1 means the actual length is unknown.
if c.Request.ContentLength == -1 {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
"error": "Missing Content-Length",
})

View File

@ -66,10 +66,9 @@ type Archive struct {
// BaseDirectory .
BaseDirectory string
// Files specifies the files to archive, this takes priority over the Ignore option, if
// unspecified, all files in the BasePath will be archived unless Ignore is set.
//
// All items in Files must be absolute within BasePath.
// Files specifies the files to archive, this takes priority over the Ignore
// option, if unspecified, all files in the BaseDirectory will be archived
// unless Ignore is set.
Files []string
// Progress wraps the writer of the archive to pass through the progress tracker.
@ -114,11 +113,20 @@ func (a *Archive) Stream(ctx context.Context, w io.Writer) error {
return errors.New("filesystem: archive.Filesystem is unset")
}
// The base directory may come with a prefixed `/`, strip it to prevent
// problems.
a.BaseDirectory = strings.TrimPrefix(a.BaseDirectory, "/")
if filesLen := len(a.Files); filesLen > 0 {
files := make([]string, filesLen)
for i, f := range a.Files {
if !strings.HasPrefix(f, a.Filesystem.Path()) {
files[i] = f
continue
}
a.Files[i] = strings.TrimPrefix(strings.TrimPrefix(f, a.Filesystem.Path()), "/")
files[i] = strings.TrimPrefix(strings.TrimPrefix(f, a.Filesystem.Path()), "/")
}
a.Files = files
}
// Choose which compression level to use based on the compression_level configuration option
@ -128,8 +136,6 @@ func (a *Archive) Stream(ctx context.Context, w io.Writer) error {
compressionLevel = pgzip.NoCompression
case "best_compression":
compressionLevel = pgzip.BestCompression
case "best_speed":
fallthrough
default:
compressionLevel = pgzip.BestSpeed
}
@ -155,7 +161,7 @@ func (a *Archive) Stream(ctx context.Context, w io.Writer) error {
i := ignore.CompileIgnoreLines(strings.Split(a.Ignore, "\n")...)
callback = a.callback(func(_ int, _, relative string, _ ufs.DirEntry) error {
if i.MatchesPath(relative) {
return ufs.SkipDir
return SkipThis
}
return nil
})
@ -165,11 +171,14 @@ func (a *Archive) Stream(ctx context.Context, w io.Writer) error {
callback = a.callback()
}
// Open the base directory we were provided.
dirfd, name, closeFd, err := fs.SafePath(a.BaseDirectory)
defer closeFd()
if err != nil {
return err
}
// Recursively walk the base directory.
return fs.WalkDirat(dirfd, name, func(dirfd int, name, relative string, d ufs.DirEntry, err error) error {
if err != nil {
return err
@ -186,16 +195,35 @@ func (a *Archive) Stream(ctx context.Context, w io.Writer) error {
// Callback function used to determine if a given file should be included in the archive
// being generated.
func (a *Archive) callback(opts ...walkFunc) walkFunc {
// Get the base directory we need to strip when walking.
//
// This is important as when we are walking, the last part of the base directory
// is present on all the paths we walk.
var base string
if a.BaseDirectory != "" {
base = filepath.Base(a.BaseDirectory) + "/"
}
return func(dirfd int, name, relative string, d ufs.DirEntry) error {
// Skip directories because we are walking them recursively.
if d.IsDir() {
return nil
}
// If base isn't empty, strip it from the relative path. This fixes an
// issue when creating an archive starting from a nested directory.
//
// See https://github.com/pterodactyl/panel/issues/5030 for more details.
if base != "" {
relative = strings.TrimPrefix(relative, base)
}
// Call the additional options passed to this callback function. If any of them return
// a non-nil error we will exit immediately.
for _, opt := range opts {
if err := opt(dirfd, name, relative, d); err != nil {
if err == SkipThis {
return nil
}
return err
}
}
@ -206,6 +234,8 @@ func (a *Archive) callback(opts ...walkFunc) walkFunc {
}
}
var SkipThis = errors.New("skip this")
// Pushes only files defined in the Files key to the final archive.
func (a *Archive) withFilesCallback() walkFunc {
return a.callback(func(_ int, _, relative string, _ ufs.DirEntry) error {
@ -225,7 +255,7 @@ func (a *Archive) withFilesCallback() walkFunc {
return nil
}
return ufs.SkipDir
return SkipThis
})
}

View File

@ -0,0 +1,100 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: Copyright (c) 2016 Matthew Holt
// Code in this file was derived from
// https://github.com/mholt/archiver/blob/v4.0.0-alpha.8/fs.go
//
// These modifications were necessary to allow us to use an already open file
// with archiver.FileFS.
package archiverext
import (
"io"
"io/fs"
"github.com/mholt/archiver/v4"
)
// FileFS allows accessing a file on disk using a consistent file system interface.
// The value should be the path to a regular file, not a directory. This file will
// be the only entry in the file system and will be at its root. It can be accessed
// within the file system by the name of "." or the filename.
//
// If the file is compressed, set the Compression field so that reads from the
// file will be transparently decompressed.
type FileFS struct {
// File is the compressed file backing the FileFS.
File fs.File
// If file is compressed, setting this field will
// transparently decompress reads.
Compression archiver.Decompressor
}
// Open opens the named file, which must be the file used to create the file system.
func (f FileFS) Open(name string) (fs.File, error) {
if err := f.checkName(name, "open"); err != nil {
return nil, err
}
if f.Compression == nil {
return f.File, nil
}
r, err := f.Compression.OpenReader(f.File)
if err != nil {
return nil, err
}
return compressedFile{f.File, r}, nil
}
// ReadDir returns a directory listing with the file as the singular entry.
func (f FileFS) ReadDir(name string) ([]fs.DirEntry, error) {
if err := f.checkName(name, "stat"); err != nil {
return nil, err
}
info, err := f.Stat(name)
if err != nil {
return nil, err
}
return []fs.DirEntry{fs.FileInfoToDirEntry(info)}, nil
}
// Stat stats the named file, which must be the file used to create the file system.
func (f FileFS) Stat(name string) (fs.FileInfo, error) {
if err := f.checkName(name, "stat"); err != nil {
return nil, err
}
return f.File.Stat()
}
func (f FileFS) checkName(name, op string) error {
if !fs.ValidPath(name) {
return &fs.PathError{Op: "open", Path: name, Err: fs.ErrInvalid}
}
// TODO: we may need better name validation.
if name != "." {
return &fs.PathError{Op: op, Path: name, Err: fs.ErrNotExist}
}
return nil
}
// compressedFile is an fs.File that specially reads
// from a decompression reader, and which closes both
// that reader and the underlying file.
type compressedFile struct {
fs.File
decomp io.ReadCloser
}
func (cf compressedFile) Read(p []byte) (int, error) {
return cf.decomp.Read(p)
}
func (cf compressedFile) Close() error {
err := cf.File.Close()
err2 := cf.decomp.Close()
if err2 != nil && err == nil {
err = err2
}
return err
}

View File

@ -16,6 +16,7 @@ import (
"github.com/mholt/archiver/v4"
"github.com/pterodactyl/wings/internal/ufs"
"github.com/pterodactyl/wings/server/filesystem/archiverext"
)
// CompressFiles compresses all the files matching the given paths in the
@ -85,11 +86,12 @@ func (fs *Filesystem) archiverFileSystem(ctx context.Context, p string) (iofs.FS
return zip.NewReader(f, info.Size())
case archiver.Archival:
return archiver.ArchiveFS{Stream: io.NewSectionReader(f, 0, info.Size()), Format: ff, Context: ctx}, nil
case archiver.Compression:
return archiverext.FileFS{File: f, Compression: ff}, nil
}
}
_ = f.Close()
return nil, nil
return nil, archiver.ErrNoMatch
}
// SpaceAvailableForDecompression looks through a given archive and determines
@ -154,6 +156,7 @@ func (fs *Filesystem) DecompressFile(ctx context.Context, dir string, file strin
}
return fs.extractStream(ctx, extractStreamOptions{
FileName: file,
Directory: dir,
Format: format,
Reader: input,
@ -188,11 +191,74 @@ type extractStreamOptions struct {
}
func (fs *Filesystem) extractStream(ctx context.Context, opts extractStreamOptions) error {
// Decompress and extract archive
// See if it's a compressed archive, such as TAR or a ZIP
ex, ok := opts.Format.(archiver.Extractor)
if !ok {
// If not, check if it's a single-file compression, such as
// .log.gz, .sql.gz, and so on
de, ok := opts.Format.(archiver.Decompressor)
if !ok {
return nil
}
// Strip the compression suffix
p := filepath.Join(opts.Directory, strings.TrimSuffix(opts.FileName, opts.Format.Name()))
// Make sure it's not ignored
if err := fs.IsIgnored(p); err != nil {
return nil
}
reader, err := de.OpenReader(opts.Reader)
if err != nil {
return err
}
defer reader.Close()
// Open the file for creation/writing
f, err := fs.unixFS.OpenFile(p, ufs.O_WRONLY|ufs.O_CREATE, 0o644)
if err != nil {
return err
}
defer f.Close()
// Read in 4 KB chunks
buf := make([]byte, 4096)
for {
n, err := reader.Read(buf)
if n > 0 {
// Check quota before writing the chunk
if quotaErr := fs.HasSpaceFor(int64(n)); quotaErr != nil {
return quotaErr
}
// Write the chunk
if _, writeErr := f.Write(buf[:n]); writeErr != nil {
return writeErr
}
// Add to quota
fs.addDisk(int64(n))
}
if err != nil {
// EOF are expected
if err == io.EOF {
break
}
// Return any other
return err
}
}
return nil
}
// Decompress and extract archive
return ex.Extract(ctx, opts.Reader, nil, func(ctx context.Context, f archiver.File) error {
if f.IsDir() {
return nil

View File

@ -1,6 +1,8 @@
package filesystem
import (
"golang.org/x/sys/unix"
"slices"
"sync"
"sync/atomic"
"time"
@ -164,6 +166,8 @@ func (fs *Filesystem) DirectorySize(root string) (int64, error) {
return 0, err
}
var hardLinks []uint64
var size atomic.Int64
err = fs.unixFS.WalkDirat(dirfd, name, func(dirfd int, name, _ string, d ufs.DirEntry, err error) error {
if err != nil {
@ -180,8 +184,16 @@ func (fs *Filesystem) DirectorySize(root string) (int64, error) {
return errors.Wrap(err, "lstatat err")
}
// TODO: detect if info is a hard-link and de-duplicate it.
// ref; https://github.com/pterodactyl/wings/pull/181/files
var sysFileInfo = info.Sys().(*unix.Stat_t)
if sysFileInfo.Nlink > 1 {
// Hard links have the same inode number
if slices.Contains(hardLinks, sysFileInfo.Ino) {
// Don't add hard links size twice
return nil
} else {
hardLinks = append(hardLinks, sysFileInfo.Ino)
}
}
size.Add(info.Size())
return nil

View File

@ -21,9 +21,6 @@ import (
"github.com/pterodactyl/wings/internal/ufs"
)
// TODO: detect and enable
var useOpenat2 = true
type Filesystem struct {
unixFS *ufs.Quota
@ -169,17 +166,23 @@ func (fs *Filesystem) Write(p string, r io.Reader, newSize int64, mode ufs.FileM
}
defer file.Close()
if newSize == 0 {
// Subtract the previous size of the file if the new size is 0.
fs.unixFS.Add(-currentSize)
} else {
// Do not use CopyBuffer here, it is wasteful as the file implements
// io.ReaderFrom, which causes it to not use the buffer anyways.
n, err := io.Copy(file, io.LimitReader(r, newSize))
var n int64
n, err = io.Copy(file, io.LimitReader(r, newSize))
// Adjust the disk usage to account for the old size and the new size of the file.
fs.unixFS.Add(n - currentSize)
}
if err := fs.chownFile(p); err != nil {
return err
}
// Return the error from io.Copy.
// Return any remaining error.
return err
}
@ -477,9 +480,9 @@ func (fs *Filesystem) ListDirectory(p string) ([]Stat, error) {
case a.IsDir() && b.IsDir():
return 0
case a.IsDir():
return 1
default:
return -1
default:
return 1
}
})

View File

@ -29,6 +29,21 @@ 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 {
s.Log().WithFields(log.Fields{"source_path": passwd.Directory}).Info("mouting generated /etc/{group,passwd} to workaround UID/GID issues")
m = append(m, environment.Mount{
Source: filepath.Join(passwd.Directory, "group"),
Target: "/etc/group",
ReadOnly: true,
})
m = append(m, environment.Mount{
Source: filepath.Join(passwd.Directory, "passwd"),
Target: "/etc/passwd",
ReadOnly: true,
})
}
// Also include any of this server's custom mounts when returning them.
return append(m, s.customMounts()...)
}
@ -56,14 +71,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
}

View File

@ -3,7 +3,6 @@ package server
import (
"context"
"fmt"
"os"
"time"
"emperror.dev/errors"
@ -161,7 +160,7 @@ func (s *Server) HandlePowerAction(action PowerAction, waitSeconds ...int) error
return s.Environment.Start(s.Context())
case PowerActionTerminate:
return s.Environment.Terminate(s.Context(), os.Kill)
return s.Environment.Terminate(s.Context(), "SIGKILL")
}
return errors.New("attempting to handle unknown power action")

View File

@ -28,6 +28,7 @@ func (s *Server) SyncWithEnvironment() {
Mounts: s.Mounts(),
Allocations: cfg.Allocations,
Limits: cfg.Build,
Labels: cfg.Labels,
})
// For Docker specific environments we also want to update the configured image