23 Commits

Author SHA1 Message Date
octeep
0e6333194a support go 1.19 2022-09-01 10:53:10 +08:00
octeep
bc77be08cc bump version to 1.0.4 2022-08-23 22:04:36 +08:00
octeep
b887606007 add print version flag 2022-08-23 21:43:30 +08:00
octeep
ab4a5212d9 Merge pull request #45 from jordemort/distroless-docker
Redo Dockerfile
2022-08-23 19:51:12 +08:00
octeep
b592741872 Do not push to registry on PRs 2022-08-23 19:40:09 +08:00
octeep
b035e2b7dd Revert "Try publishing to Docker Hub instead"
This reverts commit b2546b3219.
2022-08-23 19:37:37 +08:00
Jordan Webb
b2546b3219 Try publishing to Docker Hub instead 2022-08-21 23:04:34 -05:00
Jordan Webb
9c5b2622af Try moving permissions key to top-level 2022-08-21 09:18:14 -05:00
Jordan Webb
2ac9fad93e Try giving the workflow packages: write 2022-08-21 09:03:58 -05:00
octeep
0b72e1dded Update README.md 2022-08-21 11:09:30 +08:00
octeep
8f05071a81 Merge pull request #47 from jordemort/multiple-peers
Allow multiple peers, tunnels, and proxies
2022-08-21 11:04:52 +08:00
octeep
e102c35f85 update wireguard dependency and fix linter 2022-08-21 10:56:10 +08:00
Jordan Webb
704fc1dbe5 Add metadata to image 2022-08-20 20:04:48 -05:00
Jordan Webb
02a56cad13 Allow multiple peers, tunnels, and proxies 2022-08-20 19:11:40 -05:00
Jordan Webb
d238fef2e9 Add workflow to build container 2022-08-20 09:56:08 -05:00
Jordan Webb
be8865eeb1 Redo Dockerfile
- Build the currently checked-out code, instead of cloning the repo
inside the Dockerfile. This makes it much easier to build a container
for a particular branch or commit; people working on personal forks
will be able to build containers for their forks without modifying the
Dockerfile.

- Switch from Alpine to distroless; I couldn't actually get the current
version of the Dockerfile to build, it kept dying with some error about
gvisor. Aside from building with no trouble, the new Dockerfile reduces
the size of the image from 23MB to 9MB.

- Move Dockerfile into the root; this is a matter of taste, but allows
one to simply `docker build` the directory instead of having to also
specify the path to the Dockerfile. As part of this, I removed the
`config` and `Makefile` from the `docker` directory, since they seemed
specific to someone's setup and incomplete without that context.
2022-08-20 09:55:55 -05:00
octeep
6e3c3a25f3 update go version to 1.18 2022-05-20 12:25:48 +01:00
octeep
afcb393464 Update wireproxy.yml 2022-04-05 07:32:38 +01:00
octeep
f637b0f916 resolve host for every new connection in static tunnels 2022-04-04 20:45:28 +01:00
octeep
06d425be3a fix misspellings in CI 2022-04-04 06:02:47 +01:00
octeep
17b31c5fc7 Merge remote-tracking branch 'refs/remotes/origin/master' 2022-04-04 06:02:15 +01:00
octeep
eee0bfc80a OpenBSD unveil to prevent -d from executing other binaries 2022-04-04 06:00:38 +01:00
octeep
04dd90b25b Update wireproxy.yml 2022-04-03 19:55:16 +01:00
18 changed files with 389 additions and 1157 deletions

6
.dockerignore Normal file
View File

@@ -0,0 +1,6 @@
.dockerignore
.github
.gitignore
Dockerfile
LICENSE
README.md

View File

@@ -16,7 +16,7 @@ jobs:
- name: Setting up Go - name: Setting up Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.17 go-version: 1.19
- name: Building Windows amd64 Version - name: Building Windows amd64 Version
run: | run: |
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o WireProxy_amd64.exe -v ./cmd/wireproxy 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 - name: Setting up Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.17 go-version: 1.19
- name: Building Windows arm64 Version - name: Building Windows arm64 Version
run: | run: |
CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build -o WireProxy_arm64.exe -v ./cmd/wireproxy 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 - name: Setting up Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.17 go-version: 1.19
- name: Building Linux amd64 Version - name: Building Linux amd64 Version
run: | run: |
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o WireProxy_amd64 -v ./cmd/wireproxy CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o WireProxy_amd64 -v ./cmd/wireproxy
@@ -76,7 +76,7 @@ jobs:
- name: Setting up Go - name: Setting up Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.17 go-version: 1.19
- name: Building Linux arm64 Version - name: Building Linux arm64 Version
run: | run: |
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o WireProxy_arm64 -v ./cmd/wireproxy CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o WireProxy_arm64 -v ./cmd/wireproxy
@@ -96,7 +96,7 @@ jobs:
- name: Setting up Go - name: Setting up Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.17 go-version: 1.19
- name: Building Linux s390x Version - name: Building Linux s390x Version
run: | run: |
CGO_ENABLED=0 GOOS=linux GOARCH=s390x go build -o WireProxy_s390x -v ./cmd/wireproxy CGO_ENABLED=0 GOOS=linux GOARCH=s390x go build -o WireProxy_s390x -v ./cmd/wireproxy
@@ -116,7 +116,7 @@ jobs:
- name: Setting up Go - name: Setting up Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.17 go-version: 1.19
- name: Building Darwin amd64 Version - name: Building Darwin amd64 Version
run: | run: |
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o WireProxy_amd64 -v ./cmd/wireproxy CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o WireProxy_amd64 -v ./cmd/wireproxy
@@ -136,7 +136,7 @@ jobs:
- name: Setting up Go - name: Setting up Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.17 go-version: 1.19
- name: Building Darwin arm64 Version - name: Building Darwin arm64 Version
run: | run: |
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o WireProxy_arm64 -v ./cmd/wireproxy CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o WireProxy_arm64 -v ./cmd/wireproxy

71
.github/workflows/container.yml vendored Normal file
View 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' || '' }} .

View File

@@ -14,6 +14,8 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/setup-go@v2 - uses: actions/setup-go@v2
- uses: actions/checkout@v2 with:
go-version: '1.19'
- uses: actions/checkout@v3
- name: golangci-lint - name: golangci-lint
uses: golangci/golangci-lint-action@v2 uses: golangci/golangci-lint-action@v3

View File

@@ -1,4 +1,4 @@
name: Cross compile WirePorxy name: Cross compile WireProxy
on: on:
workflow_dispatch: workflow_dispatch:
@@ -7,14 +7,14 @@ on:
- v* - v*
jobs: jobs:
WirePorxy: WireProxy:
name: Cross compile WirePorxy name: Cross compile WireProxy
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
env: env:
workdir: ./WirePorxy workdir: ./WireProxy
steps: steps:
- name: Checkout code - name: Checkout code
@@ -22,7 +22,7 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Git clone WirePorxy - name: Git clone WireProxy
run: | run: |
git clone https://github.com/octeep/wireproxy.git ${{ env.workdir }} git clone https://github.com/octeep/wireproxy.git ${{ env.workdir }}
cp ./.github/wireproxy-releaser.yml ${{ env.workdir }}/.goreleaser.yml cp ./.github/wireproxy-releaser.yml ${{ env.workdir }}/.goreleaser.yml
@@ -30,7 +30,7 @@ jobs:
- name: Set up GoReleaser - name: Set up GoReleaser
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: "1.17" go-version: "1.19"
- name: Run GoReleaser - name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2 uses: goreleaser/goreleaser-action@v2
@@ -39,6 +39,8 @@ jobs:
workdir: ${{ env.workdir }} workdir: ${{ env.workdir }}
version: latest version: latest
args: release --rm-dist args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Release binaries - name: Release binaries
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
@@ -46,4 +48,4 @@ jobs:
tag_name: wireproxy tag_name: wireproxy
files: ${{ env.workdir }}/dist/*.tar.gz files: ${{ env.workdir }}/dist/*.tar.gz
env: env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
/main /main
/wireproxy /wireproxy
*.sw? *.sw?
/.idea
.goreleaser.yml

19
Dockerfile Normal file
View 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

View 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 ## Donation
<noscript><a href="https://liberapay.com/octeep/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a></noscript> <noscript><a href="https://liberapay.com/octeep/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a></noscript>

View File

@@ -14,6 +14,8 @@ import (
// an argument to denote that this process was spawned by -d // an argument to denote that this process was spawned by -d
const daemonProcess = "daemon-process" const daemonProcess = "daemon-process"
var version = "1.0.4-dev"
// attempts to pledge and panic if it fails // attempts to pledge and panic if it fails
// this does nothing on non-OpenBSD systems // this does nothing on non-OpenBSD systems
func pledgeOrPanic(promises string) { func pledgeOrPanic(promises string) {
@@ -23,8 +25,31 @@ func pledgeOrPanic(promises string) {
} }
} }
// 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() { func main() {
exePath := executablePath()
unveilOrPanic("/", "r")
unveilOrPanic(exePath, "x")
// only allow standard stdio operation, file reading, networking, and exec // only allow standard stdio operation, file reading, networking, and exec
// also remove unveil permission to lock unveil
pledgeOrPanic("stdio rpath inet dns proc exec") pledgeOrPanic("stdio rpath inet dns proc exec")
isDaemonProcess := len(os.Args) > 1 && os.Args[1] == daemonProcess isDaemonProcess := len(os.Args) > 1 && os.Args[1] == daemonProcess
@@ -37,8 +62,9 @@ func main() {
} }
parser := argparse.NewParser("wireproxy", "Userspace wireguard client for proxying") 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"}) 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."}) configTest := parser.Flag("n", "configtest", &argparse.Options{Help: "Configtest mode. Only check the configuration file for validity."})
err := parser.Parse(args) err := parser.Parse(args)
@@ -47,6 +73,16 @@ func main() {
return return
} }
if *printVerison {
fmt.Printf("wireproxy, version %s\n", version)
return
}
if *config == "" {
fmt.Println("configuration path is required")
return
}
if !*daemon { if !*daemon {
// remove proc and exec if they are not needed // remove proc and exec if they are not needed
pledgeOrPanic("stdio rpath inet dns") pledgeOrPanic("stdio rpath inet dns")
@@ -69,14 +105,8 @@ func main() {
} }
if *daemon { if *daemon {
programPath, err := os.Executable() args[0] = daemonProcess
if err != nil { cmd := exec.Command(exePath, args...)
programPath = args[0]
}
newArgs := []string{daemonProcess}
newArgs = append(newArgs, args[1:]...)
cmd := exec.Command(programPath, newArgs...)
err = cmd.Start() err = cmd.Start()
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Println(err.Error())

128
config.go
View File

@@ -9,19 +9,24 @@ import (
"github.com/go-ini/ini" "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 // DeviceConfig contains the information to initiate a wireguard connection
type DeviceConfig struct { type DeviceConfig struct {
SelfSecretKey string SecretKey string
SelfEndpoint []netip.Addr Endpoint []netip.Addr
PeerPublicKey string Peers []PeerConfig
PeerEndpoint string DNS []netip.Addr
DNS []netip.Addr MTU int
KeepAlive int
PreSharedKey string
MTU int
} }
type TCPClientTunnelConfig struct { type TCPClientTunnelConfig struct {
@@ -109,7 +114,7 @@ func parseNetIP(section *ini.Section, keyName string) ([]netip.Addr, error) {
return []netip.Addr{}, nil return []netip.Addr{}, nil
} }
ips := []netip.Addr{} var ips []netip.Addr
for _, str := range key.StringsWithShadows(",") { for _, str := range key.StringsWithShadows(",") {
str = strings.TrimSpace(str) str = strings.TrimSpace(str)
ip, err := netip.ParseAddr(str) ip, err := netip.ParseAddr(str)
@@ -127,7 +132,7 @@ func parseCIDRNetIP(section *ini.Section, keyName string) ([]netip.Addr, error)
return []netip.Addr{}, nil return []netip.Addr{}, nil
} }
ips := []netip.Addr{} var ips []netip.Addr
for _, str := range key.StringsWithShadows(",") { for _, str := range key.StringsWithShadows(",") {
prefix, err := netip.ParsePrefix(str) prefix, err := netip.ParsePrefix(str)
if err != nil { if err != nil {
@@ -144,6 +149,24 @@ func parseCIDRNetIP(section *ini.Section, keyName string) ([]netip.Addr, error)
return ips, nil 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) { func resolveIP(ip string) (*net.IPAddr, error) {
return net.ResolveIPAddr("ip", ip) return net.ResolveIPAddr("ip", ip)
} }
@@ -174,13 +197,13 @@ func ParseInterface(cfg *ini.File, device *DeviceConfig) error {
return err return err
} }
device.SelfEndpoint = address device.Endpoint = address
privKey, err := parseBase64KeyToHex(section, "PrivateKey") privKey, err := parseBase64KeyToHex(section, "PrivateKey")
if err != nil { if err != nil {
return err return err
} }
device.SelfSecretKey = privKey device.SecretKey = privKey
dns, err := parseNetIP(section, "DNS") dns, err := parseNetIP(section, "DNS")
if err != nil { if err != nil {
@@ -199,46 +222,58 @@ func ParseInterface(cfg *ini.File, device *DeviceConfig) error {
return nil return nil
} }
// ParsePeer parses the [Peer] section and extract the information into `device` // ParsePeer parses the [Peer] section and extract the information into `peers`
func ParsePeer(cfg *ini.File, device *DeviceConfig) error { func ParsePeers(cfg *ini.File, peers *[]PeerConfig) error {
sections, err := cfg.SectionsByName("Peer") sections, err := cfg.SectionsByName("Peer")
if len(sections) != 1 || err != nil { if len(sections) < 1 || err != nil {
return errors.New("one and only one [Peer] is expected") return errors.New("at least one [Peer] is expected")
} }
section := sections[0]
decoded, err := parseBase64KeyToHex(section, "PublicKey") for _, section := range sections {
if err != nil { peer := PeerConfig{
return err PreSharedKey: "0000000000000000000000000000000000000000000000000000000000000000",
} KeepAlive: 0,
device.PeerPublicKey = decoded }
if sectionKey, err := section.GetKey("PreSharedKey"); err == nil { decoded, err := parseBase64KeyToHex(section, "PublicKey")
value, err := encodeBase64ToHex(sectionKey.String())
if err != nil { if err != nil {
return err return err
} }
device.PreSharedKey = value peer.PublicKey = decoded
}
decoded, err = parseString(section, "Endpoint") if sectionKey, err := section.GetKey("PreSharedKey"); err == nil {
if err != nil { value, err := encodeBase64ToHex(sectionKey.String())
return err if err != nil {
} return err
decoded, err = resolveIPPAndPort(decoded) }
if err != nil { peer.PreSharedKey = value
return err }
}
device.PeerEndpoint = decoded
if sectionKey, err := section.GetKey("PersistentKeepalive"); err == nil { decoded, err = parseString(section, "Endpoint")
value, err := sectionKey.Int()
if err != nil { if err != nil {
return err 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 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 // ParseConfig takes the path of a configuration file and parses it into Configuration
func ParseConfig(path string) (*Configuration, error) { func ParseConfig(path string) (*Configuration, error) {
iniOpt := ini.LoadOptions{ iniOpt := ini.LoadOptions{
Insensitive: true, Insensitive: true,
AllowShadows: true, AllowShadows: true,
AllowNonUniqueSections: true,
} }
cfg, err := ini.LoadSources(iniOpt, path) cfg, err := ini.LoadSources(iniOpt, path)
@@ -328,9 +364,7 @@ func ParseConfig(path string) (*Configuration, error) {
} }
device := &DeviceConfig{ device := &DeviceConfig{
PreSharedKey: "0000000000000000000000000000000000000000000000000000000000000000", MTU: 1420,
KeepAlive: 0,
MTU: 1420,
} }
root := cfg.Section("") root := cfg.Section("")
@@ -348,12 +382,12 @@ func ParseConfig(path string) (*Configuration, error) {
return nil, err return nil, err
} }
err = ParsePeer(wgCfg, device) err = ParsePeers(wgCfg, &device.Peers)
if err != nil { if err != nil {
return nil, err return nil, err
} }
routinesSpawners := []RoutineSpawner{} var routinesSpawners []RoutineSpawner
err = parseRoutinesConfig(&routinesSpawners, cfg, "TCPClientTunnel", parseTCPClientTunnelConfig) err = parseRoutinesConfig(&routinesSpawners, cfg, "TCPClientTunnel", parseTCPClientTunnelConfig)
if err != nil { if err != nil {

View File

@@ -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", "--config", "/etc/wireproxy/config" ]

View File

@@ -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

View File

@@ -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
View File

@@ -1,25 +1,23 @@
module github.com/octeep/wireproxy module github.com/octeep/wireproxy
go 1.17 go 1.18
require ( 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/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
github.com/go-ini/ini v1.66.4 github.com/go-ini/ini v1.66.4
golang.org/x/net v0.0.0-20220225172249-27dd8689420f golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b
golang.zx2c4.com/go118/netip v0.0.0-20211111135330-a4a02eeacf9d suah.dev/protect v1.2.0
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
) )
require ( require (
github.com/akamensky/argparse v1.3.1 // indirect
github.com/google/btree v1.0.1 // indirect github.com/google/btree v1.0.1 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/stretchr/testify v1.8.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa // indirect golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // 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
) )

999
go.sum

File diff suppressed because it is too large Load Diff

10
net.go
View File

@@ -3,8 +3,8 @@
package wireproxy package wireproxy
import ( import (
"golang.zx2c4.com/go118/netip"
"net" "net"
"net/netip"
) )
func TCPAddrFromAddrPort(addr netip.AddrPort) *net.TCPAddr { func TCPAddrFromAddrPort(addr netip.AddrPort) *net.TCPAddr {
@@ -14,11 +14,3 @@ func TCPAddrFromAddrPort(addr netip.AddrPort) *net.TCPAddr {
Port: int(addr.Port()), 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()),
}
}

View File

@@ -13,8 +13,8 @@ import (
"github.com/armon/go-socks5" "github.com/armon/go-socks5"
"golang.zx2c4.com/go118/netip"
"golang.zx2c4.com/wireguard/tun/netstack" "golang.zx2c4.com/wireguard/tun/netstack"
"net/netip"
) )
// errorLogger is the logger to print error message // errorLogger is the logger to print error message
@@ -37,6 +37,11 @@ type RoutineSpawner interface {
SpawnRoutine(vt *VirtualTun) SpawnRoutine(vt *VirtualTun)
} }
type addressPort struct {
address string
port uint16
}
// LookupAddr lookups a hostname. // LookupAddr lookups a hostname.
// DNS traffic may or may not be routed depending on VirtualTun's setting // DNS traffic may or may not be routed depending on VirtualTun's setting
func (d VirtualTun) LookupAddr(ctx context.Context, name string) ([]string, error) { 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. // ResolveAddrWithContext 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.
// DNS traffic may or may not be routed depending on VirtualTun's setting // 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) { func (d VirtualTun) ResolveAddrWithContext(ctx context.Context, name string) (*netip.Addr, error) {
addrs, err := d.LookupAddr(ctx, name) addrs, err := d.LookupAddr(ctx, name)
@@ -101,7 +84,7 @@ func (d VirtualTun) ResolveAddrWithContext(ctx context.Context, name string) (*n
return &addr, nil 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 // 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) { func (d VirtualTun) Resolve(ctx context.Context, name string) (context.Context, net.IP, error) {
addr, err := d.ResolveAddrWithContext(ctx, name) 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 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) { func (config *Socks5Config) SpawnRoutine(vt *VirtualTun) {
conf := &socks5.Config{Dial: vt.tnet.DialContext, Resolver: vt} conf := &socks5.Config{Dial: vt.tnet.DialContext, Resolver: vt}
if username := config.Username; username != "" { if username := config.Username; username != "" {
@@ -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` // tcpClientForward starts a new connection via wireguard and forward traffic from `conn`
func tcpClientForward(tnet *netstack.Net, target *net.TCPAddr, conn net.Conn) { func tcpClientForward(vt *VirtualTun, raddr *addressPort, conn net.Conn) {
sconn, err := tnet.DialTCP(target) 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 { if err != nil {
errorLogger.Printf("TCP Client Tunnel to %s: %s\n", target, err.Error()) errorLogger.Printf("TCP Client Tunnel to %s: %s\n", target, err.Error())
return return
@@ -161,13 +176,12 @@ func tcpClientForward(tnet *netstack.Net, target *net.TCPAddr, conn net.Conn) {
go connForward(1024, conn, sconn) 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) { func (conf *TCPClientTunnelConfig) SpawnRoutine(vt *VirtualTun) {
raddr, err := vt.ResolveAddrPort(conf.Target) raddr, err := parseAddressPort(conf.Target)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
tcpAddr := TCPAddrFromAddrPort(*raddr)
server, err := net.ListenTCP("tcp", conf.BindAddress) server, err := net.ListenTCP("tcp", conf.BindAddress)
if err != nil { if err != nil {
@@ -179,13 +193,21 @@ func (conf *TCPClientTunnelConfig) SpawnRoutine(vt *VirtualTun) {
if err != nil { if err != nil {
log.Fatal(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` // tcpServerForward starts a new connection locally and forward traffic from `conn`
func tcpServerForward(target *net.TCPAddr, conn net.Conn) { func tcpServerForward(vt *VirtualTun, raddr *addressPort, conn net.Conn) {
sconn, err := net.DialTCP("tcp", nil, target) 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 { if err != nil {
errorLogger.Printf("TCP Server Tunnel to %s: %s\n", target, err.Error()) errorLogger.Printf("TCP Server Tunnel to %s: %s\n", target, err.Error())
return return
@@ -195,13 +217,12 @@ func tcpServerForward(target *net.TCPAddr, conn net.Conn) {
go connForward(1024, conn, sconn) 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) { func (conf *TCPServerTunnelConfig) SpawnRoutine(vt *VirtualTun) {
raddr, err := vt.ResolveAddrPort(conf.Target) raddr, err := parseAddressPort(conf.Target)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
tcpAddr := TCPAddrFromAddrPort(*raddr)
addr := &net.TCPAddr{Port: conf.ListenPort} addr := &net.TCPAddr{Port: conf.ListenPort}
server, err := vt.tnet.ListenTCP(addr) server, err := vt.tnet.ListenTCP(addr)
@@ -214,6 +235,6 @@ func (conf *TCPServerTunnelConfig) SpawnRoutine(vt *VirtualTun) {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
go tcpServerForward(tcpAddr, conn) go tcpServerForward(vt, raddr, conn)
} }
} }

View File

@@ -1,9 +1,12 @@
package wireproxy package wireproxy
import ( import (
"bytes"
"fmt" "fmt"
"golang.zx2c4.com/go118/netip" "net/netip"
"github.com/MakeNowJust/heredoc/v2"
"golang.zx2c4.com/wireguard/conn" "golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun/netstack" "golang.zx2c4.com/wireguard/tun/netstack"
@@ -19,15 +22,33 @@ type DeviceSetting struct {
// serialize the config into an IPC request and DeviceSetting // serialize the config into an IPC request and DeviceSetting
func createIPCRequest(conf *DeviceConfig) (*DeviceSetting, error) { func createIPCRequest(conf *DeviceConfig) (*DeviceSetting, error) {
request := fmt.Sprintf(`private_key=%s var request bytes.Buffer
public_key=%s
endpoint=%s
persistent_keepalive_interval=%d
preshared_key=%s
allowed_ip=0.0.0.0/0
allowed_ip=::0/0`, conf.SelfSecretKey, conf.PeerPublicKey, conf.PeerEndpoint, conf.KeepAlive, conf.PreSharedKey)
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 return setting, nil
} }