Compare commits

...

13 Commits

Author SHA1 Message Date
dependabot[bot] c747743d1f Chore(deps): Bump github.com/mdlayher/netlink from 1.9.0 to 1.11.2
Bumps [github.com/mdlayher/netlink](https://github.com/mdlayher/netlink) from 1.9.0 to 1.11.2.
- [Release notes](https://github.com/mdlayher/netlink/releases)
- [Changelog](https://github.com/mdlayher/netlink/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mdlayher/netlink/compare/v1.9.0...v1.11.2)

---
updated-dependencies:
- dependency-name: github.com/mdlayher/netlink
  dependency-version: 1.11.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-24 21:12:04 +00:00
dependabot[bot] 7e8d81b161 Chore(deps): Bump golang.org/x/net from 0.51.0 to 0.55.0 (#3338) 2026-05-24 23:09:52 +02:00
Quentin McGaw 21e868c89c hotfix(protonvpn): small port forwarding fixes for edge cases 2026-05-24 21:08:56 +00:00
Quentin McGaw 2e20e2df66 feat(protonvpn): use symmetric port forwarding for first port then asymmetric for next ports (#3345) 2026-05-24 22:47:58 +02:00
Quentin McGaw 6f5f518d1d chore(github): finer grain schedules for dependency checking
- default to weekly instead of daily
- check gluetun-servers daily
- check some Go modules only quartely since they are not important
2026-05-24 20:34:57 +00:00
Quentin McGaw 1998e0d04f chore(deps): remove direct dependency on golang.org/x/exp 2026-05-24 20:28:54 +00:00
Quentin McGaw 14f30bc641 docs(maintenance): clear up some finished items 2026-05-24 20:18:27 +00:00
Quentin McGaw f89e55b8ff chore(storage): remove outdated servers.json CI and documentation 2026-05-24 20:18:07 +00:00
Quentin McGaw 7ad6af0947 docs(github): remove servers.json checkbox from PR template 2026-05-24 20:13:07 +00:00
Quentin McGaw d3e089ccd7 hotfix(firewall/iptables): filter out DOCKER* chains from nat table when saving/restoring 2026-05-23 21:44:22 +00:00
Quentin McGaw 3eebbf65a8 hotfix(firewall/iptables): only restore firewall if IPv6 port redirection failed but NAT is supported 2026-05-23 21:26:08 +00:00
Quentin McGaw a1ef736b0f hotfix(portforwarding): disallow setting ports when running port forwarding code 2026-05-23 13:20:20 +00:00
Quentin McGaw 46edfe49e3 fix(portforwarding): handle empty ports without panicing 2026-05-23 13:19:37 +00:00
15 changed files with 184 additions and 232 deletions
+55 -4
View File
@@ -4,12 +4,63 @@ updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
interval: "weekly"
- package-ecosystem: docker
directory: /
schedule:
interval: "daily"
- package-ecosystem: gomod
directory: /
interval: "weekly"
- # Servers data dependency that should be updated as soon as
# possible when a new version is released, to have the latest
# servers available
package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "daily"
allow:
- dependency-name: "github.com/qdm12/gluetun-servers"
- # non important dependencies that do not need to be updated.
package-ecosystem: gomod
directory: /
schedule:
interval: "quarterly"
allow:
- dependency-name: "github.com/breml/rootcerts"
- dependency-name: "github.com/fatih/color"
- dependency-name: "github.com/golang/mock"
- dependency-name: "github.com/klauspost/compress"
- dependency-name: "github.com/klauspost/pgzip"
- dependency-name: "github.com/pelletier/go-toml/v2"
- dependency-name: "github.com/qdm12/goshutdown"
- dependency-name: "github.com/qdm12/gosplash"
- dependency-name: "github.com/qdm12/gotree"
- dependency-name: "github.com/qdm12/log"
- dependency-name: "github.com/stretchr/testify"
- dependency-name: "github.com/ulikunitz/xz"
- dependency-name: "gopkg.in/ini.v1"
- # The rest of Go modules are important and should be checked every week,
# instead of daily, to give a bit of time to avoid supply chain attacks.
package-ecosystem: gomod
directory: /
schedule:
interval: "weekly"
ignore:
- # In particular avoid amneziawg-go which have v1.x.y versions available
# on the Go modules proxy, but are not in the Github repository tags
# and are not the latest releases either. Most likely a mistake from the
# maintainers, which is persisted on the Go proxy.
dependency-name: "github.com/amnezia-vpn/amneziawg-go"
versions: ["1.x"]
- dependency-name: "github.com/qdm12/gluetun-servers"
- dependency-name: "github.com/breml/rootcerts"
- dependency-name: "github.com/fatih/color"
- dependency-name: "github.com/golang/mock"
- dependency-name: "github.com/klauspost/compress"
- dependency-name: "github.com/klauspost/pgzip"
- dependency-name: "github.com/pelletier/go-toml/v2"
- dependency-name: "github.com/qdm12/goshutdown"
- dependency-name: "github.com/qdm12/gosplash"
- dependency-name: "github.com/qdm12/gotree"
- dependency-name: "github.com/qdm12/log"
- dependency-name: "github.com/stretchr/testify"
- dependency-name: "github.com/ulikunitz/xz"
- dependency-name: "gopkg.in/ini.v1"
-1
View File
@@ -8,5 +8,4 @@
# Assertions
* [ ] I am aware that we do not accept manual changes to the servers.json file <!-- If this is your goal, please consult https://github.com/qdm12/gluetun-wiki/blob/main/setup/servers.md#update-using-the-command-line -->
* [ ] I am aware that any changes to settings should be reflected in the [wiki](https://github.com/qdm12/gluetun-wiki/)
-98
View File
@@ -1,98 +0,0 @@
name: Update servers list
on:
workflow_dispatch:
inputs:
provider:
description: "VPN Provider to update"
required: true
default: "all"
type: choice
options:
- all
- airvpn
- cyberghost
- expressvpn
- fastestvpn
- giganews
- hidemyass
- ipvanish
- ivpn
- mullvad
- nordvpn
- perfect privacy
- privado
- private internet access
- privatevpn
- protonvpn
- purevpn
- slickvpn
- surfshark
- torguard
- vpnsecure
- vpn unlimited
- vyprvpn
- windscribe
schedule:
- cron: "11 3 1 */2 *" # Run at 03:11 on the 1st of every 2nd month
jobs:
update-servers-list:
if: github.repository == 'passteque/gluetun'
runs-on: ubuntu-latest
permissions:
actions: read
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
- name: Update servers list
run: |
SELECTED_PROVIDER="${{ github.event.inputs.provider || 'all' }}"
if [ "$SELECTED_PROVIDER" = "all" ]; then
FLAGS="-all"
else
FLAGS="-providers $SELECTED_PROVIDER"
fi
go run ./cmd/gluetun/main.go update $FLAGS \
-maintainer \
-proton-email "${{ secrets.PROTON_EMAIL }}" \
-proton-password "${{ secrets.PROTON_PASSWORD }}"
- name: Check for changes
run: |
if git diff --exit-code internal/storage/servers.json >/dev/null; then
echo "Error: internal/storage/servers.json was not modified."
exit 1
fi
- name: Check no other file changes
run: |
if ! git diff --exit-code --quiet ':!internal/storage/servers.json'; then
echo "Error: Unexpected changes detected in files other than servers.json"
git status --short
exit 1
fi
- name: Create Pull Request
id: createpr
uses: peter-evans/create-pull-request@v8
with:
branch-suffix: timestamp
branch: bot/update-servers-list
base: master
delete-branch: true
title: "feat(providers/${{ github.event.inputs.provider || 'all' }}): servers data update"
body: |
This PR was automatically generated by the [Update servers list](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) workflow run.
# - name: Merge Pull Request
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# run: |
# gh pr merge ${{ steps.createpr.outputs.pull-request-number }} --auto -m -d
+10 -10
View File
@@ -12,10 +12,11 @@ require (
github.com/klauspost/compress v1.18.4
github.com/klauspost/pgzip v1.2.6
github.com/mdlayher/genetlink v1.3.2
github.com/mdlayher/netlink v1.9.0
github.com/mdlayher/netlink v1.11.2
github.com/pelletier/go-toml/v2 v2.2.4
github.com/qdm12/dns/v2 v2.0.0-rc9.0.20260421173011-9de8e7fdbe3a
github.com/qdm12/gluetun-servers v0.1.0
github.com/qdm12/goservices v0.1.1-0.20251104135713-6bee97bd4978
github.com/qdm12/gosettings v0.4.4
github.com/qdm12/goshutdown v0.3.0
github.com/qdm12/gosplash v0.2.1-0.20260305164749-b713de4fee6c
@@ -26,10 +27,9 @@ require (
github.com/ti-mo/netfilter v0.5.3
github.com/ulikunitz/xz v0.5.15
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
golang.org/x/net v0.51.0
golang.org/x/sys v0.42.0
golang.org/x/text v0.35.0
golang.org/x/net v0.55.0
golang.org/x/sys v0.45.0
golang.org/x/text v0.37.0
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
gopkg.in/ini.v1 v1.67.1
@@ -46,7 +46,7 @@ require (
github.com/google/go-cmp v0.7.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mdlayher/socket v0.5.1 // indirect
github.com/mdlayher/socket v0.6.0 // indirect
github.com/miekg/dns v1.1.62 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect
@@ -55,12 +55,12 @@ require (
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/qdm12/goservices v0.1.1-0.20251104135713-6bee97bd4978 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/mod v0.33.0 // indirect
golang.org/x/crypto v0.51.0 // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/mod v0.35.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/tools v0.42.0 // indirect
golang.org/x/tools v0.44.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
+16 -16
View File
@@ -50,10 +50,10 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o=
github.com/mdlayher/netlink v1.9.0 h1:G8+GLq2x3v4D4MVIqDdNUhTUC7TKiCy/6MDkmItfKco=
github.com/mdlayher/netlink v1.9.0/go.mod h1:YBnl5BXsCoRuwBjKKlZ+aYmEoq0r12FDA/3JC+94KDg=
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
github.com/mdlayher/netlink v1.11.2 h1:HKh2jqe+omdSWcQ88nrT7INE61B0NXfiSPFdgL4YbNI=
github.com/mdlayher/netlink v1.11.2/go.mod h1:uT2Yc/QLaZubzDpZIBi9d4GoeLwtp3x1AMeqSRrK2sA=
github.com/mdlayher/socket v0.6.0 h1:ScZPaAGyO1icQnbFrhPM8mnXyMu9qukC1K4ZoM2IQKU=
github.com/mdlayher/socket v0.6.0/go.mod h1:q7vozUAnxSqnjHc12Fik5yUKIzfZ8ITCfMkhOtE9z18=
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
@@ -120,15 +120,15 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/mod v0.4.2/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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@@ -136,8 +136,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -156,8 +156,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -167,8 +167,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -176,8 +176,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
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-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1,10 +1,10 @@
package settings
import (
"maps"
"slices"
"github.com/qdm12/gosettings/reader"
"golang.org/x/exp/maps"
)
func readObsolete(r *reader.Reader) (warnings []string) {
@@ -17,7 +17,7 @@ func readObsolete(r *reader.Reader) (warnings []string) {
"DNS_KEEP_NAMESERVER": "DNS_KEEP_NAMESERVER is obsolete because you should use the built-in server which now " +
"forwards local names to private DNS resolvers found in /etc/resolv.conf at container start",
}
sortedKeys := maps.Keys(keyToMessage)
sortedKeys := slices.Collect(maps.Keys(keyToMessage))
slices.Sort(sortedKeys)
warnings = make([]string, 0, len(keyToMessage))
for _, key := range sortedKeys {
@@ -95,7 +95,7 @@ func (p PortForwarding) Validate(vpnProvider string) (err error) {
return errors.New("port forwarding password is empty")
}
case providers.Protonvpn:
const maxPortsCount = 4
const maxPortsCount = 5
if p.PortsCount > maxPortsCount {
return fmt.Errorf("ports count too high: %d > %d", p.PortsCount, maxPortsCount)
}
+16 -18
View File
@@ -1,7 +1,6 @@
package iptables
import (
"bufio"
"context"
"fmt"
"os/exec"
@@ -97,25 +96,24 @@ func saveData(ctx context.Context, binary string) (data string, err error) {
}
return "", fmt.Errorf("running %s-save: %w", binary, err)
}
err = checkData(string(output))
if err != nil {
return "", fmt.Errorf("checking saved data: %w", err)
}
return string(output), nil
return filterData(output)
}
func checkData(data string) error {
scanner := bufio.NewScanner(strings.NewReader(data))
i := 0
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "[unsupported") {
return fmt.Errorf("unsupported revision marker found in line %d: %s", i+1, line)
func filterData(cmdOutput []byte) (filtered string, err error) {
lines := strings.Split(string(cmdOutput), "\n")
filteredLines := make([]string, 0, len(lines))
for _, line := range lines {
switch {
case strings.HasPrefix(line, ":DOCKER_OUTPUT"),
strings.HasPrefix(line, ":DOCKER_POSTROUTING"),
strings.HasPrefix(line, "-A DOCKER_OUTPUT"),
strings.HasPrefix(line, "-A DOCKER_POSTROUTING"):
// Do not touch (aka save and restore) NAT rules added by Docker
continue
case strings.Contains(line, "[unsupported revision]"):
return "", fmt.Errorf("mismatch container iptables-save and kernel: %s", line)
}
i++
filteredLines = append(filteredLines, line)
}
if scanner.Err() != nil {
return fmt.Errorf("scanning data: %w", scanner.Err())
}
return nil
return strings.Join(filteredLines, "\n"), nil
}
+1 -1
View File
@@ -278,7 +278,6 @@ func (c *Config) RedirectPort(ctx context.Context, intf string,
appendOrDelete(remove), interfaceFlag, destinationPort),
})
if err != nil {
restore(ctx) // just in case
errMessage := err.Error()
if strings.Contains(errMessage, "can't initialize ip6tables table `nat': Table does not exist") {
if !remove {
@@ -286,6 +285,7 @@ func (c *Config) RedirectPort(ctx context.Context, intf string,
}
return nil
}
restore(ctx)
return fmt.Errorf("redirecting IPv6 source port %d to destination port %d on interface %s: %w",
sourcePort, destinationPort, intf, err)
}
+5 -1
View File
@@ -15,7 +15,11 @@ func runCommand(ctx context.Context, cmder Cmder, logger Logger,
}
portsString := strings.Join(portStrings, ",")
commandString := strings.ReplaceAll(commandTemplate, "{{PORTS}}", portsString)
commandString = strings.ReplaceAll(commandString, "{{PORT}}", portStrings[0])
var firstPort string
if len(portStrings) > 0 {
firstPort = portStrings[0]
}
commandString = strings.ReplaceAll(commandString, "{{PORT}}", firstPort)
commandString = strings.ReplaceAll(commandString, "{{VPN_INTERFACE}}", vpnInterface)
return cmder.RunAndLog(ctx, commandString, logger)
}
+5 -2
View File
@@ -2,6 +2,7 @@ package service
import (
"context"
"errors"
"fmt"
"net/http"
"slices"
@@ -59,6 +60,10 @@ func (s *Service) SetPortsForwarded(ctx context.Context, ports []uint16) (err er
s.portMutex.Lock()
defer s.portMutex.Unlock()
if s.settings.PortForwarder != nil {
return errors.New("setting port forwarded at runtime is not supported with internally running port forwarding code")
}
slices.Sort(ports)
if slices.Equal(s.ports, ports) {
return nil
@@ -78,7 +83,5 @@ func (s *Service) SetPortsForwarded(ctx context.Context, ports []uint16) (err er
return fmt.Errorf("handling new ports: %w", err)
}
s.logger.Info("updated: " + portsToString(s.ports))
return nil
}
+1 -1
View File
@@ -88,7 +88,7 @@ func (s *Settings) Validate(forStartup bool) (err error) {
return errors.New("password not set")
}
case providers.Protonvpn:
const maxPortsCount = 4
const maxPortsCount = 5
if s.PortsCount > maxPortsCount {
return fmt.Errorf("ports count too high: %d > %d", s.PortsCount, maxPortsCount)
}
+4 -10
View File
@@ -92,13 +92,9 @@ func (s *Service) onNewPorts(ctx context.Context, internalToExternalPorts map[ui
s.logger.Info(portPairsToString(internalToExternalPorts))
externalPorts := slices.Collect(maps.Values(internalToExternalPorts))
autoRedirectionNeeded := false
externalToInternalPorts := make(map[uint16]uint16, len(internalToExternalPorts))
for internal, external := range internalToExternalPorts {
externalToInternalPorts[external] = internal
if internal != external {
autoRedirectionNeeded = true
}
}
slices.Sort(externalPorts)
userRedirectionEnabled := !slices.Equal(s.settings.ListeningPorts, []uint16{0})
@@ -109,23 +105,21 @@ func (s *Service) onNewPorts(ctx context.Context, internalToExternalPorts map[ui
return fmt.Errorf("allowing port in firewall: %w", err)
}
var sourcePort, destinationPort uint16
var destinationPort uint16
switch {
case userRedirectionEnabled: // precedence over auto redirection
sourcePort = externalToInternalPorts[port]
destinationPort = s.settings.ListeningPorts[i]
case autoRedirectionNeeded:
sourcePort = externalToInternalPorts[port]
case port != internalPort: // auto redirection needed, source and destination ports differ
destinationPort = port
default:
// No redirection needed, source and destination ports are the same.
continue
}
err = s.portAllower.RedirectPort(ctx, s.settings.Interface, sourcePort, destinationPort)
err = s.portAllower.RedirectPort(ctx, s.settings.Interface, internalPort, destinationPort)
if err != nil {
return fmt.Errorf("redirecting port %d to %d in firewall: %w",
sourcePort, destinationPort, err)
internalPort, destinationPort, err)
}
}
+68 -61
View File
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"maps"
"net/netip"
"strings"
"time"
@@ -12,14 +13,14 @@ import (
"github.com/qdm12/gluetun/internal/provider/utils"
)
const nonSymmetricPortStart uint16 = 56789
// PortForward obtains a VPN server side port forwarded from ProtonVPN gateway.
func (p *Provider) PortForward(ctx context.Context, objects utils.PortForwardObjects) (
internalToExternalPorts map[uint16]uint16, err error,
) {
if !objects.CanPortForward {
return nil, errors.New("server does not support port forwarding")
} else if objects.PortsCount == 0 {
return nil, nil //nolint:nilnil
}
client := natpmp.New()
@@ -39,38 +40,75 @@ func (p *Provider) PortForward(ctx context.Context, objects utils.PortForwardObj
logger := objects.Logger
logger.Debug("gateway external IPv4 address is " + externalIPv4Address.String())
const externalPort = 0
const lifetime = 60 * time.Second
p.internalToExternalPorts = make(map[uint16]uint16, objects.PortsCount)
for i := range objects.PortsCount {
internalPort := nonSymmetricPortStart + i
protoToInternalPort := map[string]uint16{
"udp": 0,
"tcp": 0,
}
protoToExternalPort := maps.Clone(protoToInternalPort)
for protocol := range protoToExternalPort {
_, assignedInternalPort, assignedExternalPort, assignedLifetime, err := client.AddPortMapping(
ctx, objects.Gateway, protocol, internalPort, externalPort, lifetime)
if err != nil {
return nil, fmt.Errorf("adding %d/%d %s port mapping: %w",
i+1, objects.PortsCount, strings.ToUpper(protocol), err)
}
checkLifetime(logger, strings.ToUpper(protocol), lifetime, assignedLifetime)
checkInternalPort(logger, internalPort, assignedInternalPort)
protoToInternalPort[protocol] = assignedInternalPort
protoToExternalPort[protocol] = assignedExternalPort
}
const lifetime = 60 * time.Second
checkInternalPorts(logger, protoToInternalPort["udp"], protoToInternalPort["tcp"])
checkExternalPorts(logger, protoToExternalPort["udp"], protoToExternalPort["tcp"])
p.internalToExternalPorts[protoToInternalPort["tcp"]] = protoToExternalPort["tcp"]
// Only one port can be a symmetric mapping
const internalPort, externalPort = 0, 1
_, assignedExternalPort, err := addPortMappingTCPUDP(ctx,
client, logger, objects.Gateway, internalPort, externalPort, lifetime)
// Note the returned assignedInternalPort is always 0 in this case
if err != nil {
return nil, fmt.Errorf("adding first port mapping: %w", err)
}
p.internalToExternalPorts[assignedExternalPort] = assignedExternalPort
// Extra ports must be non-symmetric, meaning that the internal port is
// different from the external port.
const nonSymmetricPortStart = uint16(56789)
nonSymmetricPortStartMinusOne := nonSymmetricPortStart - 1
if _, ok := p.internalToExternalPorts[nonSymmetricPortStart]; ok {
nonSymmetricPortStartMinusOne++
}
for i := uint16(1); i < objects.PortsCount; i++ {
internalPort := nonSymmetricPortStartMinusOne + i
const externalPort = 0
assignedInternalPort, assignedExternalPort, err := addPortMappingTCPUDP(ctx,
client, logger, objects.Gateway, internalPort, externalPort, lifetime)
if err != nil {
return nil, fmt.Errorf("adding %d/%d port mapping: %w", i+1, objects.PortsCount, err)
}
p.internalToExternalPorts[assignedInternalPort] = assignedExternalPort
}
return maps.Clone(p.internalToExternalPorts), nil
}
func addPortMappingTCPUDP(ctx context.Context, client *natpmp.Client, logger utils.Logger,
gateway netip.Addr, internalPort, externalPort uint16, lifetime time.Duration,
) (assignedInternalPort, assignedExternalPort uint16, err error) {
var assignedLifetime time.Duration
protocolToExternalPort := map[string]uint16{
"tcp": 0,
"udp": 0,
}
for _, protocol := range [...]string{"udp", "tcp"} {
protocolStr := strings.ToUpper(protocol)
_, assignedInternalPort, assignedExternalPort, assignedLifetime, err = client.AddPortMapping(
ctx, gateway, protocol, internalPort, externalPort, lifetime)
if err != nil {
return 0, 0, fmt.Errorf("adding %s port mapping: %w", protocolStr, err)
}
protocolToExternalPort[protocol] = assignedExternalPort
checkLifetime(logger, protocolStr, lifetime, assignedLifetime)
if internalPort != assignedInternalPort {
return 0, 0, fmt.Errorf("%s internal port requested as %d but received %d",
protocolStr, internalPort, assignedInternalPort)
} else if externalPort != 0 && externalPort != 1 && externalPort != assignedExternalPort {
return 0, 0, fmt.Errorf("%s external port requested as %d but received %d",
protocolStr, externalPort, assignedExternalPort)
}
}
if protocolToExternalPort["tcp"] != protocolToExternalPort["udp"] {
return 0, 0, fmt.Errorf("TCP and UDP external ports differ: %d and %d",
protocolToExternalPort["tcp"], protocolToExternalPort["udp"])
}
return assignedInternalPort, assignedExternalPort, nil
}
func checkLifetime(logger utils.Logger, protocol string,
requested, actual time.Duration,
) {
@@ -81,27 +119,6 @@ func checkLifetime(logger utils.Logger, protocol string,
}
}
func checkInternalPort(logger utils.Logger, sent, received uint16) {
if sent != received {
logger.Warn(fmt.Sprintf("internal port assigned %d differs from requested internal port %d",
sent, received))
}
}
func checkInternalPorts(logger utils.Logger, udpPort, tcpPort uint16) {
if udpPort != tcpPort {
logger.Warn(fmt.Sprintf("UDP internal port %d differs from TCP internal port %d",
udpPort, tcpPort))
}
}
func checkExternalPorts(logger utils.Logger, udpPort, tcpPort uint16) {
if udpPort != tcpPort {
logger.Warn(fmt.Sprintf("UDP external port %d differs from TCP external port %d",
udpPort, tcpPort))
}
}
func (p *Provider) KeepPortForward(ctx context.Context,
objects utils.PortForwardObjects,
) (err error) {
@@ -117,22 +134,12 @@ func (p *Provider) KeepPortForward(ctx context.Context,
}
objects.Logger.Debug("refreshing forwarded ports since 45 seconds have elapsed")
networkProtocols := [...]string{"udp", "tcp"}
const lifetime = 60 * time.Second
for internalPort, externalPort := range p.internalToExternalPorts {
for _, networkProtocol := range networkProtocols {
_, assignedInternalPort, assignedExternalPort, assignedLiftetime, err := client.AddPortMapping(
ctx, objects.Gateway, networkProtocol, internalPort, externalPort, lifetime)
if err != nil {
return fmt.Errorf("adding port mapping: %w", err)
}
checkLifetime(logger, networkProtocol, lifetime, assignedLiftetime)
if externalPort != assignedExternalPort {
return fmt.Errorf("external port changed from %d to %d", externalPort, assignedExternalPort)
} else if internalPort != assignedInternalPort {
return fmt.Errorf("internal port changed from %d (for external port %d) to %d",
internalPort, externalPort, assignedInternalPort)
}
_, _, err := addPortMappingTCPUDP(ctx, client, logger, objects.Gateway, internalPort, externalPort, lifetime)
if err != nil {
return fmt.Errorf("refreshing port mapping for internal port %d and external port %d: %w",
internalPort, externalPort, err)
}
objects.Logger.Debug(fmt.Sprintf("port forwarded %d maintained", externalPort))
}
-6
View File
@@ -1,19 +1,13 @@
# Maintenance
- Change `Run` methods to `Start`+`Stop`, returning channels rather than injecting them
- Go 1.18
- gofumpt
- Use netip
- Split servers.json
- Common slice of Wireguard providers in config settings
- DNS block lists as LFS and built in image
- Add HTTP server v3 as json rpc
- Use `github.com/qdm12/ddns-updater/pkg/publicip`
- Windows and Darwin development support
## Features
- Authentication with the control server
- Get announcement from Github file
- Support multiple connections in custom ovpn
- Automate IPv6 detection for OpenVPN