Skip to content

Commit

Permalink
Merge pull request #54 from goriccardo/rootca
Browse files Browse the repository at this point in the history
Add option to tunnel client to specify a root certificate authority
  • Loading branch information
mmatczuk authored Nov 24, 2017
2 parents 1455e86 + bb25f68 commit 735484a
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 26 deletions.
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ How it works:

Client opens a TLS connection to a server. Server accepts connections from known clients only, client is recognised by it's TLS certificate ID. The server is publicly available and proxies incoming connections to the client. Then the connection is further proxied in the client's network.

Tunnel is based HTTP/2 for speed and security. There is a single TCP connection between client and server and all the proxied connections are multiplexed using HTTP/2.
Tunnel is based HTTP/2 for speed and security. There is a single TCP connection between client and server and all the proxied connections are multiplexed using HTTP/2.

Common use cases:

Expand All @@ -31,7 +31,7 @@ Build the latest version.
$ go get -u github.com/mmatczuk/go-http-tunnel/cmd/...
```

Alternatively [download the latest release](https://github.com/mmatczuk/go-http-tunnel/releases/latest).
Alternatively [download the latest release](https://github.com/mmatczuk/go-http-tunnel/releases/latest).

## Running

Expand All @@ -53,7 +53,7 @@ Run client:

* Install `tunnel` binary
* Make `.tunnel` directory in your project directory
* Copy `client.key`, `client.crt` to `.tunnel`
* Copy `client.key`, `client.crt` to `.tunnel`
* Create configuration file `tunnel.yml` in `.tunnel`
* Start all tunnels

Expand All @@ -67,21 +67,21 @@ Run server:
* Make `.tunneld` directory
* Copy `server.key`, `server.crt` to `.tunneld`
* Get client identifier (`tunnel -config ./tunnel/tunnel.yml id`), identifier should look like this `YMBKT3V-ESUTZ2Z-7MRILIJ-T35FHGO-D2DHO7D-FXMGSSR-V4LBSZX-BNDONQ4`
* Start tunnel server
* Start tunnel server

```bash
$ tunneld -tlsCrt .tunneld/server.crt -tlsKey .tunneld/server.key -clients YMBKT3V-ESUTZ2Z-7MRILIJ-T35FHGO-D2DHO7D-FXMGSSR-V4LBSZX-BNDONQ4
```
```

This will run HTTP server on port `80` and HTTPS (HTTP/2) server on port `443`. If you want to use HTTPS it's recommended to get a properly signed certificate to avoid security warnings.
This will run HTTP server on port `80` and HTTPS (HTTP/2) server on port `443`. If you want to use HTTPS it's recommended to get a properly signed certificate to avoid security warnings.

## Configuration

The tunnel client `tunnel` requires configuration file, by default it will try reading `tunnel.yml` in your current working directory. If you want to specify other file use `-config` flag.

Sample configuration that exposes:

* `localhost:8080` as `webui.my-tunnel-host.com`
* `localhost:8080` as `webui.my-tunnel-host.com`
* host in private network for ssh connections

looks like this
Expand All @@ -104,10 +104,11 @@ looks like this
Configuration options:
* `server_addr`: server TCP address, i.e. `54.12.12.45:5223`
* `insecure_skip_verify`: controls whether a client verifies the server's certificate chain and host name, if using self signed certificates must be set to `true`, *default:* `false`
* `insecure_skip_verify`: controls whether a client should skip the verification of the server's certificate chain and host name. If set to `true` the client will accept *any* server certificate as valid, *default:* `false`
* `tls_crt`: path to client TLS certificate, *default:* `client.crt` *in the config file directory*
* `tls_key`: path to client TLS certificate key, *default:* `client.key` *in the config file directory*
* `tunnels / [name]`
* `root_ca`: path to trusted root certificate authority pool file, *default* is the host's root CA set
* `tunnels / [name]`
* `proto`: tunnel protocol, `http` or `tcp`
* `addr`: forward traffic to this local port number or network address, for `proto=http` this can be full URL i.e. `https://machine/sub/path/?plus=params`, supports URL schemes `http` and `https`
* `auth`: (`proto=http`) (optional) basic authentication credentials to enforce on tunneled requests, format `user:password`
Expand Down
1 change: 1 addition & 0 deletions cmd/tunnel/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type ClientConfig struct {
InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
TLSCrt string `yaml:"tls_crt"`
TLSKey string `yaml:"tls_key"`
RootCA string `yaml:"root_ca"`
Backoff BackoffConfig `yaml:"backoff"`
Tunnels map[string]*Tunnel `yaml:"tunnels"`
}
Expand Down
60 changes: 43 additions & 17 deletions cmd/tunnel/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net"
"net/url"
"os"
"sort"
Expand Down Expand Up @@ -38,14 +40,14 @@ func main() {
}

// read configuration file
c, err := loadClientConfigFromFile(opts.config)
config, err := loadClientConfigFromFile(opts.config)
if err != nil {
fatal("configuration error: %s", err)
}

switch opts.command {
case "id":
cert, err := tls.LoadX509KeyPair(c.TLSCrt, c.TLSKey)
cert, err := tls.LoadX509KeyPair(config.TLSCrt, config.TLSKey)
if err != nil {
fatal("failed to load key pair: %s", err)
}
Expand All @@ -58,7 +60,7 @@ func main() {
return
case "list":
var names []string
for n := range c.Tunnels {
for n := range config.Tunnels {
names = append(names, n)
}

Expand All @@ -72,32 +74,32 @@ func main() {
case "start":
tunnels := make(map[string]*Tunnel)
for _, arg := range opts.args {
t, ok := c.Tunnels[arg]
t, ok := config.Tunnels[arg]
if !ok {
fatal("no such tunnel %q", arg)
}
tunnels[arg] = t
}
c.Tunnels = tunnels
config.Tunnels = tunnels
}

cert, err := tls.LoadX509KeyPair(c.TLSCrt, c.TLSKey)
tlsconf, err := tlsConfig(config)
if err != nil {
fatal("failed to load certificate: %s", err)
fatal("failed to configure tls: %s", err)
}

b, err := yaml.Marshal(c)
b, err := yaml.Marshal(config)
if err != nil {
fatal("failed to load c: %s", err)
fatal("failed to load config: %s", err)
}
logger.Log("config", string(b))

client := tunnel.NewClient(&tunnel.ClientConfig{
ServerAddr: c.ServerAddr,
TLSClientConfig: tlsConfig(cert, c),
Backoff: expBackoff(c.Backoff),
Tunnels: tunnels(c.Tunnels),
Proxy: proxy(c.Tunnels, logger),
ServerAddr: config.ServerAddr,
TLSClientConfig: tlsconf,
Backoff: expBackoff(config.Backoff),
Tunnels: tunnels(config.Tunnels),
Proxy: proxy(config.Tunnels, logger),
Logger: logger,
})

Expand All @@ -106,11 +108,35 @@ func main() {
}
}

func tlsConfig(cert tls.Certificate, c *ClientConfig) *tls.Config {
func tlsConfig(config *ClientConfig) (*tls.Config, error) {
cert, err := tls.LoadX509KeyPair(config.TLSCrt, config.TLSKey)
if err != nil {
return nil, err
}

var roots *x509.CertPool
if config.RootCA != "" {
roots = x509.NewCertPool()
rootPEM, err := ioutil.ReadFile(config.RootCA)
if err != nil {
return nil, err
}
if ok := roots.AppendCertsFromPEM(rootPEM); !ok {
return nil, err
}
}

host, _, err := net.SplitHostPort(config.ServerAddr)
if err != nil {
return nil, err
}

return &tls.Config{
ServerName: host,
Certificates: []tls.Certificate{cert},
InsecureSkipVerify: c.InsecureSkipVerify,
}
InsecureSkipVerify: config.InsecureSkipVerify,
RootCAs: roots,
}, nil
}

func expBackoff(c BackoffConfig) *backoff.ExponentialBackOff {
Expand Down

0 comments on commit 735484a

Please sign in to comment.