feat: support container network mode (#1429)

Co-authored-by: nils måsén <nils@piksel.se>
Co-authored-by: Andreas Åhman <andreas.ahman@ingka.ikea.com>
pull/1724/head
schizo99 1 year ago committed by GitHub
parent bba9b2b100
commit dca45f50cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,17 @@
services:
producer:
image: qmcgaw/gluetun:v3.35.0
cap_add:
- NET_ADMIN
environment:
- VPN_SERVICE_PROVIDER=${VPN_SERVICE_PROVIDER}
- OPENVPN_USER=${OPENVPN_USER}
- OPENVPN_PASSWORD=${OPENVPN_PASSWORD}
- SERVER_COUNTRIES=${SERVER_COUNTRIES}
consumer:
depends_on:
- producer
image: nginx:1.25.1
network_mode: "service:producer"
labels:
- "com.centurylinklabs.watchtower.depends-on=/wt-contnet-producer-1"

@ -2,4 +2,6 @@ Watchtower will detect if there are links between any of the running containers
For example, imagine you were running a _mysql_ container and a _wordpress_ container which had been linked to the _mysql_ container. If watchtower were to detect that the _mysql_ container required an update, it would first shut down the linked _wordpress_ container followed by the _mysql_ container. When restarting the containers it would handle _mysql_ first and then _wordpress_ to ensure that the link continued to work. For example, imagine you were running a _mysql_ container and a _wordpress_ container which had been linked to the _mysql_ container. If watchtower were to detect that the _mysql_ container required an update, it would first shut down the linked _wordpress_ container followed by the _mysql_ container. When restarting the containers it would handle _mysql_ first and then _wordpress_ to ensure that the link continued to work.
If you want to override existing links you can use special `com.centurylinklabs.watchtower.depends-on` label with dependent container names, separated by a comma. If you want to override existing links, or if you are not using links, you can use special `com.centurylinklabs.watchtower.depends-on` label with dependent container names, separated by a comma.
When you have a depending container that is using `network_mode: service:container` then watchtower will treat that container as an implicit link.

@ -157,6 +157,22 @@ func (client dockerClient) GetContainer(containerID t.ContainerID) (t.Container,
return &Container{}, err return &Container{}, err
} }
netType, netContainerId, found := strings.Cut(string(containerInfo.HostConfig.NetworkMode), ":")
if found && netType == "container" {
parentContainer, err := client.api.ContainerInspect(bg, netContainerId)
if err != nil {
log.WithFields(map[string]interface{}{
"container": containerInfo.Name,
"error": err,
"network-container": netContainerId,
}).Warnf("Unable to resolve network container: %v", err)
} else {
// Replace the container ID with a container name to allow it to reference the re-created network container
containerInfo.HostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:%s", parentContainer.Name))
}
}
imageInfo, _, err := client.api.ImageInspectWithRaw(bg, containerInfo.Image) imageInfo, _, err := client.api.ImageInspectWithRaw(bg, containerInfo.Image)
if err != nil { if err != nil {
log.Warnf("Failed to retrieve container image info: %v", err) log.Warnf("Failed to retrieve container image info: %v", err)

@ -141,7 +141,7 @@ var _ = Describe("the client", func() {
When("no filter is provided", func() { When("no filter is provided", func() {
It("should return all available containers", func() { It("should return all available containers", func() {
mockServer.AppendHandlers(mocks.ListContainersHandler("running")) mockServer.AppendHandlers(mocks.ListContainersHandler("running"))
mockServer.AppendHandlers(mocks.GetContainerHandlers("watchtower", "running")...) mockServer.AppendHandlers(mocks.GetContainerHandlers(&mocks.Watchtower, &mocks.Running)...)
client := dockerClient{ client := dockerClient{
api: docker, api: docker,
ClientOptions: ClientOptions{PullImages: false}, ClientOptions: ClientOptions{PullImages: false},
@ -154,7 +154,7 @@ var _ = Describe("the client", func() {
When("a filter matching nothing", func() { When("a filter matching nothing", func() {
It("should return an empty array", func() { It("should return an empty array", func() {
mockServer.AppendHandlers(mocks.ListContainersHandler("running")) mockServer.AppendHandlers(mocks.ListContainersHandler("running"))
mockServer.AppendHandlers(mocks.GetContainerHandlers("watchtower", "running")...) mockServer.AppendHandlers(mocks.GetContainerHandlers(&mocks.Watchtower, &mocks.Running)...)
filter := filters.FilterByNames([]string{"lollercoaster"}, filters.NoFilter) filter := filters.FilterByNames([]string{"lollercoaster"}, filters.NoFilter)
client := dockerClient{ client := dockerClient{
api: docker, api: docker,
@ -168,7 +168,7 @@ var _ = Describe("the client", func() {
When("a watchtower filter is provided", func() { When("a watchtower filter is provided", func() {
It("should return only the watchtower container", func() { It("should return only the watchtower container", func() {
mockServer.AppendHandlers(mocks.ListContainersHandler("running")) mockServer.AppendHandlers(mocks.ListContainersHandler("running"))
mockServer.AppendHandlers(mocks.GetContainerHandlers("watchtower", "running")...) mockServer.AppendHandlers(mocks.GetContainerHandlers(&mocks.Watchtower, &mocks.Running)...)
client := dockerClient{ client := dockerClient{
api: docker, api: docker,
ClientOptions: ClientOptions{PullImages: false}, ClientOptions: ClientOptions{PullImages: false},
@ -181,7 +181,7 @@ var _ = Describe("the client", func() {
When(`include stopped is enabled`, func() { When(`include stopped is enabled`, func() {
It("should return both stopped and running containers", func() { It("should return both stopped and running containers", func() {
mockServer.AppendHandlers(mocks.ListContainersHandler("running", "exited", "created")) mockServer.AppendHandlers(mocks.ListContainersHandler("running", "exited", "created"))
mockServer.AppendHandlers(mocks.GetContainerHandlers("stopped", "watchtower", "running")...) mockServer.AppendHandlers(mocks.GetContainerHandlers(&mocks.Stopped, &mocks.Watchtower, &mocks.Running)...)
client := dockerClient{ client := dockerClient{
api: docker, api: docker,
ClientOptions: ClientOptions{PullImages: false, IncludeStopped: true}, ClientOptions: ClientOptions{PullImages: false, IncludeStopped: true},
@ -194,7 +194,7 @@ var _ = Describe("the client", func() {
When(`include restarting is enabled`, func() { When(`include restarting is enabled`, func() {
It("should return both restarting and running containers", func() { It("should return both restarting and running containers", func() {
mockServer.AppendHandlers(mocks.ListContainersHandler("running", "restarting")) mockServer.AppendHandlers(mocks.ListContainersHandler("running", "restarting"))
mockServer.AppendHandlers(mocks.GetContainerHandlers("watchtower", "running", "restarting")...) mockServer.AppendHandlers(mocks.GetContainerHandlers(&mocks.Watchtower, &mocks.Running, &mocks.Restarting)...)
client := dockerClient{ client := dockerClient{
api: docker, api: docker,
ClientOptions: ClientOptions{PullImages: false, IncludeRestarting: true}, ClientOptions: ClientOptions{PullImages: false, IncludeRestarting: true},
@ -207,7 +207,7 @@ var _ = Describe("the client", func() {
When(`include restarting is disabled`, func() { When(`include restarting is disabled`, func() {
It("should not return restarting containers", func() { It("should not return restarting containers", func() {
mockServer.AppendHandlers(mocks.ListContainersHandler("running")) mockServer.AppendHandlers(mocks.ListContainersHandler("running"))
mockServer.AppendHandlers(mocks.GetContainerHandlers("watchtower", "running")...) mockServer.AppendHandlers(mocks.GetContainerHandlers(&mocks.Watchtower, &mocks.Running)...)
client := dockerClient{ client := dockerClient{
api: docker, api: docker,
ClientOptions: ClientOptions{PullImages: false, IncludeRestarting: false}, ClientOptions: ClientOptions{PullImages: false, IncludeRestarting: false},
@ -217,6 +217,36 @@ var _ = Describe("the client", func() {
Expect(containers).NotTo(ContainElement(havingRestartingState(true))) Expect(containers).NotTo(ContainElement(havingRestartingState(true)))
}) })
}) })
When(`a container uses container network mode`, func() {
When(`the network container can be resolved`, func() {
It("should return the container name instead of the ID", func() {
consumerContainerRef := mocks.NetConsumerOK
mockServer.AppendHandlers(mocks.GetContainerHandlers(&consumerContainerRef)...)
client := dockerClient{
api: docker,
ClientOptions: ClientOptions{PullImages: false},
}
container, err := client.GetContainer(consumerContainerRef.ContainerID())
Expect(err).NotTo(HaveOccurred())
networkMode := container.ContainerInfo().HostConfig.NetworkMode
Expect(networkMode.ConnectedContainer()).To(Equal(mocks.NetSupplierContainerName))
})
})
When(`the network container cannot be resolved`, func() {
It("should still return the container ID", func() {
consumerContainerRef := mocks.NetConsumerInvalidSupplier
mockServer.AppendHandlers(mocks.GetContainerHandlers(&consumerContainerRef)...)
client := dockerClient{
api: docker,
ClientOptions: ClientOptions{PullImages: false},
}
container, err := client.GetContainer(consumerContainerRef.ContainerID())
Expect(err).NotTo(HaveOccurred())
networkMode := container.ContainerInfo().HostConfig.NetworkMode
Expect(networkMode.ConnectedContainer()).To(Equal(mocks.NetSupplierNotFoundID))
})
})
})
}) })
Describe(`ExecuteCommand`, func() { Describe(`ExecuteCommand`, func() {
When(`logging`, func() { When(`logging`, func() {

@ -196,6 +196,13 @@ func (c Container) Links() []string {
name := strings.Split(link, ":")[0] name := strings.Split(link, ":")[0]
links = append(links, name) links = append(links, name)
} }
// If the container uses another container for networking, it can be considered an implicit link
// since the container would stop working if the network supplier were to be recreated
networkMode := c.containerInfo.HostConfig.NetworkMode
if networkMode.IsContainer() {
links = append(links, networkMode.ConnectedContainer())
}
} }
return links return links

@ -3,12 +3,15 @@ package mocks
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "github.com/onsi/ginkgo"
"net/http" "net/http"
"net/url" "net/url"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
t "github.com/containrrr/watchtower/pkg/types"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
O "github.com/onsi/gomega" O "github.com/onsi/gomega"
@ -17,10 +20,9 @@ import (
func getMockJSONFile(relPath string) ([]byte, error) { func getMockJSONFile(relPath string) ([]byte, error) {
absPath, _ := filepath.Abs(relPath) absPath, _ := filepath.Abs(relPath)
buf, err := ioutil.ReadFile(absPath) buf, err := os.ReadFile(absPath)
if err != nil { if err != nil {
// logrus.WithError(err).WithField("file", absPath).Error(err) return nil, fmt.Errorf("mock JSON file %q not found: %e", absPath, err)
return nil, err
} }
return buf, nil return buf, nil
} }
@ -41,19 +43,22 @@ func respondWithJSONFile(relPath string, statusCode int, optionalHeader ...http.
} }
// GetContainerHandlers returns the handlers serving lookups for the supplied container mock files // GetContainerHandlers returns the handlers serving lookups for the supplied container mock files
func GetContainerHandlers(containerFiles ...string) []http.HandlerFunc { func GetContainerHandlers(containerRefs ...*ContainerRef) []http.HandlerFunc {
handlers := make([]http.HandlerFunc, 0, len(containerFiles)*2) handlers := make([]http.HandlerFunc, 0, len(containerRefs)*3)
for _, file := range containerFiles { for _, containerRef := range containerRefs {
handlers = append(handlers, getContainerFileHandler(file)) handlers = append(handlers, getContainerFileHandler(containerRef))
// Also append the image request since that will be called for every container // Also append any containers that the container references, if any
if file == "running" { for _, ref := range containerRef.references {
// The "running" container is the only one using image02 handlers = append(handlers, getContainerFileHandler(ref))
handlers = append(handlers, getImageFileHandler(1))
} else {
handlers = append(handlers, getImageFileHandler(0))
} }
// Also append the image request since that will be called for every container
handlers = append(handlers, getImageHandler(containerRef.image.id,
RespondWithJSONFile(containerRef.image.getFileName(), http.StatusOK),
))
} }
return handlers return handlers
} }
@ -65,24 +70,90 @@ func createFilterArgs(statuses []string) filters.Args {
return args return args
} }
var containerFileIds = map[string]string{ var defaultImage = imageRef{
"stopped": "ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b65", // watchtower
"watchtower": "3d88e0e3543281c747d88b27e246578b65ae8964ba86c7cd7522cf84e0978134", id: t.ImageID("sha256:4dbc5f9c07028a985e14d1393e849ea07f68804c4293050d5a641b138db72daa"),
"running": "b978af0b858aa8855cce46b628817d4ed58e58f2c4f66c9b9c5449134ed4c008", file: "default",
"restarting": "ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b67", }
var Watchtower = ContainerRef{
name: "watchtower",
id: "3d88e0e3543281c747d88b27e246578b65ae8964ba86c7cd7522cf84e0978134",
image: &defaultImage,
}
var Stopped = ContainerRef{
name: "stopped",
id: "ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b65",
image: &defaultImage,
}
var Running = ContainerRef{
name: "running",
id: "b978af0b858aa8855cce46b628817d4ed58e58f2c4f66c9b9c5449134ed4c008",
image: &imageRef{
// portainer
id: t.ImageID("sha256:19d07168491a3f9e2798a9bed96544e34d57ddc4757a4ac5bb199dea896c87fd"),
file: "running",
},
}
var Restarting = ContainerRef{
name: "restarting",
id: "ae8964ba86c7cd7522cf84e09781343d88e0e3543281c747d88b27e246578b67",
image: &defaultImage,
}
var netSupplierOK = ContainerRef{
id: "25e75393800b5c450a6841212a3b92ed28fa35414a586dec9f2c8a520d4910c2",
name: "net_supplier",
image: &imageRef{
// gluetun
id: t.ImageID("sha256:c22b543d33bfdcb9992cbef23961677133cdf09da71d782468ae2517138bad51"),
file: "net_producer",
},
}
var netSupplierNotFound = ContainerRef{
id: NetSupplierNotFoundID,
name: netSupplierOK.name,
isMissing: true,
}
// NetConsumerOK is used for testing `container` networking mode
// returns a container that consumes an existing supplier container
var NetConsumerOK = ContainerRef{
id: "1f6b79d2aff23244382026c76f4995851322bed5f9c50631620162f6f9aafbd6",
name: "net_consumer",
image: &imageRef{
id: t.ImageID("sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8"), // nginx
file: "net_consumer",
},
references: []*ContainerRef{&netSupplierOK},
} }
var imageIds = []string{ // NetConsumerInvalidSupplier is used for testing `container` networking mode
"sha256:4dbc5f9c07028a985e14d1393e849ea07f68804c4293050d5a641b138db72daa", // returns a container that references a supplying container that does not exist
"sha256:19d07168491a3f9e2798a9bed96544e34d57ddc4757a4ac5bb199dea896c87fd", var NetConsumerInvalidSupplier = ContainerRef{
id: NetConsumerOK.id,
name: "net_consumer-missing_supplier",
image: NetConsumerOK.image,
references: []*ContainerRef{&netSupplierNotFound},
} }
func getContainerFileHandler(file string) http.HandlerFunc { const NetSupplierNotFoundID = "badc1dbadc1dbadc1dbadc1dbadc1dbadc1dbadc1dbadc1dbadc1dbadc1dbadc"
id, ok := containerFileIds[file] const NetSupplierContainerName = "/wt-contnet-producer-1"
failTestUnless(ok)
func getContainerFileHandler(cr *ContainerRef) http.HandlerFunc {
if cr.isMissing {
return containerNotFoundResponse(string(cr.id))
}
containerFile, err := cr.getContainerFile()
if err != nil {
ginkgo.Fail(fmt.Sprintf("Failed to get container mock file: %v", err))
}
return getContainerHandler( return getContainerHandler(
id, string(cr.id),
RespondWithJSONFile(fmt.Sprintf("./mocks/data/container_%v.json", file), http.StatusOK), RespondWithJSONFile(containerFile, http.StatusOK),
) )
} }
@ -104,7 +175,7 @@ func GetContainerHandler(containerID string, containerInfo *types.ContainerJSON)
// GetImageHandler mocks the GET images/{id}/json endpoint // GetImageHandler mocks the GET images/{id}/json endpoint
func GetImageHandler(imageInfo *types.ImageInspect) http.HandlerFunc { func GetImageHandler(imageInfo *types.ImageInspect) http.HandlerFunc {
return getImageHandler(imageInfo.ID, ghttp.RespondWithJSONEncoded(http.StatusOK, imageInfo)) return getImageHandler(t.ImageID(imageInfo.ID), ghttp.RespondWithJSONEncoded(http.StatusOK, imageInfo))
} }
// ListContainersHandler mocks the GET containers/json endpoint, filtering the returned containers based on statuses // ListContainersHandler mocks the GET containers/json endpoint, filtering the returned containers based on statuses
@ -138,23 +209,13 @@ func respondWithFilteredContainers(filters filters.Args) http.HandlerFunc {
return ghttp.RespondWithJSONEncoded(http.StatusOK, filteredContainers) return ghttp.RespondWithJSONEncoded(http.StatusOK, filteredContainers)
} }
func getImageHandler(imageId string, responseHandler http.HandlerFunc) http.HandlerFunc { func getImageHandler(imageId t.ImageID, responseHandler http.HandlerFunc) http.HandlerFunc {
return ghttp.CombineHandlers( return ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", O.HaveSuffix("/images/%s/json", imageId)), ghttp.VerifyRequest("GET", O.HaveSuffix("/images/%s/json", imageId)),
responseHandler, responseHandler,
) )
} }
func getImageFileHandler(index int) http.HandlerFunc {
return getImageHandler(imageIds[index],
RespondWithJSONFile(fmt.Sprintf("./mocks/data/image%02d.json", index+1), http.StatusOK),
)
}
func failTestUnless(ok bool) {
O.ExpectWithOffset(2, ok).To(O.BeTrue(), "test setup failed")
}
// KillContainerHandler mocks the POST containers/{id}/kill endpoint // KillContainerHandler mocks the POST containers/{id}/kill endpoint
func KillContainerHandler(containerID string, found FoundStatus) http.HandlerFunc { func KillContainerHandler(containerID string, found FoundStatus) http.HandlerFunc {
responseHandler := noContentStatusResponse responseHandler := noContentStatusResponse
@ -180,7 +241,7 @@ func RemoveContainerHandler(containerID string, found FoundStatus) http.HandlerF
} }
func containerNotFoundResponse(containerID string) http.HandlerFunc { func containerNotFoundResponse(containerID string) http.HandlerFunc {
return ghttp.RespondWithJSONEncoded(http.StatusNotFound, struct{ message string }{message: "No such container: " + containerID}) return ghttp.RespondWithJSONEncoded(http.StatusNotFound, struct{ message string }{message: "No such container: " + string(containerID)})
} }
var noContentStatusResponse = ghttp.RespondWith(http.StatusNoContent, nil) var noContentStatusResponse = ghttp.RespondWith(http.StatusNoContent, nil)

@ -0,0 +1,42 @@
package mocks
import (
"fmt"
"os"
t "github.com/containrrr/watchtower/pkg/types"
)
type imageRef struct {
id t.ImageID
file string
}
func (ir *imageRef) getFileName() string {
return fmt.Sprintf("./mocks/data/image_%v.json", ir.file)
}
type ContainerRef struct {
name string
id t.ContainerID
image *imageRef
file string
references []*ContainerRef
isMissing bool
}
func (cr *ContainerRef) getContainerFile() (containerFile string, err error) {
file := cr.file
if file == "" {
file = cr.name
}
containerFile = fmt.Sprintf("./mocks/data/container_%v.json", file)
_, err = os.Stat(containerFile)
return containerFile, err
}
func (cr *ContainerRef) ContainerID() t.ContainerID {
return cr.id
}

@ -0,0 +1,205 @@
{
"Id": "1f6b79d2aff23244382026c76f4995851322bed5f9c50631620162f6f9aafbd6",
"Created": "2023-07-25T14:55:14.69155887Z",
"Path": "/docker-entrypoint.sh",
"Args": [
"nginx",
"-g",
"daemon off;"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 3743,
"ExitCode": 0,
"Error": "",
"StartedAt": "2023-07-25T14:55:15.299654437Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8",
"ResolvConfPath": "/var/lib/docker/containers/25e75393800b5c450a6841212a3b92ed28fa35414a586dec9f2c8a520d4910c2/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/25e75393800b5c450a6841212a3b92ed28fa35414a586dec9f2c8a520d4910c2/hostname",
"HostsPath": "/var/lib/docker/containers/25e75393800b5c450a6841212a3b92ed28fa35414a586dec9f2c8a520d4910c2/hosts",
"LogPath": "/var/lib/docker/containers/1f6b79d2aff23244382026c76f4995851322bed5f9c50631620162f6f9aafbd6/1f6b79d2aff23244382026c76f4995851322bed5f9c50631620162f6f9aafbd6-json.log",
"Name": "/wt-contnet-consumer-1",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "container:badc1dbadc1dbadc1dbadc1dbadc1dbadc1dbadc1dbadc1dbadc1dbadc1dbadc",
"PortBindings": {},
"RestartPolicy": {
"Name": "",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"ConsoleSize": [
0,
0
],
"CapAdd": null,
"CapDrop": null,
"CgroupnsMode": "host",
"Dns": null,
"DnsOptions": null,
"DnsSearch": null,
"ExtraHosts": [],
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": null,
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": null,
"DeviceCgroupRules": null,
"DeviceRequests": null,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": null,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"MaskedPaths": [
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/05501c86219af9f713c74c129426cf5a17dc5e42f96f7f881f443cab100280e2-init/diff:/var/lib/docker/overlay2/105427179e5628eb7e893d53e21f42f9e76278f8b5665387ecdeed54a7231137/diff:/var/lib/docker/overlay2/09785ba17f27c783ef8b44f369f9aac0ca936000b57abf22b3c54d1e6eb8e27b/diff:/var/lib/docker/overlay2/6f8acd64ae44fd4d14bcb90c105eceba46854aa3985b5b6b317bcc5692cfc286/diff:/var/lib/docker/overlay2/73d41c15edb21c5f12cf53e313f48b5da55283aafc77d35b7bc662241879d7e7/diff:/var/lib/docker/overlay2/d97b55f3d966ae031492369a98e9e00d2bd31e520290fe2034e0a2b1ed77c91e/diff:/var/lib/docker/overlay2/053e9ca65c6b64cb9d98a812ff7488c7e77938b4fb8e0c4d2ad7f8ec235f0f20/diff",
"MergedDir": "/var/lib/docker/overlay2/05501c86219af9f713c74c129426cf5a17dc5e42f96f7f881f443cab100280e2/merged",
"UpperDir": "/var/lib/docker/overlay2/05501c86219af9f713c74c129426cf5a17dc5e42f96f7f881f443cab100280e2/diff",
"WorkDir": "/var/lib/docker/overlay2/05501c86219af9f713c74c129426cf5a17dc5e42f96f7f881f443cab100280e2/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "25e75393800b",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": true,
"AttachStderr": true,
"ExposedPorts": {
"80/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"NGINX_VERSION=1.23.3",
"NJS_VERSION=0.7.9",
"PKG_RELEASE=1~bullseye"
],
"Cmd": [
"nginx",
"-g",
"daemon off;"
],
"Image": "nginx",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": [
"/docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": {
"com.docker.compose.config-hash": "8bb0e1c8c61f6d495840ba9133ebfb1e4ffda3e1adb701a011b03951848bb9fa",
"com.docker.compose.container-number": "1",
"com.docker.compose.depends_on": "producer:service_started:false",
"com.docker.compose.image": "sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8",
"com.docker.compose.oneoff": "False",
"com.docker.compose.project": "wt-contnet",
"com.docker.compose.project.config_files": "/tmp/wt-contnet/docker-compose.yaml",
"com.docker.compose.project.working_dir": "/tmp/wt-contnet",
"com.docker.compose.replace": "07bb70608f96f577aa02b9f317500e23e691c94eb099f6fb52301dfb031d0668",
"com.docker.compose.service": "consumer",
"com.docker.compose.version": "2.19.1",
"desktop.docker.io/wsl-distro": "Ubuntu",
"maintainer": "NGINX Docker Maintainers \u003cdocker-maint@nginx.com\u003e"
},
"StopSignal": "SIGQUIT"
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "",
"Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"MacAddress": "",
"Networks": {}
}
}

@ -0,0 +1,205 @@
{
"Id": "1f6b79d2aff23244382026c76f4995851322bed5f9c50631620162f6f9aafbd6",
"Created": "2023-07-25T14:55:14.69155887Z",
"Path": "/docker-entrypoint.sh",
"Args": [
"nginx",
"-g",
"daemon off;"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 3743,
"ExitCode": 0,
"Error": "",
"StartedAt": "2023-07-25T14:55:15.299654437Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8",
"ResolvConfPath": "/var/lib/docker/containers/25e75393800b5c450a6841212a3b92ed28fa35414a586dec9f2c8a520d4910c2/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/25e75393800b5c450a6841212a3b92ed28fa35414a586dec9f2c8a520d4910c2/hostname",
"HostsPath": "/var/lib/docker/containers/25e75393800b5c450a6841212a3b92ed28fa35414a586dec9f2c8a520d4910c2/hosts",
"LogPath": "/var/lib/docker/containers/1f6b79d2aff23244382026c76f4995851322bed5f9c50631620162f6f9aafbd6/1f6b79d2aff23244382026c76f4995851322bed5f9c50631620162f6f9aafbd6-json.log",
"Name": "/wt-contnet-consumer-1",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "container:25e75393800b5c450a6841212a3b92ed28fa35414a586dec9f2c8a520d4910c2",
"PortBindings": {},
"RestartPolicy": {
"Name": "",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"ConsoleSize": [
0,
0
],
"CapAdd": null,
"CapDrop": null,
"CgroupnsMode": "host",
"Dns": null,
"DnsOptions": null,
"DnsSearch": null,
"ExtraHosts": [],
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": null,
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": null,
"DeviceCgroupRules": null,
"DeviceRequests": null,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": null,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"MaskedPaths": [
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/05501c86219af9f713c74c129426cf5a17dc5e42f96f7f881f443cab100280e2-init/diff:/var/lib/docker/overlay2/105427179e5628eb7e893d53e21f42f9e76278f8b5665387ecdeed54a7231137/diff:/var/lib/docker/overlay2/09785ba17f27c783ef8b44f369f9aac0ca936000b57abf22b3c54d1e6eb8e27b/diff:/var/lib/docker/overlay2/6f8acd64ae44fd4d14bcb90c105eceba46854aa3985b5b6b317bcc5692cfc286/diff:/var/lib/docker/overlay2/73d41c15edb21c5f12cf53e313f48b5da55283aafc77d35b7bc662241879d7e7/diff:/var/lib/docker/overlay2/d97b55f3d966ae031492369a98e9e00d2bd31e520290fe2034e0a2b1ed77c91e/diff:/var/lib/docker/overlay2/053e9ca65c6b64cb9d98a812ff7488c7e77938b4fb8e0c4d2ad7f8ec235f0f20/diff",
"MergedDir": "/var/lib/docker/overlay2/05501c86219af9f713c74c129426cf5a17dc5e42f96f7f881f443cab100280e2/merged",
"UpperDir": "/var/lib/docker/overlay2/05501c86219af9f713c74c129426cf5a17dc5e42f96f7f881f443cab100280e2/diff",
"WorkDir": "/var/lib/docker/overlay2/05501c86219af9f713c74c129426cf5a17dc5e42f96f7f881f443cab100280e2/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "25e75393800b",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": true,
"AttachStderr": true,
"ExposedPorts": {
"80/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"NGINX_VERSION=1.23.3",
"NJS_VERSION=0.7.9",
"PKG_RELEASE=1~bullseye"
],
"Cmd": [
"nginx",
"-g",
"daemon off;"
],
"Image": "nginx",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": [
"/docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": {
"com.docker.compose.config-hash": "8bb0e1c8c61f6d495840ba9133ebfb1e4ffda3e1adb701a011b03951848bb9fa",
"com.docker.compose.container-number": "1",
"com.docker.compose.depends_on": "producer:service_started:false",
"com.docker.compose.image": "sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8",
"com.docker.compose.oneoff": "False",
"com.docker.compose.project": "wt-contnet",
"com.docker.compose.project.config_files": "/tmp/wt-contnet/docker-compose.yaml",
"com.docker.compose.project.working_dir": "/tmp/wt-contnet",
"com.docker.compose.replace": "07bb70608f96f577aa02b9f317500e23e691c94eb099f6fb52301dfb031d0668",
"com.docker.compose.service": "consumer",
"com.docker.compose.version": "2.19.1",
"desktop.docker.io/wsl-distro": "Ubuntu",
"maintainer": "NGINX Docker Maintainers \u003cdocker-maint@nginx.com\u003e"
},
"StopSignal": "SIGQUIT"
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "",
"Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"MacAddress": "",
"Networks": {}
}
}

@ -0,0 +1,380 @@
{
"Id": "25e75393800b5c450a6841212a3b92ed28fa35414a586dec9f2c8a520d4910c2",
"Created": "2023-07-25T14:55:14.595662628Z",
"Path": "/gluetun-entrypoint",
"Args": [],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 3648,
"ExitCode": 0,
"Error": "",
"StartedAt": "2023-07-25T14:55:15.193430103Z",
"FinishedAt": "0001-01-01T00:00:00Z",
"Health": {
"Status": "healthy",
"FailingStreak": 0,
"Log": [
{
"Start": "2023-07-25T15:00:32.078491228Z",
"End": "2023-07-25T15:00:32.194554876Z",
"ExitCode": 0,
"Output": ""
},
{
"Start": "2023-07-25T15:00:37.199245496Z",
"End": "2023-07-25T15:00:37.294845687Z",
"ExitCode": 0,
"Output": ""
},
{
"Start": "2023-07-25T15:00:42.299676089Z",
"End": "2023-07-25T15:00:42.384213818Z",
"ExitCode": 0,
"Output": ""
},
{
"Start": "2023-07-25T15:00:47.389142447Z",
"End": "2023-07-25T15:00:47.514483294Z",
"ExitCode": 0,
"Output": ""
},
{
"Start": "2023-07-25T15:00:52.518770886Z",
"End": "2023-07-25T15:00:52.644288742Z",
"ExitCode": 0,
"Output": ""
}
]
}
},
"Image": "sha256:c22b543d33bfdcb9992cbef23961677133cdf09da71d782468ae2517138bad51",
"ResolvConfPath": "/var/lib/docker/containers/25e75393800b5c450a6841212a3b92ed28fa35414a586dec9f2c8a520d4910c2/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/25e75393800b5c450a6841212a3b92ed28fa35414a586dec9f2c8a520d4910c2/hostname",
"HostsPath": "/var/lib/docker/containers/25e75393800b5c450a6841212a3b92ed28fa35414a586dec9f2c8a520d4910c2/hosts",
"LogPath": "/var/lib/docker/containers/25e75393800b5c450a6841212a3b92ed28fa35414a586dec9f2c8a520d4910c2/25e75393800b5c450a6841212a3b92ed28fa35414a586dec9f2c8a520d4910c2-json.log",
"Name": "/wt-contnet-producer-1",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "wt-contnet_default",
"PortBindings": {},
"RestartPolicy": {
"Name": "",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"ConsoleSize": [
0,
0
],
"CapAdd": [
"NET_ADMIN"
],
"CapDrop": null,
"CgroupnsMode": "host",
"Dns": null,
"DnsOptions": null,
"DnsSearch": null,
"ExtraHosts": [],
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": null,
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": null,
"DeviceCgroupRules": null,
"DeviceRequests": null,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": null,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"MaskedPaths": [
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/618bd1e7a13880c07ec7f5bfc45012a9f81d5de452f942b49d8f49b3c67a19a2-init/diff:/var/lib/docker/overlay2/0d222a3aa067159831c4111a408e40325be1085b935c98d39c2e9a01ff50b224/diff:/var/lib/docker/overlay2/a20c9490a23ee8af51898892d9bf32258d44e0e07f3799475be8e8f273a50f73/diff:/var/lib/docker/overlay2/d4c97f367c37c6ada9de57f438a3e19cc714be2a54a6f582a03de9e42d88b344/diff",
"MergedDir": "/var/lib/docker/overlay2/618bd1e7a13880c07ec7f5bfc45012a9f81d5de452f942b49d8f49b3c67a19a2/merged",
"UpperDir": "/var/lib/docker/overlay2/618bd1e7a13880c07ec7f5bfc45012a9f81d5de452f942b49d8f49b3c67a19a2/diff",
"WorkDir": "/var/lib/docker/overlay2/618bd1e7a13880c07ec7f5bfc45012a9f81d5de452f942b49d8f49b3c67a19a2/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "25e75393800b",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": true,
"AttachStderr": true,
"ExposedPorts": {
"8000/tcp": {},
"8388/tcp": {},
"8388/udp": {},
"8888/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"OPENVPN_PASSWORD=",
"SERVER_COUNTRIES=Sweden",
"VPN_SERVICE_PROVIDER=nordvpn",
"OPENVPN_USER=",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"VPN_TYPE=openvpn",
"VPN_ENDPOINT_IP=",
"VPN_ENDPOINT_PORT=",
"VPN_INTERFACE=tun0",
"OPENVPN_PROTOCOL=udp",
"OPENVPN_USER_SECRETFILE=/run/secrets/openvpn_user",
"OPENVPN_PASSWORD_SECRETFILE=/run/secrets/openvpn_password",
"OPENVPN_VERSION=2.5",
"OPENVPN_VERBOSITY=1",
"OPENVPN_FLAGS=",
"OPENVPN_CIPHERS=",
"OPENVPN_AUTH=",
"OPENVPN_PROCESS_USER=root",
"OPENVPN_CUSTOM_CONFIG=",
"WIREGUARD_PRIVATE_KEY=",
"WIREGUARD_PRESHARED_KEY=",
"WIREGUARD_PUBLIC_KEY=",
"WIREGUARD_ALLOWED_IPS=",
"WIREGUARD_ADDRESSES=",
"WIREGUARD_MTU=1400",
"WIREGUARD_IMPLEMENTATION=auto",
"SERVER_REGIONS=",
"SERVER_CITIES=",
"SERVER_HOSTNAMES=",
"ISP=",
"OWNED_ONLY=no",
"PRIVATE_INTERNET_ACCESS_OPENVPN_ENCRYPTION_PRESET=",
"VPN_PORT_FORWARDING=off",
"VPN_PORT_FORWARDING_PROVIDER=",
"VPN_PORT_FORWARDING_STATUS_FILE=/tmp/gluetun/forwarded_port",
"OPENVPN_CERT=",
"OPENVPN_KEY=",
"OPENVPN_CLIENTCRT_SECRETFILE=/run/secrets/openvpn_clientcrt",
"OPENVPN_CLIENTKEY_SECRETFILE=/run/secrets/openvpn_clientkey",
"OPENVPN_ENCRYPTED_KEY=",
"OPENVPN_ENCRYPTED_KEY_SECRETFILE=/run/secrets/openvpn_encrypted_key",
"OPENVPN_KEY_PASSPHRASE=",
"OPENVPN_KEY_PASSPHRASE_SECRETFILE=/run/secrets/openvpn_key_passphrase",
"SERVER_NUMBER=",
"SERVER_NAMES=",
"FREE_ONLY=",
"MULTIHOP_ONLY=",
"PREMIUM_ONLY=",
"FIREWALL=on",
"FIREWALL_VPN_INPUT_PORTS=",
"FIREWALL_INPUT_PORTS=",
"FIREWALL_OUTBOUND_SUBNETS=",
"FIREWALL_DEBUG=off",
"LOG_LEVEL=info",
"HEALTH_SERVER_ADDRESS=127.0.0.1:9999",
"HEALTH_TARGET_ADDRESS=cloudflare.com:443",
"HEALTH_SUCCESS_WAIT_DURATION=5s",
"HEALTH_VPN_DURATION_INITIAL=6s",
"HEALTH_VPN_DURATION_ADDITION=5s",
"DOT=on",
"DOT_PROVIDERS=cloudflare",
"DOT_PRIVATE_ADDRESS=127.0.0.1/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,::1/128,fc00::/7,fe80::/10,::ffff:7f00:1/104,::ffff:a00:0/104,::ffff:a9fe:0/112,::ffff:ac10:0/108,::ffff:c0a8:0/112",
"DOT_VERBOSITY=1",
"DOT_VERBOSITY_DETAILS=0",
"DOT_VALIDATION_LOGLEVEL=0",
"DOT_CACHING=on",
"DOT_IPV6=off",
"BLOCK_MALICIOUS=on",
"BLOCK_SURVEILLANCE=off",
"BLOCK_ADS=off",
"UNBLOCK=",
"DNS_UPDATE_PERIOD=24h",
"DNS_ADDRESS=127.0.0.1",
"DNS_KEEP_NAMESERVER=off",
"HTTPPROXY=",
"HTTPPROXY_LOG=off",
"HTTPPROXY_LISTENING_ADDRESS=:8888",
"HTTPPROXY_STEALTH=off",
"HTTPPROXY_USER=",
"HTTPPROXY_PASSWORD=",
"HTTPPROXY_USER_SECRETFILE=/run/secrets/httpproxy_user",
"HTTPPROXY_PASSWORD_SECRETFILE=/run/secrets/httpproxy_password",
"SHADOWSOCKS=off",
"SHADOWSOCKS_LOG=off",
"SHADOWSOCKS_LISTENING_ADDRESS=:8388",
"SHADOWSOCKS_PASSWORD=",
"SHADOWSOCKS_PASSWORD_SECRETFILE=/run/secrets/shadowsocks_password",
"SHADOWSOCKS_CIPHER=chacha20-ietf-poly1305",
"HTTP_CONTROL_SERVER_LOG=on",
"HTTP_CONTROL_SERVER_ADDRESS=:8000",
"UPDATER_PERIOD=0",
"UPDATER_MIN_RATIO=0.8",
"UPDATER_VPN_SERVICE_PROVIDERS=",
"PUBLICIP_FILE=/tmp/gluetun/ip",
"PUBLICIP_PERIOD=12h",
"PPROF_ENABLED=no",
"PPROF_BLOCK_PROFILE_RATE=0",
"PPROF_MUTEX_PROFILE_RATE=0",
"PPROF_HTTP_SERVER_ADDRESS=:6060",
"VERSION_INFORMATION=on",
"TZ=",
"PUID=",
"PGID="
],
"Cmd": null,
"Healthcheck": {
"Test": [
"CMD-SHELL",
"/gluetun-entrypoint healthcheck"
],
"Interval": 5000000000,
"Timeout": 5000000000,
"StartPeriod": 10000000000,
"Retries": 1
},
"Image": "qmcgaw/gluetun",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": [
"/gluetun-entrypoint"
],
"OnBuild": null,
"Labels": {
"com.docker.compose.config-hash": "6dc7dc42a86edb47039de3650a9cb9bdcf4866c113b8f9d797722c9dfd20428b",
"com.docker.compose.container-number": "1",
"com.docker.compose.depends_on": "",
"com.docker.compose.image": "sha256:c22b543d33bfdcb9992cbef23961677133cdf09da71d782468ae2517138bad51",
"com.docker.compose.oneoff": "False",
"com.docker.compose.project": "wt-contnet",
"com.docker.compose.project.config_files": "/tmp/wt-contnet/docker-compose.yaml",
"com.docker.compose.project.working_dir": "/tmp/wt-contnet",
"com.docker.compose.replace": "9bd1ce000be81819fc915aa60a1674c7573b59a26ac4643ecf427a5732b9785f",
"com.docker.compose.service": "producer",
"com.docker.compose.version": "2.19.1",
"desktop.docker.io/wsl-distro": "Ubuntu",
"org.opencontainers.image.authors": "quentin.mcgaw@gmail.com",
"org.opencontainers.image.created": "2023-07-22T16:07:05.641Z",
"org.opencontainers.image.description": "VPN client in a thin Docker container for multiple VPN providers, written in Go, and using OpenVPN or Wireguard, DNS over TLS, with a few proxy servers built-in.",
"org.opencontainers.image.documentation": "https://github.com/qdm12/gluetun",
"org.opencontainers.image.licenses": "MIT",
"org.opencontainers.image.revision": "eecfb3952f202c0de3867d88e96d80c6b0f48359",
"org.opencontainers.image.source": "https://github.com/qdm12/gluetun",
"org.opencontainers.image.title": "gluetun",
"org.opencontainers.image.url": "https://github.com/qdm12/gluetun",
"org.opencontainers.image.version": "latest"
}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "34a321b64bb1b15f994dfccff0e235f881504f240c2028876ff6683962eaa10e",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {
"8000/tcp": null,
"8388/tcp": null,
"8388/udp": null,
"8888/tcp": null
},
"SandboxKey": "/var/run/docker/netns/34a321b64bb1",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "",
"Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"MacAddress": "",
"Networks": {
"wt-contnet_default": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"wt-contnet-producer-1",
"producer",
"25e75393800b"
],
"NetworkID": "f0f652a79efc54bcad52aafb4cbcc3b5dce1acaf11b172d8678d25f665faf63d",
"EndpointID": "2429c2b5d08db6c986bbd419a52ca4dd352715d80c5aeae04742efb84b0356fc",
"Gateway": "172.19.0.1",
"IPAddress": "172.19.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:13:00:02",
"DriverOpts": null
}
}
}
}

@ -0,0 +1,115 @@
{
"Id": "sha256:904b8cb13b932e23230836850610fa45dce9eb0650d5618c2b1487c2a4f577b8",
"RepoTags": [
"nginx:latest"
],
"RepoDigests": [
"nginx@sha256:aa0afebbb3cfa473099a62c4b32e9b3fb73ed23f2a75a65ce1d4b4f55a5c2ef2"
],
"Parent": "",
"Comment": "",
"Created": "2023-03-01T18:43:12.914398123Z",
"Container": "71a4c9a59d252d7c54812429bfe5df477e54e91ebfff1939ae39ecdf055d445c",
"ContainerConfig": {
"Hostname": "71a4c9a59d25",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"80/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"NGINX_VERSION=1.23.3",
"NJS_VERSION=0.7.9",
"PKG_RELEASE=1~bullseye"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"nginx\" \"-g\" \"daemon off;\"]"
],
"Image": "sha256:6716b8a33f73b21e193bb63424ea1105eaaa6a8237fefe75570bea18c87a1711",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": [
"/docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": {
"maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
},
"StopSignal": "SIGQUIT"
},
"DockerVersion": "20.10.23",
"Author": "",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"80/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"NGINX_VERSION=1.23.3",
"NJS_VERSION=0.7.9",
"PKG_RELEASE=1~bullseye"
],
"Cmd": [
"nginx",
"-g",
"daemon off;"
],
"Image": "sha256:6716b8a33f73b21e193bb63424ea1105eaaa6a8237fefe75570bea18c87a1711",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": [
"/docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": {
"maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
},
"StopSignal": "SIGQUIT"
},
"Architecture": "amd64",
"Os": "linux",
"Size": 141838643,
"VirtualSize": 141838643,
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/09785ba17f27c783ef8b44f369f9aac0ca936000b57abf22b3c54d1e6eb8e27b/diff:/var/lib/docker/overlay2/6f8acd64ae44fd4d14bcb90c105eceba46854aa3985b5b6b317bcc5692cfc286/diff:/var/lib/docker/overlay2/73d41c15edb21c5f12cf53e313f48b5da55283aafc77d35b7bc662241879d7e7/diff:/var/lib/docker/overlay2/d97b55f3d966ae031492369a98e9e00d2bd31e520290fe2034e0a2b1ed77c91e/diff:/var/lib/docker/overlay2/053e9ca65c6b64cb9d98a812ff7488c7e77938b4fb8e0c4d2ad7f8ec235f0f20/diff",
"MergedDir": "/var/lib/docker/overlay2/105427179e5628eb7e893d53e21f42f9e76278f8b5665387ecdeed54a7231137/merged",
"UpperDir": "/var/lib/docker/overlay2/105427179e5628eb7e893d53e21f42f9e76278f8b5665387ecdeed54a7231137/diff",
"WorkDir": "/var/lib/docker/overlay2/105427179e5628eb7e893d53e21f42f9e76278f8b5665387ecdeed54a7231137/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:650abce4b096b06ac8bec2046d821d66d801af34f1f1d4c5e272ad030c7873db",
"sha256:4dc5cd799a08ff49a603870c8378ea93083bfc2a4176f56e5531997e94c195d0",
"sha256:e161c82b34d21179db1f546c1cd84153d28a17d865ccaf2dedeb06a903fec12c",
"sha256:83ba6d8ffb8c2974174c02d3ba549e7e0656ebb1bc075a6b6ee89b6c609c6a71",
"sha256:d8466e142d8710abf5b495ebb536478f7e19d9d03b151b5d5bd09df4cfb49248",
"sha256:101af4ba983b04be266217ecee414e88b23e394f62e9801c7c1bdb37cb37bcaa"
]
},
"Metadata": {
"LastTagTime": "0001-01-01T00:00:00Z"
}
}

@ -0,0 +1,210 @@
{
"Id": "sha256:c22b543d33bfdcb9992cbef23961677133cdf09da71d782468ae2517138bad51",
"RepoTags": [
"qmcgaw/gluetun:latest"
],
"RepoDigests": [
"qmcgaw/gluetun@sha256:cd532bf4ef88a348a915c6dc62a9867a2eca89aa70559b0b4a1ea15cc0e595d1"
],
"Parent": "",
"Comment": "buildkit.dockerfile.v0",
"Created": "2023-07-22T16:10:29.457146856Z",
"Container": "",
"ContainerConfig": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": null,
"Cmd": null,
"Image": "",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": null
},
"DockerVersion": "",
"Author": "",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"8000/tcp": {},
"8388/tcp": {},
"8388/udp": {},
"8888/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"VPN_SERVICE_PROVIDER=pia",
"VPN_TYPE=openvpn",
"VPN_ENDPOINT_IP=",
"VPN_ENDPOINT_PORT=",
"VPN_INTERFACE=tun0",
"OPENVPN_PROTOCOL=udp",
"OPENVPN_USER=",
"OPENVPN_PASSWORD=",
"OPENVPN_USER_SECRETFILE=/run/secrets/openvpn_user",
"OPENVPN_PASSWORD_SECRETFILE=/run/secrets/openvpn_password",
"OPENVPN_VERSION=2.5",
"OPENVPN_VERBOSITY=1",
"OPENVPN_FLAGS=",
"OPENVPN_CIPHERS=",
"OPENVPN_AUTH=",
"OPENVPN_PROCESS_USER=root",
"OPENVPN_CUSTOM_CONFIG=",
"WIREGUARD_PRIVATE_KEY=",
"WIREGUARD_PRESHARED_KEY=",
"WIREGUARD_PUBLIC_KEY=",
"WIREGUARD_ALLOWED_IPS=",
"WIREGUARD_ADDRESSES=",
"WIREGUARD_MTU=1400",
"WIREGUARD_IMPLEMENTATION=auto",
"SERVER_REGIONS=",
"SERVER_COUNTRIES=",
"SERVER_CITIES=",
"SERVER_HOSTNAMES=",
"ISP=",
"OWNED_ONLY=no",
"PRIVATE_INTERNET_ACCESS_OPENVPN_ENCRYPTION_PRESET=",
"VPN_PORT_FORWARDING=off",
"VPN_PORT_FORWARDING_PROVIDER=",
"VPN_PORT_FORWARDING_STATUS_FILE=/tmp/gluetun/forwarded_port",
"OPENVPN_CERT=",
"OPENVPN_KEY=",
"OPENVPN_CLIENTCRT_SECRETFILE=/run/secrets/openvpn_clientcrt",
"OPENVPN_CLIENTKEY_SECRETFILE=/run/secrets/openvpn_clientkey",
"OPENVPN_ENCRYPTED_KEY=",
"OPENVPN_ENCRYPTED_KEY_SECRETFILE=/run/secrets/openvpn_encrypted_key",
"OPENVPN_KEY_PASSPHRASE=",
"OPENVPN_KEY_PASSPHRASE_SECRETFILE=/run/secrets/openvpn_key_passphrase",
"SERVER_NUMBER=",
"SERVER_NAMES=",
"FREE_ONLY=",
"MULTIHOP_ONLY=",
"PREMIUM_ONLY=",
"FIREWALL=on",
"FIREWALL_VPN_INPUT_PORTS=",
"FIREWALL_INPUT_PORTS=",
"FIREWALL_OUTBOUND_SUBNETS=",
"FIREWALL_DEBUG=off",
"LOG_LEVEL=info",
"HEALTH_SERVER_ADDRESS=127.0.0.1:9999",
"HEALTH_TARGET_ADDRESS=cloudflare.com:443",
"HEALTH_SUCCESS_WAIT_DURATION=5s",
"HEALTH_VPN_DURATION_INITIAL=6s",
"HEALTH_VPN_DURATION_ADDITION=5s",
"DOT=on",
"DOT_PROVIDERS=cloudflare",
"DOT_PRIVATE_ADDRESS=127.0.0.1/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,::1/128,fc00::/7,fe80::/10,::ffff:7f00:1/104,::ffff:a00:0/104,::ffff:a9fe:0/112,::ffff:ac10:0/108,::ffff:c0a8:0/112",
"DOT_VERBOSITY=1",
"DOT_VERBOSITY_DETAILS=0",
"DOT_VALIDATION_LOGLEVEL=0",
"DOT_CACHING=on",
"DOT_IPV6=off",
"BLOCK_MALICIOUS=on",
"BLOCK_SURVEILLANCE=off",
"BLOCK_ADS=off",
"UNBLOCK=",
"DNS_UPDATE_PERIOD=24h",
"DNS_ADDRESS=127.0.0.1",
"DNS_KEEP_NAMESERVER=off",
"HTTPPROXY=",
"HTTPPROXY_LOG=off",
"HTTPPROXY_LISTENING_ADDRESS=:8888",
"HTTPPROXY_STEALTH=off",
"HTTPPROXY_USER=",
"HTTPPROXY_PASSWORD=",
"HTTPPROXY_USER_SECRETFILE=/run/secrets/httpproxy_user",
"HTTPPROXY_PASSWORD_SECRETFILE=/run/secrets/httpproxy_password",
"SHADOWSOCKS=off",
"SHADOWSOCKS_LOG=off",
"SHADOWSOCKS_LISTENING_ADDRESS=:8388",
"SHADOWSOCKS_PASSWORD=",
"SHADOWSOCKS_PASSWORD_SECRETFILE=/run/secrets/shadowsocks_password",
"SHADOWSOCKS_CIPHER=chacha20-ietf-poly1305",
"HTTP_CONTROL_SERVER_LOG=on",
"HTTP_CONTROL_SERVER_ADDRESS=:8000",
"UPDATER_PERIOD=0",
"UPDATER_MIN_RATIO=0.8",
"UPDATER_VPN_SERVICE_PROVIDERS=",
"PUBLICIP_FILE=/tmp/gluetun/ip",
"PUBLICIP_PERIOD=12h",
"PPROF_ENABLED=no",
"PPROF_BLOCK_PROFILE_RATE=0",
"PPROF_MUTEX_PROFILE_RATE=0",
"PPROF_HTTP_SERVER_ADDRESS=:6060",
"VERSION_INFORMATION=on",
"TZ=",
"PUID=",
"PGID="
],
"Cmd": null,
"Healthcheck": {
"Test": [
"CMD-SHELL",
"/gluetun-entrypoint healthcheck"
],
"Interval": 5000000000,
"Timeout": 5000000000,
"StartPeriod": 10000000000,
"Retries": 1
},
"Image": "",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": [
"/gluetun-entrypoint"
],
"OnBuild": null,
"Labels": {
"org.opencontainers.image.authors": "quentin.mcgaw@gmail.com",
"org.opencontainers.image.created": "2023-07-22T16:07:05.641Z",
"org.opencontainers.image.description": "VPN client in a thin Docker container for multiple VPN providers, written in Go, and using OpenVPN or Wireguard, DNS over TLS, with a few proxy servers built-in.",
"org.opencontainers.image.documentation": "https://github.com/qdm12/gluetun",
"org.opencontainers.image.licenses": "MIT",
"org.opencontainers.image.revision": "eecfb3952f202c0de3867d88e96d80c6b0f48359",
"org.opencontainers.image.source": "https://github.com/qdm12/gluetun",
"org.opencontainers.image.title": "gluetun",
"org.opencontainers.image.url": "https://github.com/qdm12/gluetun",
"org.opencontainers.image.version": "latest"
}
},
"Architecture": "amd64",
"Os": "linux",
"Size": 42602255,
"VirtualSize": 42602255,
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/a20c9490a23ee8af51898892d9bf32258d44e0e07f3799475be8e8f273a50f73/diff:/var/lib/docker/overlay2/d4c97f367c37c6ada9de57f438a3e19cc714be2a54a6f582a03de9e42d88b344/diff",
"MergedDir": "/var/lib/docker/overlay2/0d222a3aa067159831c4111a408e40325be1085b935c98d39c2e9a01ff50b224/merged",
"UpperDir": "/var/lib/docker/overlay2/0d222a3aa067159831c4111a408e40325be1085b935c98d39c2e9a01ff50b224/diff",
"WorkDir": "/var/lib/docker/overlay2/0d222a3aa067159831c4111a408e40325be1085b935c98d39c2e9a01ff50b224/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:78a822fe2a2d2c84f3de4a403188c45f623017d6a4521d23047c9fbb0801794c",
"sha256:122dbeefc08382d88b3fe57ad81c1e2428af5b81c172d112723a33e2a20fe880",
"sha256:3d215e55b88a99dcd7cf4349618326ab129771e12fdf6c6ef5cbb71a265dbb6c"
]
},
"Metadata": {
"LastTagTime": "0001-01-01T00:00:00Z"
}
}

@ -0,0 +1,42 @@
#!/usr/bin/env bash
set -e
function exit_env_err() {
>&2 echo "Required environment variable not set: $1"
exit 1
}
if [ -z "$VPN_SERVICE_PROVIDER" ]; then exit_env_err "VPN_SERVICE_PROVIDER"; fi
if [ -z "$OPENVPN_USER" ]; then exit_env_err "OPENVPN_USER"; fi
if [ -z "$OPENVPN_PASSWORD" ]; then exit_env_err "OPENVPN_PASSWORD"; fi
# if [ -z "$SERVER_COUNTRIES" ]; then exit_env_err "SERVER_COUNTRIES"; fi
export SERVER_COUNTRIES=${SERVER_COUNTRIES:"Sweden"}
REPO_ROOT="$(git rev-parse --show-toplevel)"
COMPOSE_FILE="$REPO_ROOT/dockerfiles/container-networking/docker-compose.yml"
DEFAULT_WATCHTOWER="$REPO_ROOT/watchtower"
WATCHTOWER="$*"
WATCHTOWER=${WATCHTOWER:-$DEFAULT_WATCHTOWER}
echo "repo root path is $REPO_ROOT"
echo "watchtower path is $WATCHTOWER"
echo "compose file path is $COMPOSE_FILE"
echo; echo "=== Forcing network container producer update..."
echo "Pull previous version of gluetun..."
docker pull qmcgaw/gluetun:v3.34.3
echo "Fake new version of gluetun by retagging v3.34.4 as v3.35.0..."
docker tag qmcgaw/gluetun:v3.34.3 qmcgaw/gluetun:v3.35.0
echo; echo "=== Creating containers..."
docker compose -p "wt-contnet" -f "$COMPOSE_FILE" up -d
echo; echo "=== Running watchtower"
$WATCHTOWER --run-once
echo; echo "=== Removing containers..."
docker compose -p "wt-contnet" -f "$COMPOSE_FILE" down
Loading…
Cancel
Save