Skip to content

Multi tenant #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 37 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
7909b24
working on MultiTenantMode support, removing non-TLS mode
ForestJohnson Dec 15, 2020
b9b61e5
bandwidth metrics & prometheus metrics publisher
ForestJohnson Dec 18, 2020
4e66a4a
support JSON metrics
ForestJohnson Dec 18, 2020
4865ead
add support for multiple servers per client
ForestJohnson Jan 20, 2021
6d225f9
split client and server into separate files :D
ForestJohnson Jan 20, 2021
3b0cd13
WIP multitenant updates while working on greenhouse
ForestJohnson Feb 14, 2021
2393bd7
update diagram and add GPL
ForestJohnson Feb 14, 2021
f6c1310
updates for greenhouse
ForestJohnson Mar 25, 2021
45ebe46
working on setting up greenhouse integration for client
ForestJohnson Mar 25, 2021
3ff3cc4
scrape hostname from HTTP request
ForestJohnson Mar 29, 2021
ca4bdab
got metric collection working with greenhouse
ForestJohnson Mar 29, 2021
6cb229d
allow inline certs in config file
ForestJohnson Apr 3, 2021
d0f0091
add clientStates endpoint for greenhouse to be able to tell if a given
ForestJohnson Apr 7, 2021
cbcbed9
debugging desktop app registration stuff
ForestJohnson Apr 7, 2021
e2d165e
move blocking IO reads outside of mutex 😅
ForestJohnson Apr 7, 2021
ae543f0
greenhouseapikey --> greenhouseapitoken
ForestJohnson Apr 12, 2021
80a61c0
add tenantInfo endpoint for greenhouse
ForestJohnson Apr 27, 2021
5bd01f4
changes from testing greenhouse desktop
ForestJohnson May 3, 2021
021cf1a
debugging stupid golang pointer crap causing wrong tunnel
ForestJohnson May 5, 2021
af74817
allow users to define tunnels for disconnected nodes
ForestJohnson May 7, 2021
9323c24
cleaning up and working on implementing threshold test mode
ForestJohnson May 11, 2021
5546ce3
support dialing unix sockets for local servers, support PROXY proto for
ForestJohnson May 16, 2021
85552b5
I don't know if this data matters or not but I figured if it does, might
ForestJohnson May 18, 2021
63b09a5
first try at adding forward proxy support to threshold client
ForestJohnson May 27, 2021
eba3f51
add forward proxy listener and update readme
ForestJohnson Jun 9, 2021
c2580fc
finishing up first try for SOCKS5 forward proxy support
ForestJohnson Jun 10, 2021
b5607fc
move module correctly
ForestJohnson Jul 1, 2021
1a2c3fd
support local SOCKS5 proxy for threshold clients
ForestJohnson Jul 1, 2021
7816c9d
finish implementing LocalSOCKS5Address and fix a couple bugs
ForestJohnson Jul 2, 2021
a3362a1
implement maximum reconnect
ForestJohnson Jul 16, 2021
1b85124
Merge branch 'multi-tenant' into forward-proxy. fix naming issues.
ForestJohnson Jul 22, 2021
54fd606
fixing go.mod go.sum issues / fixing compile errors in tests
ForestJohnson Jul 22, 2021
2aa40ea
fixing ConnWithMetrics, TunneledOutboundSOCKS5 forward proxy appears to
ForestJohnson Jul 22, 2021
f23d762
multiple threshold clients at once & tolerate greenhouse outages
ForestJohnson Sep 21, 2021
72dacc7
fixing log
ForestJohnson Sep 21, 2021
e215ed1
fixing blatantly wrong port bug and debugging changes
ForestJohnson Sep 24, 2021
6cfcabd
add picopublish build script for the greenhouse installer
ForestJohnson Sep 27, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
build
dockerbuild
localbuild.sh
config.json
threshold
231 changes: 231 additions & 0 deletions LICENSE.md

Large diffs are not rendered by default.

60 changes: 35 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,39 @@

## threshold
## threshold 🏔️⛰️🛤️⛰️🏔️

Public Internet facing gateway (TCP reverse tunnel) for server.garden.
Threshold was created to make self-hosting websites, email, and other services radically easier.

![](threshold.png)
Threshold implements a public-internet-facing gateway (TCP reverse tunnel & SOCKS5 forward proxy) for self-hosted servers.

This project was originally forked from https://github.com/koding/tunnel
The [greenhouse cloud service](https://git.sequentialread.com/forest/greenhouse) was developed in order to make threshold more easily accessible to more people. Greenhouse operates the server side of threshold as a service, charging $0.01 per GB of bandwidth.

![](readme/splash.png)

It is intended to be used to make it easier for non-tech-savvy people to host web services that are avaliable on the public internet.
Threshold server is designed to be a **relatively untrusted** service, in other words, the user doesn't need to place much trust in the environment where the server runs. It's designed so that the server operator can't spy on you. This makes it uniquely suited to bridge the "ownership vs capability" gap between a self-hosted server/homelab/datacenter and a 3rd-party public cloud environment, hence the name threshold.

This project was originally forked from https://github.com/koding/tunnel

This repository only includes the application that does the tunneling part. It does not include any other management or automation tools.

See the usage example folder for a basic test.

![Diagram](readme/Diagram.png)
![Diagram](readme/diagram.png)

This diagram was created with https://app.diagrams.net/.
To edit it, download the <a download href="readme/diagram.drawio">diagram file</a> and edit it with the https://app.diagrams.net/ web application, or you may run the application from [source](https://github.com/jgraph/drawio) if you wish.


### How it is intended to be used:

1. An automated tool creates a cloud instance and installs and configures the tunnel server on it.
1. An automated tool installs the tunnel client on the self-hoster's server computer.
1. An automated tool calls the `PUT /tunnels` api on the tunnel server's Management Port, and sends a JSON file describing which ports should be opened on the tunnel server, which client they should be tunneled to, and which service on the client they should be tunneled to, as well as whether or not the HAProxy "PROXY" protocol should be used. This connection can use TLS Client Authentication.
1. The tunnel client connects to the tunnel server on the Tunnel Control Port. This connection can use TLS Client Authentication. This connection will be held open and re-created if dropped.
1. An internet user connects to the tunnel server on one of the ports defined in the JSON. The internet user's request is tunneled through the original connection from the tunnel client, and then proxied to the web server software running on the self-hoster's server computer.
1. An automated tool creates a cloud instance and installs and configures the threshold server on it.
1. An automated tool installs the threshold client on the self-hoster's server computer.
1. An automated tool calls the `PUT /tunnels` api on the threshold server's Management Port, and sends a JSON file describing which ports should be opened on the threshold server, which client they should be tunneled to, and which service on the client they should be tunneled to, as well as whether or not the HAProxy "PROXY" protocol should be used. This connection can use TLS Client Authentication.
1. The threshold client connects to the threshold server on the Tunnel Control Port. This connection can use TLS Client Authentication. This connection will be held open and re-created if dropped.
1. An internet user connects to the threshold server on one of the ports defined in the JSON. The internet user's request is tunneled through the original connection from the threshold client, and then proxied to the web server software running on the self-hoster's server computer.
1. (OPTIONAL) The server operator installs software (for example, email server) which requires outgoing requests to "come from" the same IP address that the server is listening for connections at.
1. The email server or other software connects to the threshold client for SOCKS5 forward proxy. The threshold client forwards this connection through the existing tunnel connection to the threshold server (secured by TLS), then the threshold server handles the SOCKS5 connection and proxies it to the destination requested by the email server or other software.

Note: if you wish to easily create server/client key pairs that will work with threshold, see: https://git.sequentialread.com/forest/make-fake-cert

### Output from Usage example showing how it works:

Expand All @@ -36,7 +47,6 @@ Starting the "listener" test app. It listens on port 9001. This would be your w
"DebugLog": false,
"TunnelControlPort": 9056,
"ManagementPort": 9057,
"UseTls": true,
"CaCertificateFile": "InternalCA+chain.crt",
"ServerTlsKeyFile": "localhost.key",
"ServerTlsCertificateFile": "localhost+chain.crt"
Expand All @@ -48,11 +58,10 @@ Starting the tunnel client. Client Identifier: TestClient1
2020/08/06 14:00:04 theshold client is starting up using config:
{
"DebugLog": false,
"ClientIdentifier": "TestClient1",
"ClientId": "TestClient1",
"ServerHost": "localhost",
"ServerTunnelControlPort": 9056,
"ServerManagementPort": 9057,
"UseTls": true,
"ServiceToLocalAddrMap": {
"fooService": "127.0.0.1:9001"
},
Expand All @@ -69,7 +78,7 @@ Sending the tunnel configuration to the server.
HTTP PUT localhost:9057/tunnels:
now listening on 127.0.0.1:9000

[{"HaProxyProxyProtocol":true,"ListenAddress":"127.0.0.1","ListenHostnameGlob":"*","ListenPort":9000,"BackEndService":"fooService","ClientIdentifier":"TestClient1"}]
[{"HaProxyProxyProtocol":true,"ListenAddress":"127.0.0.1","ListenHostnameGlob":"*","ListenPort":9000,"BackEndService":"fooService","ClientId":"TestClient1"}]

Starting the "sender" test app.
It connects to the front end port of the tunnel (port 9000). This would be your end user who wants to use the web application.
Expand All @@ -93,10 +102,10 @@ Note how the listener sees the original source IP and port, not the source IP an

I have a few requirements for this system.

* It should be 100% automatable. It is intended to be used in a situation where it is unreasonable to ask the user to configure thier router, for example, they don't know how, they don't want to, or they are not allowed to (For example they live in a dorm where the University manages the network).
* Users have control over their own data. We do not entrust cloud providers or 3rd parties with our data, TLS keys/certificates, etc. In terms of every day usage, this is a TLS connection from an internet user directly to the self-hoster's computer. It is opaque to the cloud provider.
* If the cloud provider wants to launch a Man in the Middle attack, even if they could secretly obtain a trusted cert to use, it will not be easy to hide from the user as long as the user (or software that they installed) is anticipating it.
* It should support Failover/High Avaliability of services. Therefore, it needs to be able to have multiple tunnel clients connected at once, which can be hot-swapped via a Management API.
* It should be 100% automatable. It is intended to be used in a situation where it is unreasonable to ask the user to perform any sort of advanced manual configuration.
* Users have control over their own data. We do not entrust cloud providers or 3rd parties with our data, even those who are hosting our threshold server. TLS keys/certificates, security-relevant configurations, etc only exist on the user-controlled computer. The cloud provider doesn't get access to any information or capability beyond what the user's ISP (Internet Service Provider) would normally have.
* If the cloud provider wants to launch a Man in the Middle attack against the threshold user, they will run into the same problems that an ISP would.
* It should support Failover/High Avaliability of services. Therefore, it needs to be able to have multiple tunnel clients connected at once, which can be hot-swapped via a management API.

### What did you add on top of the koding/tunnel package?

Expand All @@ -111,15 +120,16 @@ I have a few requirements for this system.
* Introduced concept of a "service" string instead of port number, so the client decides what ports to connect to, not the server.
* Added support TLS SNI based virtual hosts. (Hostname based routing)
* Fixed various bugs related to connection lifecycle.
* Added a tunneled SOCKS5 proxy to support applications like email servers which need to be able to dial out from the same IP address that they recieve connections at.

### How to build

```
go build -o tunnel -tags netgo
go build -o threshold
```

# -tags netgo? what?
# this is a work around for dynamic linking on alpine linux
# see: https://stackoverflow.com/questions/36279253/go-compiled-binary-wont-run-in-an-alpine-docker-container-on-ubuntu-host
### How to build the docker image:

docker build -t sequentialread/tunnel:0.0.1 .
```
```
./build-docker.sh
```
4 changes: 2 additions & 2 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ function build() {
rm -rf build
mkdir build

GOOS=linux GOARCH=$1 go build -o build/threshold
GOOS=linux GOARCH=$1 go build -tags 'osusergo netgo' -ldflags='-extldflags=-static' -o build/threshold

sha256sum build/threshold

Expand Down Expand Up @@ -46,4 +46,4 @@ function build() {

build arm
build amd64
build arm64
#build arm64
7 changes: 5 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
module git.sequentialread.com/forest/tunnel
module git.sequentialread.com/forest/threshold

go 1.14

require (
github.com/armon/go-proxyproto v0.0.0-20180202201750-5b7edb60ff5f
git.sequentialread.com/forest/pkg-errors v0.9.2
github.com/armon/go-proxyproto v0.0.0-20210323213023-7e956b284f0a
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
github.com/cenkalti/backoff v2.1.0+incompatible
github.com/gorilla/websocket v1.4.0
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
)
17 changes: 13 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
git.sequentialread.com/forest/tunnel v0.0.0-20170601195443-35a8b95662bf h1:2flo/nnhfe3sSxQ/MHlK7KoY54tQ1pAvMzkh0ZOxyH4=
git.sequentialread.com/forest/tunnel v0.0.0-20170601195443-35a8b95662bf/go.mod h1:i+PvDDsWjggoCQOO8bGJJKRB9qfxmHk5yzIEA/h8dzg=
github.com/armon/go-proxyproto v0.0.0-20180202201750-5b7edb60ff5f h1:SaJ6yqg936TshyeFZqQE+N+9hYkIeL9AMr7S4voCl10=
github.com/armon/go-proxyproto v0.0.0-20180202201750-5b7edb60ff5f/go.mod h1:QmP9hvJ91BbJmGVGSbutW19IC0Q9phDCLGaomwTJbgU=
git.sequentialread.com/forest/pkg-errors v0.9.2 h1:j6pwbL6E+TmE7TD0tqRtGwuoCbCfO6ZR26Nv5nest9g=
git.sequentialread.com/forest/pkg-errors v0.9.2/go.mod h1:8TkJ/f8xLWFIAid20aoqgDZcCj9QQt+FU+rk415XO1w=
github.com/armon/go-proxyproto v0.0.0-20210323213023-7e956b284f0a h1:AP/vsCIvJZ129pdm9Ek7bH7yutN3hByqsMoNrWAxRQc=
github.com/armon/go-proxyproto v0.0.0-20210323213023-7e956b284f0a/go.mod h1:QmP9hvJ91BbJmGVGSbutW19IC0Q9phDCLGaomwTJbgU=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/cenkalti/backoff v2.1.0+incompatible h1:FIRvWBZrzS4YC7NT5cOuZjexzFvIr+Dbi6aD1cZaNBk=
github.com/cenkalti/backoff v2.1.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
Loading