-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(resource): new lacework_agent_access_token
** lacework_agent_access_token To connect to the Lacework platform, Lacework agents require an agent access token. Use this resource to mange agent tokens within your Lacework account. !> **Warning:** Agent tokens should be treated as secret and not published. A token uniquely identifies a Lacework customer. If you suspect your token has been publicly exposed or compromised, generate a new token, update the new token on all machines using the old token. When complete, the old token can safely be disabled without interrupting Lacework services. You can use the agent token name to logically separate your deployments, for example, by environment types (QA, Dev, etc.) or system types (CentOS, RHEL, etc.). -> **Note:** The Lacework agent runs on most Linux distributions. For more detailed information, see [Supported Operating Systems.](https://support.lacework.com/hc/en-us/articles/360005230014-Supported-Operating-Systems). !> **Warning:** By design, agent tokens cannot be deleted. Running terraform destroy will only disable the token. ** Example Usage ```hcl resource "lacework_agent_access_token" "k8s" { name = "prod" description = "k8s deployment for production env" } ``` ** Argument Reference The following arguments are supported: * `name` - (Required) The agent access token name. * `description` - (Optional) The agent access token description. * `enabled` - (Optional) The state of the external integration. Defaults to `true`. ** Attributes Reference In addition to the arguments listed above, the following computed attributes are exported: * `token` - The agent access token. ** Import A Lacework agent access token can be imported using the token itself, e.g. ``` $ terraform import lacework_agent_access_token.k8s YourAgentToken ``` -> **Note:** To list all agent access tokens in your Lacework account, use the Lacework CLI command `lacework agent token list`. To install this tool follow [this documentation](https://github.com/lacework/go-sdk/wiki/CLI-Documentation#installation). Closes #41 Signed-off-by: Salim Afiune Maya <[email protected]>
- Loading branch information
Showing
506 changed files
with
92,068 additions
and
807 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
provider "lacework" {} | ||
|
||
resource "lacework_agent_access_token" "k8s" { | ||
name = "k8s-deployments" | ||
description = "Token for K8S clusters" | ||
} |
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
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,23 @@ | ||
package lacework | ||
|
||
import ( | ||
"math/rand" | ||
"time" | ||
) | ||
|
||
var ( | ||
charset = "abcdefghijklmnopqrstuvwxyz0123456789" | ||
randomSeed *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano())) | ||
) | ||
|
||
func randomString(length int) string { | ||
return stringFromCharset(length, charset) | ||
} | ||
|
||
func stringFromCharset(length int, charset string) string { | ||
bytes := make([]byte, length) | ||
for i := range bytes { | ||
bytes[i] = charset[randomSeed.Intn(len(charset))] | ||
} | ||
return string(bytes) | ||
} |
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,250 @@ | ||
package lacework | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"time" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema" | ||
|
||
"github.com/lacework/go-sdk/api" | ||
) | ||
|
||
func resourceLaceworkAgentAccessToken() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceLaceworkAgentAccessTokenCreate, | ||
Read: resourceLaceworkAgentAccessTokenRead, | ||
Update: resourceLaceworkAgentAccessTokenUpdate, | ||
Delete: resourceLaceworkAgentAccessTokenDelete, | ||
|
||
Importer: &schema.ResourceImporter{ | ||
State: importLaceworkAgentAccessToken, | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
"description": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
}, | ||
"enabled": { | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
Default: true, | ||
}, | ||
"last_updated_time": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"created_time": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"account": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"version": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"token": { | ||
Type: schema.TypeString, | ||
Sensitive: true, | ||
Computed: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceLaceworkAgentAccessTokenCreate(d *schema.ResourceData, meta interface{}) error { | ||
var ( | ||
lacework = meta.(*api.Client) | ||
tokenName = d.Get("name").(string) | ||
tokenDesc = d.Get("description").(string) | ||
tokenEnabled = d.Get("enabled").(bool) | ||
) | ||
|
||
log.Printf("[INFO] Creating agent access token. name=%s, description=%s, enabled=%t", | ||
tokenName, tokenDesc, tokenEnabled) | ||
response, err := lacework.Agents.CreateToken(tokenName, tokenDesc) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
log.Println("[INFO] Verifying server response data") | ||
err = validateAgentTokenResponse(&response) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// @afiune at this point in time, we know the data field has a value | ||
token := response.Data[0] | ||
d.SetId(token.AccessToken) | ||
d.Set("name", token.TokenAlias) | ||
d.Set("token", token.AccessToken) | ||
d.Set("description", token.Props.Description) | ||
d.Set("account", token.Account) | ||
d.Set("version", token.Version) | ||
d.Set("enabled", token.Status()) | ||
d.Set("last_updated_time", token.LastUpdatedTime.Format(time.RFC3339)) | ||
d.Set("created_time", token.Props.CreatedTime.Format(time.RFC3339)) | ||
|
||
// very unusual but, if the user creates a token disabled, update its status | ||
if !tokenEnabled { | ||
log.Println("[INFO] Disabling agent access token.") | ||
_, err = lacework.Agents.UpdateTokenStatus(token.AccessToken, false) | ||
if err != nil { | ||
return err | ||
} | ||
d.Set("enabled", false) | ||
} | ||
|
||
log.Printf("[INFO] Agent access token created.") | ||
return nil | ||
} | ||
|
||
func resourceLaceworkAgentAccessTokenRead(d *schema.ResourceData, meta interface{}) error { | ||
lacework := meta.(*api.Client) | ||
|
||
log.Printf("[INFO] Reading agent access token.") | ||
response, err := lacework.Agents.GetToken(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, token := range response.Data { | ||
if token.AccessToken == d.Id() { | ||
d.Set("name", token.TokenAlias) | ||
d.Set("token", token.AccessToken) | ||
d.Set("description", token.Props.Description) | ||
d.Set("enabled", token.Status()) | ||
d.Set("account", token.Account) | ||
d.Set("version", token.Version) | ||
d.Set("last_updated_time", token.LastUpdatedTime.Format(time.RFC3339)) | ||
d.Set("created_time", token.Props.CreatedTime.Format(time.RFC3339)) | ||
|
||
log.Printf("[INFO] Read agent access token. name=%s, description=%s, enabled=%t", | ||
token.TokenAlias, token.Props.Description, token.Status()) | ||
return nil | ||
} | ||
} | ||
|
||
d.SetId("") | ||
return nil | ||
} | ||
|
||
func resourceLaceworkAgentAccessTokenUpdate(d *schema.ResourceData, meta interface{}) error { | ||
var ( | ||
lacework = meta.(*api.Client) | ||
token = api.AgentTokenRequest{ | ||
TokenAlias: d.Get("name").(string), | ||
Enabled: 0, | ||
Props: &api.AgentTokenProps{ | ||
Description: d.Get("description").(string), | ||
}, | ||
} | ||
) | ||
|
||
if d.Get("enabled").(bool) { | ||
token.Enabled = 1 | ||
} | ||
|
||
log.Printf("[INFO] Updating agent access token. name=%s, description=%s, enabled=%t", | ||
token.TokenAlias, token.Props.Description, d.Get("enabled").(bool)) | ||
response, err := lacework.Agents.UpdateToken(d.Id(), token) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
log.Println("[INFO] Verifying server response data") | ||
err = validateAgentTokenResponse(&response) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// @afiune at this point in time, we know the data field has a value | ||
nToken := response.Data[0] | ||
d.Set("name", nToken.TokenAlias) | ||
d.Set("token", nToken.AccessToken) | ||
d.Set("description", nToken.Props.Description) | ||
d.Set("enabled", nToken.Status()) | ||
d.Set("account", nToken.Account) | ||
d.Set("version", nToken.Version) | ||
d.Set("last_updated_time", nToken.LastUpdatedTime.Format(time.RFC3339)) | ||
d.Set("created_time", nToken.Props.CreatedTime.Format(time.RFC3339)) | ||
|
||
log.Printf("[INFO] Agent access token updated") | ||
return nil | ||
} | ||
|
||
func resourceLaceworkAgentAccessTokenDelete(d *schema.ResourceData, meta interface{}) error { | ||
var ( | ||
lacework = meta.(*api.Client) | ||
tokenName = fmt.Sprintf("%s-%s-deleted", d.Get("name").(string), randomString(5)) | ||
token = api.AgentTokenRequest{ | ||
TokenAlias: tokenName, | ||
Enabled: 0, | ||
} | ||
) | ||
|
||
// @afiune agent access tokens, by design, cannot be deleted, instead of deleting | ||
// them, we only disable them, but we will also modify its TokenAlias since that | ||
// field has a unique constraint. There can't be two tokens with the same alias. | ||
|
||
log.Printf("[INFO] Disabling agent access token. name=%s", tokenName) | ||
_, err := lacework.Agents.UpdateToken(d.Id(), token) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
log.Printf("[INFO] Agent access token disabled and updated with name '%s'.", tokenName) | ||
return nil | ||
} | ||
|
||
func importLaceworkAgentAccessToken(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { | ||
lacework := meta.(*api.Client) | ||
|
||
log.Printf("[INFO] Importing agent access token.") | ||
response, err := lacework.Agents.GetToken(d.Id()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
for _, token := range response.Data { | ||
if token.AccessToken == d.Id() { | ||
log.Printf("[INFO] agent access token found. name=%s, description=%s, enabled=%t", | ||
token.TokenAlias, token.Props.Description, token.Status()) | ||
return []*schema.ResourceData{d}, nil | ||
} | ||
} | ||
|
||
log.Printf("[INFO] Raw response: %v\n", response) | ||
return nil, fmt.Errorf( | ||
"Unable to import Lacework resource. Agent access token '%s' was not found.", | ||
d.Id(), | ||
) | ||
} | ||
|
||
// validateAgentTokenResponse checks weather or not the server response has | ||
// any inconsistent data, it returns a friendly error message describing the | ||
// problem and how to report it | ||
func validateAgentTokenResponse(response *api.AgentTokensResponse) error { | ||
if len(response.Data) == 0 { | ||
// @afiune this edge case should never happen, if we land here it means that | ||
// something went wrong in the server side of things (Lacework API), so let | ||
// us inform that to our users | ||
msg := ` | ||
Unable to read sever response data. (empty 'data' field) | ||
This was an unexpected behavior, verify that your agent token was | ||
created successfully and report this issue to [email protected] | ||
` | ||
return fmt.Errorf(msg) | ||
} | ||
|
||
return nil | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.