Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mTLS flags to install and enroll commands #4007

Merged
merged 16 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.5.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.5.0 h1:niI3WQ+01Lnp2r5LxK8SyNhrPJe13vBiOkqrDRK2oTA=
github.com/elastic/elastic-agent-client/v7 v7.5.0/go.mod h1:DYoX95xjC4BW/p2avyu724Qr2+hoUIz9eCU9CVS1d+0=
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 != "" {
AndersonQ marked this conversation as resolved.
Show resolved Hide resolved
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
Loading