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
It adds support for encrypted client certificate key during install/enroll, which done by the cli flag `--elastic-agent-cert-key-passphrase`.
  • Loading branch information
AndersonQ committed Sep 12, 2024
1 parent 1a449cd commit 1fa216d
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 1fa216d

Please sign in to comment.