-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(cli): find and fix AWS instances w/o agents (#934)
This commit is the first milestone for Project Capture The Flag. Project CTF takes a user's cloud credentials, lists all of their hosts, and installs the agent on hosts without agents. The purpose of Project CTF is to allow customers to easily close gaps in their agent deployments by using the CLI to find and install the agent on instances that the customer would have trouble reaching otherwise. This commit adds a new subcommand to the Lacework CLI called aws-install. The user should invoke this command via lacework agent aws-install. aws-install is currently a no-op and is used as a directory for further subcommands. This commit adds two subcommands to aws-install called ec2ic and ec2-ssh. The user should invoke them as either lacework agent ctf aws-install ec2ic or lacework agent ctf aws-install ec2-ssh. The ec2ic subcommand uses EC2InstanceConnect to send SSH keys to the user's EC2 instances. EC2InstanceConnect is only supported on official channel AMIs that are Ubuntu 16.04 or later or Amazon Linux 2, so the customer will not be able to use ec2ic for instances running other AMIs. ec2ic adds flags to filter instances on tag, tag key, and region. The ec2-ssh subcommand uses existing SSH authentication to install agents on the user's EC2 instances. ec2-ssh assumes that all instances that it finds will have SSH access via the authentication method passed via CLI flags. ec2-ssh also adds flags to filter instances on tag, tag key, and region. ec2ic and ec2-ssh both have the limitation that they only work for instances with public IP addresses that are open to the internet on port 22. Fixes RAIN-37375 Fixes RAIN-38278 Signed-off-by: nschmeller <[email protected]> Co-authored-by: Salim Afiune <[email protected]> Co-authored-by: Chris Golden <[email protected]>
- Loading branch information
1 parent
76e6cc8
commit 4e507c0
Showing
999 changed files
with
456,067 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// | ||
// Author:: Nicholas Schmeller (<[email protected]>) | ||
// Copyright:: Copyright 2022, Lacework Inc. | ||
// License:: Apache License, Version 2.0 | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
|
||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var ( | ||
agentInstallAWSEC2ICCmd = &cobra.Command{ | ||
Use: "ec2ic", | ||
Args: cobra.NoArgs, | ||
Short: "Use EC2InstanceConnect to securely connect to EC2 instances", | ||
RunE: installAWSEC2IC, | ||
Long: `This command installs the agent on all EC2 instances in an AWS account using EC2InstanceConnect. | ||
To filter by one or more regions: | ||
lacework agent install ec2ic --include_regions us-west-2,us-east-2 | ||
To filter by instance tag: | ||
lacework agent install ec2ic --tag TagName,TagValue | ||
To filter by instance tag key: | ||
lacework agent install ec2ic --tag_key TagName | ||
To explicitly specify the username for all SSH logins: | ||
lacework agent install ec2ic --ssh_username <your-user> | ||
AWS credentials are read from the following environment variables: | ||
- AWS_ACCESS_KEY_ID | ||
- AWS_SECRET_ACCESS_KEY | ||
- AWS_SESSION_TOKEN (optional) | ||
- AWS_REGION (optional) | ||
This command will automatically add hosts with successful connections to | ||
'~/.ssh/known_hosts' unless specified with '--trust_host_key=false'.`, | ||
} | ||
) | ||
|
||
func init() { | ||
// 'agent install ec2ic' flags | ||
agentInstallAWSEC2ICCmd.Flags().StringVar(&agentCmdState.InstallTagKey, | ||
"tag_key", "", "only install agents on infra with this tag key set", | ||
) | ||
agentInstallAWSEC2ICCmd.Flags().StringSliceVar(&agentCmdState.InstallTag, | ||
"tag", []string{}, "only install agents on infra with this tag", | ||
) | ||
agentInstallAWSEC2ICCmd.Flags().StringVar(&agentCmdState.InstallAgentToken, | ||
"token", "", "agent access token", | ||
) | ||
agentInstallAWSEC2ICCmd.Flags().BoolVar(&agentCmdState.InstallTrustHostKey, | ||
"trust_host_key", true, "automatically add host keys to the ~/.ssh/known_hosts file", | ||
) | ||
agentInstallAWSEC2ICCmd.Flags().StringSliceVarP(&agentCmdState.InstallIncludeRegions, | ||
"include_regions", "r", []string{}, "list of regions to filter on", | ||
) | ||
agentInstallAWSEC2ICCmd.Flags().StringVar(&agentCmdState.InstallSshUser, | ||
"ssh_username", "", "username to login with", | ||
) | ||
} | ||
|
||
func installAWSEC2IC(_ *cobra.Command, _ []string) error { | ||
runners, err := awsDescribeInstances() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, runner := range runners { | ||
cli.Log.Debugw("runner info: ", "user", runner.Runner.User, "region", runner.Region, "az", runner.AvailabilityZone, "instance ID", runner.InstanceID, "hostname", runner.Runner.Hostname) | ||
err := runner.SendAndUseIdentityFile() | ||
if err != nil { | ||
cli.Log.Debugw("ec2ic failed", "err", err) | ||
continue | ||
} | ||
|
||
if err := verifyAccessToRemoteHost(&runner.Runner); err != nil { | ||
cli.Log.Debugw("verifyAccessToRemoteHost failed") | ||
return err | ||
} | ||
|
||
if alreadyInstalled := isAgentInstalledOnRemoteHost(&runner.Runner); alreadyInstalled != nil { | ||
cli.Log.Debugw("agent already installed on host, skipping") | ||
continue | ||
} | ||
|
||
token := agentCmdState.InstallAgentToken | ||
if token == "" { | ||
// user didn't provide an agent token | ||
cli.Log.Debugw("agent token not provided") | ||
var err error | ||
token, err = selectAgentAccessToken() | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
cmd := fmt.Sprintf("sudo sh -c \"curl -sSL %s | sh -s -- %s\"", agentInstallDownloadURL, token) | ||
err = runInstallCommandOnRemoteHost(&runner.Runner, cmd) | ||
if err != nil { | ||
return errors.Wrap(err, "runInstallCommandOnRemoteHost failed for instance "+runner.InstanceID) | ||
} | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// | ||
// Author:: Nicholas Schmeller (<[email protected]>) | ||
// Copyright:: Copyright 2022, Lacework Inc. | ||
// License:: Apache License, Version 2.0 | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
|
||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"sync" | ||
"testing" | ||
|
||
"github.com/lacework/go-sdk/lwrunner" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
// Requires AWS credentials in the shell environment | ||
// Lists runners, sends keys, attempts to connect | ||
// Example command to run: | ||
// `aws-vault exec default -- go test -run TestAwsEC2ICDescribeInstances` | ||
// If AWS credentials are already present in the shell environment, only use: | ||
// `go test -run TestAwsEC2ICDescribeInstances` | ||
func TestAwsEC2ICDescribeInstances(t *testing.T) { | ||
if _, ok := os.LookupEnv("AWS_SECRET_ACCESS_KEY"); !ok { | ||
t.Skip("aws credentials not found in environment, skipping test") | ||
} | ||
|
||
cli.LogLevel = "DEBUG" | ||
agentCmdState.InstallTrustHostKey = true | ||
agentCmdState.InstallTagKey = "CaptureTheFlagPlayer" | ||
cli.NonInteractive() | ||
|
||
runners, err := awsDescribeInstances() | ||
assert.NoError(t, err) | ||
|
||
wg := new(sync.WaitGroup) | ||
for _, runner := range runners { | ||
wg.Add(1) | ||
go func(runner *lwrunner.AWSRunner) { | ||
out := fmt.Sprintf("--------- Runner ---------\nRegion: %v\nInstance ID: %v\n", runner.Region, runner.InstanceID) | ||
|
||
err = runner.SendAndUseIdentityFile() | ||
assert.NoError(t, err) | ||
|
||
err = verifyAccessToRemoteHost(&runner.Runner) | ||
assert.NoError(t, err) | ||
|
||
if alreadyInstalled := isAgentInstalledOnRemoteHost(&runner.Runner); alreadyInstalled != nil { | ||
out += alreadyInstalled.Error() + "\n" | ||
} | ||
|
||
fmt.Println(out) | ||
wg.Done() | ||
}(runner) | ||
} | ||
wg.Wait() | ||
} |
Oops, something went wrong.