Added custom CA support to fleetctl client (#1931)

This commit is contained in:
adamenger 2018-10-01 17:23:46 -05:00 committed by Zachary Wasserman
parent 85ff9d6cf1
commit a99313533d
3 changed files with 51 additions and 4 deletions

View File

@ -27,7 +27,7 @@ func unauthenticatedClientFromCLI(c *cli.Context) (*service.Client, error) {
return nil, errors.New("set the Fleet API address with: fleetctl config set --address https://localhost:8080") return nil, errors.New("set the Fleet API address with: fleetctl config set --address https://localhost:8080")
} }
fleet, err := service.NewClient(cc.Address, cc.TLSSkipVerify) fleet, err := service.NewClient(cc.Address, cc.TLSSkipVerify, cc.RootCA)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "error creating Fleet API client handler") return nil, errors.Wrap(err, "error creating Fleet API client handler")
} }

View File

@ -26,6 +26,7 @@ type Context struct {
Email string `json:"email"` Email string `json:"email"`
Token string `json:"token"` Token string `json:"token"`
TLSSkipVerify bool `json:"tls-skip-verify"` TLSSkipVerify bool `json:"tls-skip-verify"`
RootCA string `json:"rootca"`
} }
func configFlag() cli.Flag { func configFlag() cli.Flag {
@ -113,6 +114,8 @@ func getConfigValue(c *cli.Context, key string) (interface{}, error) {
return currentContext.Email, nil return currentContext.Email, nil
case "token": case "token":
return currentContext.Token, nil return currentContext.Token, nil
case "rootca":
return currentContext.RootCA, nil
case "tls-skip-verify": case "tls-skip-verify":
if currentContext.TLSSkipVerify { if currentContext.TLSSkipVerify {
return true, nil return true, nil
@ -155,6 +158,8 @@ func setConfigValue(c *cli.Context, key, value string) error {
currentContext.Email = value currentContext.Email = value
case "token": case "token":
currentContext.Token = value currentContext.Token = value
case "rootca":
currentContext.RootCA = value
case "tls-skip-verify": case "tls-skip-verify":
boolValue, err := strconv.ParseBool(value) boolValue, err := strconv.ParseBool(value)
if err != nil { if err != nil {
@ -180,6 +185,7 @@ func configSetCommand() cli.Command {
flEmail string flEmail string
flToken string flToken string
flTLSSkipVerify bool flTLSSkipVerify bool
flRootCA string
) )
return cli.Command{ return cli.Command{
Name: "set", Name: "set",
@ -215,6 +221,13 @@ func configSetCommand() cli.Command {
Destination: &flTLSSkipVerify, Destination: &flTLSSkipVerify,
Usage: "Skip TLS certificate validation", Usage: "Skip TLS certificate validation",
}, },
cli.StringFlag{
Name: "rootca",
EnvVar: "ROOTCA",
Value: "",
Destination: &flRootCA,
Usage: "Specify RootCA chain used to communicate with fleet",
},
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
set := false set := false
@ -251,6 +264,14 @@ func configSetCommand() cli.Command {
fmt.Printf("[+] Set the tls-skip-verify config key to \"true\" in the %q context\n", c.String("context")) fmt.Printf("[+] Set the tls-skip-verify config key to \"true\" in the %q context\n", c.String("context"))
} }
if flRootCA != "" {
set = true
if err := setConfigValue(c, "rootca", flRootCA); err != nil {
return errors.Wrap(err, "error setting rootca")
}
fmt.Printf("[+] Set the rootca config key to %q in the %q context\n", flRootCA, c.String("context"))
}
if !set { if !set {
return cli.ShowCommandHelp(c, "set") return cli.ShowCommandHelp(c, "set")
} }
@ -278,7 +299,7 @@ func configGetCommand() cli.Command {
// validate key // validate key
switch key { switch key {
case "address", "email", "token", "tls-skip-verify": case "address", "email", "token", "tls-skip-verify", "rootca":
default: default:
return cli.ShowCommandHelp(c, "get") return cli.ShowCommandHelp(c, "get")
} }

View File

@ -3,8 +3,10 @@ package service
import ( import (
"bytes" "bytes"
"crypto/tls" "crypto/tls"
"crypto/x509"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
@ -20,7 +22,7 @@ type Client struct {
insecureSkipVerify bool insecureSkipVerify bool
} }
func NewClient(addr string, insecureSkipVerify bool) (*Client, error) { func NewClient(addr string, insecureSkipVerify bool, rootCA string) (*Client, error) {
if !strings.HasPrefix(addr, "https://") { if !strings.HasPrefix(addr, "https://") {
return nil, errors.New("Addrress must start with https://") return nil, errors.New("Addrress must start with https://")
} }
@ -30,9 +32,33 @@ func NewClient(addr string, insecureSkipVerify bool) (*Client, error) {
return nil, errors.Wrap(err, "parsing URL") return nil, errors.Wrap(err, "parsing URL")
} }
rootCAPool, err := x509.SystemCertPool()
if err != nil {
return nil, errors.Wrap(err, "loading system cert pool")
}
if rootCA != "" {
// set up empty cert pool
rootCAPool = x509.NewCertPool()
// read in the root cert file specified in the context
certs, err := ioutil.ReadFile(rootCA)
if err != nil {
return nil, errors.Wrap(err, "reading root CA")
}
// add certs to new cert pool
if ok := rootCAPool.AppendCertsFromPEM(certs); !ok {
return nil, errors.Wrap(err, "adding root CA")
}
}
httpClient := &http.Client{ httpClient := &http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: insecureSkipVerify}, TLSClientConfig: &tls.Config{
InsecureSkipVerify: insecureSkipVerify,
RootCAs: rootCAPool,
},
}, },
} }