Open qdm12 opened 4 years ago
Ouch, 2020. Please. Maybe add Dante support instead?
@alpe12 you can plug in any socks server container for now (dante or other server implementing the socks protocol).
Right now a SOCKS5 proxy built-in is not a priority since there is already an http proxy and a shadowsocks server (encrypted socks5)
To anyone who might be interested. This is what I'm using now to get socks5:
version: "3"
services:
gluetun:
image: qmcgaw/gluetun
container_name: gluetun
cap_add:
- NET_ADMIN
ports:
- 8888:8888/tcp # HTTP proxy
- 1080:1080 #socks5 proxy
volumes:
- ./config:/gluetun
environment:
- VPNSP=nordvpn
- VPN_TYPE=openvpn
- OPENVPN_USER=>USER<
- OPENVPN_PASSWORD=>PASSWORD<
- REGION=Brazil
- OPENVPN_PROTOCOL=udp
- OPENVPN_FLAGS=--auth-nocache
- TZ=America/Sao_Paulo
- HTTPPROXY=on
socks5:
image: serjs/go-socks5-proxy
depends_on:
- gluetun
network_mode: "service:gluetun"
If anybody still needs this, I forked gluetun and added support for socks5.
https://github.com/x0st/gluetun
docker run -it --rm --cap-add=NET_ADMIN -e VPN_SERVICE_PROVIDER=... -e OPENVPN_USER=... -e OPENVPN_PASSWORD=... -e SOCKS5=on -e SOCKS5_PASSWORD=123 -p 1080:1080 gluetun
You can pass any username, only password will be checked.
curl -X GET https://github.com -x socks5://user:123@127.0.0.1:1080
What's the image name for this new image with socks5 support ?
I'm using docker compose and the image "qmcgaw/gluetun". I tried with "x0st/gluetun" and docker returns an error.
@x0st Well done, Those changes look pretty good. Have you considered opening a PR to merge those changes back to Gluetun?
What's the image name for this new image with socks5 support ?
I'm using docker compose and the image "qmcgaw/gluetun". I tried with "x0st/gluetun" and docker returns an error.
I didn't publish the image. Just clone the repo and build it yourself :)
docker build -t gluetun-socks5 .
docker run ... gluetun-socks5
@x0st Well done, Those changes look pretty good. Have you considered opening a PR to merge those changes back to Gluetun?
The socks5 lib is not mine, I just found it on github. I use it in all my projects, but I am not sure if it's any good. I don't want to merge into open source something I can't guarantee will work well :)
I went a bit crazy a few days ago and I have a working socks5 server (no gssapi for now) locally. But it's blocked by various maintenance bits I'm working on (#1742 first, then a 'loops rework'), so probably a few more weeks, but it'll come ultimately. Although really, just plugin another socks5 container through Gluetun and it should do the job just fine I think.
The problem is that sabnzbd won't support http proxy according to their dev. They do support socks5. So this means only option is to use the network container bridge which isn't working for me and would be a lot easier just to use socks5. Thought I should just raise that as sabnzbd is a common component.
@x0st Thanks for your effort, but getting build error:
internal/socks5/loop.go:6: File is not `gci`-ed with --skip-generated -s standard -s default (gci)
"github.com/qdm12/gluetun/internal/socks5/lib"
internal/socks5/loop.go:13: File is not `gci`-ed with --skip-generated -s standard -s default (gci)
"github.com/qdm12/gluetun/internal/models"
cmd/gluetun/main.go:7: File is not `gci`-ed with --skip-generated -s standard -s default (gci)
"github.com/qdm12/gluetun/internal/socks5"
internal/socks5/lib/request.go:180:1: cognitive complexity 53 of func `(*Request).transformUDP` is high (> 30) (gocognit)
func (r *Request) transformUDP() {
^
internal/socks5/lib/request.go:264:13: appendAssign: append result not assigned to the same slice (gocritic)
body := append(exchange.headerData, buf[:ln]...)
^
internal/socks5/lib/request.go:56:25: commentFormatting: put a space between `//` and comment text (gocritic)
if !r.tcpGram.viaUDP { //tcp
^
internal/socks5/lib/request.go:69:4: commentFormatting: put a space between `//` and comment text (gocritic)
//IPv4, len is 4
^
internal/socks5/lib/request.go:73:4: commentFormatting: put a space between `//` and comment text (gocritic)
//IPv6, len is 16
^
internal/socks5/loop.go:9: File is not `goimports`-ed (goimports)
"time"
cmd/gluetun/main.go:14: File is not `goimports`-ed (goimports)
_ "time/tzdata"
internal/socks5/lib/request.go:57: line is 137 characters (lll)
if conn, err := net.DialTimeout("tcp", r.tcpGram.networkString(), time.Second*time.Duration(r.server.writeTimeoutSecond)); err != nil {
cmd/gluetun/main.go:452: line is 136 characters (lll)
socks5Handler, socks5Ctx, socks5Done := goshutdown.NewGoRoutineHandler("socks5 proxy", goroutine.OptionTimeout(defaultShutdownTimeout))
cmd/gluetun/main.go:457: line is 156 characters (lll)
shadowsocksHandler, shadowsocksCtx, shadowsocksDone := goshutdown.NewGoRoutineHandler("shadowsocks proxy", goroutine.OptionTimeout(defaultShutdownTimeout))
internal/socks5/lib/request.go:154:11: superfluous-else: if block ends with a break statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
} else {
_ = r.ClientConn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(r.server.writeTimeoutSecond)))
if _, err := r.ClientConn.Write(buf[:ln]); err != nil {
break
}
}
internal/socks5/lib/request.go:168:10: superfluous-else: if block ends with a break statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
} else {
_ = r.RemoteConn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(r.server.writeTimeoutSecond)))
if _, err := r.RemoteConn.Write(buf[:ln]); err != nil {
break
}
}
internal/socks5/lib/protocol.go:11:2: var-naming: const CmdUdpAssociate should be CmdUDPAssociate (revive)
CmdUdpAssociate byte = 0x03
^
internal/socks5/lib/server.go:10:18: unexported-return: exported func NewServer returns unexported type *lib.server, which can be annoying to use (revive)
func NewServer() *server {
^
internal/socks5/lib/udp_protocol.go:54:10: indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
} else {
p.ip = ipAddr.IP
p.port = int(binary.BigEndian.Uint16(buf[5+domainLen : 5+domainLen+2]))
}
internal/socks5/lib/server.go:54:9: indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
} else {
return nil
}
internal/socks5/lib/tcp_protocol.go:41:9: indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (revive)
} else {
p.cmd = cmd
p.atyp = atyp
}
internal/socks5/lib/tcp_protocol.go:55:10: indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
} else {
p.ip = addr.IP
}
internal/socks5/lib/tcp_protocol.go:95:9: indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (revive)
} else {
return []byte{Version, MethodNoAuth}, nil
}
internal/socks5/lib/tcp_protocol.go:142:9: indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (revive)
} else {
return []byte{0x01, 0x00}, nil
}
internal/socks5/lib/request.go:214: unnecessary trailing newline (whitespace)
}()
internal/socks5/lib/request.go:251: unnecessary trailing newline (whitespace)
} else {
internal/socks5/lib/request.go:63:4: type assertion must be checked (forcetypeassert)
r.RemoteConn = conn.(*net.TCPConn)
^
internal/socks5/lib/request.go:66:3: type assertion must be checked (forcetypeassert)
bindIP := r.ClientConn.LocalAddr().(*net.TCPAddr).IP
^
internal/socks5/lib/request.go:79:50: type assertion must be checked (forcetypeassert)
binary.BigEndian.PutUint16(portByte[:], uint16(r.ClientConn.LocalAddr().(*net.TCPAddr).Port))
^
internal/socks5/lib/server.go:50:10: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"server is not running\")" (goerr113)
return errors.New("server is not running")
^
internal/socks5/lib/tcp_protocol.go:66:11: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"remote address error\")" (goerr113)
return errors.New("remote address error")
^
internal/socks5/lib/tcp_protocol.go:86:39: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"unsupported socks version\")" (goerr113)
return []byte{Version, MethodNone}, errors.New("unsupported socks version")
^
internal/socks5/lib/tcp_protocol.go:115:30: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"unsupported auth version or username is empty\")" (goerr113)
return []byte{0x01, 0x01}, errors.New("unsupported auth version or username is empty")
^
internal/socks5/lib/tcp_protocol.go:131:30: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"password is empty\")" (goerr113)
return []byte{0x01, 0x01}, errors.New("password is empty")
^
internal/socks5/lib/tcp_protocol.go:141:30: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"username or password invalid\")" (goerr113)
return []byte{0x01, 0x01}, errors.New("username or password invalid")
^
internal/socks5/lib/tcp_protocol.go:159:9: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"unsupported socks version\")" (goerr113)
err = errors.New("unsupported socks version")
^
internal/socks5/lib/tcp_protocol.go:164:9: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"unsupported CMD\")" (goerr113)
err = errors.New("unsupported CMD")
^
internal/socks5/lib/tcp_protocol.go:186:10: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"length of domain is zero\")" (goerr113)
err = errors.New("length of domain is zero")
^
internal/socks5/lib/tcp_protocol.go:202:9: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"unsupported ATYP\")" (goerr113)
err = errors.New("unsupported ATYP")
^
internal/socks5/lib/udp_exchange.go:24:15: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"UDPExchange is expired\")" (goerr113)
return nil, errors.New("UDPExchange is expired")
^
internal/socks5/lib/udp_protocol.go:28:20: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"fail\")" (goerr113)
return nil, nil, errors.New("fail")
^
internal/socks5/lib/udp_protocol.go:37:21: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"header is too short for IPv4\")" (goerr113)
return nil, nil, errors.New("header is too short for IPv4")
^
internal/socks5/lib/udp_protocol.go:45:21: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"header is too short for domain\")" (goerr113)
return nil, nil, errors.New("header is too short for domain")
^
internal/socks5/lib/udp_protocol.go:49:21: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"header is too short for domain\")" (goerr113)
return nil, nil, errors.New("header is too short for domain")
^
internal/socks5/lib/udp_protocol.go:53:21: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"can't resolve domain:\" + p.domain)" (goerr113)
return nil, nil, errors.New("can't resolve domain:" + p.domain)
^
internal/socks5/lib/udp_protocol.go:62:21: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"header is too short for IPv6\")" (goerr113)
return nil, nil, errors.New("header is too short for IPv6")
^
internal/socks5/lib/udp_protocol.go:69:20: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"unsupported atyp\")" (goerr113)
return nil, nil, errors.New("unsupported atyp")
^
internal/socks5/lib/request.go:67:26: mnd: Magic number: 22, in <argument> detected (gomnd)
res := make([]byte, 0, 22)
^
internal/socks5/lib/request.go:106:26: mnd: Magic number: 22, in <argument> detected (gomnd)
res := make([]byte, 0, 22)
^
internal/socks5/lib/request.go:149:23: mnd: Magic number: 1024, in <argument> detected (gomnd)
buf := make([]byte, 1024*8)
^
internal/socks5/lib/request.go:163:22: mnd: Magic number: 1024, in <argument> detected (gomnd)
buf := make([]byte, 1024*8)
^
internal/socks5/lib/request.go:202:36: mnd: Magic number: 2, in <argument> detected (gomnd)
case <-time.After(time.Second * 2):
^
internal/socks5/lib/request.go:217:22: mnd: Magic number: 65535, in <argument> detected (gomnd)
buf := make([]byte, 65535)
^
internal/socks5/lib/request.go:237:88: mnd: Magic number: 60, in <argument> detected (gomnd)
r.udpGram.UDPExchangeMap[r.udpGram.remoteAddr().String()] = NewUDPExchange(header, 60)
^
internal/socks5/lib/request.go:253:20: mnd: Magic number: 60, in <argument> detected (gomnd)
exchange.Delay(60)
^
internal/socks5/lib/request.go:265:20: mnd: Magic number: 60, in <argument> detected (gomnd)
exchange.Delay(60)
^
internal/socks5/lib/tcp_protocol.go:78:33: mnd: Magic number: 2, in <argument> detected (gomnd)
if buf, err := p.readBuf(conn, 2); err != nil {
^
internal/socks5/lib/tcp_protocol.go:107:33: mnd: Magic number: 2, in <argument> detected (gomnd)
if buf, err := p.readBuf(conn, 2); err != nil {
^
internal/socks5/lib/tcp_protocol.go:148:32: mnd: Magic number: 4, in <argument> detected (gomnd)
if buf, er := p.readBuf(conn, 4); er != nil {
^
internal/socks5/lib/tcp_protocol.go:171:36: mnd: Magic number: 4, in <argument> detected (gomnd)
addrBytes, err = p.readBuf(conn, 4)
^
internal/socks5/lib/tcp_protocol.go:196:36: mnd: Magic number: 16, in <argument> detected (gomnd)
addrBytes, err = p.readBuf(conn, 16)
^
internal/socks5/lib/tcp_protocol.go:226:57: mnd: Magic number: 10, in <argument> detected (gomnd)
_ = conn.SetReadDeadline(time.Now().Add(time.Second * 10))
^
internal/socks5/lib/tcp_protocol.go:237:58: mnd: Magic number: 10, in <argument> detected (gomnd)
_ = conn.SetWriteDeadline(time.Now().Add(time.Second * 10))
^
internal/socks5/lib/udp_protocol.go:36:17: mnd: Magic number: 10, in <condition> detected (gomnd)
if len(buf) < 10 {
^
internal/socks5/lib/udp_protocol.go:44:17: mnd: Magic number: 5, in <condition> detected (gomnd)
if len(buf) < 5 {
^
internal/socks5/lib/udp_protocol.go:61:17: mnd: Magic number: 22, in <condition> detected (gomnd)
if len(buf) < 22 {
^
internal/socks5/lib/server.go:12:23: mnd: Magic number: 600, in <assign> detected (gomnd)
readTimeoutSecond: 600,
^
internal/socks5/lib/server.go:13:23: mnd: Magic number: 30, in <assign> detected (gomnd)
writeTimeoutSecond: 30,
^
internal/socks5/lib/tcp_protocol.go:236:5: S1009: should omit nil check; len() for []byte is defined as zero (gosimple)
if data != nil && len(data) > 0 {
^
internal/socks5/lib/request.go:58:4: S1038: should use log.Printf(...) instead of log.Println(fmt.Sprintf(...)) (gosimple)
log.Println(fmt.Sprintf("dial target dest: %v", err))
^
internal/socks5/lib/tcp_protocol.go:151:3: naked return in func `getAddr` with 70 lines of code (nakedret)
return
^
internal/socks5/lib/tcp_protocol.go:161:3: naked return in func `getAddr` with 70 lines of code (nakedret)
return
^
internal/socks5/lib/tcp_protocol.go:166:3: naked return in func `getAddr` with 70 lines of code (nakedret)
return
^
internal/socks5/lib/server.go:84:2: error is not nil (line 75) but it returns nil (nilerr)
return nil
^
ERROR: Service 'gluetun' failed to build : The command '/bin/sh -c golangci-lint run --timeout=10m' returned a non-zero code: 1
How is it going? @qdm12 Your weeks are longer than mine. 🧐😋
Is there any news about socks5 in Gluetun?
There is a socks proxy in binhex/arch-delugevpn though I haven't exactly gotten it to work yet. But it's socks5. So maybe try that since it isn't a priority here.
As another workaround option, the official tailscale docker image has a socks5 server too; https://tailscale.com/kb/1282/docker#ts_socks5_server. If you don't use tailscale, the rest of this answer might not be very useful for you.
I was already running a tailscale container, so it was pretty easy to just put tailscale behind network_mode: service:gluetun
:
gluetun:
image: qmcgaw/gluetun
container_name: gluetun
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
ports:
- 1080:1080 #socks5 proxy
tailscale:
image: tailscale/tailscale
network_mode: "service:gluetun"
environment:
- TS_SOCKS5_SERVER=:1080
(I've removed much of the config for both gluetun/tailscale in this example)
Now I can just point my socks5-client (e.g firefox) at my_gluetun_proxy_ip:1080
and all traffic gets funneled through my gluetun container/vpn.
In my full setup, I also have the tailscale-container act as an exit-node in my tailnet, which means by simply changing my phone's exit-node in the TS app, it'll funnel all traffic through gluetun
SOCKS5 plaintext proxy in Go
When you don't want to use a Shadowsocks client and don't care about encryption (i.e. in your LAN) or because your browser is limited to SOCKS5 and doesn't have a Shadowsocks extension. Also for low power devices where encryption can have a performance penalty.
This should be added in Go and should be relatively straight forward to implement.