bootcfg/*: Require TLS client auth for gRPC API

* gRPC API server requires a CA certificate to verify
and authenticate clients (passed via -ca-file)
* gRPC clients must authenticate with a client certificate
and key (passed via -cert-file and -key-file)
This commit is contained in:
Dalton Hubble
2016-06-07 18:00:18 -07:00
parent af76aa6951
commit baa7f5e025
4 changed files with 56 additions and 11 deletions

View File

@@ -12,6 +12,7 @@ Configuration arguments can be provided as flags or as environment variables.
| -rpc-address | BOOTCFG_RPC_ADDRESS | (gRPC API disabled) | 127.0.0.1:8081 |
| -cert-file | BOOTCFG_CERT_FILE | /etc/bootcfg/server.crt | ./examples/etc/bootcfg/server.crt |
| -key-file | BOOTCFG_KEY_FILE | /etc/bootcfg/server.key | ./examples/etc/bootcfg/server.key
| -ca-file | BOOTCFG_CA_FILE | /etc/bootcfg/ca.crt | ./examples/etc/bootcfg/ca.crt |
| -key-ring-path | BOOTCFG_KEY_RING_PATH | (no key ring) | ~/.secrets/vault/bootcfg/secring.gpg |
| (no flag) | BOOTCFG_PASSPHRASE | (no passphrase) | "secret passphrase" |
@@ -27,7 +28,9 @@ Configuration arguments can be provided as flags or as environment variables.
|:---------|:--------------------------------------------------|
| CA certificate | /etc/bootcfg/ca.crt |
| Server certificate | /etc/bootcfg/server.crt |
| Server Private Key | /etc/bootcfg/server.key |
| Server private key | /etc/bootcfg/server.key |
| Client certificate | /etc/bootcfg/client.crt |
| Client private key | /etc/bootcfg/client.key |
## Version
@@ -63,7 +66,7 @@ Run the Docker image. Mounts are used to add the provided examples.
### gRPC API
The gRPC API can be enabled with the `-rpc-address` flag and by providing a TLS server certificate and key with `-cert-file` and `-key-file`. gRPC clients (such as `bootcmd`) must verify the server's certificate with a CA bundle.
The gRPC API can be enabled with the `-rpc-address` flag and by providing a TLS server certificate and key with `-cert-file` and `-key-file` and a CA certificate for authenticating clients with `-ca-file`. gRPC clients (such as `bootcmd`) must verify the server's certificate with a CA bundle passed via `-ca-file` and present a client certificate and key via `-cert-file` and `-key-file`.
Run the ACI with rkt and TLS credentials from `examples/etc/bootcfg`.
@@ -71,7 +74,7 @@ Run the ACI with rkt and TLS credentials from `examples/etc/bootcfg`.
A `bootcmd` client can call the gRPC API running at the IP used in the rkt example.
./bin/bootcmd profile list --endpoints 172.15.0.2:8081 --ca-file examples/etc/bootcfg/ca.crt
./bin/bootcmd profile list --endpoints 172.15.0.2:8081 --ca-file examples/etc/bootcfg/ca.crt --cert-file examples/etc/bootcfg/client.crt --key-file examples/etc/bootcfg/client.key
Run the Docker image with TLS credentials from `examples/etc/bootcfg`.
@@ -79,7 +82,7 @@ Run the Docker image with TLS credentials from `examples/etc/bootcfg`.
A `bootcmd` client can call the gRPC API running at the IP used in the Docker example.
./bin/bootcmd profile list --endpoints 127.0.0.1:8081 --ca-file examples/etc/bootcfg/root.crt
./bin/bootcmd profile list --endpoints 127.0.0.1:8081 --ca-file examples/etc/bootcfg/ca.crt --cert-file examples/etc/bootcfg/client.crt --key-file examples/etc/bootcfg/client.key
#### With [OpenPGP Signing](openpgp.md)

View File

@@ -22,15 +22,20 @@ To get help about a resource or command, run "bootcmd help resource"`,
// globalFlags can be set for any subcommand.
globalFlags = struct {
Endpoints []string
CAFile string
endpoints []string
caFile string
certFile string
keyFile string
}{}
)
func init() {
RootCmd.PersistentFlags().StringSliceVar(&globalFlags.Endpoints, "endpoints", []string{"127.0.0.1:8081"}, "gRPC Endpoints")
// gRPC Client TLS
RootCmd.PersistentFlags().StringVar(&globalFlags.CAFile, "ca-file", "/etc/bootcfg/ca.crt", "Path to the CA bundle to verify certificates of TLS servers")
RootCmd.PersistentFlags().StringSliceVar(&globalFlags.endpoints, "endpoints", []string{"127.0.0.1:8081"}, "gRPC Endpoints")
// gRPC TLS Server Verification
RootCmd.PersistentFlags().StringVar(&globalFlags.caFile, "ca-file", "/etc/bootcfg/ca.crt", "Path to the CA bundle to verify certificates of TLS servers")
// gRPC TLS Client Authentication
RootCmd.PersistentFlags().StringVar(&globalFlags.certFile, "cert-file", "/etc/bootcfg/client.crt", "Path to the client TLS certificate file")
RootCmd.PersistentFlags().StringVar(&globalFlags.keyFile, "key-file", "/etc/bootcfg/client.key", "Path to the client TLS key file")
cobra.EnablePrefixMatching = true
}
@@ -81,5 +86,18 @@ func tlsInfoFromCmd(cmd *cobra.Command) *tlsutil.TLSInfo {
if err != nil {
exitWithError(ExitBadArgs, err)
}
return &tlsutil.TLSInfo{CAFile: caFile}
certFile, err := cmd.Flags().GetString("cert-file")
if err != nil {
exitWithError(ExitBadArgs, err)
}
keyFile, err := cmd.Flags().GetString("key-file")
if err != nil {
exitWithError(ExitBadArgs, err)
}
return &tlsutil.TLSInfo{
CAFile: caFile,
CertFile: certFile,
KeyFile: keyFile,
}
}

View File

@@ -4,7 +4,7 @@ import (
"crypto/tls"
)
// TLSInfo prepares tls.Config's from TLS file inputs.
// TLSInfo prepares tls.Config's from TLS filename inputs.
type TLSInfo struct {
CAFile string
CertFile string
@@ -13,29 +13,49 @@ type TLSInfo struct {
// ClientConfig returns a tls.Config for client use.
func (info *TLSInfo) ClientConfig() (*tls.Config, error) {
// CA for verifying the server
pool, err := NewCertPool([]string{info.CAFile})
if err != nil {
return nil, err
}
// client certificate (for authentication)
cert, err := tls.LoadX509KeyPair(info.CertFile, info.KeyFile)
if err != nil {
return nil, err
}
return &tls.Config{
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: false,
// CA bundle the client should trust when verifying a server
RootCAs: pool,
// Client certificates to authenticate to the server
Certificates: []tls.Certificate{cert},
}, nil
}
// ServerConfig returns a tls.Config for server use.
func (info *TLSInfo) ServerConfig() (*tls.Config, error) {
// server certificate to present to clients
cert, err := tls.LoadX509KeyPair(info.CertFile, info.KeyFile)
if err != nil {
return nil, err
}
// CA for authenticating clients
pool, err := NewCertPool([]string{info.CAFile})
if err != nil {
return nil, err
}
return &tls.Config{
MinVersion: tls.VersionTLS12,
// Certificates the server should present to clients
Certificates: []tls.Certificate{cert},
// Client Authentication (required)
ClientAuth: tls.RequireAndVerifyClientCert,
// CA for verifying and authorizing client certificates
ClientCAs: pool,
}, nil
}

View File

@@ -34,6 +34,7 @@ func main() {
logLevel string
certFile string
keyFile string
caFile string
keyRingPath string
version bool
help bool
@@ -49,6 +50,8 @@ func main() {
// gRPC Server TLS
flag.StringVar(&flags.certFile, "cert-file", "/etc/bootcfg/server.crt", "Path to the server TLS certificate file")
flag.StringVar(&flags.keyFile, "key-file", "/etc/bootcfg/server.key", "Path to the server TLS key file")
// TLS Client Authentication
flag.StringVar(&flags.caFile, "ca-file", "/etc/bootcfg/ca.crt", "Path to the CA verify and authenticate client certificates")
// Signing
flag.StringVar(&flags.keyRingPath, "key-ring-path", "", "Path to a private keyring file")
@@ -133,6 +136,7 @@ func main() {
tlsinfo := tlsutil.TLSInfo{
CertFile: flags.certFile,
KeyFile: flags.keyFile,
CAFile: flags.caFile,
}
tlscfg, err := tlsinfo.ServerConfig()
if err != nil {