diff --git a/README.md b/README.md index 1419e30..87e594a 100755 --- a/README.md +++ b/README.md @@ -107,7 +107,9 @@ With container name, no label needs to be added _(most of the time)_. ### Labels (docker) -See [compose.example.yml](compose.example.yml) for more +See [docker.md](docs/docker.md#docker-compose-example) for examples + +When `go-proxy` is running in `host` network mode, see [here](docs/docker.md#docker-compose-example-host-network) for extra instructions - `proxy.aliases`: comma separated aliases for subdomain matching @@ -145,12 +147,13 @@ Below labels has a **`proxy..`** prefix (i.e. `proxy.nginx.scheme: http`) - `sub`: (experimental) remove path prefix from URL and also append path to HTML link attributes (`src`, `href` and `action`) and Javascript `fetch(url)` by response body substitution e.g. apps.y.z/app1 -> webdav:80, `href="/app1/path/to/file"` -> `href="/path/to/file"` -- `load_balance`: enable load balance (docker only) +- `load_balance`: _(Docker only)_ enable load balance - allowed: `1`, `true` ### Environment variables - `GOPROXY_DEBUG`: set to `1` or `true` to enable debug behaviors (i.e. output, etc.) +- `GOPROXY_HOST_NETWORK`: _(Docker only)_ set to `1` when `network_mode: host` ### Config File diff --git a/docs/docker.md b/docs/docker.md index 5319c1a..f1d8fba 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -15,17 +15,19 @@ 3. Verify folder structure and then `cd go-proxy` - ```plain - go-proxy - ├── certs - ├── compose.yml - └── config - ├── config.yml - └── providers.yml - ``` + ```plain + go-proxy + ├── certs + ├── compose.yml + └── config + ├── config.yml + └── providers.yml + ``` 4. Enable HTTPs _(optional)_ + - To use autocert feature + - completing `autocert` section in `config/config.yml` - mount `certs/` to `/app/certs` to store obtained certs @@ -38,7 +40,7 @@ 5. Modify `compose.yml` fit your needs - Add networks to make sure it is in the same network with other containers, or make sure `proxy..host` is reachable + Add networks to make sure it is in the same network with other containers, or make sure `proxy..host` is reachable 6. Run `docker compose up -d` to start the container @@ -48,77 +50,193 @@ - Firewall issues - If you are using `ufw` with vpn that drop all inbound traffic except vpn, run below: + If you are using `ufw` with vpn that drop all inbound traffic except vpn, run below: - `sudo ufw allow from 172.16.0.0/16 to 100.64.0.0/10` + `sudo ufw allow from 172.16.0.0/16 to 100.64.0.0/10` - Explaination: + Explaination: - Docker network is usually `172.16.0.0/16` + Docker network is usually `172.16.0.0/16` - Tailscale is used as an example, `100.64.0.0/10` will be the CIDR + Tailscale is used as an example, `100.64.0.0/10` will be the CIDR - You can also list CIDRs of all docker bridge networks by: + You can also list CIDRs of all docker bridge networks by: - `docker network inspect $(docker network ls | awk '$3 == "bridge" { print $1}') | jq -r '.[] | .Name + " " + .IPAM.Config[0].Subnet' -` + `docker network inspect $(docker network ls | awk '$3 == "bridge" { print $1}') | jq -r '.[] | .Name + " " + .IPAM.Config[0].Subnet' -` -## Docker compose example +## Docker compose example (bridge network) ```yaml volumes: adg-work: adg-conf: mc-data: + palworld: + nginx: services: - adg: - image: adguard/adguardhome - restart: unless-stopped - labels: - - proxy.aliases=adg,adg-dns,adg-setup - - proxy.adg.port=80 - - proxy.adg-setup.port=3000 - - proxy.adg-dns.scheme=udp - - proxy.adg-dns.port=20000:dns - volumes: - - adg-work:/opt/adguardhome/work - - adg-conf:/opt/adguardhome/conf - mc: - image: itzg/minecraft-server - tty: true - stdin_open: true - container_name: mc - restart: unless-stopped - labels: - - proxy.mc.scheme=tcp - - proxy.mc.port=20001:25565 - environment: - EULA: "TRUE" - volumes: - - mc-data:/data - go-proxy: - image: ghcr.io/yusing/go-proxy - container_name: go-proxy - restart: always - ports: - - 80:80 # http - - 443:443 # optional, https - - 8080:8080 # http panel - - 8443:8443 # optional, https panel + adg: + image: adguard/adguardhome + restart: unless-stopped + labels: + - proxy.aliases=adg,adg-dns,adg-setup + - proxy.adg.port=80 + - proxy.adg-setup.port=3000 + - proxy.adg-dns.scheme=udp + - proxy.adg-dns.port=20000:dns + volumes: + - adg-work:/opt/adguardhome/work + - adg-conf:/opt/adguardhome/conf + mc: + image: itzg/minecraft-server + tty: true + stdin_open: true + container_name: mc + restart: unless-stopped + labels: + - proxy.mc.scheme=tcp + - proxy.mc.port=20001:25565 + environment: + - EULA=TRUE + volumes: + - mc-data:/data + palworld: + image: thijsvanloef/palworld-server-docker:latest + restart: unless-stopped + container_name: pal + stop_grace_period: 30s + labels: + - proxy.aliases=pal1,pal2 + - proxy.*.scheme=udp + - proxy.pal1.port=20002:8211 + - proxy.pal2.port=20003:27015 + environment: ... + volumes: + - palworld:/palworld + nginx: + image: nginx + container_name: nginx + volumes: + - nginx:/usr/share/nginx/html + go-proxy: + image: ghcr.io/yusing/go-proxy + container_name: go-proxy + restart: always + ports: + - 80:80 # http + - 443:443 # optional, https + - 8080:8080 # http panel + - 8443:8443 # optional, https panel - - 53:20000/udp # adguardhome - - 25565:20001/tcp # minecraft - volumes: - - ./config:/app/config - - /var/run/docker.sock:/var/run/docker.sock:ro - labels: - - proxy.aliases=gp - - proxy.panel.port=8080 + - 53:20000/udp # adguardhome + - 25565:20001/tcp # minecraft + - 8211:20002/udp # palworld + - 27015:20003/udp # palworld + volumes: + - ./config:/app/config + - /var/run/docker.sock:/var/run/docker.sock:ro + labels: + - proxy.aliases=gp + - proxy.panel.port=8080 ``` ### Services URLs - `gp.yourdomain.com`: go-proxy web panel -- `adg-setup.yourdomain.com`: adguard setup (first time running) +- `adg-setup.yourdomain.com`: adguard setup (first time setup) - `adg.yourdomain.com`: adguard dashboard +- `nginx.yourdomain.com`: nginx - `yourdomain.com:53`: adguard dns - `yourdomain.com:25565`: minecraft server +- `yourdomain.com:8211`: palworld server + +## Docker compose example (host network) + +### Notice + +When `go-proxy` is running in `host` network mode, you must: + +- set `GOPROXY_HOST_NETWORK=1` +- map ports to host explicitly +- add an asterisk sign **(*)** before `port` number under `labels` + +```yaml +volumes: + adg-work: + adg-conf: + mc-data: + palworld: + nginx: +services: + adg: + image: adguard/adguardhome + restart: unless-stopped + ports: # map random ports to container ports + - 80 + - 3000 + - 53/udp + - 53/tcp + labels: + - proxy.aliases=adg,adg-dns,adg-setup + # add an asterisk (*) before to find host port automatically + - proxy.adg.port=*80 + - proxy.adg-setup.port=*3000 + - proxy.adg-dns.scheme=udp + - proxy.adg-dns.port=*53 + volumes: + - adg-work:/opt/adguardhome/work + - adg-conf:/opt/adguardhome/conf + mc: + image: itzg/minecraft-server + tty: true + stdin_open: true + container_name: mc + restart: unless-stopped + ports: + - 25565 + labels: + - proxy.mc.scheme=tcp + - proxy.mc.port=*25565 + environment: + - EULA=TRUE + volumes: + - mc-data:/data + palworld: + image: thijsvanloef/palworld-server-docker:latest + restart: unless-stopped + container_name: pal + stop_grace_period: 30s + ports: + - 8211/udp + - 27015/udp + labels: + - proxy.aliases=pal1,pal2 + - proxy.*.scheme=udp + - proxy.pal1.port=*8211 + - proxy.pal2.port=*27015 + environment: ... + volumes: + - palworld:/palworld + nginx: + image: nginx + container_name: nginx + # for single port container, host port will be found automatically + ports: + - 80 + volumes: + - nginx:/usr/share/nginx/html + go-proxy: + image: ghcr.io/yusing/go-proxy + container_name: go-proxy + restart: always + network_mode: host # no port mapping needed for host network mode + environment: + - GOPROXY_HOST_NETWORK=1 # required for host network mode + volumes: + - ./config:/app/config + - /var/run/docker.sock:/var/run/docker.sock:ro + labels: + - proxy.aliases=gp + - proxy.panel.port=808 +``` + +**Same services URLs as [`bridge`](#services-urls) example!** diff --git a/go.mod b/go.mod index a91d211..3c1c403 100755 --- a/go.mod +++ b/go.mod @@ -14,10 +14,9 @@ require ( ) require ( - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect - github.com/cloudflare/cloudflare-go v0.92.0 // indirect + github.com/Microsoft/go-winio v0.4.14 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/cloudflare/cloudflare-go v0.86.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/go-connections v0.5.0 // indirect @@ -28,9 +27,7 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.5 // indirect github.com/miekg/dns v1.1.58 // indirect @@ -42,21 +39,15 @@ require ( github.com/pkg/errors v0.9.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect go.opentelemetry.io/otel v1.24.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.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/sdk v1.24.0 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect - go.opentelemetry.io/proto/otlp v1.1.0 // indirect golang.org/x/crypto v0.21.0 // indirect - golang.org/x/mod v0.16.0 // indirect + golang.org/x/mod v0.14.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.19.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect - google.golang.org/grpc v1.61.1 // indirect - google.golang.org/protobuf v1.32.0 // indirect + golang.org/x/tools v0.17.0 // indirect gotest.tools/v3 v3.5.1 // indirect ) diff --git a/go.sum b/go.sum index 90065ab..7b9124a 100755 --- a/go.sum +++ b/go.sum @@ -1,11 +1,11 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -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/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/cloudflare/cloudflare-go v0.92.0 h1:ltJvGvqZ4G6Fm2hHOYZ5RWpJQcrM0oDrsjjZydZhFJQ= -github.com/cloudflare/cloudflare-go v0.92.0/go.mod h1:nUqvBUUDRxNzsDSQjbqUNWHEIYAoUlgRmcAzMKlFdKs= +github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +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/cloudflare/cloudflare-go v0.86.0 h1:jEKN5VHNYNYtfDL2lUFLTRo+nOVNPFxpXTstVx0rqHI= +github.com/cloudflare/cloudflare-go v0.86.0/go.mod h1:wYW/5UP02TUfBToa/yKbQHV+r6h1NnJ1Je7XjuGM4Jw= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -40,11 +40,9 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -60,6 +58,7 @@ github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= 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/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -80,6 +79,7 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -88,13 +88,15 @@ github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XF github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKPXCfD2aieJis= github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= @@ -120,8 +122,8 @@ 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/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.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -133,10 +135,11 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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= @@ -150,8 +153,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 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.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= -golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= 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= @@ -163,8 +166,6 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU= google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY= google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/src/go-proxy/constants.go b/src/go-proxy/constants.go index 4e194f0..39192a8 100644 --- a/src/go-proxy/constants.go +++ b/src/go-proxy/constants.go @@ -168,6 +168,8 @@ const ( const udpBufferSize = 1500 +var isHostNetworkMode = os.Getenv("GOPROXY_HOST_NETWORK") == "1" + var logLevel = func() logrus.Level { switch os.Getenv("GOPROXY_DEBUG") { case "1", "true": diff --git a/src/go-proxy/docker_provider.go b/src/go-proxy/docker_provider.go index f02437c..ccdb609 100755 --- a/src/go-proxy/docker_provider.go +++ b/src/go-proxy/docker_provider.go @@ -3,6 +3,7 @@ package main import ( "fmt" "net/http" + "strconv" "strings" "time" @@ -37,16 +38,14 @@ func (p *Provider) getContainerProxyConfigs(container *types.Container, clientIP aliases = strings.Split(aliasesLabel, ",") } + if clientIP == "" && isHostNetworkMode { + clientIP = "127.0.0.1" + } isRemote := clientIP != "" - ne := NewNestedError("invalid label config").Subjectf("container %s", containerName) - defer func() { - if ne.HasExtras() { - p.l.Error(ne) - } - }() - for _, alias := range aliases { + ne := NewNestedError("invalid label config").Subjectf("container %s", containerName) + l := p.l.WithField("container", containerName).WithField("alias", alias) config := NewProxyConfig(p) prefix := fmt.Sprintf("proxy.%s.", alias) @@ -84,9 +83,23 @@ func (p *Provider) getContainerProxyConfigs(container *types.Container, clientIP } } if !isValidScheme(config.Scheme) { - l.Warnf("unsupported scheme: %s, using http", config.Scheme) - config.Scheme = "http" + ne.Extra("unsupported scheme").Subject(config.Scheme) } + + if isRemote && strings.HasPrefix(config.Port, "*") { + var err error + // find matching port + srcPort := config.Port[1:] + config.Port, err = findMatchingContainerPort(container,srcPort) + if err != nil { + ne.ExtraError(NewNestedErrorFrom(err).Subjectf("alias %s", alias)) + } + if isStreamScheme(config.Scheme) { + config.Port = fmt.Sprintf("%s:%s", srcPort, config.Port) + } + } + + if config.Host == "" { switch { case isRemote: @@ -112,6 +125,10 @@ func (p *Provider) getContainerProxyConfigs(container *types.Container, clientIP } config.Alias = alias + if ne.HasExtras() { + l.Error(ne) + continue + } cfgs = append(cfgs, config) } return cfgs @@ -203,6 +220,21 @@ func selectPort(c *types.Container, isRemote bool) uint16 { return selectPortInternal(c, getPrivatePort) } +// used when isRemote is true +func findMatchingContainerPort(c *types.Container, ps string) (string, error) { + p, err := strconv.Atoi(ps) + if err != nil { + return "", err + } + pWant := uint16(p) + for _, pGot := range c.Ports { + if pGot.PrivatePort == pWant { + return fmt.Sprintf("%d", pGot.PublicPort), nil + } + } + return "", fmt.Errorf("port %d not found", p) +} + func selectPortInternal(c *types.Container, getPort func(types.Port) uint16) uint16 { imageName := getImageName(c) // if is known image -> use known port diff --git a/src/go-proxy/route.go b/src/go-proxy/route.go index 7ef3ac0..6873533 100755 --- a/src/go-proxy/route.go +++ b/src/go-proxy/route.go @@ -43,9 +43,4 @@ func isStreamScheme(s string) bool { } } return false -} - -// id -> target -type StreamRoutes SafeMap[string, StreamRoute] - -var streamRoutes StreamRoutes = NewSafeMapOf[StreamRoutes]() +} \ No newline at end of file diff --git a/src/go-proxy/stream_route.go b/src/go-proxy/stream_route.go index 4ffc8a9..43aedf0 100755 --- a/src/go-proxy/stream_route.go +++ b/src/go-proxy/stream_route.go @@ -138,13 +138,14 @@ func (route *StreamRouteBase) Logger() logrus.FieldLogger { } func (route *StreamRouteBase) Start() { + route.wg.Wait() route.ensurePort() if err := route.Setup(); err != nil { route.l.Errorf("failed to setup: %v", err) return } - streamRoutes.Set(route.id, route) route.started = true + streamRoutes.Set(route.id, route) route.wg.Add(2) go route.grAcceptConnections() go route.grHandleConnections() @@ -230,3 +231,8 @@ func (route *StreamRouteBase) grHandleConnections() { } } } + +// id -> target +type StreamRoutes SafeMap[string, StreamRoute] + +var streamRoutes StreamRoutes = NewSafeMapOf[StreamRoutes]() \ No newline at end of file diff --git a/src/go-proxy/udp_route.go b/src/go-proxy/udp_route.go index acc5a6c..cefd637 100755 --- a/src/go-proxy/udp_route.go +++ b/src/go-proxy/udp_route.go @@ -24,7 +24,7 @@ type UDPConn struct { *BidirectionalPipe } -type UDPConnMap map[net.Addr]*UDPConn +type UDPConnMap map[string]*UDPConn func NewUDPRoute(base *StreamRouteBase) StreamImpl { return &UDPRoute{ @@ -67,30 +67,33 @@ func (route *UDPRoute) Accept() (interface{}, error) { return nil, io.ErrShortBuffer } - conn, ok := route.connMap[srcAddr] + key := srcAddr.String() + conn, ok := route.connMap[key] if !ok { route.connMapMutex.Lock() - srcConn, err := net.DialUDP("udp", nil, srcAddr) - if err != nil { - return nil, err + if conn, ok = route.connMap[key]; !ok { + srcConn, err := net.DialUDP("udp", nil, srcAddr) + if err != nil { + return nil, err + } + dstConn, err := net.DialUDP("udp", nil, route.targetAddr) + if err != nil { + srcConn.Close() + return nil, err + } + pipeCtx, pipeCancel := context.WithCancel(context.Background()) + go func() { + <-route.stopCh + pipeCancel() + }() + conn = &UDPConn{ + srcConn, + dstConn, + NewBidirectionalPipe(pipeCtx, sourceRWCloser{in, dstConn}, sourceRWCloser{in, srcConn}), + } + route.connMap[key] = conn } - dstConn, err := net.DialUDP("udp", nil, route.targetAddr) - if err != nil { - srcConn.Close() - return nil, err - } - pipeCtx, pipeCancel := context.WithCancel(context.Background()) - go func() { - <-route.stopCh - pipeCancel() - }() - conn = &UDPConn{ - srcConn, - dstConn, - NewBidirectionalPipe(pipeCtx, sourceRWCloser{in, dstConn}, sourceRWCloser{in, srcConn}), - } - route.connMap[srcAddr] = conn route.connMapMutex.Unlock() } @@ -108,8 +111,11 @@ func (route *UDPRoute) CloseListeners() { route.listeningConn = nil } for _, conn := range route.connMap { + if err := conn.src.Close(); err != nil { + route.l.Errorf("error closing src conn: %w", err) + } if err := conn.dst.Close(); err != nil { - route.l.Error(err) + route.l.Error("error closing dst conn: %w", err) } } route.connMap = make(UDPConnMap) @@ -117,18 +123,9 @@ func (route *UDPRoute) CloseListeners() { type sourceRWCloser struct { server *net.UDPConn - target *net.UDPConn -} - -func (w sourceRWCloser) Read(p []byte) (int, error) { - n, _, err := w.target.ReadFrom(p) - return n, err + *net.UDPConn } func (w sourceRWCloser) Write(p []byte) (int, error) { - return w.server.WriteToUDP(p, w.target.RemoteAddr().(*net.UDPAddr)) // TODO: support non udp -} - -func (w sourceRWCloser) Close() error { - return w.target.Close() + return w.server.WriteToUDP(p, w.RemoteAddr().(*net.UDPAddr)) // TODO: support non udp }