Skip to content

Commit

Permalink
Add mTLS flags to install and enroll commands (#4007)
Browse files Browse the repository at this point in the history
Add mTLS flags to install and enroll commands
  • Loading branch information
michel-laterman authored Jan 18, 2024
1 parent aa6cd3e commit 249430b
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 20 deletions.
4 changes: 2 additions & 2 deletions NOTICE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1166,11 +1166,11 @@ SOFTWARE

--------------------------------------------------------------------------------
Dependency : github.com/elastic/elastic-agent-libs
Version: v0.7.3
Version: v0.7.4
Licence type (autodetected): Apache-2.0
--------------------------------------------------------------------------------

Contents of probable licence file $GOMODCACHE/github.com/elastic/[email protected].3/LICENSE:
Contents of probable licence file $GOMODCACHE/github.com/elastic/[email protected].4/LICENSE:

Apache License
Version 2.0, January 2004
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Kind can be one of:
# - breaking-change: a change to previously-documented behavior
# - deprecation: functionality that is being removed in a later release
# - bug-fix: fixes a problem in a previous version
# - enhancement: extends functionality but does not break or fix existing behavior
# - feature: new functionality
# - known-issue: problems that we are aware of in a given version
# - security: impacts on the security of a product or a user’s deployment.
# - upgrade: important information for someone upgrading from a prior version
# - other: does not fit into any of the other categories
kind: feature

# Change summary; a 80ish characters long description of the change.
summary: Add mTLS flags to install/enroll

# Long description; in case the summary is not enough to describe the change
# this field accommodate a description without length limits.
# NOTE: This field will be rendered only for breaking-change and known-issue kinds at the moment.
description: |
Add mTLS flags to the install/enroll commands to allow fleet-server to
use client certs when connecting to Elasticsearch, and to allow
elastic-agent to use client certs when connecting to fleet-server.
Fleet-server will use the CAs passed in `--certificate-authorities` to
validate any client certs. Agent client certs do not influence auth in
fleet-server, an enrollment token, or API key is still required.
# Affected component; a word indicating the component this changeset affects.
component:

# PR URL; optional; the PR number that added the changeset.
# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added.
# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number.
# Please provide it if you are adding a fragment for a different PR.
#pr: https://github.com/owner/repo/1234

# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of).
# If not present is automatically filled by the tooling with the issue linked to the PR number.
#issue: https://github.com/owner/repo/1234
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ require (
github.com/elastic/e2e-testing v1.1.0
github.com/elastic/elastic-agent-autodiscover v0.6.6
github.com/elastic/elastic-agent-client/v7 v7.8.0
github.com/elastic/elastic-agent-libs v0.7.3
github.com/elastic/elastic-agent-libs v0.7.4
github.com/elastic/elastic-agent-system-metrics v0.9.1
github.com/elastic/elastic-transport-go/v8 v8.3.0
github.com/elastic/go-elasticsearch/v8 v8.10.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -800,8 +800,8 @@ github.com/elastic/elastic-agent-autodiscover v0.6.6 h1:P1y0dDpbhJc7Uw/xe85irPEa
github.com/elastic/elastic-agent-autodiscover v0.6.6/go.mod h1:chulyCAyZb/njMHgzkhC/yWnt8v/Y6eCRUhmFVnsA5o=
github.com/elastic/elastic-agent-client/v7 v7.8.0 h1:GHFzDJIWpdgI0qDk5EcqbQJGvwTsl2E2vQK3/xe+MYQ=
github.com/elastic/elastic-agent-client/v7 v7.8.0/go.mod h1:ihtjqJzYiIltlRhNruaSSc0ogxIhqPD5hOMKq16cI1s=
github.com/elastic/elastic-agent-libs v0.7.3 h1:tc6JDXYR+2XFMHJVv+7+M0OwAbZPxm3caLJEd943dlE=
github.com/elastic/elastic-agent-libs v0.7.3/go.mod h1:9hlSaDPm0XTrUWrZjwvckgov1pDHnsGyybzAjNe/1wA=
github.com/elastic/elastic-agent-libs v0.7.4 h1:/cmwOLwNAyJDNeR6sFIbHCDHDLPX2zAb/MAxQq7BRpo=
github.com/elastic/elastic-agent-libs v0.7.4/go.mod h1:pGMj5myawdqu+xE+WKvM5FQzKQ/MonikkWOzoFTJxaU=
github.com/elastic/elastic-agent-system-metrics v0.9.1 h1:r0ofKHgPpl+W09ie7tzGcCDC0d4NZbQUv37rSgHf4FM=
github.com/elastic/elastic-agent-system-metrics v0.9.1/go.mod h1:9C1UEfj0P687HAzZepHszN6zXA+2tN2Lx3Osvq1zby8=
github.com/elastic/elastic-integration-corpus-generator-tool v0.5.0/go.mod h1:uf9N86y+UACGybdEhZLpwZ93XHWVhsYZAA4c2T2v6YM=
Expand Down
63 changes: 63 additions & 0 deletions internal/pkg/agent/cmd/enroll.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ func addEnrollFlags(cmd *cobra.Command) {
cmd.Flags().StringP("fleet-server-es-ca", "", "", "Path to certificate authority to use with communicate with elasticsearch")
cmd.Flags().StringP("fleet-server-es-ca-trusted-fingerprint", "", "", "Elasticsearch certificate authority's SHA256 fingerprint")
cmd.Flags().BoolP("fleet-server-es-insecure", "", false, "Disables validation of certificates")
cmd.Flags().StringP("fleet-server-es-cert", "", "", "Client certificate to use when connecting to Elasticsearch.")
cmd.Flags().StringP("fleet-server-es-cert-key", "", "", "Client private key to use when connecing to Elasticsearch.")
cmd.Flags().StringP("fleet-server-service-token", "", "", "Service token to use for communication with elasticsearch")
cmd.Flags().StringP("fleet-server-service-token-path", "", "", "Filepath for service token secret file to use for communication with elasticsearch")
cmd.Flags().StringP("fleet-server-policy", "", "", "Start and run a Fleet Server on this specific policy")
Expand All @@ -63,10 +65,13 @@ func addEnrollFlags(cmd *cobra.Command) {
cmd.Flags().StringP("fleet-server-cert", "", "", "Certificate to use for exposed Fleet Server HTTPS endpoint")
cmd.Flags().StringP("fleet-server-cert-key", "", "", "Private key to use for exposed Fleet Server HTTPS endpoint")
cmd.Flags().StringP("fleet-server-cert-key-passphrase", "", "", "Path for private key passphrase file used to decrypt certificate key")
cmd.Flags().StringP("fleet-server-client-auth", "", "none", "Fleet-server mTLS client authentication for connecting elastic-agents. Must be one of [none, optional, required]")
cmd.Flags().StringSliceP("header", "", []string{}, "Headers used in communication with elasticsearch")
cmd.Flags().BoolP("fleet-server-insecure-http", "", false, "Expose Fleet Server over HTTP (not recommended; insecure)")
cmd.Flags().StringP("certificate-authorities", "a", "", "Comma separated list of root certificate for server verifications")
cmd.Flags().StringP("ca-sha256", "p", "", "Comma separated list of certificate authorities hash pins used for certificate verifications")
cmd.Flags().StringP("elastic-agent-cert", "", "", "Elastic-agent client certificate to use with fleet-server during authentication")
cmd.Flags().StringP("elastic-agent-cert-key", "", "", "Elastic-agent client certificate to use with fleet-server during authentication")
cmd.Flags().BoolP("insecure", "i", false, "Allow insecure connection to fleet-server")
cmd.Flags().StringP("staging", "", "", "Configures agent to download artifacts from a staging build")
cmd.Flags().StringP("proxy-url", "", "", "Configures the proxy url")
Expand All @@ -86,10 +91,26 @@ func validateEnrollFlags(cmd *cobra.Command) error {
if ca != "" && !filepath.IsAbs(ca) {
return errors.New("--certificate-authorities must be provided as an absolute path", errors.M("path", ca), errors.TypeConfig)
}
cert, _ := cmd.Flags().GetString("elastic-agent-cert")
if cert != "" && !filepath.IsAbs(cert) {
return errors.New("--elastic-agent-cert must be provided as an absolute path", errors.M("path", cert), errors.TypeConfig)
}
key, _ := cmd.Flags().GetString("elastic-agent-cert-key")
if key != "" && !filepath.IsAbs(key) {
return errors.New("--elastic-agent-cert-key must be provided as an absolute path", errors.M("path", key), errors.TypeConfig)
}
esCa, _ := cmd.Flags().GetString("fleet-server-es-ca")
if esCa != "" && !filepath.IsAbs(esCa) {
return errors.New("--fleet-server-es-ca must be provided as an absolute path", errors.M("path", esCa), errors.TypeConfig)
}
esCert, _ := cmd.Flags().GetString("fleet-server-es-cert")
if esCert != "" && !filepath.IsAbs(esCert) {
return errors.New("--fleet-server-es-cert must be provided as an absolute path", errors.M("path", esCert), errors.TypeConfig)
}
esCertKey, _ := cmd.Flags().GetString("fleet-server-es-cert-key")
if esCertKey != "" && !filepath.IsAbs(esCertKey) {
return errors.New("--fleet-server-es-cert-key must be provided as an absolute path", errors.M("path", esCertKey), errors.TypeConfig)
}
fCert, _ := cmd.Flags().GetString("fleet-server-cert")
if fCert != "" && !filepath.IsAbs(fCert) {
return errors.New("--fleet-server-cert must be provided as an absolute path", errors.M("path", fCert), errors.TypeConfig)
Expand All @@ -110,6 +131,13 @@ func validateEnrollFlags(cmd *cobra.Command) error {
if fPassphrase != "" && !filepath.IsAbs(fPassphrase) {
return errors.New("--fleet-server-cert-key-passphrase must be provided as an absolute path", errors.M("path", fPassphrase), errors.TypeConfig)
}
fClientAuth, _ := cmd.Flags().GetString("fleet-server-client-auth")
switch fClientAuth {
case "none", "optional", "required":
// NOTE we can split this case if we want to do additional checks when optional or required is passed.
default:
return errors.New("--fleet-server-client-auth must be one of [none, optional, required]")
}
return nil
}

Expand All @@ -124,6 +152,8 @@ func buildEnrollmentFlags(cmd *cobra.Command, url string, token string) []string
fElasticSearchCA, _ := cmd.Flags().GetString("fleet-server-es-ca")
fElasticSearchCASHA256, _ := cmd.Flags().GetString("fleet-server-es-ca-trusted-fingerprint")
fElasticSearchInsecure, _ := cmd.Flags().GetBool("fleet-server-es-insecure")
fElasticSearchClientCert, _ := cmd.Flags().GetString("fleet-server-es-cert")
fElasticSearchClientCertKey, _ := cmd.Flags().GetString("fleet-server-es-cert-key")
fServiceToken, _ := cmd.Flags().GetString("fleet-server-service-token")
fServiceTokenPath, _ := cmd.Flags().GetString("fleet-server-service-token-path")
fPolicy, _ := cmd.Flags().GetString("fleet-server-policy")
Expand All @@ -132,9 +162,12 @@ func buildEnrollmentFlags(cmd *cobra.Command, url string, token string) []string
fCert, _ := cmd.Flags().GetString("fleet-server-cert")
fCertKey, _ := cmd.Flags().GetString("fleet-server-cert-key")
fPassphrase, _ := cmd.Flags().GetString("fleet-server-cert-key-passphrase")
fClientAuth, _ := cmd.Flags().GetString("fleet-server-client-auth")
fHeaders, _ := cmd.Flags().GetStringSlice("header")
fInsecure, _ := cmd.Flags().GetBool("fleet-server-insecure-http")
ca, _ := cmd.Flags().GetString("certificate-authorities")
cert, _ := cmd.Flags().GetString("elastic-agent-cert")
key, _ := cmd.Flags().GetString("elastic-agent-cert-key")
sha256, _ := cmd.Flags().GetString("ca-sha256")
insecure, _ := cmd.Flags().GetBool("insecure")
staging, _ := cmd.Flags().GetString("staging")
Expand Down Expand Up @@ -167,6 +200,14 @@ func buildEnrollmentFlags(cmd *cobra.Command, url string, token string) []string
args = append(args, "--fleet-server-es-ca-trusted-fingerprint")
args = append(args, fElasticSearchCASHA256)
}
if fElasticSearchClientCert != "" {
args = append(args, "--fleet-server-es-cert")
args = append(args, fElasticSearchClientCert)
}
if fElasticSearchClientCertKey != "" {
args = append(args, "--fleet-server-es-cert-key")
args = append(args, fElasticSearchClientCertKey)
}
if fServiceToken != "" {
args = append(args, "--fleet-server-service-token")
args = append(args, fServiceToken)
Expand Down Expand Up @@ -199,6 +240,10 @@ func buildEnrollmentFlags(cmd *cobra.Command, url string, token string) []string
args = append(args, "--fleet-server-cert-key-passphrase")
args = append(args, fPassphrase)
}
if fClientAuth != "" {
args = append(args, "--fleet-server-client-auth")
args = append(args, fClientAuth)
}
if daemonTimeout != 0 {
args = append(args, "--daemon-timeout")
args = append(args, daemonTimeout.String())
Expand All @@ -220,6 +265,14 @@ func buildEnrollmentFlags(cmd *cobra.Command, url string, token string) []string
args = append(args, "--certificate-authorities")
args = append(args, ca)
}
if cert != "" {
args = append(args, "--elastic-agent-cert")
args = append(args, cert)
}
if key != "" {
args = append(args, "--elastic-agent-cert-key")
args = append(args, key)
}
if sha256 != "" {
args = append(args, "--ca-sha256")
args = append(args, sha256)
Expand Down Expand Up @@ -328,6 +381,8 @@ func enroll(streams *cli.IOStreams, cmd *cobra.Command) error {
fElasticSearchCA, _ := cmd.Flags().GetString("fleet-server-es-ca")
fElasticSearchCASHA256, _ := cmd.Flags().GetString("fleet-server-es-ca-trusted-fingerprint")
fElasticSearchInsecure, _ := cmd.Flags().GetBool("fleet-server-es-insecure")
fElasticSearchClientCert, _ := cmd.Flags().GetString("fleet-server-es-cert")
fElasticSearchClientCertKey, _ := cmd.Flags().GetString("fleet-server-es-cert-key")
fHeaders, _ := cmd.Flags().GetStringSlice("header")
fServiceToken, _ := cmd.Flags().GetString("fleet-server-service-token")
fServiceTokenPath, _ := cmd.Flags().GetString("fleet-server-service-token-path")
Expand All @@ -338,6 +393,7 @@ func enroll(streams *cli.IOStreams, cmd *cobra.Command) error {
fCert, _ := cmd.Flags().GetString("fleet-server-cert")
fCertKey, _ := cmd.Flags().GetString("fleet-server-cert-key")
fPassphrase, _ := cmd.Flags().GetString("fleet-server-cert-key-passphrase")
fClientAuth, _ := cmd.Flags().GetString("fleet-server-client-auth")
fInsecure, _ := cmd.Flags().GetBool("fleet-server-insecure-http")
proxyURL, _ := cmd.Flags().GetString("proxy-url")
proxyDisabled, _ := cmd.Flags().GetBool("proxy-disabled")
Expand All @@ -352,6 +408,8 @@ func enroll(streams *cli.IOStreams, cmd *cobra.Command) error {
CAs := cli.StringToSlice(caStr)
caSHA256str, _ := cmd.Flags().GetString("ca-sha256")
caSHA256 := cli.StringToSlice(caSHA256str)
cert, _ := cmd.Flags().GetString("elastic-agent-cert")
key, _ := cmd.Flags().GetString("elastic-agent-cert-key")

ctx := handleSignal(context.Background())

Expand All @@ -369,6 +427,8 @@ func enroll(streams *cli.IOStreams, cmd *cobra.Command) error {
URL: url,
CAs: CAs,
CASha256: caSHA256,
Certificate: cert,
Key: key,
Insecure: insecure,
UserProvidedMetadata: make(map[string]interface{}),
Staging: staging,
Expand All @@ -385,6 +445,8 @@ func enroll(streams *cli.IOStreams, cmd *cobra.Command) error {
ElasticsearchCA: fElasticSearchCA,
ElasticsearchCASHA256: fElasticSearchCASHA256,
ElasticsearchInsecure: fElasticSearchInsecure,
ElasticsearchCert: fElasticSearchClientCert,
ElasticsearchCertKey: fElasticSearchClientCertKey,
ServiceToken: fServiceToken,
ServiceTokenPath: fServiceTokenPath,
PolicyID: fPolicy,
Expand All @@ -393,6 +455,7 @@ func enroll(streams *cli.IOStreams, cmd *cobra.Command) error {
Cert: fCert,
CertKey: fCertKey,
CertKeyPassphrasePath: fPassphrase,
ClientAuth: fClientAuth,
Insecure: fInsecure,
SpawnAgent: !fromInstall,
Headers: mapFromEnvList(fHeaders),
Expand Down
37 changes: 36 additions & 1 deletion internal/pkg/agent/cmd/enroll_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ type enrollCmdFleetServerOption struct {
ElasticsearchCA string
ElasticsearchCASHA256 string
ElasticsearchInsecure bool
ElasticsearchCert string
ElasticsearchCertKey string
ServiceToken string
ServiceTokenPath string
PolicyID string
Expand All @@ -89,6 +91,7 @@ type enrollCmdFleetServerOption struct {
Cert string
CertKey string
CertKeyPassphrasePath string
ClientAuth string
Insecure bool
SpawnAgent bool
Headers map[string]string
Expand All @@ -101,6 +104,8 @@ type enrollCmdOption struct {
InternalURL string `yaml:"-"`
CAs []string `yaml:"ca,omitempty"`
CASha256 []string `yaml:"ca_sha256,omitempty"`
Certificate string `yaml:"certificate,omitempty"`
Key string `yaml:"key,omitempty"`
Insecure bool `yaml:"insecure,omitempty"`
EnrollAPIKey string `yaml:"enrollment_key,omitempty"`
Staging string `yaml:"staging,omitempty"`
Expand Down Expand Up @@ -137,6 +142,12 @@ func (e *enrollCmdOption) remoteConfig() (remote.Config, error) {
if e.Insecure {
tlsCfg.VerificationMode = tlscommon.VerifyNone
}
if e.Certificate != "" || e.Key != "" {
tlsCfg.Certificate = tlscommon.CertificateConfig{
Certificate: e.Certificate,
Key: e.Key,
}
}

cfg.Transport.TLS = &tlsCfg

Expand Down Expand Up @@ -344,6 +355,8 @@ func (c *enrollCmd) fleetServerBootstrap(ctx context.Context, persistentConfig m
c.options.FleetServer.PolicyID,
c.options.FleetServer.Host, c.options.FleetServer.Port, c.options.FleetServer.InternalPort,
c.options.FleetServer.Cert, c.options.FleetServer.CertKey, c.options.FleetServer.CertKeyPassphrasePath, c.options.FleetServer.ElasticsearchCA, c.options.FleetServer.ElasticsearchCASHA256,
c.options.CAs, c.options.FleetServer.ClientAuth,
c.options.FleetServer.ElasticsearchCert, c.options.FleetServer.ElasticsearchCertKey,
c.options.FleetServer.Headers,
c.options.ProxyURL,
c.options.ProxyDisabled,
Expand Down Expand Up @@ -570,6 +583,8 @@ func (c *enrollCmd) enroll(ctx context.Context, persistentConfig map[string]inte
c.options.FleetServer.PolicyID,
c.options.FleetServer.Host, c.options.FleetServer.Port, c.options.FleetServer.InternalPort,
c.options.FleetServer.Cert, c.options.FleetServer.CertKey, c.options.FleetServer.CertKeyPassphrasePath, c.options.FleetServer.ElasticsearchCA, c.options.FleetServer.ElasticsearchCASHA256,
c.options.CAs, c.options.FleetServer.ClientAuth,
c.options.FleetServer.ElasticsearchCert, c.options.FleetServer.ElasticsearchCertKey,
c.options.FleetServer.Headers,
c.options.ProxyURL, c.options.ProxyDisabled, c.options.ProxyHeaders,
c.options.FleetServer.ElasticsearchInsecure,
Expand Down Expand Up @@ -921,6 +936,8 @@ func createFleetServerBootstrapConfig(
connStr, serviceToken, serviceTokenPath, policyID, host string,
port uint16, internalPort uint16,
cert, key, passphrasePath, esCA, esCASHA256 string,
cas []string, clientAuth string,
esClientCert, esClientCertKey string,
headers map[string]string,
proxyURL string,
proxyDisabled bool,
Expand Down Expand Up @@ -951,6 +968,16 @@ func createFleetServerBootstrapConfig(
es.TLS.CATrustedFingerprint = esCASHA256
}
}
if esClientCert != "" || esClientCertKey != "" {
if es.TLS == nil {
es.TLS = &tlscommon.Config{}
}

es.TLS.Certificate = tlscommon.CertificateConfig{
Certificate: esClientCert,
Key: esClientCertKey,
}
}
if host == "" {
host = defaultFleetServerHost
}
Expand Down Expand Up @@ -988,7 +1015,7 @@ func createFleetServerBootstrapConfig(
cfg.Server.Policy = &configuration.FleetServerPolicyConfig{ID: policyID}
}
if cert != "" || key != "" {
cfg.Server.TLS = &tlscommon.Config{
cfg.Server.TLS = &tlscommon.ServerConfig{
Certificate: tlscommon.CertificateConfig{
Certificate: cert,
Key: key,
Expand All @@ -998,6 +1025,14 @@ func createFleetServerBootstrapConfig(
if insecure {
cfg.Server.TLS.VerificationMode = tlscommon.VerifyNone
}

cfg.Server.TLS.CAs = cas

var cAuth tlscommon.TLSClientAuth
cfg.Server.TLS.ClientAuth = &cAuth
if err := cfg.Server.TLS.ClientAuth.Unpack(clientAuth); err != nil {
return nil, errors.New(err, "failed to unpack --fleet-server-client-auth", errors.TypeConfig)
}
}

if localFleetServer {
Expand Down
Loading

0 comments on commit 249430b

Please sign in to comment.