From b6edf3acf96520065198affe1397ebc8983d3d9e Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sun, 25 Sep 2022 18:49:48 -0600 Subject: [PATCH] environment(docker): set outgoing ip correctly (#135) Closes https://github.com/pterodactyl/panel/issues/3841 --- environment/allocations.go | 5 +++++ environment/docker.go | 12 +++++----- environment/docker/container.go | 39 ++++++++++++++++++++++++++++----- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/environment/allocations.go b/environment/allocations.go index 71fbeb8..e55a2b8 100644 --- a/environment/allocations.go +++ b/environment/allocations.go @@ -12,6 +12,11 @@ import ( // Defines the allocations available for a given server. When using the Docker environment // driver these correspond to mappings for the container that allow external connections. type Allocations struct { + // ForceOutgoingIP causes a dedicated bridge network to be created for the + // server with a special option, causing Docker to SNAT outgoing traffic to + // the DefaultMapping's IP. This is important to servers which rely on external + // services that check the IP of the server (Source Engine servers, for example). + ForceOutgoingIP bool `json:"force_outgoing_ip"` // Defines the default allocation that should be used for this server. This is // what will be used for {SERVER_IP} and {SERVER_PORT} when modifying configuration // files or the startup arguments for a server. diff --git a/environment/docker.go b/environment/docker.go index 8542758..cbecbc3 100644 --- a/environment/docker.go +++ b/environment/docker.go @@ -41,12 +41,12 @@ func ConfigureDocker(ctx context.Context) error { nw := config.Get().Docker.Network resource, err := cli.NetworkInspect(ctx, nw.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 - } - } else { + if !client.IsErrNotFound(err) { + return err + } + + log.Info("creating missing pterodactyl0 interface, this could take a few seconds...") + if err := createDockerNetwork(ctx, cli); err != nil { return err } } diff --git a/environment/docker/container.go b/environment/docker/container.go index 6973e78..366d45b 100644 --- a/environment/docker/container.go +++ b/environment/docker/container.go @@ -147,10 +147,12 @@ func (e *Environment) InSituUpdate() error { // currently available for it. If the container already exists it will be // returned. func (e *Environment) Create() error { + ctx := context.Background() + // If the container already exists don't hit the user with an error, just return // the current information about it which is what we would do when creating the // container anyways. - if _, err := e.ContainerInspect(context.Background()); err == nil { + if _, err := e.ContainerInspect(ctx); err == nil { return nil } else if !client.IsErrNotFound(err) { return errors.Wrap(err, "environment/docker: failed to inspect container") @@ -190,7 +192,34 @@ func (e *Environment) Create() error { }, } - tmpfsSize := strconv.Itoa(int(config.Get().Docker.TmpfsSize)) + networkMode := container.NetworkMode(config.Get().Docker.Network.Mode) + if a.ForceOutgoingIP { + e.log().Debug("environment/docker: forcing outgoing IP address") + networkName := strings.ReplaceAll(e.Id, "-", "") + networkMode = container.NetworkMode(networkName) + + if _, err := e.client.NetworkInspect(ctx, networkName, types.NetworkInspectOptions{}); err != nil { + if !client.IsErrNotFound(err) { + return err + } + + if _, err := e.client.NetworkCreate(ctx, networkName, types.NetworkCreate{ + Driver: "bridge", + EnableIPv6: false, + Internal: false, + Attachable: false, + Ingress: false, + ConfigOnly: false, + Options: map[string]string{ + "encryption": "false", + "com.docker.network.bridge.default_bridge": "false", + "com.docker.network.host_ipv4": a.DefaultMapping.Ip, + }, + }); err != nil { + return err + } + } + } hostConf := &container.HostConfig{ PortBindings: a.DockerBindings(), @@ -202,7 +231,7 @@ func (e *Environment) Create() error { // Configure the /tmp folder mapping in containers. This is necessary for some // games that need to make use of it for downloads and other installation processes. Tmpfs: map[string]string{ - "/tmp": "rw,exec,nosuid,size=" + tmpfsSize + "M", + "/tmp": "rw,exec,nosuid,size=" + strconv.Itoa(int(config.Get().Docker.TmpfsSize)) + "M", }, // Define resource limits for the container based on the data passed through @@ -231,10 +260,10 @@ func (e *Environment) Create() error { "setpcap", "mknod", "audit_write", "net_raw", "dac_override", "fowner", "fsetid", "net_bind_service", "sys_chroot", "setfcap", }, - NetworkMode: container.NetworkMode(config.Get().Docker.Network.Mode), + NetworkMode: networkMode, } - if _, err := e.client.ContainerCreate(context.Background(), conf, hostConf, nil, nil, e.Id); err != nil { + if _, err := e.client.ContainerCreate(ctx, conf, hostConf, nil, nil, e.Id); err != nil { return errors.Wrap(err, "environment/docker: failed to create container") }