-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit d225c15
Showing
6 changed files
with
703 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# Mole | ||
|
||
Mole creates publicly accessible HTTP/HTTPS/TCP/UDP tunnels for local development. | ||
Tunnels are implemented using ssh remote port forwarding. Subdomains are assigned | ||
using an auto-incrementing id for every forwarding request. | ||
|
||
## Installation | ||
|
||
Install the mole server. You will need go and openssh installed before preceeding. | ||
|
||
```bash | ||
go get -u github.com/michalnicp/mole | ||
``` | ||
|
||
Generate the ssh host key file. | ||
|
||
```bash | ||
ssh-keygen -t rsa -b 2048 -f /etc/mole/ssh_host_key | ||
``` | ||
|
||
Start mole. | ||
|
||
```bash | ||
$ mole | ||
mole version 0.0.0 | ||
``` | ||
|
||
## Configuration | ||
|
||
Mole is configured using environment variables | ||
|
||
| Variable | Default | Description | | ||
|-----------------------|-------------------|---------------------------------------------| | ||
| `SERVER_NAME` | | The fully qualified domain name (hostname). | | ||
| `HTTP_ADDR` | `:8080` | The http server address to listen on. | | ||
| `SSH_ADDR` | `:2022` | The ssh server address to listen on. | | ||
| `SSH_HOST_KEY_PATH` | `ssh_host_key` | The path to the ssh server host key file. | | ||
| `SSH_AUTHORIZED_KEYS` | `authorized_keys` | The path to the authorized keys file. | | ||
|
||
## Quickstart | ||
|
||
This quickstart assumes that the `mole` server is running and available at `example.com`. | ||
You can use any ssh client that supports remote forwarding. For example, using `openssh` | ||
|
||
```bash | ||
$ ssh example.com -p 2022 -R 8000:localhost:8000 | ||
mole version 0.0.0 | ||
forwarding http://1.example.com->localhost:8000 | ||
``` | ||
|
||
Test using `ncat`. The following will start `ncat` listening on port `8000` | ||
|
||
```bash | ||
$ printf 'HTTP/1.1 200 OK\r\n' | ncat -l 8000 | ||
``` | ||
|
||
Then, in another terminal, make an http request using `curl` | ||
|
||
```bash | ||
$ curl 1.example.com | ||
``` | ||
|
||
You should see the following output from `ncat` | ||
|
||
```bash | ||
$ printf 'HTTP/1.1 200 OK\r\n' | ncat -l 8000 | ||
GET / HTTP/1.1 | ||
Host: 1.example.com | ||
User-Agent: curl/7.68.0 | ||
Accept: */* | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
module github.com/michalnicp/mole | ||
|
||
go 1.13 | ||
|
||
require ( | ||
github.com/caarlos0/env v3.5.0+incompatible | ||
github.com/fsnotify/fsnotify v1.4.7 | ||
golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1 | ||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 | ||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
github.com/caarlos0/env v3.5.0+incompatible h1:Yy0UN8o9Wtr/jGHZDpCBLpNrzcFLLM2yixi/rBrKyJs= | ||
github.com/caarlos0/env v3.5.0+incompatible/go.mod h1:tdCsowwCzMLdkqRYDlHpZCp2UooDD3MspDBjZ2AD02Y= | ||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= | ||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= | ||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||
golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1 h1:anGSYQpPhQwXlwsu5wmfq0nWkCNaMEMUwAv13Y92hd8= | ||
golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= | ||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA= | ||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
"net/http" | ||
"net/http/httputil" | ||
"os" | ||
"os/signal" | ||
"strconv" | ||
"strings" | ||
"syscall" | ||
|
||
"github.com/caarlos0/env" | ||
"github.com/michalnicp/mole/ssh" | ||
) | ||
|
||
type config struct { | ||
// ServerName is the server's fully qualified domain name (hostname). | ||
ServerName string `env:"SERVER_NAME"` | ||
HTTPAddr string `env:"HTTP_ADDR" envDefault:":8080"` | ||
SSHAddr string `env:"SSH_ADDR" envDefault:":2022"` | ||
SSHHostKey string `env:"SSH_HOST_KEY" envDefault:"ssh_host_key"` | ||
SSHAuthorizedKeys string `env:"SSH_AUTHORIZED_KEYS" envDefault:"authorized_keys"` | ||
} | ||
|
||
func main() { | ||
log.SetFlags(log.LstdFlags | log.Lshortfile) | ||
|
||
// read config from env | ||
var cfg config | ||
if err := env.Parse(&cfg); err != nil { | ||
log.Fatalf("parse environment: %v", err) | ||
} | ||
|
||
// create ssh server | ||
sshServer, err := ssh.NewServer(cfg.SSHAddr) | ||
if err != nil { | ||
log.Fatalf("create ssh server: %v", err) | ||
} | ||
|
||
if err := sshServer.Start(); err != nil { | ||
log.Fatalf("start ssh server: %v", err) | ||
} | ||
|
||
// create reverse proxy | ||
domain := cfg.ServerName | ||
proxy := &httputil.ReverseProxy{ | ||
Director: func(req *http.Request) { | ||
subdomain := strings.TrimSuffix(req.Host, "."+domain) | ||
id64, err := strconv.ParseUint(subdomain, 10, 64) | ||
if err != nil { | ||
log.Printf("parse host: %s: %v", req.Host, err) | ||
return | ||
} | ||
id := uint(id64) | ||
|
||
addr, ok := sshServer.GetForwardAddr(id) | ||
if !ok { | ||
log.Printf("forward address not found: %d", id) | ||
return | ||
} | ||
|
||
// If there was an error above this line, the scheme will not be set. | ||
// This results in the request failing with a 502 Bad Gateway, which | ||
// is what we want. See proxy.ErrorHandler. | ||
|
||
req.URL.Scheme = "http" | ||
req.URL.Host = addr | ||
|
||
// TODO: Should we modify req.Host as well? | ||
// req.Host = req.URL.Host | ||
}, | ||
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) { | ||
if err.Error() != "unsupported protocol scheme \"\"" { | ||
log.Printf("http: proxy error: %v", err) | ||
} | ||
w.WriteHeader(http.StatusBadGateway) | ||
}, | ||
} | ||
|
||
httpServer := http.Server{ | ||
Addr: cfg.HTTPAddr, | ||
Handler: proxy, | ||
} | ||
|
||
go func() { | ||
log.Printf("starting http server; http_addr=%s", cfg.HTTPAddr) | ||
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { | ||
log.Printf("start http server: %v", err) | ||
} | ||
}() | ||
|
||
sigs := make(chan os.Signal, 1) | ||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) | ||
|
||
s := <-sigs | ||
log.Printf("caught %s, shutting down", s) | ||
|
||
if err := httpServer.Close(); err != nil { | ||
log.Printf("close http server: %v", err) | ||
} | ||
|
||
if err := sshServer.Close(); err != nil { | ||
log.Printf("close ssh server: %v", err) | ||
} | ||
|
||
// FIXME: get this down to 2 | ||
// time.Sleep(300 * time.Millisecond) | ||
// pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) | ||
} |
Oops, something went wrong.