Skip to content

Commit

Permalink
feat(cli): new agent access token command (#256)
Browse files Browse the repository at this point in the history
New `lacework agent` command!
```
$ lacework agent --help
Manage agents and agent access tokens in your account.

To analyze application, host and user behavior, Lacework uses a lightweight agent,
which securely forwards collected metadata to the Lacework cloud for analysis. The
agent requires minimal system resources and runs on most 64-bit Linux distributions.

For a complete list of supported operating systems, visit:

    https://support.lacework.com/hc/en-us/articles/360005230014-Supported-Operating-Systems

Usage:
  lacework agent [command]

Available Commands:
  token       manage agent access tokens

Flags:
  -h, --help   help for agent

Global Flags:
  -a, --account string      account subdomain of URL (i.e. <ACCOUNT>.lacework.net)
  -k, --api_key string      access key id
  -s, --api_secret string   secret access key
      --debug               turn on debug logging
      --json                switch commands output from human-readable to json format
      --nocolor             turn off colors
      --noninteractive      turn off interactive mode (disable spinners, prompts, etc.)
  -p, --profile string      switch between profiles configured at ~/.lacework.toml

Use "lacework agent [command] --help" for more information about a command.
```

This command contains only one sub-command at the moment, the `lacework
agent token` command to manage agent access tokens. In the future we are
planning to extend this agent command to do other things like, list
agents, generate deployment scripts, and perhaps even install agents on
remote hosts.

JIRA: https://lacework.atlassian.net/browse/ALLY-236

Signed-off-by: Salim Afiune Maya <[email protected]>
  • Loading branch information
afiune committed Dec 7, 2020
1 parent df5f8cf commit 7f8ba11
Show file tree
Hide file tree
Showing 104 changed files with 22,583 additions and 3 deletions.
133 changes: 133 additions & 0 deletions api/agent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//
// Author:: Salim Afiune Maya (<[email protected]>)
// Copyright:: Copyright 2020, 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 api

import (
"fmt"
)

// AgentsService is a service that interacts with the Agent Access Tokens
// endpoints from the Lacework Server
type AgentsService struct {
client *Client
}

// ListTokens returns a list of agent access tokens in a Lacework account
func (svc *AgentsService) ListTokens() (response AgentTokensResponse, err error) {
err = svc.client.RequestDecoder("GET", apiAgentTokens, nil, &response)
return
}

// GetToken returns details about an agent access token
func (svc *AgentsService) GetToken(token string) (response AgentTokensResponse, err error) {
err = svc.client.RequestDecoder("GET", fmt.Sprintf(apiAgentTokenFromID, token), nil, &response)
return
}

// CreateToken creates a new agent access token
func (svc *AgentsService) CreateToken(name, desc string) (response AgentTokensResponse, err error) {
err = svc.client.RequestEncoderDecoder("POST",
apiAgentTokens,
AgentTokenRequest{
TokenAlias: name,
Enabled: 1,
Props: AgentTokenProps{
Description: desc,
},
},
&response,
)
return
}

// UpdateToken updates an agent access token with the provided request data
func (svc *AgentsService) UpdateToken(token string, data AgentTokenRequest) (
response AgentTokensResponse,
err error,
) {
err = svc.client.RequestEncoderDecoder("PUT",
fmt.Sprintf(apiAgentTokenFromID, token),
data,
&response,
)
return
}

// UpdateTokenStatus updates only the status of an agent access token (enable or disable)
func (svc *AgentsService) UpdateTokenStatus(token string, enable bool) (
response AgentTokensResponse,
err error,
) {

request := AgentTokenRequest{Enabled: 0}
if enable {
request.Enabled = 1
}
err = svc.client.RequestEncoderDecoder("PUT",
fmt.Sprintf(apiAgentTokenFromID, token),
request,
&response,
)
return
}

type AgentTokensResponse struct {
Data []AgentToken `json:"data"`
Ok bool `json:"ok"`
Message string `json:"message"`
}

type AgentToken struct {
AccessToken string `json:"ACCESS_TOKEN"`
Account string `json:"ACCOUNT"`
LastUpdatedTime *Json16DigitTime `json:"LAST_UPDATED_TIME"`
Props *AgentTokenProps `json:"PROPS,omitempty"`
TokenAlias string `json:"TOKEN_ALIAS"`
Enabled string `json:"TOKEN_ENABLED"`
Version string `json:"VERSION"`
}

// @afiune this API returns a string as a boolean, so we have to do this mokeypatch
func (t AgentToken) PrettyStatus() string {
if t.Enabled == "true" {
return "Enabled"
}
return "Disabled"
}
func (t AgentToken) Status() bool {
return t.Enabled == "true"
}

func (t AgentToken) EnabledInt() int {
if t.Enabled == "true" {
return 1
}
return 0
}

type AgentTokenRequest struct {
TokenAlias string `json:"TOKEN_ALIAS,omitempty"`
Enabled int `json:"TOKEN_ENABLED"`
Props AgentTokenProps `json:"PROPS,omitempty"`
}

type AgentTokenProps struct {
CreatedTime *Json16DigitTime `json:"CREATED_TIME,omitempty"`
Description string `json:"DESCRIPTION,omitempty"`
}
57 changes: 57 additions & 0 deletions api/agent_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// Author:: Salim Afiune Maya (<[email protected]>)
// Copyright:: Copyright 2020, 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 api_test

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/lacework/go-sdk/api"
)

func TestAgentTokenPrettyStatus(t *testing.T) {
subject := api.AgentToken{Enabled: "true"}
assert.Equal(t, "Enabled", subject.PrettyStatus())

subject.Enabled = "false"
assert.Equal(t, "Disabled", subject.PrettyStatus())
subject.Enabled = "anything else"
assert.Equal(t, "Disabled", subject.PrettyStatus())
}

func TestAgentTokenStatus(t *testing.T) {
subject := api.AgentToken{Enabled: "true"}
assert.True(t, subject.Status())

subject.Enabled = "false"
assert.False(t, subject.Status())
subject.Enabled = "anything else"
assert.False(t, subject.Status())
}

func TestAgentTokenEnabledInt(t *testing.T) {
subject := api.AgentToken{Enabled: "true"}
assert.Equal(t, 1, subject.EnabledInt())

subject.Enabled = "false"
assert.Equal(t, 0, subject.EnabledInt())
subject.Enabled = "anything else"
assert.Equal(t, 0, subject.EnabledInt())
}
3 changes: 3 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ const (
apiIntegrationSchema = "external/integrations/schema/%s"
apiTokens = "access/tokens"

apiAgentTokens = "external/tokens"
apiAgentTokenFromID = "external/tokens/%s"

apiVulnerabilitiesContainerScan = "external/vulnerabilities/container/repository/images/scan"
apiVulnerabilitiesContainerScanStatus = "external/vulnerabilities/container/reqId/%s"
apiVulnerabilitiesAssessmentFromImageID = "external/vulnerabilities/container/imageId/%s"
Expand Down
2 changes: 2 additions & 0 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type Client struct {
headers map[string]string

LQL *LQLService
Agents *AgentsService
Events *EventsService
Compliance *ComplianceService
Integrations *IntegrationsService
Expand Down Expand Up @@ -92,6 +93,7 @@ func NewClient(account string, opts ...Option) (*Client, error) {
c: &http.Client{Timeout: defaultTimeout},
}
c.LQL = &LQLService{c}
c.Agents = &AgentsService{c}
c.Events = &EventsService{c}
c.Compliance = &ComplianceService{c}
c.Integrations = &IntegrationsService{c}
Expand Down
4 changes: 2 additions & 2 deletions cli/cmd/access_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ var (
// accessTokenCmd represents the access-token command
accessTokenCmd = &cobra.Command{
Use: "access-token",
Short: "generate temporary access tokens",
Long: `Generates a temporary access token that can be used to access the
Short: "generate temporary API access tokens",
Long: `Generates a temporary API access token that can be used to access the
Lacework API. The token will be valid for the duration that you specify.`,
Args: cobra.NoArgs,
RunE: generateAccessToken,
Expand Down
Loading

0 comments on commit 7f8ba11

Please sign in to comment.