Skip to content

Commit

Permalink
tls_client_ca_file option for verifying client (#3034)
Browse files Browse the repository at this point in the history
  • Loading branch information
gobins authored and jefferai committed Aug 3, 2017
1 parent 4c7c181 commit f166016
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 4 deletions.
1 change: 1 addition & 0 deletions command/server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,7 @@ func parseListeners(result *Config, list *ast.ObjectList) error {
"tls_cipher_suites",
"tls_prefer_server_cipher_suites",
"tls_require_and_verify_client_cert",
"tls_client_ca_file",
"token",
}
if err := checkHCLKeys(item.Val, valid); err != nil {
Expand Down
52 changes: 52 additions & 0 deletions command/server/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"testing"
"time"

"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/hashicorp/vault/helper/logformat"
log "github.com/mgutz/logxi/v1"
)
Expand Down Expand Up @@ -243,6 +245,56 @@ func TestLoadConfigDir(t *testing.T) {
}
}

func TestParseListeners(t *testing.T) {
obj, _ := hcl.Parse(strings.TrimSpace(`
listener "tcp" {
address = "127.0.0.1:443"
cluster_address = "127.0.0.1:8201"
tls_disable = false
tls_cert_file = "./certs/server.crt"
tls_key_file = "./certs/server.key"
tls_client_ca_file = "./certs/rootca.crt"
tls_min_version = "tls12"
tls_require_and_verify_client_cert = true
}`))

var config Config
list, _ := obj.Node.(*ast.ObjectList)
objList := list.Filter("listener")
parseListeners(&config, objList)
listeners := config.Listeners
if len(listeners) == 0 {
t.Fatalf("expected at least one listener in the config")
}
listener := listeners[0]
if listener.Type != "tcp" {
t.Fatalf("expected tcp listener in the config")
}

expected := &Config{
Listeners: []*Listener{
&Listener{
Type: "tcp",
Config: map[string]interface{}{
"address": "127.0.0.1:443",
"cluster_address": "127.0.0.1:8201",
"tls_disable": false,
"tls_cert_file": "./certs/server.crt",
"tls_key_file": "./certs/server.key",
"tls_client_ca_file": "./certs/rootca.crt",
"tls_min_version": "tls12",
"tls_require_and_verify_client_cert": true,
},
},
},
}

if !reflect.DeepEqual(config, *expected) {
t.Fatalf("expected \n\n%#v\n\n to be \n\n%#v\n\n", config, *expected)
}

}

func TestParseConfig_badTopLevel(t *testing.T) {
logger := logformat.NewVaultLogger(log.LevelTrace)

Expand Down
14 changes: 14 additions & 0 deletions command/server/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import (
// certificates that use it can be parsed.
_ "crypto/sha512"
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"io/ioutil"
"net"

"github.com/hashicorp/vault/helper/parseutil"
Expand Down Expand Up @@ -104,6 +106,18 @@ func listenerWrapTLS(
if requireClient {
tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
}
if tlsClientCaFile, ok := config["tls_client_ca_file"]; ok {
caPool := x509.NewCertPool()
data, err := ioutil.ReadFile(tlsClientCaFile.(string))
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to read tls_client_ca_file: %v", err)
}

if !caPool.AppendCertsFromPEM(data) {
return nil, nil, nil, fmt.Errorf("failed to parse CA certificate in tls_client_ca_file")
}
tlsConf.ClientCAs = caPool
}
}

ln = tls.NewListener(ln, tlsConf)
Expand Down
17 changes: 13 additions & 4 deletions command/server/listener_tcp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,27 @@ func TestTCPListener_tls(t *testing.T) {
}

ln, _, _, err := tcpListenerFactory(map[string]interface{}{
"address": "127.0.0.1:0",
"tls_cert_file": wd + "reload_foo.pem",
"tls_key_file": wd + "reload_foo.key",
"address": "127.0.0.1:0",
"tls_cert_file": wd + "reload_foo.pem",
"tls_key_file": wd + "reload_foo.key",
"tls_require_and_verify_client_cert": "true",
"tls_client_ca_file": wd + "reload_ca.pem",
}, nil)
if err != nil {
t.Fatalf("err: %s", err)
}
cwd, _ := os.Getwd()

clientCert, _ := tls.LoadX509KeyPair(
cwd+"/test-fixtures/reload/reload_foo.pem",
cwd+"/test-fixtures/reload/reload_foo.key")

connFn := func(lnReal net.Listener) (net.Conn, error) {
conn, err := tls.Dial("tcp", ln.Addr().String(), &tls.Config{
RootCAs: certPool,
RootCAs: certPool,
Certificates: []tls.Certificate{clientCert},
})

if err != nil {
return nil, err
}
Expand Down
3 changes: 3 additions & 0 deletions website/source/docs/configuration/listener/tcp.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ listener "tcp" {
authentication for this listener; the listener will require a presented
client cert that successfully validates against system CAs.

- `tls_client_ca_file` `(string: "")` – PEM-encoded Certificate Authority file
used for checking the authenticity of client.

## `tcp` Listener Examples

### Configuring TLS
Expand Down

0 comments on commit f166016

Please sign in to comment.