Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b83c00c46 | ||
|
|
0e6333194a | ||
|
|
bc77be08cc | ||
|
|
b887606007 | ||
|
|
ab4a5212d9 | ||
|
|
b592741872 | ||
|
|
b035e2b7dd | ||
|
|
b2546b3219 | ||
|
|
9c5b2622af | ||
|
|
2ac9fad93e | ||
|
|
0b72e1dded | ||
|
|
8f05071a81 | ||
|
|
e102c35f85 | ||
|
|
704fc1dbe5 | ||
|
|
02a56cad13 | ||
|
|
d238fef2e9 | ||
|
|
be8865eeb1 | ||
|
|
6e3c3a25f3 | ||
|
|
afcb393464 | ||
|
|
f637b0f916 | ||
|
|
06d425be3a | ||
|
|
17b31c5fc7 | ||
|
|
eee0bfc80a | ||
|
|
04dd90b25b | ||
|
|
2b116ffad6 | ||
|
|
6ab6551686 | ||
|
|
6bdeffe3f2 | ||
|
|
9224e79eea | ||
|
|
d1711b02b6 |
6
.dockerignore
Normal file
6
.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
||||
.dockerignore
|
||||
.github
|
||||
.gitignore
|
||||
Dockerfile
|
||||
LICENSE
|
||||
README.md
|
||||
2
.github/wireproxy-releaser.yml
vendored
2
.github/wireproxy-releaser.yml
vendored
@@ -11,8 +11,6 @@ builds:
|
||||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
- freebsd
|
||||
- openbsd
|
||||
goarch:
|
||||
- arm
|
||||
- arm64
|
||||
|
||||
14
.github/workflows/build.yml
vendored
14
.github/workflows/build.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
- name: Setting up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
go-version: 1.19
|
||||
- name: Building Windows amd64 Version
|
||||
run: |
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o WireProxy_amd64.exe -v ./cmd/wireproxy
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
- name: Setting up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
go-version: 1.19
|
||||
- name: Building Windows arm64 Version
|
||||
run: |
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build -o WireProxy_arm64.exe -v ./cmd/wireproxy
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
- name: Setting up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
go-version: 1.19
|
||||
- name: Building Linux amd64 Version
|
||||
run: |
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o WireProxy_amd64 -v ./cmd/wireproxy
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
- name: Setting up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
go-version: 1.19
|
||||
- name: Building Linux arm64 Version
|
||||
run: |
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o WireProxy_arm64 -v ./cmd/wireproxy
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
- name: Setting up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
go-version: 1.19
|
||||
- name: Building Linux s390x Version
|
||||
run: |
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=s390x go build -o WireProxy_s390x -v ./cmd/wireproxy
|
||||
@@ -116,7 +116,7 @@ jobs:
|
||||
- name: Setting up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
go-version: 1.19
|
||||
- name: Building Darwin amd64 Version
|
||||
run: |
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o WireProxy_amd64 -v ./cmd/wireproxy
|
||||
@@ -136,7 +136,7 @@ jobs:
|
||||
- name: Setting up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
go-version: 1.19
|
||||
- name: Building Darwin arm64 Version
|
||||
run: |
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o WireProxy_arm64 -v ./cmd/wireproxy
|
||||
|
||||
71
.github/workflows/container.yml
vendored
Normal file
71
.github/workflows/container.yml
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
name: Build container
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
# Allow for manually running
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
container_tag:
|
||||
description: Tag for container
|
||||
default: "latest"
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
container:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
env:
|
||||
CONTAINER_NAME: ghcr.io/${{ github.repository }}
|
||||
BUILD_PLATFORMS: linux/amd64,linux/arm,linux/arm64,linux/ppc64le,linux/s390x
|
||||
RAW_CONTAINER_TAG: ${{ github.event.inputs.container_tag || github.event.pull_request.head.ref || 'latest' }}
|
||||
RAW_REF_NAME: ${{ github.event.pull_request.head.ref || github.ref }}
|
||||
|
||||
steps:
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2.0.0
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
# Needed for buildx gha cache to work
|
||||
- name: Expose GitHub Runtime
|
||||
uses: crazy-max/ghaction-github-runtime@v2
|
||||
|
||||
- name: Build container
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
CONTAINER_TAG=$(echo "$RAW_CONTAINER_TAG" | sed 's/[^a-zA-Z0-9]\+/-/')
|
||||
REF_NAME=$(echo "$RAW_REF_NAME" | sed -r 's#^refs/(heads|tags)/##')
|
||||
|
||||
docker buildx build \
|
||||
--platform "$BUILD_PLATFORMS" \
|
||||
--tag "$CONTAINER_NAME:$CONTAINER_TAG" \
|
||||
--label "org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}" \
|
||||
--label "org.opencontainers.image.documentation=${{ github.server_url }}/${{ github.repository }}" \
|
||||
--label "org.opencontainers.image.url=${{ github.server_url }}/${{ github.repository }}/packages" \
|
||||
--label "org.opencontainers.image.ref.name=$REF_NAME" \
|
||||
--label "org.opencontainers.image.revision=${{ github.sha }}" \
|
||||
--label "org.opencontainers.image.vendor=${{ github.repository_owner }}" \
|
||||
--label "org.opencontainers.image.created=$(date -u --rfc-3339=seconds)" \
|
||||
--cache-from type=gha \
|
||||
--cache-to type=gha,mode=max \
|
||||
--pull ${{ github.event_name == 'push' && '--push' || '' }} .
|
||||
6
.github/workflows/golangci-lint.yml
vendored
6
.github/workflows/golangci-lint.yml
vendored
@@ -14,6 +14,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
go-version: '1.19'
|
||||
- uses: actions/checkout@v3
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
|
||||
16
.github/workflows/wireproxy.yml
vendored
16
.github/workflows/wireproxy.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Cross compile WirePorxy
|
||||
name: Cross compile WireProxy
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -7,14 +7,14 @@ on:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
WirePorxy:
|
||||
WireProxy:
|
||||
|
||||
name: Cross compile WirePorxy
|
||||
name: Cross compile WireProxy
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
env:
|
||||
workdir: ./WirePorxy
|
||||
workdir: ./WireProxy
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Git clone WirePorxy
|
||||
- name: Git clone WireProxy
|
||||
run: |
|
||||
git clone https://github.com/octeep/wireproxy.git ${{ env.workdir }}
|
||||
cp ./.github/wireproxy-releaser.yml ${{ env.workdir }}/.goreleaser.yml
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
- name: Set up GoReleaser
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "1.17"
|
||||
go-version: "1.19"
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
@@ -39,6 +39,8 @@ jobs:
|
||||
workdir: ${{ env.workdir }}
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Release binaries
|
||||
uses: softprops/action-gh-release@v1
|
||||
@@ -46,4 +48,4 @@ jobs:
|
||||
tag_name: wireproxy
|
||||
files: ${{ env.workdir }}/dist/*.tar.gz
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
/main
|
||||
/wireproxy
|
||||
*.sw?
|
||||
/.idea
|
||||
.goreleaser.yml
|
||||
|
||||
19
Dockerfile
Normal file
19
Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
||||
# Start by building the application.
|
||||
FROM golang:1.18 as build
|
||||
|
||||
WORKDIR /usr/src/wireproxy
|
||||
COPY . .
|
||||
|
||||
RUN CGO_ENABLED=0 go build ./cmd/wireproxy
|
||||
|
||||
# Now copy it into our base image.
|
||||
FROM gcr.io/distroless/static-debian11:nonroot
|
||||
COPY --from=build /usr/src/wireproxy/wireproxy /usr/bin/wireproxy
|
||||
|
||||
VOLUME [ "/etc/wireproxy"]
|
||||
ENTRYPOINT [ "/usr/bin/wireproxy" ]
|
||||
CMD [ "--config", "/etc/wireproxy/config" ]
|
||||
|
||||
LABEL org.opencontainers.image.title wireproxy
|
||||
LABEL org.opencontainers.image.description "Wireguard client that exposes itself as a socks5 proxy"
|
||||
LABEL org.opencontainers.image.licenses ISC
|
||||
13
Makefile
Normal file
13
Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
export GO ?= go
|
||||
|
||||
.PHONY: all
|
||||
all: wireproxy
|
||||
|
||||
.PHONY: wireproxy
|
||||
wireproxy:
|
||||
tag="$$(git describe --tag 2>/dev/null)" && \
|
||||
${GO} build -ldflags "-X 'main.version=$$tag'" ./cmd/wireproxy
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
${RM} wireproxy
|
||||
35
README.md
35
README.md
@@ -52,7 +52,7 @@ Arguments:
|
||||
```
|
||||
git clone https://github.com/octeep/wireproxy
|
||||
cd wireproxy
|
||||
go build ./cmd/wireproxy
|
||||
make
|
||||
```
|
||||
|
||||
# Sample config file
|
||||
@@ -116,6 +116,39 @@ WGConfig = <path to the wireguard config>
|
||||
...
|
||||
```
|
||||
|
||||
Having multiple peers is also supported. `AllowedIPs` would need to be specified
|
||||
such that wireproxy would know which peer to forward to.
|
||||
```
|
||||
[Interface]
|
||||
Address = 10.254.254.40/32
|
||||
PrivateKey = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=
|
||||
|
||||
[Peer]
|
||||
Endpoint = 192.168.0.204:51820
|
||||
PublicKey = YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY=
|
||||
AllowedIPs = 10.254.254.100/32
|
||||
PersistentKeepalive = 25
|
||||
|
||||
[Peer]
|
||||
PublicKey = ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ=
|
||||
AllowedIPs = 10.254.254.1/32, fdee:1337:c000:d00d::1/128
|
||||
Endpoint = 172.16.0.185:44044
|
||||
PersistentKeepalive = 25
|
||||
|
||||
|
||||
[TCPServerTunnel]
|
||||
ListenPort = 5000
|
||||
Target = service-one.servicenet:5000
|
||||
|
||||
[TCPServerTunnel]
|
||||
ListenPort = 5001
|
||||
Target = service-two.servicenet:5001
|
||||
|
||||
[TCPServerTunnel]
|
||||
ListenPort = 5080
|
||||
Target = service-three.servicenet:80
|
||||
```
|
||||
|
||||
## Donation
|
||||
<noscript><a href="https://liberapay.com/octeep/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a></noscript>
|
||||
|
||||
|
||||
@@ -14,17 +14,42 @@ import (
|
||||
// an argument to denote that this process was spawned by -d
|
||||
const daemonProcess = "daemon-process"
|
||||
|
||||
var version = "1.0.5-dev"
|
||||
|
||||
// attempts to pledge and panic if it fails
|
||||
// this does nothing on non-OpenBSD systems
|
||||
func pledgeOrPanic(promises string) {
|
||||
err := protect.Pledge(promises)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// attempts to unveil and panic if it fails
|
||||
// this does nothing on non-OpenBSD systems
|
||||
func unveilOrPanic(path string, flags string) {
|
||||
err := protect.Unveil(path, flags)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// get the executable path via syscalls or infer it from argv
|
||||
func executablePath() string {
|
||||
programPath, err := os.Executable()
|
||||
if err != nil {
|
||||
return os.Args[0]
|
||||
}
|
||||
return programPath
|
||||
}
|
||||
|
||||
func main() {
|
||||
exePath := executablePath()
|
||||
unveilOrPanic("/", "r")
|
||||
unveilOrPanic(exePath, "x")
|
||||
|
||||
// only allow standard stdio operation, file reading, networking, and exec
|
||||
// also remove unveil permission to lock unveil
|
||||
pledgeOrPanic("stdio rpath inet dns proc exec")
|
||||
|
||||
isDaemonProcess := len(os.Args) > 1 && os.Args[1] == daemonProcess
|
||||
@@ -37,8 +62,9 @@ func main() {
|
||||
}
|
||||
parser := argparse.NewParser("wireproxy", "Userspace wireguard client for proxying")
|
||||
|
||||
config := parser.String("c", "config", &argparse.Options{Required: true, Help: "Path of configuration file"})
|
||||
config := parser.String("c", "config", &argparse.Options{Help: "Path of configuration file"})
|
||||
daemon := parser.Flag("d", "daemon", &argparse.Options{Help: "Make wireproxy run in background"})
|
||||
printVerison := parser.Flag("v", "version", &argparse.Options{Help: "Print version"})
|
||||
configTest := parser.Flag("n", "configtest", &argparse.Options{Help: "Configtest mode. Only check the configuration file for validity."})
|
||||
|
||||
err := parser.Parse(args)
|
||||
@@ -47,6 +73,16 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
if *printVerison {
|
||||
fmt.Printf("wireproxy, version %s\n", version)
|
||||
return
|
||||
}
|
||||
|
||||
if *config == "" {
|
||||
fmt.Println("configuration path is required")
|
||||
return
|
||||
}
|
||||
|
||||
if !*daemon {
|
||||
// remove proc and exec if they are not needed
|
||||
pledgeOrPanic("stdio rpath inet dns")
|
||||
@@ -54,7 +90,7 @@ func main() {
|
||||
|
||||
conf, err := wireproxy.ParseConfig(*config)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if *configTest {
|
||||
@@ -69,14 +105,8 @@ func main() {
|
||||
}
|
||||
|
||||
if *daemon {
|
||||
programPath, err := os.Executable()
|
||||
if err != nil {
|
||||
programPath = args[0]
|
||||
}
|
||||
|
||||
newArgs := []string{daemonProcess}
|
||||
newArgs = append(newArgs, args[1:]...)
|
||||
cmd := exec.Command(programPath, newArgs...)
|
||||
args[0] = daemonProcess
|
||||
cmd := exec.Command(exePath, args...)
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
@@ -89,7 +119,7 @@ func main() {
|
||||
|
||||
tnet, err := wireproxy.StartWireguard(conf.Device)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, spawner := range conf.Routines {
|
||||
|
||||
128
config.go
128
config.go
@@ -9,19 +9,24 @@ import (
|
||||
|
||||
"github.com/go-ini/ini"
|
||||
|
||||
"golang.zx2c4.com/go118/netip"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
type PeerConfig struct {
|
||||
PublicKey string
|
||||
PreSharedKey string
|
||||
Endpoint string
|
||||
KeepAlive int
|
||||
AllowedIPs []netip.Prefix
|
||||
}
|
||||
|
||||
// DeviceConfig contains the information to initiate a wireguard connection
|
||||
type DeviceConfig struct {
|
||||
SelfSecretKey string
|
||||
SelfEndpoint []netip.Addr
|
||||
PeerPublicKey string
|
||||
PeerEndpoint string
|
||||
DNS []netip.Addr
|
||||
KeepAlive int
|
||||
PreSharedKey string
|
||||
MTU int
|
||||
SecretKey string
|
||||
Endpoint []netip.Addr
|
||||
Peers []PeerConfig
|
||||
DNS []netip.Addr
|
||||
MTU int
|
||||
}
|
||||
|
||||
type TCPClientTunnelConfig struct {
|
||||
@@ -109,7 +114,7 @@ func parseNetIP(section *ini.Section, keyName string) ([]netip.Addr, error) {
|
||||
return []netip.Addr{}, nil
|
||||
}
|
||||
|
||||
ips := []netip.Addr{}
|
||||
var ips []netip.Addr
|
||||
for _, str := range key.StringsWithShadows(",") {
|
||||
str = strings.TrimSpace(str)
|
||||
ip, err := netip.ParseAddr(str)
|
||||
@@ -127,7 +132,7 @@ func parseCIDRNetIP(section *ini.Section, keyName string) ([]netip.Addr, error)
|
||||
return []netip.Addr{}, nil
|
||||
}
|
||||
|
||||
ips := []netip.Addr{}
|
||||
var ips []netip.Addr
|
||||
for _, str := range key.StringsWithShadows(",") {
|
||||
prefix, err := netip.ParsePrefix(str)
|
||||
if err != nil {
|
||||
@@ -144,6 +149,24 @@ func parseCIDRNetIP(section *ini.Section, keyName string) ([]netip.Addr, error)
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
func parseAllowedIPs(section *ini.Section) ([]netip.Prefix, error) {
|
||||
key := section.Key("AllowedIPs")
|
||||
if key == nil {
|
||||
return []netip.Prefix{}, nil
|
||||
}
|
||||
|
||||
var ips []netip.Prefix
|
||||
for _, str := range key.StringsWithShadows(",") {
|
||||
prefix, err := netip.ParsePrefix(str)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ips = append(ips, prefix)
|
||||
}
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
func resolveIP(ip string) (*net.IPAddr, error) {
|
||||
return net.ResolveIPAddr("ip", ip)
|
||||
}
|
||||
@@ -174,13 +197,13 @@ func ParseInterface(cfg *ini.File, device *DeviceConfig) error {
|
||||
return err
|
||||
}
|
||||
|
||||
device.SelfEndpoint = address
|
||||
device.Endpoint = address
|
||||
|
||||
privKey, err := parseBase64KeyToHex(section, "PrivateKey")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
device.SelfSecretKey = privKey
|
||||
device.SecretKey = privKey
|
||||
|
||||
dns, err := parseNetIP(section, "DNS")
|
||||
if err != nil {
|
||||
@@ -199,46 +222,58 @@ func ParseInterface(cfg *ini.File, device *DeviceConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParsePeer parses the [Peer] section and extract the information into `device`
|
||||
func ParsePeer(cfg *ini.File, device *DeviceConfig) error {
|
||||
// ParsePeer parses the [Peer] section and extract the information into `peers`
|
||||
func ParsePeers(cfg *ini.File, peers *[]PeerConfig) error {
|
||||
sections, err := cfg.SectionsByName("Peer")
|
||||
if len(sections) != 1 || err != nil {
|
||||
return errors.New("one and only one [Peer] is expected")
|
||||
if len(sections) < 1 || err != nil {
|
||||
return errors.New("at least one [Peer] is expected")
|
||||
}
|
||||
section := sections[0]
|
||||
|
||||
decoded, err := parseBase64KeyToHex(section, "PublicKey")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
device.PeerPublicKey = decoded
|
||||
for _, section := range sections {
|
||||
peer := PeerConfig{
|
||||
PreSharedKey: "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
KeepAlive: 0,
|
||||
}
|
||||
|
||||
if sectionKey, err := section.GetKey("PreSharedKey"); err == nil {
|
||||
value, err := encodeBase64ToHex(sectionKey.String())
|
||||
decoded, err := parseBase64KeyToHex(section, "PublicKey")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
device.PreSharedKey = value
|
||||
}
|
||||
peer.PublicKey = decoded
|
||||
|
||||
decoded, err = parseString(section, "Endpoint")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
decoded, err = resolveIPPAndPort(decoded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
device.PeerEndpoint = decoded
|
||||
if sectionKey, err := section.GetKey("PreSharedKey"); err == nil {
|
||||
value, err := encodeBase64ToHex(sectionKey.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
peer.PreSharedKey = value
|
||||
}
|
||||
|
||||
if sectionKey, err := section.GetKey("PersistentKeepalive"); err == nil {
|
||||
value, err := sectionKey.Int()
|
||||
decoded, err = parseString(section, "Endpoint")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
device.KeepAlive = value
|
||||
}
|
||||
decoded, err = resolveIPPAndPort(decoded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
peer.Endpoint = decoded
|
||||
|
||||
if sectionKey, err := section.GetKey("PersistentKeepalive"); err == nil {
|
||||
value, err := sectionKey.Int()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
peer.KeepAlive = value
|
||||
}
|
||||
|
||||
peer.AllowedIPs, err = parseAllowedIPs(section)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*peers = append(*peers, peer)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -318,8 +353,9 @@ func parseRoutinesConfig(routines *[]RoutineSpawner, cfg *ini.File, sectionName
|
||||
// ParseConfig takes the path of a configuration file and parses it into Configuration
|
||||
func ParseConfig(path string) (*Configuration, error) {
|
||||
iniOpt := ini.LoadOptions{
|
||||
Insensitive: true,
|
||||
AllowShadows: true,
|
||||
Insensitive: true,
|
||||
AllowShadows: true,
|
||||
AllowNonUniqueSections: true,
|
||||
}
|
||||
|
||||
cfg, err := ini.LoadSources(iniOpt, path)
|
||||
@@ -328,9 +364,7 @@ func ParseConfig(path string) (*Configuration, error) {
|
||||
}
|
||||
|
||||
device := &DeviceConfig{
|
||||
PreSharedKey: "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
KeepAlive: 0,
|
||||
MTU: 1420,
|
||||
MTU: 1420,
|
||||
}
|
||||
|
||||
root := cfg.Section("")
|
||||
@@ -348,12 +382,12 @@ func ParseConfig(path string) (*Configuration, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ParsePeer(wgCfg, device)
|
||||
err = ParsePeers(wgCfg, &device.Peers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
routinesSpawners := []RoutineSpawner{}
|
||||
var routinesSpawners []RoutineSpawner
|
||||
|
||||
err = parseRoutinesConfig(&routinesSpawners, cfg, "TCPClientTunnel", parseTCPClientTunnelConfig)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
FROM golang:alpine AS go-build
|
||||
|
||||
RUN apk --no-cache add --update git
|
||||
RUN git clone https://github.com/octeep/wireproxy.git
|
||||
RUN cd ./wireproxy && go build ./cmd/wireproxy
|
||||
|
||||
|
||||
FROM alpine:latest
|
||||
|
||||
RUN apk upgrade
|
||||
COPY --from=go-build /go/wireproxy/wireproxy /usr/bin/
|
||||
|
||||
VOLUME [ "/etc/wireproxy"]
|
||||
ENTRYPOINT [ "/usr/bin/wireproxy", "/etc/wireproxy/config" ]
|
||||
@@ -1,10 +0,0 @@
|
||||
build:
|
||||
docker build -t wireproxy .
|
||||
|
||||
run:
|
||||
docker run \
|
||||
--rm --tty --interactive \
|
||||
--name=wireproxy \
|
||||
--publish 2534:2534 \
|
||||
--volume "${PWD}/config:/etc/wireproxy/config:ro" \
|
||||
wireproxy
|
||||
@@ -1,12 +0,0 @@
|
||||
[Interface]
|
||||
Address = ###Interface - Address###
|
||||
PrivateKey = ###Interface - PrivateKey###
|
||||
DNS = ###Interface - DNS###
|
||||
|
||||
[Peer]
|
||||
PublicKey = ###Peer - PublicKey###
|
||||
Endpoint = ###Peer - Endpoint###
|
||||
|
||||
# Socks5 create a socks5 proxy on your LAN, and any traffic would be routed via wireguard
|
||||
[Socks5]
|
||||
BindAddress = 0.0.0.0:2534
|
||||
22
go.mod
22
go.mod
@@ -1,25 +1,23 @@
|
||||
module github.com/octeep/wireproxy
|
||||
|
||||
go 1.17
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/MakeNowJust/heredoc/v2 v2.0.1
|
||||
github.com/akamensky/argparse v1.3.1
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
|
||||
github.com/go-ini/ini v1.66.4
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
golang.zx2c4.com/go118/netip v0.0.0-20211111135330-a4a02eeacf9d
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220202223031-3b95c81cc178
|
||||
golang.zx2c4.com/wireguard/tun/netstack v0.0.0-20220310012736-ae6bc4dd64e1
|
||||
gvisor.dev/gvisor v0.0.0-20211020211948-f76a604701b6
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b
|
||||
suah.dev/protect v1.2.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/akamensky/argparse v1.3.1 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa // indirect
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
|
||||
github.com/stretchr/testify v1.8.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
||||
suah.dev/protect v1.2.0 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20220817001344-846276b3dbc5 // indirect
|
||||
)
|
||||
|
||||
10
net.go
10
net.go
@@ -3,8 +3,8 @@
|
||||
package wireproxy
|
||||
|
||||
import (
|
||||
"golang.zx2c4.com/go118/netip"
|
||||
"net"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
func TCPAddrFromAddrPort(addr netip.AddrPort) *net.TCPAddr {
|
||||
@@ -14,11 +14,3 @@ func TCPAddrFromAddrPort(addr netip.AddrPort) *net.TCPAddr {
|
||||
Port: int(addr.Port()),
|
||||
}
|
||||
}
|
||||
|
||||
func UDPAddrFromAddrPort(addr netip.AddrPort) *net.UDPAddr {
|
||||
return &net.UDPAddr{
|
||||
IP: addr.Addr().AsSlice(),
|
||||
Zone: addr.Addr().Zone(),
|
||||
Port: int(addr.Port()),
|
||||
}
|
||||
}
|
||||
|
||||
113
routine.go
113
routine.go
@@ -13,8 +13,8 @@ import (
|
||||
|
||||
"github.com/armon/go-socks5"
|
||||
|
||||
"golang.zx2c4.com/go118/netip"
|
||||
"golang.zx2c4.com/wireguard/tun/netstack"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// errorLogger is the logger to print error message
|
||||
@@ -37,6 +37,11 @@ type RoutineSpawner interface {
|
||||
SpawnRoutine(vt *VirtualTun)
|
||||
}
|
||||
|
||||
type addressPort struct {
|
||||
address string
|
||||
port uint16
|
||||
}
|
||||
|
||||
// LookupAddr lookups a hostname.
|
||||
// DNS traffic may or may not be routed depending on VirtualTun's setting
|
||||
func (d VirtualTun) LookupAddr(ctx context.Context, name string) ([]string, error) {
|
||||
@@ -47,29 +52,7 @@ func (d VirtualTun) LookupAddr(ctx context.Context, name string) ([]string, erro
|
||||
}
|
||||
}
|
||||
|
||||
// ResolveAddrPort resolves a hostname and returns an AddrPort.
|
||||
// DNS traffic may or may not be routed depending on VirtualTun's setting
|
||||
func (d VirtualTun) ResolveAddrPort(saddr string) (*netip.AddrPort, error) {
|
||||
name, sport, err := net.SplitHostPort(saddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addr, err := d.ResolveAddrWithContext(context.Background(), name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(sport)
|
||||
if err != nil || port < 0 || port > 65535 {
|
||||
return nil, &net.OpError{Op: "dial", Err: errors.New("port must be numeric")}
|
||||
}
|
||||
|
||||
addrPort := netip.AddrPortFrom(*addr, uint16(port))
|
||||
return &addrPort, nil
|
||||
}
|
||||
|
||||
// ResolveAddrPort resolves a hostname and returns an AddrPort.
|
||||
// ResolveAddrWithContext resolves a hostname and returns an AddrPort.
|
||||
// DNS traffic may or may not be routed depending on VirtualTun's setting
|
||||
func (d VirtualTun) ResolveAddrWithContext(ctx context.Context, name string) (*netip.Addr, error) {
|
||||
addrs, err := d.LookupAddr(ctx, name)
|
||||
@@ -101,7 +84,7 @@ func (d VirtualTun) ResolveAddrWithContext(ctx context.Context, name string) (*n
|
||||
return &addr, nil
|
||||
}
|
||||
|
||||
// ResolveAddrPort resolves a hostname and returns an IP.
|
||||
// Resolve resolves a hostname and returns an IP.
|
||||
// DNS traffic may or may not be routed depending on VirtualTun's setting
|
||||
func (d VirtualTun) Resolve(ctx context.Context, name string) (context.Context, net.IP, error) {
|
||||
addr, err := d.ResolveAddrWithContext(ctx, name)
|
||||
@@ -112,7 +95,31 @@ func (d VirtualTun) Resolve(ctx context.Context, name string) (context.Context,
|
||||
return ctx, addr.AsSlice(), nil
|
||||
}
|
||||
|
||||
// Spawns a socks5 server.
|
||||
func parseAddressPort(endpoint string) (*addressPort, error) {
|
||||
name, sport, err := net.SplitHostPort(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(sport)
|
||||
if err != nil || port < 0 || port > 65535 {
|
||||
return nil, &net.OpError{Op: "dial", Err: errors.New("port must be numeric")}
|
||||
}
|
||||
|
||||
return &addressPort{address: name, port: uint16(port)}, nil
|
||||
}
|
||||
|
||||
func (d VirtualTun) resolveToAddrPort(endpoint *addressPort) (*netip.AddrPort, error) {
|
||||
addr, err := d.ResolveAddrWithContext(context.Background(), endpoint.address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addrPort := netip.AddrPortFrom(*addr, endpoint.port)
|
||||
return &addrPort, nil
|
||||
}
|
||||
|
||||
// SpawnRoutine spawns a socks5 server.
|
||||
func (config *Socks5Config) SpawnRoutine(vt *VirtualTun) {
|
||||
conf := &socks5.Config{Dial: vt.tnet.DialContext, Resolver: vt}
|
||||
if username := config.Username; username != "" {
|
||||
@@ -122,11 +129,11 @@ func (config *Socks5Config) SpawnRoutine(vt *VirtualTun) {
|
||||
}
|
||||
server, err := socks5.New(conf)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := server.ListenAndServe("tcp", config.BindAddress); err != nil {
|
||||
log.Panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,8 +157,16 @@ func connForward(bufSize int, from io.ReadWriteCloser, to io.ReadWriteCloser) {
|
||||
}
|
||||
|
||||
// tcpClientForward starts a new connection via wireguard and forward traffic from `conn`
|
||||
func tcpClientForward(tnet *netstack.Net, target *net.TCPAddr, conn net.Conn) {
|
||||
sconn, err := tnet.DialTCP(target)
|
||||
func tcpClientForward(vt *VirtualTun, raddr *addressPort, conn net.Conn) {
|
||||
target, err := vt.resolveToAddrPort(raddr)
|
||||
if err != nil {
|
||||
errorLogger.Printf("TCP Server Tunnel to %s: %s\n", target, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
tcpAddr := TCPAddrFromAddrPort(*target)
|
||||
|
||||
sconn, err := vt.tnet.DialTCP(tcpAddr)
|
||||
if err != nil {
|
||||
errorLogger.Printf("TCP Client Tunnel to %s: %s\n", target, err.Error())
|
||||
return
|
||||
@@ -161,31 +176,38 @@ func tcpClientForward(tnet *netstack.Net, target *net.TCPAddr, conn net.Conn) {
|
||||
go connForward(1024, conn, sconn)
|
||||
}
|
||||
|
||||
// Spawns a local TCP server which acts as a proxy to the specified target
|
||||
// SpawnRoutine spawns a local TCP server which acts as a proxy to the specified target
|
||||
func (conf *TCPClientTunnelConfig) SpawnRoutine(vt *VirtualTun) {
|
||||
raddr, err := vt.ResolveAddrPort(conf.Target)
|
||||
raddr, err := parseAddressPort(conf.Target)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
tcpAddr := TCPAddrFromAddrPort(*raddr)
|
||||
|
||||
server, err := net.ListenTCP("tcp", conf.BindAddress)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for {
|
||||
conn, err := server.Accept()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
go tcpClientForward(vt.tnet, tcpAddr, conn)
|
||||
go tcpClientForward(vt, raddr, conn)
|
||||
}
|
||||
}
|
||||
|
||||
// tcpServerForward starts a new connection locally and forward traffic from `conn`
|
||||
func tcpServerForward(target *net.TCPAddr, conn net.Conn) {
|
||||
sconn, err := net.DialTCP("tcp", nil, target)
|
||||
func tcpServerForward(vt *VirtualTun, raddr *addressPort, conn net.Conn) {
|
||||
target, err := vt.resolveToAddrPort(raddr)
|
||||
if err != nil {
|
||||
errorLogger.Printf("TCP Server Tunnel to %s: %s\n", target, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
tcpAddr := TCPAddrFromAddrPort(*target)
|
||||
|
||||
sconn, err := net.DialTCP("tcp", nil, tcpAddr)
|
||||
if err != nil {
|
||||
errorLogger.Printf("TCP Server Tunnel to %s: %s\n", target, err.Error())
|
||||
return
|
||||
@@ -195,25 +217,24 @@ func tcpServerForward(target *net.TCPAddr, conn net.Conn) {
|
||||
go connForward(1024, conn, sconn)
|
||||
}
|
||||
|
||||
// Spawns a TCP server on wireguard which acts as a proxy to the specified target
|
||||
// SpawnRoutine spawns a TCP server on wireguard which acts as a proxy to the specified target
|
||||
func (conf *TCPServerTunnelConfig) SpawnRoutine(vt *VirtualTun) {
|
||||
raddr, err := vt.ResolveAddrPort(conf.Target)
|
||||
raddr, err := parseAddressPort(conf.Target)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
tcpAddr := TCPAddrFromAddrPort(*raddr)
|
||||
|
||||
addr := &net.TCPAddr{Port: conf.ListenPort}
|
||||
server, err := vt.tnet.ListenTCP(addr)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for {
|
||||
conn, err := server.Accept()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
go tcpServerForward(tcpAddr, conn)
|
||||
go tcpServerForward(vt, raddr, conn)
|
||||
}
|
||||
}
|
||||
|
||||
38
wireguard.go
38
wireguard.go
@@ -1,9 +1,12 @@
|
||||
package wireproxy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"golang.zx2c4.com/go118/netip"
|
||||
"net/netip"
|
||||
|
||||
"github.com/MakeNowJust/heredoc/v2"
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/tun/netstack"
|
||||
@@ -19,14 +22,33 @@ type DeviceSetting struct {
|
||||
|
||||
// serialize the config into an IPC request and DeviceSetting
|
||||
func createIPCRequest(conf *DeviceConfig) (*DeviceSetting, error) {
|
||||
request := fmt.Sprintf(`private_key=%s
|
||||
public_key=%s
|
||||
endpoint=%s
|
||||
persistent_keepalive_interval=%d
|
||||
preshared_key=%s
|
||||
allowed_ip=0.0.0.0/0`, conf.SelfSecretKey, conf.PeerPublicKey, conf.PeerEndpoint, conf.KeepAlive, conf.PreSharedKey)
|
||||
var request bytes.Buffer
|
||||
|
||||
setting := &DeviceSetting{ipcRequest: request, dns: conf.DNS, deviceAddr: conf.SelfEndpoint, mtu: conf.MTU}
|
||||
request.WriteString(fmt.Sprintf("private_key=%s\n", conf.SecretKey))
|
||||
|
||||
for _, peer := range conf.Peers {
|
||||
request.WriteString(fmt.Sprintf(heredoc.Doc(`
|
||||
public_key=%s
|
||||
endpoint=%s
|
||||
persistent_keepalive_interval=%d
|
||||
preshared_key=%s
|
||||
`),
|
||||
peer.PublicKey, peer.Endpoint, peer.KeepAlive, peer.PreSharedKey,
|
||||
))
|
||||
|
||||
if len(peer.AllowedIPs) > 0 {
|
||||
for _, ip := range peer.AllowedIPs {
|
||||
request.WriteString(fmt.Sprintf("allowed_ip=%s\n", ip.String()))
|
||||
}
|
||||
} else {
|
||||
request.WriteString(heredoc.Doc(`
|
||||
allowed_ip=0.0.0.0/0
|
||||
allowed_ip=::0/0
|
||||
`))
|
||||
}
|
||||
}
|
||||
|
||||
setting := &DeviceSetting{ipcRequest: request.String(), dns: conf.DNS, deviceAddr: conf.Endpoint, mtu: conf.MTU}
|
||||
return setting, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user