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

Elastic Defend install path runtime prevention #3114

Merged
merged 9 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# 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: enhancement

# Change summary; a 80ish characters long description of the change.
summary: Elastic Defend runtime prevention, if agent is installed in non-default location

# 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:

# Affected component; a word indicating the component this changeset affects.
component: agent

# 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
7 changes: 6 additions & 1 deletion internal/pkg/agent/application/paths/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const (
// AgentLockFileName is the name of the overall Elastic Agent file lock.
AgentLockFileName = "agent.lock"
tempSubdir = "tmp"
tempSubdirPerms = 0770
tempSubdirPerms = 0o770

darwin = "darwin"
)
Expand Down Expand Up @@ -254,3 +254,8 @@ func binaryDir(baseDir string) string {
func BinaryPath(baseDir, agentName string) string {
return filepath.Join(binaryDir(baseDir), agentName)
}

// InstallPath returns the top level directory Agent will be installed into.
func InstallPath(basePath string) string {
return filepath.Join(basePath, "Elastic", "Agent")
}
6 changes: 1 addition & 5 deletions internal/pkg/agent/cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func installCmd(streams *cli.IOStreams, cmd *cobra.Command) error {
return fmt.Errorf("unable to perform install command, not executed with %s permissions", utils.PermissionUser)
}

topPath := installPath(basePath)
topPath := paths.InstallPath(basePath)

status, reason := install.Status(topPath)
force, _ := cmd.Flags().GetBool("force")
Expand Down Expand Up @@ -238,7 +238,3 @@ func installCmd(streams *cli.IOStreams, cmd *cobra.Command) error {
fmt.Fprint(streams.Out, "Elastic Agent has been successfully installed.\n")
return nil
}

func installPath(basePath string) string {
return filepath.Join(basePath, "Elastic", "Agent")
}
3 changes: 2 additions & 1 deletion internal/pkg/agent/cmd/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/spf13/cobra"
"github.com/stretchr/testify/require"

"github.com/elastic/elastic-agent/internal/pkg/agent/application/paths"
"github.com/elastic/elastic-agent/internal/pkg/cli"
)

Expand All @@ -23,7 +24,7 @@ func TestInstallPath(t *testing.T) {

for name, basePath := range tests {
t.Run(name, func(t *testing.T) {
p := installPath(basePath)
p := paths.InstallPath(basePath)
require.Equal(t, basePath+"/Elastic/Agent", p)
})
}
Expand Down
3 changes: 2 additions & 1 deletion internal/pkg/agent/cmd/install_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/spf13/cobra"
"github.com/stretchr/testify/require"

"github.com/elastic/elastic-agent/internal/pkg/agent/application/paths"
"github.com/elastic/elastic-agent/internal/pkg/cli"
)

Expand All @@ -23,7 +24,7 @@ func TestInstallPath(t *testing.T) {

for name, basePath := range tests {
t.Run(name, func(t *testing.T) {
p := installPath(basePath)
p := paths.InstallPath(basePath)
require.Equal(t, basePath+`\Elastic\Agent`, p)
})
}
Expand Down
17 changes: 11 additions & 6 deletions pkg/component/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/elastic/elastic-agent-client/v7/pkg/client"
"github.com/elastic/elastic-agent-client/v7/pkg/proto"
"github.com/elastic/elastic-agent-libs/logp"
"github.com/elastic/elastic-agent/internal/pkg/agent/application/paths"
"github.com/elastic/elastic-agent/internal/pkg/agent/transpiler"
"github.com/elastic/elastic-agent/internal/pkg/eql"
"github.com/elastic/elastic-agent/pkg/features"
Expand Down Expand Up @@ -323,8 +324,7 @@ func (r *RuntimeSpecs) componentForInputType(
if componentErr == nil {
if output.shipperEnabled {
var shipperType string
shipperType, componentErr =
r.getSupportedShipperType(inputSpec, output.outputType)
shipperType, componentErr = r.getSupportedShipperType(inputSpec, output.outputType)

if componentErr == nil {
// We've found a valid shipper, construct the reference
Expand Down Expand Up @@ -406,8 +406,7 @@ func (r *RuntimeSpecs) componentsForOutput(output outputI, featureFlags *feature

// create the shipper components to go with the inputs
for shipperType := range shipperTypes {
shipperComponent, ok :=
r.componentForShipper(shipperType, output, components, featureFlags)
shipperComponent, ok := r.componentForShipper(shipperType, output, components, featureFlags)
if ok {
components = append(components, shipperComponent)
}
Expand All @@ -421,7 +420,6 @@ func (r *RuntimeSpecs) componentForShipper(
inputComponents []Component,
featureFlags *features.Flags,
) (Component, bool) {

shipperSpec := r.shipperSpecs[shipperType] // type always exists at this point
shipperCompID := fmt.Sprintf("%s-%s", shipperType, output.name)

Expand Down Expand Up @@ -826,6 +824,9 @@ func varsForPlatform(platform PlatformDetail) (*transpiler.Vars, error) {
return nil, err
}
return transpiler.NewVars("", map[string]interface{}{
"install": map[string]interface{}{
"in_default": paths.ArePathsEqual(paths.Top(), paths.InstallPath(paths.DefaultBasePath)),
},
"runtime": map[string]interface{}{
"platform": platform.String(),
"os": platform.OS,
Expand All @@ -848,6 +849,7 @@ func validateRuntimeChecks(
if err != nil {
return err
}
preventionMessages := []string{}
for _, prevention := range runtime.Preventions {
expression, err := eql.New(prevention.Condition)
if err != nil {
Expand All @@ -862,9 +864,12 @@ func validateRuntimeChecks(
}
if preventionTrigger {
// true means the prevention valid (so input should not run)
return NewErrInputRuntimeCheckFail(prevention.Message)
preventionMessages = append(preventionMessages, prevention.Message)
}
}
if len(preventionMessages) > 0 {
return NewErrInputRuntimeCheckFail(strings.Join(preventionMessages, ", "))
}
return nil
}

Expand Down
29 changes: 20 additions & 9 deletions pkg/component/component_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import (
)

func TestToComponents(t *testing.T) {
var linuxAMD64Platform = PlatformDetail{
linuxAMD64Platform := PlatformDetail{
Platform: Platform{
OS: Linux,
Arch: AMD64,
Expand Down Expand Up @@ -1711,7 +1711,8 @@ func TestToComponents(t *testing.T) {
headers: &testHeadersProvider{headers: map[string]string{
"header-one": "val-1",
}},
}, {
},
{
Name: "Headers injection merge",
Platform: linuxAMD64Platform,
Policy: map[string]interface{}{
Expand Down Expand Up @@ -1892,7 +1893,7 @@ func TestToComponents(t *testing.T) {
for i, expected := range scenario.Result {
actual := result[i]
if expected.Err != nil {
assert.Equal(t, expected.Err, actual.Err)
assert.Contains(t, actual.Err.Error(), expected.Err.Error())
assert.EqualValues(t, expected.Units, actual.Units)
} else {
assert.NoError(t, actual.Err, "Expected no error for component "+actual.ID)
Expand Down Expand Up @@ -1950,6 +1951,9 @@ func TestPreventionsAreValid(t *testing.T) {
// you update `docs/component-specs.md` in the same PR to document
// the change.
vars, err := transpiler.NewVars("", map[string]interface{}{
"install": map[string]interface{}{
"in_default": true,
},
"runtime": map[string]interface{}{
"platform": "platform",
"os": "os",
Expand Down Expand Up @@ -2042,37 +2046,44 @@ func TestInjectingInputPolicyID(t *testing.T) {
}{
{"NilEverything", nil, nil, nil},
{"NilInput", fleetPolicy, nil, nil},
{"NilPolicy", nil,
{
"NilPolicy", nil,
map[string]interface{}{},
map[string]interface{}{},
},
{"EmptyPolicy", map[string]interface{}{},
{
"EmptyPolicy",
map[string]interface{}{},
map[string]interface{}{},
map[string]interface{}{},
},
{"CreatePolicyRevision", fleetPolicy,
{
"CreatePolicyRevision", fleetPolicy,
map[string]interface{}{},
map[string]interface{}{
"policy": map[string]interface{}{"revision": testRevision},
},
},
{"NilPolicyObjectType", fleetPolicy,
{
"NilPolicyObjectType", fleetPolicy,
map[string]interface{}{
"policy": nil,
},
map[string]interface{}{
"policy": map[string]interface{}{"revision": testRevision},
},
},
{"InjectPolicyRevision", fleetPolicy,
{
"InjectPolicyRevision", fleetPolicy,
map[string]interface{}{
"policy": map[string]interface{}{"key": "value"},
},
map[string]interface{}{
"policy": map[string]interface{}{"key": "value", "revision": testRevision},
},
},
{"UnknownPolicyObjectType", fleetPolicy,
{
"UnknownPolicyObjectType", fleetPolicy,
map[string]interface{}{
"policy": map[string]int{"key": 10},
},
Expand Down
10 changes: 1 addition & 9 deletions pkg/testing/tools/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,7 @@ import (
)

// InstallAgent force install the Elastic Agent through agentFixture.
func InstallAgent(fleetUrl string, enrollmentToken string, agentFixture *atesting.Fixture) ([]byte, error) {
installOpts := atesting.InstallOpts{
NonInteractive: true,
Force: true,
EnrollOpts: atesting.EnrollOpts{
URL: fleetUrl,
EnrollmentToken: enrollmentToken,
},
}
func InstallAgent(installOpts atesting.InstallOpts, agentFixture *atesting.Fixture) ([]byte, error) {
return agentFixture.Install(context.Background(), &installOpts)
}

Expand Down
8 changes: 6 additions & 2 deletions pkg/testing/tools/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func WaitForPolicyRevision(t *testing.T, client *kibana.Client, agentID string,
// InstallAgentWithPolicy creates the given policy, enrolls the given agent
// fixture in Fleet using the default Fleet Server, waits for the agent to be
// online, and returns the created policy.
func InstallAgentWithPolicy(t *testing.T, agentFixture *atesting.Fixture, kibClient *kibana.Client, createPolicyReq kibana.AgentPolicy) (*kibana.PolicyResponse, error) {
func InstallAgentWithPolicy(t *testing.T, installOpts atesting.InstallOpts, agentFixture *atesting.Fixture, kibClient *kibana.Client, createPolicyReq kibana.AgentPolicy) (*kibana.PolicyResponse, error) {
t.Helper()

policy, err := kibClient.CreatePolicy(createPolicyReq)
Expand All @@ -79,7 +79,11 @@ func InstallAgentWithPolicy(t *testing.T, agentFixture *atesting.Fixture, kibCli

// Enroll agent
t.Logf("Unpacking and installing Elastic Agent")
output, err := InstallAgent(fleetServerURL, enrollmentToken.APIKey, agentFixture)
installOpts.EnrollOpts = atesting.EnrollOpts{
URL: fleetServerURL,
EnrollmentToken: enrollmentToken.APIKey,
}
output, err := InstallAgent(installOpts, agentFixture)
if err != nil {
t.Log(string(output))
return nil, fmt.Errorf("unable to enroll Elastic Agent: %w", err)
Expand Down
8 changes: 8 additions & 0 deletions specs/endpoint-security.spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ inputs:
message: "No support for RHEL7 on arm64"
- condition: ${user.root} == false
message: "Elastic Agent must be running as root"
- condition: ${install.in_default} == false
message: "Elastic Defend requires Elastic Agent be installed at the default installation path"
service:
cport: 6788
log:
Expand Down Expand Up @@ -57,6 +59,10 @@ inputs:
proxied_actions:
- UNENROLL
- UPGRADE
runtime:
preventions:
- condition: ${install.in_default} == false
message: "Elastic Defend requires Elastic Agent be installed at the default installation path"
service:
cport: 6788
log:
Expand All @@ -76,6 +82,8 @@ inputs:
preventions:
- condition: ${user.root} == false
message: "Elastic Agent must be running as Administrator or SYSTEM"
- condition: ${install.in_default} == false
message: "Elastic Defend requires Elastic Agent be installed at the default installation path"
service:
cport: 6788
log:
Expand Down
Loading
Loading