Skip to content

Commit

Permalink
add support for CLI flag for mTLS client certificate key passphrase (#…
Browse files Browse the repository at this point in the history
…5494)

It adds support for encrypted client certificate key during install/enroll, which done by the cli flag `--elastic-agent-cert-key-passphrase`.

(cherry picked from commit 346e5be)
  • Loading branch information
AndersonQ authored and mergify[bot] committed Sep 20, 2024
1 parent b58197d commit 2815579
Show file tree
Hide file tree
Showing 5 changed files with 308 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# 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 support for passphrase protected mTLS client certificate key during 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: |
Adds `--elastic-agent-cert-key-passphrase` command line flag for the `install`
and `enroll` commands. The new flag accepts a absolute path for a file containing
a passphrase to be used to decrypt the mTLS client certificate key.
# 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
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,46 @@ func TestPolicyChangeHandler_handlePolicyChange_FleetClientSettings(t *testing.T
"unexpected error when applying fleet.ssl.certificate and key")
},
},
{
name: "certificate and key without passphrase clear out previous passphrase",
originalCfg: &configuration.Configuration{
Fleet: &configuration.FleetAgentConfig{
Client: remote.Config{
Host: fleetmTLSServer.URL,
Transport: httpcommon.HTTPTransportSettings{
TLS: &tlscommon.Config{
CAs: []string{string(fleetRootPair.Cert)},
Certificate: tlscommon.CertificateConfig{
Certificate: "some certificate",
Key: "some key",
Passphrase: "",
PassphrasePath: "/path/to/passphrase",
},
},
},
},
AccessAPIKey: "ignore",
},
Settings: configuration.DefaultSettingsConfig(),
},
newCfg: map[string]interface{}{
"fleet.ssl.enabled": true,
"fleet.ssl.certificate": string(agentChildPair.Cert),
"fleet.ssl.key": string(agentChildPair.Key),
},
setterCalledCount: 1,
wantCAs: []string{string(fleetRootPair.Cert)},
wantCertificateConfig: tlscommon.CertificateConfig{
Certificate: string(agentChildPair.Cert),
Key: string(agentChildPair.Key),
Passphrase: "",
PassphrasePath: "",
},
assertErr: func(t *testing.T, err error) {
assert.NoError(t, err,
"unexpected error when applying fleet.ssl.certificate and key")
},
},
{
name: "certificate and key with passphrase_path is applied when present",
originalCfg: &configuration.Configuration{
Expand Down
18 changes: 18 additions & 0 deletions internal/pkg/agent/cmd/enroll.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func addEnrollFlags(cmd *cobra.Command) {
cmd.Flags().StringP("ca-sha256", "p", "", "Comma-separated list of certificate authority hash pins for server verification used by Elastic Agent and Fleet Server")
cmd.Flags().StringP("elastic-agent-cert", "", "", "Elastic Agent client certificate to use with Fleet Server during mTLS authentication")
cmd.Flags().StringP("elastic-agent-cert-key", "", "", "Elastic Agent client private key to use with Fleet Server during mTLS authentication")
cmd.Flags().StringP("elastic-agent-cert-key-passphrase", "", "", "Path for private key passphrase file used to decrypt Elastic Agent client certificate key")
cmd.Flags().BoolP("insecure", "i", false, "Allow insecure connection made by the Elastic Agent. It's also required to use a Fleet Server on a HTTP endpoint")
cmd.Flags().StringP("staging", "", "", "Configures Elastic Agent to download artifacts from a staging build")
cmd.Flags().StringP("proxy-url", "", "", "Configures the proxy URL: when bootstrapping Fleet Server, it's the proxy used by Fleet Server to connect to Elasticsearch; when enrolling the Elastic Agent to Fleet Server, it's the proxy used by the Elastic Agent to connect to Fleet Server")
Expand Down Expand Up @@ -111,6 +112,16 @@ func validateEnrollFlags(cmd *cobra.Command) error {
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)
}
keyPassphrase, _ := cmd.Flags().GetString("elastic-agent-cert-key-passphrase")
if keyPassphrase != "" {
if !filepath.IsAbs(keyPassphrase) {
return errors.New("--elastic-agent-cert-key-passphrase must be provided as an absolute path", errors.M("path", keyPassphrase), errors.TypeConfig)
}

if cert == "" || key == "" {
return errors.New("--elastic-agent-cert and --elastic-agent-cert-key must be provided when using --elastic-agent-cert-key-passphrase", errors.M("path", keyPassphrase), 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)
Expand Down Expand Up @@ -180,6 +191,7 @@ func buildEnrollmentFlags(cmd *cobra.Command, url string, token string) []string
ca, _ := cmd.Flags().GetString("certificate-authorities")
cert, _ := cmd.Flags().GetString("elastic-agent-cert")
key, _ := cmd.Flags().GetString("elastic-agent-cert-key")
keyPassphrase, _ := cmd.Flags().GetString("elastic-agent-cert-key-passphrase")
sha256, _ := cmd.Flags().GetString("ca-sha256")
insecure, _ := cmd.Flags().GetBool("insecure")
staging, _ := cmd.Flags().GetString("staging")
Expand Down Expand Up @@ -285,6 +297,10 @@ func buildEnrollmentFlags(cmd *cobra.Command, url string, token string) []string
args = append(args, "--elastic-agent-cert-key")
args = append(args, key)
}
if keyPassphrase != "" {
args = append(args, "--elastic-agent-cert-key-passphrase")
args = append(args, keyPassphrase)
}
if sha256 != "" {
args = append(args, "--ca-sha256")
args = append(args, sha256)
Expand Down Expand Up @@ -422,6 +438,7 @@ func enroll(streams *cli.IOStreams, cmd *cobra.Command) error {
caSHA256 := cli.StringToSlice(caSHA256str)
cert, _ := cmd.Flags().GetString("elastic-agent-cert")
key, _ := cmd.Flags().GetString("elastic-agent-cert-key")
keyPassphrase, _ := cmd.Flags().GetString("elastic-agent-cert-key-passphrase")

ctx := handleSignal(context.Background())

Expand Down Expand Up @@ -449,6 +466,7 @@ func enroll(streams *cli.IOStreams, cmd *cobra.Command) error {
CASha256: caSHA256,
Certificate: cert,
Key: key,
KeyPassphrasePath: keyPassphrase,
Insecure: insecure,
UserProvidedMetadata: make(map[string]interface{}),
Staging: staging,
Expand Down
6 changes: 4 additions & 2 deletions internal/pkg/agent/cmd/enroll_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ type enrollCmdOption struct {
CASha256 []string `yaml:"ca_sha256,omitempty"`
Certificate string `yaml:"certificate,omitempty"`
Key string `yaml:"key,omitempty"`
KeyPassphrasePath string `yaml:"key_passphrase_path,omitempty"`
Insecure bool `yaml:"insecure,omitempty"`
EnrollAPIKey string `yaml:"enrollment_key,omitempty"`
Staging string `yaml:"staging,omitempty"`
Expand Down Expand Up @@ -149,8 +150,9 @@ func (e *enrollCmdOption) remoteConfig() (remote.Config, error) {
}
if e.Certificate != "" || e.Key != "" {
tlsCfg.Certificate = tlscommon.CertificateConfig{
Certificate: e.Certificate,
Key: e.Key,
Certificate: e.Certificate,
Key: e.Key,
PassphrasePath: e.KeyPassphrasePath,
}
}

Expand Down
Loading

0 comments on commit 2815579

Please sign in to comment.