Compare commits

..
10 Commits
Author SHA1 Message Date
Hermes 13b66aeea6 docs: add LAN socks5 compose deployment 2026-05-29 11:55:16 +08:00
Sergey Bogatyrets 3c8d31b9e9 Merge pull request #83 from serjs/dependabot/go_modules/golang.org/x/net-0.46.0
Bump golang.org/x/net from 0.38.0 to 0.46.0
2025-10-09 18:26:29 +03:00
dependabot[bot] 38627992d9 Bump golang.org/x/net from 0.38.0 to 0.46.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.38.0 to 0.46.0.
- [Commits](https://github.com/golang/net/compare/v0.38.0...v0.46.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.46.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-09 08:06:55 +00:00
serj f1cd03ff76 Update changelog 2025-10-07 13:10:58 +03:00
serj 1cdfcea4ad Add new REQUIRE_AUTH config parameter, to disable running socks5 server without auth by default 2025-09-25 14:40:50 +03:00
Sergey Bogatyrets 1f5e188a14 Create LICENSE 2025-09-23 21:58:56 +03:00
serj 14bdfbb6a2 Add PROXY_LISTEN_IP env variable to set listen interface 2025-09-23 21:48:10 +03:00
serj f0eb61962e Moving armon/go-socks5 to forked repo with additional changes 2025-09-23 21:39:33 +03:00
serj 4298d972a9 Comment out IP limits for futhure vendoring refactoring 2025-09-23 19:05:44 +03:00
serj 7cabaad451 Bump golang version 2025-09-23 18:38:10 +03:00
17 changed files with 251 additions and 53 deletions
-3
View File
@@ -1,3 +0,0 @@
PROXY_USER=someuser
PROXY_PASSWORD=somepass
PROXY_PORT=1080
+9
View File
@@ -0,0 +1,9 @@
# Authentication is enabled by default. Change these before production use.
REQUIRE_AUTH=true
PROXY_USER=socksuser
PROXY_PASSWORD=change-me-please
PROXY_PORT=1080
# Optional filters. Empty means allow all.
ALLOWED_DEST_FQDN=
ALLOWED_IPS=
+3
View File
@@ -0,0 +1,3 @@
.env
*.log
.DS_Store
+14 -5
View File
@@ -3,12 +3,21 @@ All notable changes to this project will be documented in this file.
## [Unreleased - available on :latest tag for docker image]
### Changed
- Migrate to distroless docker image from scratch
-
### Added
- New ALLOWED_DEST_FQDN config env paramteter for filtering dest FQND based on regex patterns
- New SetIPWhitelist config env paramteter for setting whitelist set of ip addresses which allowed to use proxy connection
- Dependabot version updates automation
## [v0.0.4] - 2025-10-07
**Important:** :warning: Starting with this release, the proxy requires authentication by default. The `REQUIRE_AUTH` parameter is now set to `true` by default. Please refer to the documentation for details on this change.
### Changed
- Migrated to a distroless Docker image from scratch.
- Moved go-socks5 to a modified forked repository at https://github.com/serjs/go-socks5 as a dependency.
### Added
- Added `REQUIRE_AUTH` parameter with a default value of `true` to enforce authentication for the proxy.
- Added `ALLOWED_DEST_FQDN` config environment parameter for filtering destination FQDNs based on regex patterns.
- Added `SetIPWhitelist` config environment parameter for setting a whitelist of IP addresses allowed to use the proxy connection.
- Implemented Dependabot version updates automation.
## [v0.0.3] - 2021-07-07
### Added
+1 -1
View File
@@ -1,4 +1,4 @@
ARG GOLANG_VERSION="1.19.1"
ARG GOLANG_VERSION="1.25"
FROM golang:$GOLANG_VERSION-alpine as builder
RUN apk --no-cache add tzdata
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Sergey Bogatyrets
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+49
View File
@@ -0,0 +1,49 @@
# serjs/socks5-server LAN IP deployment
This folder contains compose examples for running `serjs/go-socks5-proxy`.
## Important finding on this Mac / OrbStack host
The Mac is on LAN `192.168.2.0/24` as `192.168.2.69`.
Docker is running inside OrbStack, whose Docker daemon network interface is `198.19.249.2/24`, not the physical `en0` LAN interface.
Because of that, Docker `macvlan`/`ipvlan` cannot attach directly to macOS `en0` from inside OrbStack. In testing, Compose can create a macvlan container with metadata IP `192.168.2.200`, but the Mac/LAN cannot ARP or connect to it (`192.168.2.200:1080` times out and ARP stays incomplete). So a true same-LAN container IP is normally feasible on a native Linux host with a real NIC, but not directly on this current macOS/OrbStack setup via only docker-compose.
## Recommended on this Mac: host-published port
Use `docker-compose.yml`. It runs the SOCKS5 server and publishes port `1080` on the Mac:
```bash
cp .env.example .env
# edit .env
# then:
docker compose up -d
```
Access from LAN:
```text
192.168.2.69:1080
```
## Real LAN IP version for a Linux host
Use `docker-compose.macvlan-linux.yml` on a Linux machine physically connected to `192.168.2.0/24`.
Before using it:
1. Make sure `192.168.2.200` is outside DHCP range or reserved for this container.
2. Replace `parent: eth0` with the Linux host NIC name.
3. Confirm the intended default gateway. In this chat BOSS asked for `192.168.2.1`, but this Mac's current default gateway is `192.168.2.8`.
Run:
```bash
docker compose -f docker-compose.macvlan-linux.yml up -d
```
Access:
```text
192.168.2.200:1080
```
+8 -10
View File
@@ -1,7 +1,6 @@
# go-socks5-proxy
![Latest tag from master branch](https://github.com/serjs/socks5-server/workflows/Latest%20tag%20from%20master%20branch/badge.svg)
![Release tag](https://github.com/serjs/socks5-server/workflows/Release%20tag/badge.svg)
Simple socks5 server using go-socks5 with authentication, allowed ips list and destination FQDNs filtering
@@ -11,16 +10,15 @@ Simple socks5 server using go-socks5 with authentication, allowed ips list and d
```docker run -d --name socks5 -p 1080:1080 -e PROXY_USER=<PROXY_USER> -e PROXY_PASSWORD=<PROXY_PASSWORD> serjs/go-socks5-proxy```
- Leave `PROXY_USER` and `PROXY_PASSWORD` empty for skip authentication options while running socks5 server, see example below
- Run docker container using specific container port and expose it to host port 1090
- Run docker container using specifit container port and expose it to host port 1090, without auth creds
```docker run -d --name socks5 -p 1090:9090 -e PROXY_PORT=9090 serjs/go-socks5-proxy```
```docker run -d --name socks5 -p 1090:9090 -e PROXY_USER=<PROXY_USER> -e PROXY_PASSWORD=<PROXY_PASSWORD> -e PROXY_PORT=9090 serjs/go-socks5-proxy```
# List of supported config parameters
|ENV variable|Type|Default|Description|
|------------|----|-------|-----------|
|REQUIRE_AUTH|String|true|Allow accepting socks5 connections without auth creds. Not recommended untill you use other protections mechanisms like Whitelists Subnets using Firewall or Proxy itself|
|PROXY_USER|String|EMPTY|Set proxy user (also required existed PROXY_PASS)|
|PROXY_PASSWORD|String|EMPTY|Set proxy password for auth, used with PROXY_USER|
|PROXY_PORT|String|1080|Set listen port for application inside docker container|
@@ -30,7 +28,7 @@ Simple socks5 server using go-socks5 with authentication, allowed ips list and d
# Build your own image:
`docker-compose -f docker-compose.build.yml up -d`\
Just don't forget to set parameters in the `.env` file.
Just don't forget to set parameters in the `.env` file (`cp .env.example .env)` and edit it with your config parameters
# Test running service
@@ -38,19 +36,19 @@ Assuming that you are using container on 1080 host docker port
## Without authentication
```curl --socks5 <docker host ip>:1080 https://ifcfg.co``` - result must show docker host ip (for bridged network)
```curl --socks5 <docker host ip>:1080 https://ipinfo.io``` - result must show docker host ip (for bridged network)
or
```docker run --rm curlimages/curl:7.65.3 -s --socks5 <docker host ip>:1080 https://ifcfg.co```
```docker run --rm curlimages/curl:7.65.3 -s --socks5 <docker host ip>:1080 https://ipinfo.io```
## With authentication
```curl --socks5 <docker host ip>:1080 -U <PROXY_USER>:<PROXY_PASSWORD> http://ifcfg.co```
```curl --socks5 <docker host ip>:1080 -U <PROXY_USER>:<PROXY_PASSWORD> https://ipinfo.io```
or
```docker run --rm curlimages/curl:7.65.3 -s --socks5 <PROXY_USER>:<PROXY_PASSWORD>@<docker host ip>:1080 http://ifcfg.co```
```docker run --rm curlimages/curl:7.65.3 -s --socks5 <PROXY_USER>:<PROXY_PASSWORD>@<docker host ip>:1080 https://ipinfo.io```
# Authors
+26
View File
@@ -0,0 +1,26 @@
# Alternative for native Linux Docker hosts. ipvlan L2 often works better than
# macvlan on switches/APs that dislike multiple MAC addresses behind one port.
# Not expected to work directly on macOS OrbStack for the same VM/NIC reason.
services:
socks5:
image: serjs/go-socks5-proxy:latest
container_name: socks5-server-lan
restart: unless-stopped
env_file:
- .env
networks:
socks5_lan:
ipv4_address: 192.168.2.200
networks:
socks5_lan:
driver: ipvlan
driver_opts:
parent: eth0
ipvlan_mode: l2
ipam:
config:
- subnet: 192.168.2.0/24
gateway: 192.168.2.1
ip_range: 192.168.2.200/32
+26
View File
@@ -0,0 +1,26 @@
# Use this only on a native Linux Docker host with a real NIC on 192.168.2.0/24.
# It is not expected to work directly on macOS OrbStack because the Docker daemon
# is inside a VM and cannot attach macvlan to macOS en0.
services:
socks5:
image: serjs/go-socks5-proxy:latest
container_name: socks5-server-lan
restart: unless-stopped
env_file:
- .env
networks:
socks5_lan:
ipv4_address: 192.168.2.200
networks:
socks5_lan:
driver: macvlan
driver_opts:
# Replace eth0 with the Linux host's physical NIC, e.g. eno1/enp3s0.
parent: eth0
ipam:
config:
- subnet: 192.168.2.0/24
gateway: 192.168.2.1
ip_range: 192.168.2.200/32
+12 -6
View File
@@ -1,9 +1,15 @@
version: '3'
services:
socks5:
restart: always
image: serjs/go-socks5-proxy
env_file: .env
image: serjs/go-socks5-proxy:latest
container_name: socks5-server
restart: unless-stopped
env_file:
- .env
ports:
- "1080:1080"
- "0.0.0.0:1080:1080"
healthcheck:
test: ["CMD", "/socks5", "--help"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
+4 -2
View File
@@ -1,10 +1,12 @@
module github.com/serjs/socks5-server
go 1.23.0
go 1.24.0
require (
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
github.com/caarlos0/env/v6 v6.10.1
)
require golang.org/x/net v0.38.0
require golang.org/x/net v0.46.0
replace github.com/armon/go-socks5 => github.com/serjs/go-socks5 v0.0.0-20250923183437-3920b97ee0d2
+4 -4
View File
@@ -1,6 +1,6 @@
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/caarlos0/env/v6 v6.10.1 h1:t1mPSxNpei6M5yAeu1qtRdPAK29Nbcf/n3G7x+b3/II=
github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
github.com/serjs/go-socks5 v0.0.0-20250923183437-3920b97ee0d2 h1:dIXY/Lrkd1rGXcN60Fc0M2x0p5/C2XcbBbtPCvVvAa4=
github.com/serjs/go-socks5 v0.0.0-20250923183437-3920b97ee0d2/go.mod h1:N2PhU16m3olAb71DduLys4mYR3oQboD4uLJmSXCSuMA=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
+17 -5
View File
@@ -4,7 +4,6 @@ import (
"log"
"net"
"os"
"github.com/armon/go-socks5"
"github.com/caarlos0/env/v6"
)
@@ -15,6 +14,8 @@ type params struct {
Port string `env:"PROXY_PORT" envDefault:"1080"`
AllowedDestFqdn string `env:"ALLOWED_DEST_FQDN" envDefault:""`
AllowedIPs []string `env:"ALLOWED_IPS" envSeparator:"," envDefault:""`
ListenIP string `env:"PROXY_LISTEN_IP" envDefault:"0.0.0.0"`
RequireAuth bool `env:"REQUIRE_AUTH" envDefault:"true"`
}
func main() {
@@ -30,12 +31,17 @@ func main() {
Logger: log.New(os.Stdout, "", log.LstdFlags),
}
if cfg.User+cfg.Password != "" {
if cfg.RequireAuth {
if cfg.User == "" || cfg.Password == "" {
log.Fatalln("Error: REQUIRE_AUTH is true, but PROXY_USER and PROXY_PASSWORD are not set. The application will now exit.")
}
creds := socks5.StaticCredentials{
os.Getenv("PROXY_USER"): os.Getenv("PROXY_PASSWORD"),
cfg.User: cfg.Password,
}
cator := socks5.UserPassAuthenticator{Credentials: creds}
socks5conf.AuthMethods = []socks5.Authenticator{cator}
} else {
log.Println("Warning: Running the proxy server without authentication. This is NOT recommended for public servers.")
}
if cfg.AllowedDestFqdn != "" {
@@ -56,8 +62,14 @@ func main() {
server.SetIPWhitelist(whitelist)
}
log.Printf("Start listening proxy service on port %s\n", cfg.Port)
if err := server.ListenAndServe("tcp", ":"+cfg.Port); err != nil {
listenAddr := ":" + cfg.Port
if cfg.ListenIP != "" {
listenAddr = cfg.ListenIP + ":" + cfg.Port
}
log.Printf("Start listening proxy service on %s\n", listenAddr)
if err := server.ListenAndServe("tcp", listenAddr); err != nil {
log.Fatal(err)
}
}
+32
View File
@@ -55,6 +55,7 @@ type Config struct {
type Server struct {
config *Config
authMethods map[uint8]Authenticator
isIPAllowed func(net.IP) bool
}
// New creates a new Server and potentially returns an error
@@ -93,6 +94,11 @@ func New(conf *Config) (*Server, error) {
server.authMethods[a.GetCode()] = a
}
// Set default IP whitelist function
server.isIPAllowed = func(ip net.IP) bool {
return true // default allow all IPs
}
return server, nil
}
@@ -117,11 +123,37 @@ func (s *Server) Serve(l net.Listener) error {
return nil
}
// SetIPWhitelist sets the function to check if a given IP is allowed
func (s *Server) SetIPWhitelist(allowedIPs []net.IP) {
s.isIPAllowed = func(ip net.IP) bool {
for _, allowedIP := range allowedIPs {
if ip.Equal(allowedIP) {
return true
}
}
return false
}
}
// ServeConn is used to serve a single connection.
func (s *Server) ServeConn(conn net.Conn) error {
defer conn.Close()
bufConn := bufio.NewReader(conn)
// Check client IP against whitelist
clientIP, _, err := net.SplitHostPort(conn.RemoteAddr().String())
if err != nil {
s.config.Logger.Printf("[ERR] socks: Failed to get client IP address: %v", err)
return err
}
ip := net.ParseIP(clientIP)
if s.isIPAllowed(ip) {
s.config.Logger.Printf("[INFO] socks: Connection from allowed IP address: %s", clientIP)
} else {
s.config.Logger.Printf("[WARN] socks: Connection from not allowed IP address: %s", clientIP)
return fmt.Errorf("connection from not allowed IP address")
}
// Read the version byte
version := []byte{0}
if _, err := bufConn.Read(version); err != nil {
+21 -14
View File
@@ -6,7 +6,7 @@
// cancellation signals, and other request-scoped values across API boundaries
// and between processes.
// As of Go 1.7 this package is available in the standard library under the
// name [context], and migrating to it can be done automatically with [go fix].
// name [context].
//
// Incoming requests to a server should create a [Context], and outgoing
// calls to servers should accept a Context. The chain of function
@@ -38,8 +38,6 @@
//
// See https://go.dev/blog/context for example code for a server that uses
// Contexts.
//
// [go fix]: https://go.dev/cmd/go#hdr-Update_packages_to_use_new_APIs
package context
import (
@@ -51,36 +49,37 @@ import (
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
//
//go:fix inline
type Context = context.Context
// Canceled is the error returned by [Context.Err] when the context is canceled
// for some reason other than its deadline passing.
//
//go:fix inline
var Canceled = context.Canceled
// DeadlineExceeded is the error returned by [Context.Err] when the context is canceled
// due to its deadline passing.
//
//go:fix inline
var DeadlineExceeded = context.DeadlineExceeded
// Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming
// requests.
func Background() Context {
return background
}
//
//go:fix inline
func Background() Context { return context.Background() }
// TODO returns a non-nil, empty Context. Code should use context.TODO when
// it's unclear which Context to use or it is not yet available (because the
// surrounding function has not yet been extended to accept a Context
// parameter).
func TODO() Context {
return todo
}
var (
background = context.Background()
todo = context.TODO()
)
//
//go:fix inline
func TODO() Context { return context.TODO() }
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
@@ -95,6 +94,8 @@ type CancelFunc = context.CancelFunc
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this [Context] complete.
//
//go:fix inline
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
return context.WithCancel(parent)
}
@@ -108,6 +109,8 @@ func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this [Context] complete.
//
//go:fix inline
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
return context.WithDeadline(parent, d)
}
@@ -122,6 +125,8 @@ func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
// defer cancel() // releases resources if slowOperation completes before timeout elapses
// return slowOperation(ctx)
// }
//
//go:fix inline
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
return context.WithTimeout(parent, timeout)
}
@@ -139,6 +144,8 @@ func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
// interface{}, context keys often have concrete type
// struct{}. Alternatively, exported context key variables' static
// type should be a pointer or interface.
//
//go:fix inline
func WithValue(parent Context, key, val interface{}) Context {
return context.WithValue(parent, key, val)
}
+4 -3
View File
@@ -1,9 +1,10 @@
# github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
# github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 => github.com/serjs/go-socks5 v0.0.0-20250923183437-3920b97ee0d2
## explicit
github.com/armon/go-socks5
# github.com/caarlos0/env/v6 v6.10.1
## explicit; go 1.17
github.com/caarlos0/env/v6
# golang.org/x/net v0.38.0
## explicit; go 1.23.0
# golang.org/x/net v0.46.0
## explicit; go 1.24.0
golang.org/x/net/context
# github.com/armon/go-socks5 => github.com/serjs/go-socks5 v0.0.0-20250923183437-3920b97ee0d2