mirror of
https://github.com/whyvl/wireproxy.git
synced 2025-06-29 01:18:01 +02:00
Compare commits
No commits in common. "master" and "v1.0.9" have entirely different histories.
16 changed files with 139 additions and 354 deletions
1
.github/workflows/container.yml
vendored
1
.github/workflows/container.yml
vendored
|
@ -59,7 +59,6 @@ jobs:
|
||||||
docker buildx build \
|
docker buildx build \
|
||||||
--platform "$BUILD_PLATFORMS" \
|
--platform "$BUILD_PLATFORMS" \
|
||||||
--tag "$CONTAINER_NAME:$CONTAINER_TAG" \
|
--tag "$CONTAINER_NAME:$CONTAINER_TAG" \
|
||||||
--tag "$CONTAINER_NAME:$GITHUB_SHA" \
|
|
||||||
--label "org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}" \
|
--label "org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}" \
|
||||||
--label "org.opencontainers.image.documentation=${{ 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.url=${{ github.server_url }}/${{ github.repository }}/packages" \
|
||||||
|
|
65
README.md
65
README.md
|
@ -1,5 +1,4 @@
|
||||||
# wireproxy
|
# wireproxy
|
||||||
|
|
||||||
[](./LICENSE)
|
[](./LICENSE)
|
||||||
[](https://github.com/octeep/wireproxy/actions)
|
[](https://github.com/octeep/wireproxy/actions)
|
||||||
[](https://pkg.go.dev/github.com/octeep/wireproxy)
|
[](https://pkg.go.dev/github.com/octeep/wireproxy)
|
||||||
|
@ -7,14 +6,12 @@
|
||||||
A wireguard client that exposes itself as a socks5/http proxy or tunnels.
|
A wireguard client that exposes itself as a socks5/http proxy or tunnels.
|
||||||
|
|
||||||
# What is this
|
# What is this
|
||||||
|
|
||||||
`wireproxy` is a completely userspace application that connects to a wireguard peer,
|
`wireproxy` is a completely userspace application that connects to a wireguard peer,
|
||||||
and exposes a socks5/http proxy or tunnels on the machine. This can be useful if you need
|
and exposes a socks5/http proxy or tunnels on the machine. This can be useful if you need
|
||||||
to connect to certain sites via a wireguard peer, but can't be bothered to setup a new network
|
to connect to certain sites via a wireguard peer, but can't be bothered to setup a new network
|
||||||
interface for whatever reasons.
|
interface for whatever reasons.
|
||||||
|
|
||||||
# Why you might want this
|
# Why you might want this
|
||||||
|
|
||||||
- You simply want to use wireguard as a way to proxy some traffic.
|
- You simply want to use wireguard as a way to proxy some traffic.
|
||||||
- You don't want root permission just to change wireguard settings.
|
- You don't want root permission just to change wireguard settings.
|
||||||
|
|
||||||
|
@ -23,33 +20,23 @@ and configured my browser to use wireproxy for certain sites. It's pretty useful
|
||||||
wireproxy is completely isolated from my network interfaces, and I don't need root to configure
|
wireproxy is completely isolated from my network interfaces, and I don't need root to configure
|
||||||
anything.
|
anything.
|
||||||
|
|
||||||
Users who want something similar but for Amnezia VPN can use [this fork](https://github.com/artem-russkikh/wireproxy-awg)
|
Users who want something similar but for Amnezia VPN can use [this fork](https://github.com/juev/wireproxy/tree/feature/amnezia-go)
|
||||||
of wireproxy by [@artem-russkikh](https://github.com/artem-russkikh).
|
of wireproxy by [@juev](https://github.com/juev).
|
||||||
|
|
||||||
# Sponsor
|
|
||||||
|
|
||||||
This project is supported by [IPRoyal](https://iproyal.com/?r=795836). You can get premium quality proxies at unbeatable prices
|
|
||||||
with a discount using [this referral link](https://iproyal.com/?r=795836)! 🚀
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
# Feature
|
# Feature
|
||||||
|
|
||||||
- TCP static routing for client and server
|
- TCP static routing for client and server
|
||||||
- SOCKS5/HTTP proxy (currently only CONNECT is supported)
|
- SOCKS5/HTTP proxy (currently only CONNECT is supported)
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
- UDP Support in SOCKS5
|
- UDP Support in SOCKS5
|
||||||
- UDP static routing
|
- UDP static routing
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
```
|
||||||
```bash
|
./wireproxy -c [path to config]
|
||||||
./wireproxy [-c path to config]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```
|
||||||
usage: wireproxy [-h|--help] [-c|--config "<value>"] [-s|--silent]
|
usage: wireproxy [-h|--help] [-c|--config "<value>"] [-s|--silent]
|
||||||
[-d|--daemon] [-i|--info "<value>"] [-v|--version]
|
[-d|--daemon] [-i|--info "<value>"] [-v|--version]
|
||||||
[-n|--configtest]
|
[-n|--configtest]
|
||||||
|
@ -60,36 +47,27 @@ Arguments:
|
||||||
|
|
||||||
-h --help Print help information
|
-h --help Print help information
|
||||||
-c --config Path of configuration file
|
-c --config Path of configuration file
|
||||||
Default paths: /etc/wireproxy/wireproxy.conf, $HOME/.config/wireproxy.conf
|
|
||||||
-s --silent Silent mode
|
-s --silent Silent mode
|
||||||
-d --daemon Make wireproxy run in background
|
-d --daemon Make wireproxy run in background
|
||||||
-i --info Specify the address and port for exposing health status
|
-i --info Specify the address and port for exposing health status
|
||||||
-v --version Print version
|
-v --version Print version
|
||||||
-n --configtest Configtest mode. Only check the configuration file for
|
-n --configtest Configtest mode. Only check the configuration file for
|
||||||
validity.
|
validity.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
# Build instruction
|
# Build instruction
|
||||||
|
```
|
||||||
```bash
|
|
||||||
git clone https://github.com/octeep/wireproxy
|
git clone https://github.com/octeep/wireproxy
|
||||||
cd wireproxy
|
cd wireproxy
|
||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
|
||||||
# Install
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go install github.com/pufferffish/wireproxy/cmd/wireproxy@v1.0.9 # or @latest
|
|
||||||
```
|
|
||||||
|
|
||||||
# Use with VPN
|
# Use with VPN
|
||||||
|
|
||||||
Instructions for using wireproxy with Firefox container tabs and auto-start on MacOS can be found [here](/UseWithVPN.md).
|
Instructions for using wireproxy with Firefox container tabs and auto-start on MacOS can be found [here](/UseWithVPN.md).
|
||||||
|
|
||||||
# Sample config file
|
# Sample config file
|
||||||
|
```
|
||||||
```ini
|
|
||||||
# The [Interface] and [Peer] configurations follow the same semantics and meaning
|
# The [Interface] and [Peer] configurations follow the same semantics and meaning
|
||||||
# of a wg-quick configuration. To understand what these fields mean, please refer to:
|
# of a wg-quick configuration. To understand what these fields mean, please refer to:
|
||||||
# https://wiki.archlinux.org/title/WireGuard#Persistent_configuration
|
# https://wiki.archlinux.org/title/WireGuard#Persistent_configuration
|
||||||
|
@ -98,7 +76,6 @@ Instructions for using wireproxy with Firefox container tabs and auto-start on M
|
||||||
Address = 10.200.200.2/32 # The subnet should be /32 and /128 for IPv4 and v6 respectively
|
Address = 10.200.200.2/32 # The subnet should be /32 and /128 for IPv4 and v6 respectively
|
||||||
# MTU = 1420 (optional)
|
# MTU = 1420 (optional)
|
||||||
PrivateKey = uCTIK+56CPyCvwJxmU5dBfuyJvPuSXAq1FzHdnIxe1Q=
|
PrivateKey = uCTIK+56CPyCvwJxmU5dBfuyJvPuSXAq1FzHdnIxe1Q=
|
||||||
# PrivateKey = $MY_WIREGUARD_PRIVATE_KEY # Alternatively, reference environment variables
|
|
||||||
DNS = 10.200.200.1
|
DNS = 10.200.200.1
|
||||||
|
|
||||||
[Peer]
|
[Peer]
|
||||||
|
@ -156,8 +133,7 @@ BindAddress = 127.0.0.1:25345
|
||||||
|
|
||||||
Alternatively, if you already have a wireguard config, you can import it in the
|
Alternatively, if you already have a wireguard config, you can import it in the
|
||||||
wireproxy config file like this:
|
wireproxy config file like this:
|
||||||
|
```
|
||||||
```ini
|
|
||||||
WGConfig = <path to the wireguard config>
|
WGConfig = <path to the wireguard config>
|
||||||
|
|
||||||
# Same semantics as above
|
# Same semantics as above
|
||||||
|
@ -173,8 +149,7 @@ WGConfig = <path to the wireguard config>
|
||||||
|
|
||||||
Having multiple peers is also supported. `AllowedIPs` would need to be specified
|
Having multiple peers is also supported. `AllowedIPs` would need to be specified
|
||||||
such that wireproxy would know which peer to forward to.
|
such that wireproxy would know which peer to forward to.
|
||||||
|
```
|
||||||
```ini
|
|
||||||
[Interface]
|
[Interface]
|
||||||
Address = 10.254.254.40/32
|
Address = 10.254.254.40/32
|
||||||
PrivateKey = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=
|
PrivateKey = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=
|
||||||
|
@ -206,8 +181,7 @@ Target = service-three.servicenet:80
|
||||||
```
|
```
|
||||||
|
|
||||||
Wireproxy can also allow peers to connect to it:
|
Wireproxy can also allow peers to connect to it:
|
||||||
|
```
|
||||||
```ini
|
|
||||||
[Interface]
|
[Interface]
|
||||||
ListenPort = 5400
|
ListenPort = 5400
|
||||||
...
|
...
|
||||||
|
@ -217,9 +191,7 @@ PublicKey = YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY=
|
||||||
AllowedIPs = 10.254.254.100/32
|
AllowedIPs = 10.254.254.100/32
|
||||||
# Note there is no Endpoint defined here.
|
# Note there is no Endpoint defined here.
|
||||||
```
|
```
|
||||||
|
|
||||||
# Health endpoint
|
# Health endpoint
|
||||||
|
|
||||||
Wireproxy supports exposing a health endpoint for monitoring purposes.
|
Wireproxy supports exposing a health endpoint for monitoring purposes.
|
||||||
The argument `--info/-i` specifies an address and port (e.g. `localhost:9080`), which exposes a HTTP server that provides health status metric of the server.
|
The argument `--info/-i` specifies an address and port (e.g. `localhost:9080`), which exposes a HTTP server that provides health status metric of the server.
|
||||||
|
|
||||||
|
@ -230,8 +202,7 @@ Currently two endpoints are implemented:
|
||||||
`/readyz`: This responds with a json which shows the last time a pong is received from an IP specified with `CheckAlive`. When `CheckAlive` is set, a ping is sent out to addresses in `CheckAlive` per `CheckAliveInterval` seconds (defaults to 5) via wireguard. If a pong has not been received from one of the addresses within the last `CheckAliveInterval` seconds (+2 seconds for some leeway to account for latency), then it would respond with a 503, otherwise a 200.
|
`/readyz`: This responds with a json which shows the last time a pong is received from an IP specified with `CheckAlive`. When `CheckAlive` is set, a ping is sent out to addresses in `CheckAlive` per `CheckAliveInterval` seconds (defaults to 5) via wireguard. If a pong has not been received from one of the addresses within the last `CheckAliveInterval` seconds (+2 seconds for some leeway to account for latency), then it would respond with a 503, otherwise a 200.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
```
|
||||||
```ini
|
|
||||||
[Interface]
|
[Interface]
|
||||||
PrivateKey = censored
|
PrivateKey = censored
|
||||||
Address = 10.2.0.2/32
|
Address = 10.2.0.2/32
|
||||||
|
@ -247,10 +218,8 @@ Endpoint = 149.34.244.174:51820
|
||||||
[Socks5]
|
[Socks5]
|
||||||
BindAddress = 127.0.0.1:25344
|
BindAddress = 127.0.0.1:25344
|
||||||
```
|
```
|
||||||
|
|
||||||
`/readyz` would respond with
|
`/readyz` would respond with
|
||||||
|
```
|
||||||
```text
|
|
||||||
< HTTP/1.1 503 Service Unavailable
|
< HTTP/1.1 503 Service Unavailable
|
||||||
< Date: Thu, 11 Apr 2024 00:54:59 GMT
|
< Date: Thu, 11 Apr 2024 00:54:59 GMT
|
||||||
< Content-Length: 35
|
< Content-Length: 35
|
||||||
|
@ -260,18 +229,15 @@ BindAddress = 127.0.0.1:25344
|
||||||
```
|
```
|
||||||
|
|
||||||
And for:
|
And for:
|
||||||
|
```
|
||||||
```ini
|
|
||||||
[Interface]
|
[Interface]
|
||||||
PrivateKey = censored
|
PrivateKey = censored
|
||||||
Address = 10.2.0.2/32
|
Address = 10.2.0.2/32
|
||||||
DNS = 10.2.0.1
|
DNS = 10.2.0.1
|
||||||
CheckAlive = 1.1.1.1
|
CheckAlive = 1.1.1.1
|
||||||
```
|
```
|
||||||
|
|
||||||
`/readyz` would respond with
|
`/readyz` would respond with
|
||||||
|
```
|
||||||
```text
|
|
||||||
< HTTP/1.1 200 OK
|
< HTTP/1.1 200 OK
|
||||||
< Date: Thu, 11 Apr 2024 00:56:21 GMT
|
< Date: Thu, 11 Apr 2024 00:56:21 GMT
|
||||||
< Content-Length: 23
|
< Content-Length: 23
|
||||||
|
@ -285,5 +251,4 @@ If nothing is set for `CheckAlive`, an empty JSON object with 200 will be the re
|
||||||
The peer which the ICMP ping packet is routed to depends on the `AllowedIPs` set for each peers.
|
The peer which the ICMP ping packet is routed to depends on the `AllowedIPs` set for each peers.
|
||||||
|
|
||||||
# Stargazers over time
|
# Stargazers over time
|
||||||
|
|
||||||
[](https://starchart.cc/octeep/wireproxy)
|
[](https://starchart.cc/octeep/wireproxy)
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
# Getting a Wireguard Server
|
# Getting a Wireguard Server
|
||||||
|
|
||||||
You can create your own wireguard server using a host service like DigitalOcean,
|
You can create your own wireguard server using a host service like DigitalOcean,
|
||||||
or you can get a VPN service that provides WireGuard configs.
|
or you can get a VPN service that provides WireGuard configs.
|
||||||
|
|
||||||
I recommend ProtonVPN, because it is highly secure and has a great WireGuard
|
I recommend ProtonVPN, because it is highly secure and has a great WireGuard
|
||||||
config generator.
|
config generator.
|
||||||
|
|
||||||
Simply go to <https://account.protonvpn.com/downloads> and scroll down to the
|
Simply go to https://account.protonvpn.com/downloads and scroll down to the
|
||||||
wireguard section to generate your configs, then paste into the appropriate
|
wireguard section to generate your configs, then paste into the appropriate
|
||||||
section below.
|
section below.
|
||||||
|
|
||||||
|
@ -26,11 +25,9 @@ naming should also be similar (e.g.
|
||||||
`/Users/jonny/Library/LaunchAgents/com.ProtonUS.adblock.plist`)
|
`/Users/jonny/Library/LaunchAgents/com.ProtonUS.adblock.plist`)
|
||||||
|
|
||||||
## Config File
|
## Config File
|
||||||
|
|
||||||
Make sure you use a unique port for every separate server
|
Make sure you use a unique port for every separate server
|
||||||
I recommend you set proxy authentication, you can use the same user/pass for all
|
I recommend you set proxy authentication, you can use the same user/pass for all
|
||||||
|
```
|
||||||
```ini
|
|
||||||
# Link to the Downloaded config
|
# Link to the Downloaded config
|
||||||
WGConfig = /Users/jonny/vpntabs/ProtonUS.adblock.server.conf
|
WGConfig = /Users/jonny/vpntabs/ProtonUS.adblock.server.conf
|
||||||
|
|
||||||
|
@ -46,27 +43,24 @@ BindAddress = 127.0.0.1:25344 # Update the port here for each new server
|
||||||
```
|
```
|
||||||
|
|
||||||
## Startup Script File
|
## Startup Script File
|
||||||
|
|
||||||
This is a bash script to facilitate startup, not strictly essential, but adds
|
This is a bash script to facilitate startup, not strictly essential, but adds
|
||||||
ease.
|
ease.
|
||||||
Note, you MUST update the first path to wherever you installed this code to.
|
Note, you MUST update the first path to wherever you installed this code to.
|
||||||
Make sure you use the path for the config file above, not the one you downloaded
|
Make sure you use the path for the config file above, not the one you downloaded
|
||||||
from e.g. protonvpn.
|
from e.g. protonvpn.
|
||||||
|
```
|
||||||
```bash
|
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
/Users/jonny/wireproxy/wireproxy -c /Users/jonny/vpntabs/ProtonUS.adblock.conf
|
/Users/jonny/wireproxy/wireproxy -c /Users/jonny/vpntabs/ProtonUS.adblock.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
## MacOS LaunchAgent
|
## MacOS LaunchAgent
|
||||||
|
|
||||||
To make it run every time you start your computer, you can create a launch agent
|
To make it run every time you start your computer, you can create a launch agent
|
||||||
in `$HOME/Library/LaunchAgents`. Name reference above.
|
in `$HOME/Library/LaunchAgents`. Name reference above.
|
||||||
|
|
||||||
That file should contain the following, the label should be the same as the file
|
That file should contain the following, the label should be the same as the file
|
||||||
name and the paths should be set correctly:
|
name and the paths should be set correctly:
|
||||||
|
|
||||||
```xml
|
```
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
|
@ -76,7 +70,7 @@ name and the paths should be set correctly:
|
||||||
<key>Program</key>
|
<key>Program</key>
|
||||||
<string>/Users/jonny/vpntabs/ProtonUS.adblock.sh</string>
|
<string>/Users/jonny/vpntabs/ProtonUS.adblock.sh</string>
|
||||||
<key>RunAtLoad</key>
|
<key>RunAtLoad</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>KeepAlive</key>
|
<key>KeepAlive</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
|
@ -88,7 +82,6 @@ To enable it, run
|
||||||
`launchtl start ~/Library/LaunchAgents/com.PortonUS.adblock.plist`
|
`launchtl start ~/Library/LaunchAgents/com.PortonUS.adblock.plist`
|
||||||
|
|
||||||
# Firefox Setup
|
# Firefox Setup
|
||||||
|
|
||||||
You will need to enable the Multi Account Container Tabs extension and a proxy extension, I
|
You will need to enable the Multi Account Container Tabs extension and a proxy extension, I
|
||||||
recommend Sideberry, but Container Proxy also works.
|
recommend Sideberry, but Container Proxy also works.
|
||||||
|
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.6 KiB |
|
@ -22,12 +22,6 @@ 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"
|
||||||
|
|
||||||
// default paths for wireproxy config file
|
|
||||||
var default_config_paths = []string {
|
|
||||||
"/etc/wireproxy/wireproxy.conf",
|
|
||||||
os.Getenv("HOME")+"/.config/wireproxy.conf",
|
|
||||||
}
|
|
||||||
|
|
||||||
var version = "1.0.8-dev"
|
var version = "1.0.8-dev"
|
||||||
|
|
||||||
func panicIfError(err error) {
|
func panicIfError(err error) {
|
||||||
|
@ -57,16 +51,6 @@ func executablePath() string {
|
||||||
return programPath
|
return programPath
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if default config file paths exist
|
|
||||||
func configFilePath() (string, bool) {
|
|
||||||
for _, path := range default_config_paths {
|
|
||||||
if _, err := os.Stat(path); err == nil {
|
|
||||||
return path, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
func lock(stage string) {
|
func lock(stage string) {
|
||||||
switch stage {
|
switch stage {
|
||||||
case "boot":
|
case "boot":
|
||||||
|
@ -193,12 +177,8 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if *config == "" {
|
if *config == "" {
|
||||||
if path, config_exist := configFilePath(); config_exist {
|
fmt.Println("configuration path is required")
|
||||||
*config = path
|
return
|
||||||
} else {
|
|
||||||
fmt.Println("configuration path is required")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*daemon {
|
if !*daemon {
|
||||||
|
|
72
config.go
72
config.go
|
@ -5,7 +5,6 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-ini/ini"
|
"github.com/go-ini/ini"
|
||||||
|
@ -69,18 +68,6 @@ func parseString(section *ini.Section, keyName string) (string, error) {
|
||||||
if key == nil {
|
if key == nil {
|
||||||
return "", errors.New(keyName + " should not be empty")
|
return "", errors.New(keyName + " should not be empty")
|
||||||
}
|
}
|
||||||
value := key.String()
|
|
||||||
if strings.HasPrefix(value, "$") {
|
|
||||||
if strings.HasPrefix(value, "$$") {
|
|
||||||
return strings.Replace(value, "$$", "$", 1), nil
|
|
||||||
}
|
|
||||||
var ok bool
|
|
||||||
value, ok = os.LookupEnv(strings.TrimPrefix(value, "$"))
|
|
||||||
if !ok {
|
|
||||||
return "", errors.New(keyName + " references unset environment variable " + key.String())
|
|
||||||
}
|
|
||||||
return value, nil
|
|
||||||
}
|
|
||||||
return key.String(), nil
|
return key.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,21 +122,15 @@ func encodeBase64ToHex(key string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseNetIP(section *ini.Section, keyName string) ([]netip.Addr, error) {
|
func parseNetIP(section *ini.Section, keyName string) ([]netip.Addr, error) {
|
||||||
key, err := parseString(section, keyName)
|
key := section.Key(keyName)
|
||||||
if err != nil {
|
if key == nil {
|
||||||
if strings.Contains(err.Error(), "should not be empty") {
|
return []netip.Addr{}, nil
|
||||||
return []netip.Addr{}, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
keys := strings.Split(key, ",")
|
keys := key.StringsWithShadows(",")
|
||||||
var ips = make([]netip.Addr, 0, len(keys))
|
var ips = make([]netip.Addr, 0, len(keys))
|
||||||
for _, str := range keys {
|
for _, str := range keys {
|
||||||
str = strings.TrimSpace(str)
|
str = strings.TrimSpace(str)
|
||||||
if len(str) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ip, err := netip.ParseAddr(str)
|
ip, err := netip.ParseAddr(str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -160,53 +141,34 @@ func parseNetIP(section *ini.Section, keyName string) ([]netip.Addr, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseCIDRNetIP(section *ini.Section, keyName string) ([]netip.Addr, error) {
|
func parseCIDRNetIP(section *ini.Section, keyName string) ([]netip.Addr, error) {
|
||||||
key, err := parseString(section, keyName)
|
key := section.Key(keyName)
|
||||||
if err != nil {
|
if key == nil {
|
||||||
if strings.Contains(err.Error(), "should not be empty") {
|
return []netip.Addr{}, nil
|
||||||
return []netip.Addr{}, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
keys := strings.Split(key, ",")
|
keys := key.StringsWithShadows(",")
|
||||||
var ips = make([]netip.Addr, 0, len(keys))
|
var ips = make([]netip.Addr, 0, len(keys))
|
||||||
for _, str := range keys {
|
for _, str := range keys {
|
||||||
str = strings.TrimSpace(str)
|
prefix, err := netip.ParsePrefix(str)
|
||||||
if len(str) == 0 {
|
if err != nil {
|
||||||
continue
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if addr, err := netip.ParseAddr(str); err == nil {
|
addr := prefix.Addr()
|
||||||
ips = append(ips, addr)
|
ips = append(ips, addr)
|
||||||
} else {
|
|
||||||
prefix, err := netip.ParsePrefix(str)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
addr := prefix.Addr()
|
|
||||||
ips = append(ips, addr)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ips, nil
|
return ips, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseAllowedIPs(section *ini.Section) ([]netip.Prefix, error) {
|
func parseAllowedIPs(section *ini.Section) ([]netip.Prefix, error) {
|
||||||
key, err := parseString(section, "AllowedIPs")
|
key := section.Key("AllowedIPs")
|
||||||
if err != nil {
|
if key == nil {
|
||||||
if strings.Contains(err.Error(), "should not be empty") {
|
return []netip.Prefix{}, nil
|
||||||
return []netip.Prefix{}, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
keys := strings.Split(key, ",")
|
keys := key.StringsWithShadows(",")
|
||||||
var ips = make([]netip.Prefix, 0, len(keys))
|
var ips = make([]netip.Prefix, 0, len(keys))
|
||||||
for _, str := range keys {
|
for _, str := range keys {
|
||||||
str = strings.TrimSpace(str)
|
|
||||||
if len(str) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
prefix, err := netip.ParsePrefix(str)
|
prefix, err := netip.ParsePrefix(str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
package wireproxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-ini/ini"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func loadIniConfig(config string) (*ini.File, error) {
|
|
||||||
iniOpt := ini.LoadOptions{
|
|
||||||
Insensitive: true,
|
|
||||||
AllowShadows: true,
|
|
||||||
AllowNonUniqueSections: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
return ini.LoadSources(iniOpt, []byte(config))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWireguardConfWithoutSubnet(t *testing.T) {
|
|
||||||
const config = `
|
|
||||||
[Interface]
|
|
||||||
PrivateKey = LAr1aNSNF9d0MjwUgAVC4020T0N/E5NUtqVv5EnsSz0=
|
|
||||||
Address = 10.5.0.2
|
|
||||||
DNS = 1.1.1.1
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = e8LKAc+f9xEzq9Ar7+MfKRrs+gZ/4yzvpRJLRJ/VJ1w=
|
|
||||||
AllowedIPs = 0.0.0.0/0, ::/0
|
|
||||||
Endpoint = 94.140.11.15:51820
|
|
||||||
PersistentKeepalive = 25`
|
|
||||||
var cfg DeviceConfig
|
|
||||||
iniData, err := loadIniConfig(config)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ParseInterface(iniData, &cfg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWireguardConfWithSubnet(t *testing.T) {
|
|
||||||
const config = `
|
|
||||||
[Interface]
|
|
||||||
PrivateKey = LAr1aNSNF9d0MjwUgAVC4020T0N/E5NUtqVv5EnsSz0=
|
|
||||||
Address = 10.5.0.2/23
|
|
||||||
DNS = 1.1.1.1
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = e8LKAc+f9xEzq9Ar7+MfKRrs+gZ/4yzvpRJLRJ/VJ1w=
|
|
||||||
AllowedIPs = 0.0.0.0/0, ::/0
|
|
||||||
Endpoint = 94.140.11.15:51820
|
|
||||||
PersistentKeepalive = 25`
|
|
||||||
var cfg DeviceConfig
|
|
||||||
iniData, err := loadIniConfig(config)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ParseInterface(iniData, &cfg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWireguardConfWithManyAddress(t *testing.T) {
|
|
||||||
const config = `
|
|
||||||
[Interface]
|
|
||||||
PrivateKey = mBsVDahr1XIu9PPd17UmsDdB6E53nvmS47NbNqQCiFM=
|
|
||||||
Address = 100.96.0.190,2606:B300:FFFF:fe8a:2ac6:c7e8:b021:6f5f/128
|
|
||||||
DNS = 198.18.0.1,198.18.0.2
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = SHnh4C2aDXhp1gjIqceGhJrhOLSeNYcqWLKcYnzj00U=
|
|
||||||
AllowedIPs = 0.0.0.0/0,::/0
|
|
||||||
Endpoint = 192.200.144.22:51820`
|
|
||||||
var cfg DeviceConfig
|
|
||||||
iniData, err := loadIniConfig(config)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ParseInterface(iniData, &cfg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
7
go.mod
7
go.mod
|
@ -9,16 +9,17 @@ require (
|
||||||
github.com/akamensky/argparse v1.4.0
|
github.com/akamensky/argparse v1.4.0
|
||||||
github.com/go-ini/ini v1.67.0
|
github.com/go-ini/ini v1.67.0
|
||||||
github.com/landlock-lsm/go-landlock v0.0.0-20240216195629-efb66220540a
|
github.com/landlock-lsm/go-landlock v0.0.0-20240216195629-efb66220540a
|
||||||
|
github.com/sourcegraph/conc v0.3.0
|
||||||
github.com/things-go/go-socks5 v0.0.5
|
github.com/things-go/go-socks5 v0.0.5
|
||||||
golang.org/x/net v0.33.0
|
golang.org/x/net v0.23.0
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
||||||
suah.dev/protect v1.2.3
|
suah.dev/protect v1.2.3
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/google/btree v1.1.2 // indirect
|
github.com/google/btree v1.1.2 // indirect
|
||||||
golang.org/x/crypto v0.31.0 // indirect
|
golang.org/x/crypto v0.21.0 // indirect
|
||||||
golang.org/x/sys v0.28.0 // indirect
|
golang.org/x/sys v0.18.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 // indirect
|
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 // indirect
|
||||||
|
|
14
go.sum
14
go.sum
|
@ -12,17 +12,19 @@ github.com/landlock-lsm/go-landlock v0.0.0-20240216195629-efb66220540a h1:dz+a1M
|
||||||
github.com/landlock-lsm/go-landlock v0.0.0-20240216195629-efb66220540a/go.mod h1:1NY/VPO8xm3hXw3f+M65z+PJDLUaZA5cu7OfanxoUzY=
|
github.com/landlock-lsm/go-landlock v0.0.0-20240216195629-efb66220540a/go.mod h1:1NY/VPO8xm3hXw3f+M65z+PJDLUaZA5cu7OfanxoUzY=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||||
|
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/things-go/go-socks5 v0.0.5 h1:qvKaGcBkfDrUL33SchHN93srAmYGzb4CxSM2DPYufe8=
|
github.com/things-go/go-socks5 v0.0.5 h1:qvKaGcBkfDrUL33SchHN93srAmYGzb4CxSM2DPYufe8=
|
||||||
github.com/things-go/go-socks5 v0.0.5/go.mod h1:mtzInf8v5xmsBpHZVbIw2YQYhc4K0jRwzfsH64Uh0IQ=
|
github.com/things-go/go-socks5 v0.0.5/go.mod h1:mtzInf8v5xmsBpHZVbIw2YQYhc4K0jRwzfsH64Uh0IQ=
|
||||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||||
|
|
60
http.go
60
http.go
|
@ -10,6 +10,8 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sourcegraph/conc"
|
||||||
)
|
)
|
||||||
|
|
||||||
const proxyAuthHeaderKey = "Proxy-Authorization"
|
const proxyAuthHeaderKey = "Proxy-Authorization"
|
||||||
|
@ -29,23 +31,23 @@ func (s *HTTPServer) authenticate(req *http.Request) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auth := req.Header.Get(proxyAuthHeaderKey)
|
auth := req.Header.Get(proxyAuthHeaderKey)
|
||||||
if auth == "" {
|
if auth != "" {
|
||||||
return http.StatusProxyAuthRequired, fmt.Errorf("%s", http.StatusText(http.StatusProxyAuthRequired))
|
enc := strings.TrimPrefix(auth, "Basic ")
|
||||||
|
str, err := base64.StdEncoding.DecodeString(enc)
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusNotAcceptable, fmt.Errorf("decode username and password failed: %w", err)
|
||||||
|
}
|
||||||
|
pairs := bytes.SplitN(str, []byte(":"), 2)
|
||||||
|
if len(pairs) != 2 {
|
||||||
|
return http.StatusLengthRequired, fmt.Errorf("username and password format invalid")
|
||||||
|
}
|
||||||
|
if s.auth.Valid(string(pairs[0]), string(pairs[1])) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return http.StatusUnauthorized, fmt.Errorf("username and password not matching")
|
||||||
}
|
}
|
||||||
|
|
||||||
enc := strings.TrimPrefix(auth, "Basic ")
|
return http.StatusProxyAuthRequired, fmt.Errorf(http.StatusText(http.StatusProxyAuthRequired))
|
||||||
str, err := base64.StdEncoding.DecodeString(enc)
|
|
||||||
if err != nil {
|
|
||||||
return http.StatusNotAcceptable, fmt.Errorf("decode username and password failed: %w", err)
|
|
||||||
}
|
|
||||||
pairs := bytes.SplitN(str, []byte(":"), 2)
|
|
||||||
if len(pairs) != 2 {
|
|
||||||
return http.StatusLengthRequired, fmt.Errorf("username and password format invalid")
|
|
||||||
}
|
|
||||||
if s.auth.Valid(string(pairs[0]), string(pairs[1])) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
return http.StatusUnauthorized, fmt.Errorf("username and password not matching")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HTTPServer) handleConn(req *http.Request, conn net.Conn) (peer net.Conn, err error) {
|
func (s *HTTPServer) handleConn(req *http.Request, conn net.Conn) (peer net.Conn, err error) {
|
||||||
|
@ -101,11 +103,7 @@ func (s *HTTPServer) serve(conn net.Conn) {
|
||||||
|
|
||||||
code, err := s.authenticate(req)
|
code, err := s.authenticate(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp := responseWith(req, code)
|
_ = responseWith(req, code).Write(conn)
|
||||||
if code == http.StatusProxyAuthRequired {
|
|
||||||
resp.Header.Set("Proxy-Authenticate", "Basic realm=\"Proxy\"")
|
|
||||||
}
|
|
||||||
_ = resp.Write(conn)
|
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -129,19 +127,17 @@ func (s *HTTPServer) serve(conn net.Conn) {
|
||||||
log.Println("dial proxy failed: peer nil")
|
log.Println("dial proxy failed: peer nil")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer conn.Close()
|
wg := conc.NewWaitGroup()
|
||||||
defer peer.Close()
|
wg.Go(func() {
|
||||||
|
_, err = io.Copy(conn, peer)
|
||||||
_, _ = io.Copy(conn, peer)
|
_ = conn.Close()
|
||||||
}()
|
})
|
||||||
|
wg.Go(func() {
|
||||||
go func() {
|
_, err = io.Copy(peer, conn)
|
||||||
defer conn.Close()
|
_ = peer.Close()
|
||||||
defer peer.Close()
|
})
|
||||||
|
wg.Wait()
|
||||||
_, _ = io.Copy(peer, conn)
|
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
# Running wireproxy with rc.d
|
|
||||||
|
|
||||||
If you're on a rc.d-based distro, you'll most likely want to run Wireproxy as a systemd unit.
|
|
||||||
|
|
||||||
The provided systemd unit assumes you have the wireproxy executable installed on `/bin/wireproxy` and a configuration file stored at `/etc/wireproxy.conf`. These paths can be customized by editing the unit file.
|
|
||||||
|
|
||||||
# Setting up the unit
|
|
||||||
|
|
||||||
1. Copy the `wireproxy` file from this directory to `/usr/local/etc/rc.d`.
|
|
||||||
|
|
||||||
2. If necessary, customize the unit.
|
|
||||||
Edit the parts with `procname`, `command`, `wireproxy_conf` to point to the executable and the configuration file.
|
|
||||||
|
|
||||||
4. Add the following lines to `/etc/rc.conf` to enable wireproxy
|
|
||||||
`wireproxy_enable="YES"`
|
|
||||||
|
|
||||||
5. Start wireproxy service and check status
|
|
||||||
```
|
|
||||||
sudo service wireproxy start
|
|
||||||
sudo service wireproxy status
|
|
||||||
```
|
|
|
@ -1,30 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
#
|
|
||||||
# PROVIDE: wireproxy
|
|
||||||
# REQUIRE: DAEMON
|
|
||||||
# KEYWORD: nojail
|
|
||||||
#
|
|
||||||
|
|
||||||
#
|
|
||||||
# Add the following lines to /etc/rc.conf to enable wireproxy:
|
|
||||||
#
|
|
||||||
#wireproxy_enable="YES"
|
|
||||||
#
|
|
||||||
|
|
||||||
. /etc/rc.subr
|
|
||||||
|
|
||||||
name=wireproxy
|
|
||||||
rcvar=wireproxy_enable
|
|
||||||
|
|
||||||
load_rc_config $name
|
|
||||||
procname="/bin/wireproxy"
|
|
||||||
|
|
||||||
wireproxy_enable=${wireproxy_enable:-"NO"}
|
|
||||||
|
|
||||||
wireproxy_bin=/bin/wireproxy
|
|
||||||
wireproxy_conf=/etc/wireproxy.conf
|
|
||||||
|
|
||||||
command=${wireproxy_bin}
|
|
||||||
command_args="-s -d -c ${wireproxy_conf}"
|
|
||||||
|
|
||||||
run_rc_command "$1"
|
|
57
routine.go
57
routine.go
|
@ -21,9 +21,9 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/sourcegraph/conc"
|
||||||
"github.com/things-go/go-socks5"
|
"github.com/things-go/go-socks5"
|
||||||
"github.com/things-go/go-socks5/bufferpool"
|
"github.com/things-go/go-socks5/bufferpool"
|
||||||
|
|
||||||
|
@ -48,8 +48,7 @@ type VirtualTun struct {
|
||||||
SystemDNS bool
|
SystemDNS bool
|
||||||
Conf *DeviceConfig
|
Conf *DeviceConfig
|
||||||
// PingRecord stores the last time an IP was pinged
|
// PingRecord stores the last time an IP was pinged
|
||||||
PingRecord map[string]uint64
|
PingRecord map[string]uint64
|
||||||
PingRecordLock *sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoutineSpawner spawns a routine (e.g. socks5, tcp static routes) after the configuration is parsed
|
// RoutineSpawner spawns a routine (e.g. socks5, tcp static routes) after the configuration is parsed
|
||||||
|
@ -189,9 +188,6 @@ func (c CredentialValidator) Valid(username, password string) bool {
|
||||||
|
|
||||||
// connForward copy data from `from` to `to`
|
// connForward copy data from `from` to `to`
|
||||||
func connForward(from io.ReadWriteCloser, to io.ReadWriteCloser) {
|
func connForward(from io.ReadWriteCloser, to io.ReadWriteCloser) {
|
||||||
defer from.Close()
|
|
||||||
defer to.Close()
|
|
||||||
|
|
||||||
_, err := io.Copy(to, from)
|
_, err := io.Copy(to, from)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorLogger.Printf("Cannot forward traffic: %s\n", err.Error())
|
errorLogger.Printf("Cannot forward traffic: %s\n", err.Error())
|
||||||
|
@ -214,8 +210,20 @@ func tcpClientForward(vt *VirtualTun, raddr *addressPort, conn net.Conn) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
go connForward(sconn, conn)
|
go func() {
|
||||||
go connForward(conn, sconn)
|
wg := conc.NewWaitGroup()
|
||||||
|
wg.Go(func() {
|
||||||
|
connForward(sconn, conn)
|
||||||
|
})
|
||||||
|
wg.Go(func() {
|
||||||
|
connForward(conn, sconn)
|
||||||
|
})
|
||||||
|
wg.Wait()
|
||||||
|
_ = sconn.Close()
|
||||||
|
_ = conn.Close()
|
||||||
|
sconn = nil
|
||||||
|
conn = nil
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// STDIOTcpForward starts a new connection via wireguard and forward traffic from `conn`
|
// STDIOTcpForward starts a new connection via wireguard and forward traffic from `conn`
|
||||||
|
@ -240,8 +248,18 @@ func STDIOTcpForward(vt *VirtualTun, raddr *addressPort) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
go connForward(os.Stdin, sconn)
|
go func() {
|
||||||
go connForward(sconn, stdout)
|
wg := conc.NewWaitGroup()
|
||||||
|
wg.Go(func() {
|
||||||
|
connForward(os.Stdin, sconn)
|
||||||
|
})
|
||||||
|
wg.Go(func() {
|
||||||
|
connForward(sconn, stdout)
|
||||||
|
})
|
||||||
|
wg.Wait()
|
||||||
|
_ = sconn.Close()
|
||||||
|
sconn = nil
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpawnRoutine 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
|
||||||
|
@ -291,9 +309,20 @@ func tcpServerForward(vt *VirtualTun, raddr *addressPort, conn net.Conn) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
go connForward(sconn, conn)
|
go func() {
|
||||||
go connForward(conn, sconn)
|
gr := conc.NewWaitGroup()
|
||||||
|
gr.Go(func() {
|
||||||
|
connForward(sconn, conn)
|
||||||
|
})
|
||||||
|
gr.Go(func() {
|
||||||
|
connForward(conn, sconn)
|
||||||
|
})
|
||||||
|
gr.Wait()
|
||||||
|
_ = sconn.Close()
|
||||||
|
_ = conn.Close()
|
||||||
|
sconn = nil
|
||||||
|
conn = nil
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpawnRoutine 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
|
||||||
|
@ -446,9 +475,7 @@ func (d VirtualTun) pingIPs() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d.PingRecordLock.Lock()
|
|
||||||
d.PingRecord[addr.String()] = uint64(time.Now().Unix())
|
d.PingRecord[addr.String()] = uint64(time.Now().Unix())
|
||||||
d.PingRecordLock.Unlock()
|
|
||||||
|
|
||||||
defer socket.Close()
|
defer socket.Close()
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -8,7 +8,7 @@ The provided systemd unit assumes you have the wireproxy executable installed on
|
||||||
|
|
||||||
1. Copy the `wireproxy.service` file from this directory to `/etc/systemd/system/`, or use the following cURL command to download it:
|
1. Copy the `wireproxy.service` file from this directory to `/etc/systemd/system/`, or use the following cURL command to download it:
|
||||||
```bash
|
```bash
|
||||||
curl https://raw.githubusercontent.com/pufferffish/wireproxy/master/systemd/wireproxy.service | sudo tee /etc/systemd/system/wireproxy.service
|
sudo curl https://raw.githubusercontent.com/pufferffish/wireproxy/master/systemd/wireproxy.service > /etc/systemd/system/wireproxy.service
|
||||||
```
|
```
|
||||||
|
|
||||||
2. If necessary, customize the unit.
|
2. If necessary, customize the unit.
|
||||||
|
|
|
@ -40,7 +40,7 @@ RestrictAddressFamilies=AF_INET AF_INET6 AF_NETLINK
|
||||||
RestrictNamespaces=true
|
RestrictNamespaces=true
|
||||||
RestrictRealtime=true
|
RestrictRealtime=true
|
||||||
SystemCallArchitectures=native
|
SystemCallArchitectures=native
|
||||||
SystemCallFilter=@system-service @sandbox
|
SystemCallFilter=@system-service
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|
32
wireguard.go
32
wireguard.go
|
@ -3,7 +3,6 @@ package wireproxy
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
|
@ -15,14 +14,14 @@ import (
|
||||||
|
|
||||||
// DeviceSetting contains the parameters for setting up a tun interface
|
// DeviceSetting contains the parameters for setting up a tun interface
|
||||||
type DeviceSetting struct {
|
type DeviceSetting struct {
|
||||||
IpcRequest string
|
ipcRequest string
|
||||||
DNS []netip.Addr
|
dns []netip.Addr
|
||||||
DeviceAddr []netip.Addr
|
deviceAddr []netip.Addr
|
||||||
MTU int
|
mtu int
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateIPCRequest 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) {
|
||||||
var request bytes.Buffer
|
var request bytes.Buffer
|
||||||
|
|
||||||
request.WriteString(fmt.Sprintf("private_key=%s\n", conf.SecretKey))
|
request.WriteString(fmt.Sprintf("private_key=%s\n", conf.SecretKey))
|
||||||
|
@ -55,23 +54,23 @@ func CreateIPCRequest(conf *DeviceConfig) (*DeviceSetting, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setting := &DeviceSetting{IpcRequest: request.String(), DNS: conf.DNS, DeviceAddr: conf.Endpoint, MTU: conf.MTU}
|
setting := &DeviceSetting{ipcRequest: request.String(), dns: conf.DNS, deviceAddr: conf.Endpoint, mtu: conf.MTU}
|
||||||
return setting, nil
|
return setting, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartWireguard creates a tun interface on netstack given a configuration
|
// StartWireguard creates a tun interface on netstack given a configuration
|
||||||
func StartWireguard(conf *DeviceConfig, logLevel int) (*VirtualTun, error) {
|
func StartWireguard(conf *DeviceConfig, logLevel int) (*VirtualTun, error) {
|
||||||
setting, err := CreateIPCRequest(conf)
|
setting, err := createIPCRequest(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tun, tnet, err := netstack.CreateNetTUN(setting.DeviceAddr, setting.DNS, setting.MTU)
|
tun, tnet, err := netstack.CreateNetTUN(setting.deviceAddr, setting.dns, setting.mtu)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
dev := device.NewDevice(tun, conn.NewDefaultBind(), device.NewLogger(logLevel, ""))
|
dev := device.NewDevice(tun, conn.NewDefaultBind(), device.NewLogger(logLevel, ""))
|
||||||
err = dev.IpcSet(setting.IpcRequest)
|
err = dev.IpcSet(setting.ipcRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -82,11 +81,10 @@ func StartWireguard(conf *DeviceConfig, logLevel int) (*VirtualTun, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return &VirtualTun{
|
return &VirtualTun{
|
||||||
Tnet: tnet,
|
Tnet: tnet,
|
||||||
Dev: dev,
|
Dev: dev,
|
||||||
Conf: conf,
|
Conf: conf,
|
||||||
SystemDNS: len(setting.DNS) == 0,
|
SystemDNS: len(setting.dns) == 0,
|
||||||
PingRecord: make(map[string]uint64),
|
PingRecord: make(map[string]uint64),
|
||||||
PingRecordLock: new(sync.Mutex),
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue