Merge pull request #54 from goriccardo/rootca

Add option to tunnel client to specify a root certificate authority
This commit is contained in:
Michał Matczuk 2017-11-24 12:04:42 +01:00 committed by GitHub
commit 735484a01a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 55 additions and 27 deletions

View file

@ -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:
@ -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
@ -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
@ -67,13 +67,13 @@ 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
@ -81,7 +81,7 @@ The tunnel client `tunnel` requires configuration file, by default it will try r
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
@ -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`

View file

@ -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"`
}

View file

@ -8,6 +8,8 @@ import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net"
"net/url"
"os"
"sort"
@ -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)
}
@ -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)
}
@ -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,
})
@ -106,11 +108,35 @@ func main() {
}
}
func tlsConfig(cert tls.Certificate, c *ClientConfig) *tls.Config {
return &tls.Config{
Certificates: []tls.Certificate{cert},
InsecureSkipVerify: c.InsecureSkipVerify,
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: config.InsecureSkipVerify,
RootCAs: roots,
}, nil
}
func expBackoff(c BackoffConfig) *backoff.ExponentialBackOff {