Skip to content

Commit

Permalink
Support Masking of Entire Resources (#4)
Browse files Browse the repository at this point in the history
* Support masking entire resources

* Add screenshots

* Update README.yaml
  • Loading branch information
osterman authored Jan 21, 2019
1 parent f281df6 commit c423c18
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 23 deletions.
23 changes: 17 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ It's 100% Open Source and licensed under the [APACHE2](LICENSE).



## Screenshots


![terraform plan](https://user-images.githubusercontent.com/52489/51474936-c1003800-1d35-11e9-9e25-11245388a372.png)
*Example of masking output from a `terraform plan` execution*
![terraform apply](https://user-images.githubusercontent.com/52489/51475052-248a6580-1d36-11e9-9f55-5ad46bf77bcb.png)
*Example of masking output from a `terraform apply` execution*


## Introduction

Expand All @@ -54,11 +62,14 @@ __NOTE__: `tfmask` will preserve the name of the nodes in the graph

__NOTE__: The utility supports a number of configuration settings which can be passed via environment variables.

| Environment Variable | Description | Default |
|----------------------|----------------------------------------------|------------|
| `TFMASK_CHAR` | Character used to mask all output | `*` |
| `TFMASK_REGEX` | Regular expression used to match graph nodes | [see code] |

| Environment Variable | Description | Default |
|--------------------------|------------------------------------------------|------------|
| `TFMASK_CHAR` | Character used to mask all output | `*` |
| `TFMASK_VALUES_REGEX` | Regular expression used to match values | [see code] |
| `TFMASK_RESOURCES_REGEX` | Regular expression used to match resources | [see code] |

__IMPORTANT__: Pass `-no-color` to `terraform plan` and `terraform apply` for proper parsing

The basic usage looks like this. We're going to run `terraform plan` and filter it through `tfmask`:

```sh
Expand All @@ -74,7 +85,7 @@ Example `.envrc`:
```sh
# Export terraform environment
export TFMASK_CHAR="#"
export TFMASK_REGEX="(?i)^.*(secret|password|oauth|token|key).*$"
export TFMASK_VALUES_REGEX="(?i)^.*(secret|password|oauth|token|key).*$"
```

<details>
Expand Down
25 changes: 19 additions & 6 deletions README.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,29 @@ introduction: |-
__NOTE__: `tfmask` will preserve the name of the nodes in the graph
screenshots:
- name: "terraform plan"
description: "Example of masking output from a `terraform plan` execution"
url: "https://user-images.githubusercontent.com/52489/51474936-c1003800-1d35-11e9-9e25-11245388a372.png"

- name: "terraform apply"
description: "Example of masking output from a `terraform apply` execution"
url: "https://user-images.githubusercontent.com/52489/51475052-248a6580-1d36-11e9-9f55-5ad46bf77bcb.png"

# How to use this project
usage: |-
__NOTE__: The utility supports a number of configuration settings which can be passed via environment variables.
| Environment Variable | Description | Default |
|----------------------|----------------------------------------------|------------|
| `TFMASK_CHAR` | Character used to mask all output | `*` |
| `TFMASK_REGEX` | Regular expression used to match graph nodes | [see code] |
| Environment Variable | Description | Default |
|--------------------------|------------------------------------------------|------------|
| `TFMASK_CHAR` | Character used to mask all output | `*` |
| `TFMASK_VALUES_REGEX` | Regular expression used to match values | [see code] |
| `TFMASK_RESOURCES_REGEX` | Regular expression used to match resources | [see code] |
__IMPORTANT__: Pass `-no-color` to `terraform plan` and `terraform apply` for proper parsing
The basic usage looks like this. We're going to run `terraform plan` and filter it through `tfmask`:
```sh
Expand All @@ -70,7 +83,7 @@ usage: |-
```sh
# Export terraform environment
export TFMASK_CHAR="#"
export TFMASK_REGEX="(?i)^.*(secret|password|oauth|token|key).*$"
export TFMASK_VALUES_REGEX="(?i)^.*(oauth|secret|token|password|key|result).*$"
```
<details>
Expand Down
67 changes: 56 additions & 11 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,34 +32,79 @@ func main() {
var tfmaskChar = getEnv("TFMASK_CHAR", "*")

// Pattern representing sensitive output
var tfmaskRegex = getEnv("TFMASK_REGEX", "(?i)^.*(oauth|secret|token|password|key).*$")
var tfmaskValuesRegex = getEnv("TFMASK_VALUES_REGEX", "(?i)^.*(oauth|secret|token|password|key|result).*$")

// Pattern representing sensitive resource
var tfmaskResourceRegex = getEnv("TFMASK_RESOURCES_REGEX", "(?i)^(random_id).*$")

// stage.0.action.0.configuration.OAuthToken: "" => "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
reTfPlanLine := regexp.MustCompile("^( +)([a-zA-Z0-9%._-]+):( +)\"(.*?)\" +=> +\"(.*?)\"")
reTfSensitive := regexp.MustCompile(tfmaskRegex)
reTfPlanLine := regexp.MustCompile("^( +)([a-zA-Z0-9%._-]+):( +)([\"<])(.*?)([>\"]) +=> +([\"<])(.*?)([>\"])(.*)$")

// random_id.some_id: Refreshing state... (ID: itILf4x5lqleQV9ZwT2gH-Zg3yuXM8pdUu6VFTX...P5vqUmggDweOoxFMPY5t9thA0SJE2EZIhcHbsQ)
reTfPlanStatusLine := regexp.MustCompile("^(.*?): (.*?) +\\(ID: (.*?)\\)$")


// -/+ random_string.postgres_admin_password (tainted) (new resource required)
reTfPlanCurrentResource := regexp.MustCompile("^([~/+-]+) (.*?) +(.*)$")
reTfApplyCurrentResource := regexp.MustCompile("^([a-z].*?): (.*?)$")
currentResource := ""

reTfValues := regexp.MustCompile(tfmaskValuesRegex)
reTfResource := regexp.MustCompile(tfmaskResourceRegex)
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
line := scanner.Text()
if reTfPlanLine.MatchString(line) {
if reTfPlanCurrentResource.MatchString(line) {
match := reTfPlanCurrentResource.FindStringSubmatch(line)
currentResource = match[2]
} else if reTfApplyCurrentResource.MatchString(line) {
match := reTfApplyCurrentResource.FindStringSubmatch(line)
currentResource = match[1]
}

if reTfPlanStatusLine.MatchString(line) {
match := reTfPlanStatusLine.FindStringSubmatch(line)
resource := match[1]
id := match[3]
if reTfResource.MatchString(resource) {
line = strings.Replace(line, id, strings.Repeat(tfmaskChar, utf8.RuneCountInString(id)), 1)
}
fmt.Println(line)
} else if reTfPlanLine.MatchString(line) {
match := reTfPlanLine.FindStringSubmatch(line)
leadingWhitespace := match[1]
property := match[2]
property := match[2] // something like `stage.0.action.0.configuration.OAuthToken`
trailingWhitespace := match[3]
firstQuote := match[4] // < or "
oldValue := match[5]
secondQuote := match[6] // > or "
thirdQuote := match[7] // < or "
newValue := match[8]
fourthQuote := match[9] // > or "
postfix := match[10]

if reTfSensitive.MatchString(property) {
oldValue := strings.Repeat(tfmaskChar, utf8.RuneCountInString(match[4]))
newValue := strings.Repeat(tfmaskChar, utf8.RuneCountInString(match[5]))
fmt.Printf("%v%v:%v\"%v\" => \"%v\"\n", leadingWhitespace, property, trailingWhitespace, oldValue, newValue)
if reTfValues.MatchString(property) || reTfResource.MatchString(currentResource) {
// The value inside the "..." or <...>
if oldValue != "sensitive" && oldValue != "computed" && oldValue != "<computed" {
oldValue = strings.Repeat(tfmaskChar, utf8.RuneCountInString(oldValue))
}
// The value inside the "..." or <...>
if newValue != "sensitive" && newValue != "computed" && newValue != "<computed" {
newValue = strings.Repeat(tfmaskChar, utf8.RuneCountInString(newValue))
}
fmt.Printf("%v%v:%v%v%v%v => %v%v%v%v\n",
leadingWhitespace, property, trailingWhitespace, firstQuote, oldValue, secondQuote, thirdQuote, newValue, fourthQuote, postfix)
} else {
fmt.Println(line)
}
} else {
// We matched nothing
fmt.Println(line)
}
}

if err := scanner.Err(); err != nil {
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
}
}
11 changes: 11 additions & 0 deletions tests/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
clean:
rm -rf .terraform

test: clean
terraform init
terraform plan -no-color | ../release/tfmask
terraform apply -auto-approve -no-color | ../release/tfmask
terraform taint random_string.some_password
terraform taint random_id.some_id
terraform plan -no-color | ../release/tfmask
terraform apply -auto-approve -no-color | ../release/tfmask
9 changes: 9 additions & 0 deletions tests/test.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
resource "random_string" "some_password" {
length = 16
special = true
override_special = "/@\" "
}

resource "random_id" "some_id" {
byte_length = "64"
}

0 comments on commit c423c18

Please sign in to comment.