2018-05-04 16:53:21 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2021-08-24 12:50:03 +00:00
|
|
|
"crypto/tls"
|
|
|
|
"crypto/x509"
|
2021-11-22 14:13:26 +00:00
|
|
|
"errors"
|
2021-07-16 18:28:13 +00:00
|
|
|
"flag"
|
2018-05-04 16:53:21 +00:00
|
|
|
"fmt"
|
2021-08-26 13:28:53 +00:00
|
|
|
"io"
|
2021-08-24 12:50:03 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
2021-07-16 18:28:13 +00:00
|
|
|
"os"
|
2020-11-18 00:02:14 +00:00
|
|
|
"runtime"
|
2018-05-04 16:53:21 +00:00
|
|
|
|
2021-06-26 04:46:51 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/service"
|
2021-03-13 00:42:38 +00:00
|
|
|
"github.com/urfave/cli/v2"
|
2018-05-04 16:53:21 +00:00
|
|
|
)
|
|
|
|
|
2018-07-16 16:35:21 +00:00
|
|
|
func unauthenticatedClientFromCLI(c *cli.Context) (*service.Client, error) {
|
2021-08-24 12:50:03 +00:00
|
|
|
cc, err := clientConfigFromCLI(c)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2021-07-16 18:28:13 +00:00
|
|
|
}
|
|
|
|
|
2021-08-26 13:28:53 +00:00
|
|
|
return unauthenticatedClientFromConfig(cc, getDebug(c), c.App.Writer)
|
2021-08-24 12:50:03 +00:00
|
|
|
}
|
2018-05-04 16:53:21 +00:00
|
|
|
|
2021-10-22 18:41:17 +00:00
|
|
|
var invalidSessionErr = errors.New("Invalid session. Please log in with: fleetctl login")
|
|
|
|
|
2021-08-24 12:50:03 +00:00
|
|
|
func clientFromCLI(c *cli.Context) (*service.Client, error) {
|
|
|
|
fleet, err := unauthenticatedClientFromCLI(c)
|
2018-05-04 16:53:21 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-08-24 12:50:03 +00:00
|
|
|
configPath, context := c.String("config"), c.String("context")
|
|
|
|
|
2021-10-07 13:19:10 +00:00
|
|
|
// if a config file is explicitly provided, do not set an invalid arbitrary token
|
|
|
|
if !c.IsSet("config") && flag.Lookup("test.v") != nil {
|
2021-08-24 12:50:03 +00:00
|
|
|
fleet.SetToken("AAAA")
|
|
|
|
return fleet, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add authentication token
|
|
|
|
t, err := getConfigValue(configPath, context, "token")
|
|
|
|
if err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return nil, fmt.Errorf("error getting token from the config: %w", err)
|
2021-08-24 12:50:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if token, ok := t.(string); ok {
|
|
|
|
if token == "" {
|
|
|
|
return nil, errors.New("Please log in with: fleetctl login")
|
|
|
|
}
|
|
|
|
fleet.SetToken(token)
|
|
|
|
} else {
|
2021-11-22 14:13:26 +00:00
|
|
|
return nil, fmt.Errorf("token config value was not a string: %+v", t)
|
2021-08-24 12:50:03 +00:00
|
|
|
}
|
|
|
|
|
2021-10-22 18:41:17 +00:00
|
|
|
// Perform a "/me" request to verify the session is valid.
|
|
|
|
response, err := fleet.AuthenticatedDo("GET", "/api/v1/fleet/me", "", nil)
|
|
|
|
if err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return nil, fmt.Errorf("Failed to execute session check: %w", err)
|
2021-10-22 18:41:17 +00:00
|
|
|
}
|
|
|
|
defer response.Body.Close()
|
|
|
|
|
|
|
|
switch response.StatusCode {
|
|
|
|
case http.StatusOK:
|
|
|
|
// OK
|
|
|
|
case http.StatusUnauthorized:
|
|
|
|
return nil, invalidSessionErr
|
|
|
|
default:
|
2021-11-22 14:13:26 +00:00
|
|
|
return nil, fmt.Errorf("session check received status: %d", response.StatusCode)
|
2021-10-22 18:41:17 +00:00
|
|
|
}
|
|
|
|
|
2021-08-24 12:50:03 +00:00
|
|
|
return fleet, nil
|
|
|
|
}
|
|
|
|
|
2021-08-26 13:28:53 +00:00
|
|
|
func unauthenticatedClientFromConfig(cc Context, debug bool, w io.Writer) (*service.Client, error) {
|
|
|
|
options := []service.ClientOption{service.SetClientWriter(w)}
|
|
|
|
|
2021-08-24 12:50:03 +00:00
|
|
|
if flag.Lookup("test.v") != nil {
|
2021-08-26 13:28:53 +00:00
|
|
|
return service.NewClient(
|
|
|
|
os.Getenv("FLEET_SERVER_ADDRESS"), true, "", "", options...)
|
2018-05-04 16:53:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if cc.Address == "" {
|
|
|
|
return nil, errors.New("set the Fleet API address with: fleetctl config set --address https://localhost:8080")
|
|
|
|
}
|
|
|
|
|
2020-11-18 00:02:14 +00:00
|
|
|
if runtime.GOOS == "windows" && cc.RootCA == "" && !cc.TLSSkipVerify {
|
|
|
|
return nil, errors.New("Windows clients must configure rootca (secure) or tls-skip-verify (insecure)")
|
|
|
|
}
|
|
|
|
|
2021-08-24 12:50:03 +00:00
|
|
|
if debug {
|
2021-02-03 02:55:16 +00:00
|
|
|
options = append(options, service.EnableClientDebug())
|
|
|
|
}
|
|
|
|
|
|
|
|
fleet, err := service.NewClient(
|
|
|
|
cc.Address,
|
|
|
|
cc.TLSSkipVerify,
|
|
|
|
cc.RootCA,
|
|
|
|
cc.URLPrefix,
|
|
|
|
options...,
|
|
|
|
)
|
2018-05-04 16:53:21 +00:00
|
|
|
if err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return nil, fmt.Errorf("error creating Fleet API client handler: %w", err)
|
2018-05-04 16:53:21 +00:00
|
|
|
}
|
|
|
|
|
2018-07-16 16:35:21 +00:00
|
|
|
return fleet, nil
|
|
|
|
}
|
|
|
|
|
2021-08-24 12:50:03 +00:00
|
|
|
// returns an HTTP client and the parsed URL for the configured server's
|
|
|
|
// address. The reason why this exists instead of using
|
|
|
|
// unauthenticatedClientFromConfig is because this doesn't apply the same rules
|
|
|
|
// around TLS config - in particular, it only sets a root CA if one is
|
|
|
|
// explicitly configured.
|
|
|
|
func rawHTTPClientFromConfig(cc Context) (*http.Client, *url.URL, error) {
|
|
|
|
if flag.Lookup("test.v") != nil {
|
|
|
|
cc.Address = os.Getenv("FLEET_SERVER_ADDRESS")
|
|
|
|
}
|
|
|
|
baseURL, err := url.Parse(cc.Address)
|
2018-07-16 16:35:21 +00:00
|
|
|
if err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return nil, nil, fmt.Errorf("parse address: %w", err)
|
2018-07-16 16:35:21 +00:00
|
|
|
}
|
|
|
|
|
2021-08-24 12:50:03 +00:00
|
|
|
var rootCA *x509.CertPool
|
|
|
|
if cc.RootCA != "" {
|
|
|
|
rootCA = x509.NewCertPool()
|
|
|
|
// read in the root cert file specified in the context
|
|
|
|
certs, err := ioutil.ReadFile(cc.RootCA)
|
|
|
|
if err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return nil, nil, fmt.Errorf("reading root CA: %w", err)
|
2021-08-24 12:50:03 +00:00
|
|
|
}
|
2021-01-29 01:15:38 +00:00
|
|
|
|
2021-08-24 12:50:03 +00:00
|
|
|
// add certs to pool
|
|
|
|
if ok := rootCA.AppendCertsFromPEM(certs); !ok {
|
|
|
|
return nil, nil, errors.New("failed to add certificates to root CA pool")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cli := &http.Client{
|
|
|
|
Transport: &http.Transport{
|
|
|
|
TLSClientConfig: &tls.Config{
|
|
|
|
InsecureSkipVerify: cc.TLSSkipVerify,
|
|
|
|
RootCAs: rootCA,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
return cli, baseURL, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func clientConfigFromCLI(c *cli.Context) (Context, error) {
|
2021-07-16 18:28:13 +00:00
|
|
|
if flag.Lookup("test.v") != nil {
|
2021-08-24 12:50:03 +00:00
|
|
|
return Context{
|
|
|
|
Address: os.Getenv("FLEET_SERVER_ADDRESS"),
|
|
|
|
TLSSkipVerify: true,
|
|
|
|
}, nil
|
2021-07-16 18:28:13 +00:00
|
|
|
}
|
|
|
|
|
2021-08-24 12:50:03 +00:00
|
|
|
var zeroCtx Context
|
|
|
|
|
|
|
|
if err := makeConfigIfNotExists(c.String("config")); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return zeroCtx, fmt.Errorf("error verifying that config exists at %s: %w", c.String("config"), err)
|
2018-05-04 16:53:21 +00:00
|
|
|
}
|
|
|
|
|
2021-08-24 12:50:03 +00:00
|
|
|
config, err := readConfig(c.String("config"))
|
|
|
|
if err != nil {
|
|
|
|
return zeroCtx, err
|
2018-05-04 16:53:21 +00:00
|
|
|
}
|
|
|
|
|
2021-08-24 12:50:03 +00:00
|
|
|
cc, ok := config.Contexts[c.String("context")]
|
|
|
|
if !ok {
|
|
|
|
return zeroCtx, fmt.Errorf("context %q is not found", c.String("context"))
|
|
|
|
}
|
|
|
|
return cc, nil
|
2018-05-04 16:53:21 +00:00
|
|
|
}
|