From 82c17f8271543b10b9bce93dd3adc4c7f7304a38 Mon Sep 17 00:00:00 2001 From: Andrea Lusuardi - uovobw Date: Wed, 30 Sep 2015 19:05:45 +0200 Subject: [PATCH 1/4] Add client certificate options to default configuration file --- .gotty | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.gotty b/.gotty index 84751fa..48dd520 100644 --- a/.gotty +++ b/.gotty @@ -30,6 +30,15 @@ // [string] Default TLS key file path // tls_key_file = "~/.gotty.key" +// [bool] Enable client certificate authentication +// enable_client_certificate = false + +// [string] Client Certificate CA file for validation +// client_ca_file = "~/.gotty.ca.crt" + +// [bool] Enable client certificate validation against the provided CA file +// enable_client_certificate_verification = false + // [string] Custom index.html file // index_file = "" From 5de1ece3886cf929ac261aeae4446e2186e1f1ea Mon Sep 17 00:00:00 2001 From: Andrea Lusuardi - uovobw Date: Wed, 30 Sep 2015 19:06:05 +0200 Subject: [PATCH 2/4] Update readme for client certificate authentication --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 4f1a440..a4e4d0a 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,9 @@ By default, GoTTY starts a web server at port 8080. Open the URL on your web bro --tls, -t Enable TLS/SSL [$GOTTY_TLS] --tls-crt "~/.gotty.key" TLS/SSL crt file path [$GOTTY_TLS_CRT] --tls-key "~/.gotty.crt" TLS/SSL key file path [$GOTTY_TLS_KEY] +--client, -C Enable Client Certificate [$GOTTY_CLIENT] +--client-ca-file "~/.gotty.ca.crt" Client CA certificate file [$GOTTY_CLIENT_CA_FILE] +--client-verify Enable verification of client certificate [$GOTTY_CLIENT_VERIFY] --index Custom index file [$GOTTY_INDEX] --title-format "GoTTY - {{ .Command }} ({{ .Hostname }})" Title format of browser window [$GOTTY_TITLE_FORMAT] --reconnect Enable reconnection [$GOTTY_RECONNECT] @@ -101,6 +104,8 @@ All traffic between the server and clients are NOT encrypted by default. When yo openssl req -x509 -nodes -days 9999 -newkey rsa:2048 -keyout ~/.gotty.key -out ~/.gotty.crt ``` +For added security you can use an SSL/TLS client certificate by enabling it with the `-C` option (this requires the `-t` or `--tls` flag to be set). This requires all client connecting to provide a valid certificate that can be validated (use the `--client-verify` option to make verification mandatory) against the CA file that is provided via the `--client-ca-file` option. + (NOTE: For Safari uses, see [how to enable self-signed certificates for WebSockets](http://blog.marcon.me/post/24874118286/secure-websockets-safari) when use self-signed certificates) ## Sharing with Multiple Clients From 5eb5959c93e7fa6f5a71f78f3e8714e11552ea48 Mon Sep 17 00:00:00 2001 From: Andrea Lusuardi - uovobw Date: Wed, 30 Sep 2015 19:06:28 +0200 Subject: [PATCH 3/4] Add tls client certificate authentication flags --- main.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index 7a619f6..5be9792 100644 --- a/main.go +++ b/main.go @@ -28,6 +28,9 @@ func main() { flag{"tls", "t", "Enable TLS/SSL"}, flag{"tls-crt", "", "TLS/SSL crt file path"}, flag{"tls-key", "", "TLS/SSL key file path"}, + flag{"client", "C", "Enable Client Certificate"}, + flag{"client-ca-file", "", "Client CA certificate file"}, + flag{"client-verify", "", "Enable verification of client certificate"}, flag{"index", "", "Custom index.html file"}, flag{"title-format", "", "Title format of browser window"}, flag{"reconnect", "", "Enable reconnection"}, @@ -36,12 +39,15 @@ func main() { } mappingHint := map[string]string{ - "index": "IndexFile", - "tls": "EnableTLS", - "tls-crt": "TLSCrtFile", - "tls-key": "TLSKeyFile", - "random-url": "EnableRandomUrl", - "reconnect": "EnableReconnect", + "index": "IndexFile", + "tls": "EnableTLS", + "tls-crt": "TLSCrtFile", + "tls-key": "TLSKeyFile", + "client": "EnableClientCertificate", + "client-ca-file": "ClientCAFile", + "client-verify": "EnableClientCertificateVerification", + "random-url": "EnableRandomUrl", + "reconnect": "EnableReconnect", } cliFlags, err := generateFlags(flags, mappingHint) From 7321b43f670b7fac29be9574bdd8ca07db6977fe Mon Sep 17 00:00:00 2001 From: Andrea Lusuardi - uovobw Date: Wed, 30 Sep 2015 19:06:55 +0200 Subject: [PATCH 4/4] Add client certificate fields to the configuration struct Add relevant logic --- app/app.go | 94 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 32 deletions(-) diff --git a/app/app.go b/app/app.go index 998c9db..41a3074 100644 --- a/app/app.go +++ b/app/app.go @@ -2,6 +2,8 @@ package app import ( "crypto/rand" + "crypto/tls" + "crypto/x509" "encoding/base64" "errors" "io/ioutil" @@ -34,41 +36,47 @@ type App struct { } type Options struct { - Address string `hcl:"address"` - Port string `hcl:"port"` - PermitWrite bool `hcl:"permit_write"` - EnableBasicAuth bool `hcl:"enable_basic_auth"` - Credential string `hcl:"credential"` - EnableRandomUrl bool `hcl:"enable_random_url"` - RandomUrlLength int `hcl:"random_url_length"` - IndexFile string `hcl:"index_file"` - EnableTLS bool `hcl:"enable_tls"` - TLSCrtFile string `hcl:"tls_crt_file"` - TLSKeyFile string `hcl:"tls_key_file"` - TitleFormat string `hcl:"title_format"` - EnableReconnect bool `hcl:"enable_reconnect"` - ReconnectTime int `hcl:"reconnect_time"` - Once bool `hcl:"once"` - Preferences map[string]interface{} `hcl:"preferences"` + Address string `hcl:"address"` + Port string `hcl:"port"` + PermitWrite bool `hcl:"permit_write"` + EnableBasicAuth bool `hcl:"enable_basic_auth"` + Credential string `hcl:"credential"` + EnableRandomUrl bool `hcl:"enable_random_url"` + RandomUrlLength int `hcl:"random_url_length"` + IndexFile string `hcl:"index_file"` + EnableTLS bool `hcl:"enable_tls"` + TLSCrtFile string `hcl:"tls_crt_file"` + TLSKeyFile string `hcl:"tls_key_file"` + EnableClientCertificate bool `hcl:"enable_client_certificate"` + ClientCAFile string `hcl:"client_ca_file"` + EnableClientCertificateVerification bool `hcl:"enable_client_certificate_verification"` + TitleFormat string `hcl:"title_format"` + EnableReconnect bool `hcl:"enable_reconnect"` + ReconnectTime int `hcl:"reconnect_time"` + Once bool `hcl:"once"` + Preferences map[string]interface{} `hcl:"preferences"` } var DefaultOptions = Options{ - Address: "", - Port: "8080", - PermitWrite: false, - EnableBasicAuth: false, - Credential: "", - EnableRandomUrl: false, - RandomUrlLength: 8, - IndexFile: "", - EnableTLS: false, - TLSCrtFile: "~/.gotty.crt", - TLSKeyFile: "~/.gotty.key", - TitleFormat: "GoTTY - {{ .Command }} ({{ .Hostname }})", - EnableReconnect: false, - ReconnectTime: 10, - Once: false, - Preferences: make(map[string]interface{}), + Address: "", + Port: "8080", + PermitWrite: false, + EnableBasicAuth: false, + Credential: "", + EnableRandomUrl: false, + RandomUrlLength: 8, + IndexFile: "", + EnableTLS: false, + TLSCrtFile: "~/.gotty.crt", + TLSKeyFile: "~/.gotty.key", + EnableClientCertificate: false, + ClientCAFile: "~/.gotty.ca.crt", + EnableClientCertificateVerification: false, + TitleFormat: "GoTTY - {{ .Command }} ({{ .Hostname }})", + EnableReconnect: false, + ReconnectTime: 10, + Once: false, + Preferences: make(map[string]interface{}), } func New(command []string, options *Options) (*App, error) { @@ -195,6 +203,28 @@ func (app *App) Run() error { keyFile := ExpandHomeDir(app.options.TLSKeyFile) log.Printf("TLS crt file: " + crtFile) log.Printf("TLS key file: " + keyFile) + if app.options.EnableClientCertificate { + caFile := ExpandHomeDir(app.options.ClientCAFile) + log.Printf("Client CA file: " + caFile) + caCert, err := ioutil.ReadFile(caFile) + if err != nil { + return errors.New("Cannot open CA file " + caFile) + } + caCertPool := x509.NewCertPool() + if !caCertPool.AppendCertsFromPEM(caCert) { + return errors.New("Cannot parse CA file data in " + caFile) + } + tlsVerifyPolicy := tls.RequireAnyClientCert + if app.options.EnableClientCertificateVerification { + log.Print("Enabling verification of client certificate") + tlsVerifyPolicy = tls.RequireAndVerifyClientCert + } + tlsConfig := &tls.Config{ + ClientCAs: caCertPool, + ClientAuth: tlsVerifyPolicy, + } + app.server.TLSConfig = tlsConfig + } err = app.server.ListenAndServeTLS(crtFile, keyFile) } else { err = app.server.ListenAndServe()