Skip to content

Commit

Permalink
Added authentication, improved readme
Browse files Browse the repository at this point in the history
  • Loading branch information
ameshkov committed Feb 2, 2024
1 parent d6093d4 commit ef5d002
Show file tree
Hide file tree
Showing 10 changed files with 385 additions and 27 deletions.
123 changes: 122 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,123 @@
[![Go Report Card](https://goreportcard.com/badge/github.com/ameshkov/udptlspipe)](https://goreportcard.com/report/ameshkov/udptlspipe)
[![Latest release](https://img.shields.io/github/release/ameshkov/udptlspipe/all.svg)](https://github.com/ameshkov/udptlspipe/releases)

# udptlspipe
Simple TLS wrapper UDP connections

`udptlspipe` is a very simple TLS wrapper for UDP sessions with active probing
protection. The main purpose of it is to wrap WireGuard, OpenVPN or any other
similar UDP sessions. Inspired by [dtlspipe][dtlspipe].

This tool is very simple and created to solve a very specific issue and I intend
to keep it that way.

* [Features](#features)
* [Why would you need it?](#why)
* [How to install udptlspipe](#install)
* [How to use udptlspipe](#howtouse)
* [All command-line arguments](#allcmdarguments)

[dtlspipe]: https://github.com/SenseUnit/dtlspipe

<a id="features"></a>

## Features

* Cross-platform (Windows/macOS/Linux/Android/*BSD).
* Simple configuration, no complicated configuration files.
* Mimics Google Chrome's TLS ClientHello.
* Active probing protection in server mode.
* Suitable to wrap WireGuard, OpenVPN, and other UDP session.

<a id="why"></a>

## Why would you need it

There are several use-cases when this tool may be useful, the most popular ones
are:

* You're in a network where UDP-based protocol (like WireGuard) is forbidden.
* You're in a network where UDP works unreliably.

<a id="install"></a>

## How to install udptlspipe

* Using homebrew:
```shell
brew install ameshkov/tap/udptlspipe
```
* From source:
```shell
go install github.com/ameshkov/udptlspipe@latest
```
* You can get a binary from the [releases page][releases].

[releases]: https://github.com/ameshkov/udptlspipe/releases

<a id="howtouse"></a>

## How to use udptlspipe

### Generic case

Let's assume you have the following setup:
* You have a server with a public IP address `1.2.3.4` (**tunnel server**).
* You also have a UDP service running on `2.3.4.5:8123` (**udp server**).
* You want to access the UDP server securely from your **local machine** and
wrap your UDP datagrams with a TLS layer.
Run the following command on your **tunnel server**.
```shell
udptlspipe --server -l 0.0.0.0:443 -d 2.3.4.5:8123 -p SecurePassword
```
Now run the following command on your **local machine**:
```shell
udptlspipe -l 127.0.0.1:8123 -d 1.2.3.4:443 -p SecurePassword
```
Now instead of using **udp server** address on your local machine use
`127.0.0.1:8123`.
In the end here's the pipe that you have:

**you** → UDP → **local udptlspipe** → TLS → **tunnel server** → UDP → **udp
server**.

### WireGuard

`udptlspipe` setup is completely the same as for the generic case, but you also
need to make some adjustments to the WireGuard client configuration:

* Use the address of the `udptlspipe` client as an endpoint in your WireGuard
client configuration.
* Add `MTU = 1280` to the `[Peer]` section of both WireGuard client and server
configuration files.
* Exclude the `udptlspipe` server IP from `AllowedIPs` in the WireGuard client
configuration. This [calculator][wireguardcalculator] may help you.

[wireguardcalculator]: https://www.procustodibus.com/blog/2021/03/wireguard-allowedips-calculator/

<a id="allcmdarguments"></a>

## All command-line arguments

```shell
Usage:
udptlspipe [OPTIONS]
Application Options:
-s, --server Enables the server mode. By default it runs in client mode.
-l, --listen=<IP:Port> Address the tool will be listening to (required).
-d, --destination=<IP:Port> Address the tool will connect to (required).
-p, --password=<password> Password is used to detect if the client is allowed.
-x, --proxy=[protocol://username:password@]host[:port] URL of a proxy to use when connecting to the destination address
(optional).
-v, --verbose Verbose output (optional).
Help Options:
-h, --help Show this help message
```
10 changes: 9 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,20 @@ go 1.21.6
require (
github.com/AdguardTeam/golibs v0.20.0
github.com/jessevdk/go-flags v1.5.0
github.com/refraction-networking/utls v1.6.2
github.com/stretchr/testify v1.8.4
golang.org/x/net v0.20.0
)

require (
github.com/andybalholm/brotli v1.0.6 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.15.0 // indirect
github.com/quic-go/quic-go v0.40.1 // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/sys v0.16.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
45 changes: 42 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,17 +1,56 @@
github.com/AdguardTeam/golibs v0.20.0 h1:A9FIdYq7wUKhFYy3z+YZ/Aw5oFUYgW+xgaVAJ0pnnPY=
github.com/AdguardTeam/golibs v0.20.0/go.mod h1:3WunclLLfrVAq7fYQRhd6f168FHOEMssnipVXCxDL/w=
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
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/quic-go/quic-go v0.40.1 h1:X3AGzUNFs0jVuO3esAGnTfvdgvL4fq655WaOi1snv1Q=
github.com/quic-go/quic-go v0.40.1/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c=
github.com/refraction-networking/utls v1.6.2 h1:iTeeGY0o6nMNcGyirxkD5bFIsVctP5InGZ3E0HrzS7k=
github.com/refraction-networking/utls v1.6.2/go.mod h1:yil9+7qSl+gBwJqztoQseO6Pr3h62pQoY1lXiNR/FPs=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
36 changes: 36 additions & 0 deletions internal/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@
package cmd

import (
"context"
"errors"
"fmt"
"os"
"os/signal"
"syscall"
"time"

"github.com/AdguardTeam/golibs/log"
"github.com/ameshkov/udptlspipe/internal/pipe"
"github.com/ameshkov/udptlspipe/internal/version"
goFlags "github.com/jessevdk/go-flags"
)
Expand Down Expand Up @@ -36,4 +41,35 @@ func Main() {
if o.Verbose {
log.SetLevel(log.DEBUG)
}

srv, err := pipe.NewServer(o.ListenAddr, o.DestinationAddr, o.Password, o.ProxyURL, o.ServerMode)
if err != nil {
log.Error("Failed to initialize server: %v", err)

os.Exit(1)
}

err = srv.Start()
if err != nil {
log.Error("Failed to start the server: %v", err)

os.Exit(1)
}

// Subscribe to the OS events.
signalChannel := make(chan os.Signal, 1)
signal.Notify(signalChannel, syscall.SIGINT, syscall.SIGTERM)

// Wait until the user stops the tool.
<-signalChannel

// Gracefully shutdown the server.
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Second*10))
defer cancel()
err = srv.Shutdown(ctx)
if err != nil {
log.Info("Failed to gracefully shutdown the server: %v", err)
}

log.Info("Exiting udptlspipe.")
}
14 changes: 11 additions & 3 deletions internal/cmd/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,26 @@ import (
type Options struct {
// ServerMode controls whether the tool works in the server mode.
// By default, the tool will work in the client mode.
ServerMode bool `short:"s" long:"pipe" description:"Enables pipe mode." optional:"yes" optional-value:"true"`
ServerMode bool `short:"s" long:"server" description:"Enables the server mode. By default it runs in client mode." optional:"yes" optional-value:"true"`

// ListenAddr is the address the tool will be listening to. If it's in the
// pipe mode, it will listen to tcp://, if it's in the client mode, it
// will listen to udp://.
ListenAddr string `short:"l" long:"listen" description:"Address the tool will be listening to." value-name:"<IP>:<Port>"`
ListenAddr string `short:"l" long:"listen" description:"Address the tool will be listening to (required)." value-name:"<IP:Port>" required:"true"`

// DestinationAddr is the address the tool will connect to. Depending on the
// mode (pipe or client) this address has different semantics. In the
// client mode this is the address of the udptlspipe pipe. In the pipe
// mode this is the address where the received traffic will be passed.
DestinationAddr string `short:"d" long:"destination" description:"Address the tool will connect to." value-name:"<IP>:<Port>"`
DestinationAddr string `short:"d" long:"destination" description:"Address the tool will connect to (required)." value-name:"<IP:Port>" required:"true"`

// Password is used to detect if the client is actually allowed to use
// udptlspipe. If it's not allowed, the server returns a stub web page.
Password string `short:"p" long:"password" description:"Password is used to detect if the client is allowed." value-name:"<password>"`

// ProxyURL is the proxy address that should be used when connecting to the
// destination address.
ProxyURL string `short:"x" long:"proxy" description:"URL of a proxy to use when connecting to the destination address (optional)." value-name:"[protocol://username:password@]host[:port]"`

// Verbose defines whether we should write the DEBUG-level log or not.
Verbose bool `short:"v" long:"verbose" description:"Verbose output (optional)." optional:"yes" optional-value:"true"`
Expand Down
Loading

0 comments on commit ef5d002

Please sign in to comment.