Skip to content

Commit

Permalink
Initial Kubernetes checks (#983)
Browse files Browse the repository at this point in the history
* Added first kubernetes checks

* fix example code

* Add k8s to init

Co-authored-by: Owen Rumney <owen.rumney@aquasec.com>
  • Loading branch information
liamg and Owen Rumney committed Jul 30, 2021
1 parent 223ac98 commit 628e15e
Show file tree
Hide file tree
Showing 11 changed files with 481 additions and 6 deletions.
1 change: 1 addition & 0 deletions cmd/tfsec-skeleton/generation.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var providers = []string{
"General",
"GitHub",
"Google",
"Kubernetes",
"OpenStack",
"Oracle",
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/tfsec-skeleton/interactive.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,10 @@ func (input *Input) gatherInputsInteractively() error {
if requirementIndex == len(requirementStrs)-1 {
input.Requirement = requirements.Custom(input.RequiredTypes[0], input.RequiredLabels[0], exampleCode)
} else {
dotPath := prompt.EnterInput("Enter the path to the attribute to check (e.g. settings.encryption.enabled): ")
input.AttributeName = prompt.EnterInput("Enter the path to the attribute to check (e.g. settings.encryption.enabled): ")
expectedValue := enterInterfaceValue("Enter the value the attribute must have to pass the check (e.g. true): ")
failForDefault := inputYesNo("Should the check fail if a default value is used?")
input.Requirement = requirements.NewAttributeRequirement(input.RequiredTypes[0], input.RequiredLabels[0], dotPath, expectedValue, failForDefault, exampleCode, requirementTypes[requirementIndex])
input.Requirement = requirements.NewAttributeRequirement(input.RequiredTypes[0], input.RequiredLabels[0], input.AttributeName, expectedValue, failForDefault, exampleCode, requirementTypes[requirementIndex])
}

_ = deleteSwap()
Expand Down
4 changes: 2 additions & 2 deletions cmd/tfsec-skeleton/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func Test_{{.TestName}}_FailureExamples(t *testing.T) {
rule, err := scanner.GetRuleById(expectedCode)
if err != nil {
t.FailNow()
t.Fatalf("Rule not found: %s", expectedCode)
}
for i, badExample := range rule.Documentation.BadExample {
t.Logf("Running bad example for '%s' #%d", expectedCode, i+1)
Expand All @@ -87,7 +87,7 @@ func Test_{{.TestName}}_SuccessExamples(t *testing.T) {
rule, err := scanner.GetRuleById(expectedCode)
if err != nil {
t.FailNow()
t.Fatalf("Rule not found: %s", expectedCode)
}
for i, example := range rule.Documentation.GoodExample {
t.Logf("Running good example for '%s' #%d", expectedCode, i+1)
Expand Down
1 change: 1 addition & 0 deletions internal/app/tfsec/rules/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import (
_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/google/iam"
_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/google/sql"
_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/google/storage"
_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/kubernetes/network"
_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/openstack/compute"
_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/openstack/fw"
_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/oracle/compute"
Expand Down
183 changes: 183 additions & 0 deletions internal/app/tfsec/rules/kubernetes/network/no_public_egress_rule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package network

// ATTENTION!
// This rule was autogenerated!
// Before making changes, consider updating the generator.

import (
"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
"github.com/aquasecurity/tfsec/internal/app/tfsec/cidr"
"github.com/aquasecurity/tfsec/internal/app/tfsec/hclcontext"
"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
"github.com/aquasecurity/tfsec/pkg/provider"
"github.com/aquasecurity/tfsec/pkg/result"
"github.com/aquasecurity/tfsec/pkg/rule"
"github.com/aquasecurity/tfsec/pkg/severity"
)

func init() {
scanner.RegisterCheckRule(rule.Rule{
Provider: provider.KubernetesProvider,
Service: "network",
ShortCode: "no-public-egress",
Documentation: rule.RuleDocumentation{
Summary: "Public egress should not be allowed via network policies",
Explanation: `You should not expose infrastructure to the public internet except where explicitly required`,
Impact: "Exfiltration of data to the public internet",
Resolution: "Remove public access except where explicitly required",
BadExample: []string{`
resource "kubernetes_network_policy" "bad_example" {
metadata {
name = "terraform-example-network-policy"
namespace = "default"
}
spec {
pod_selector {
match_expressions {
key = "name"
operator = "In"
values = ["webfront", "api"]
}
}
egress {
ports {
port = "http"
protocol = "TCP"
}
ports {
port = "8125"
protocol = "UDP"
}
to {
ip_block {
cidr = "0.0.0.0/0"
except = [
"10.0.0.0/24",
"10.0.1.0/24",
]
}
}
}
ingress {
ports {
port = "http"
protocol = "TCP"
}
ports {
port = "8125"
protocol = "UDP"
}
from {
ip_block {
cidr = "10.0.0.0/16"
except = [
"10.0.0.0/24",
"10.0.1.0/24",
]
}
}
}
policy_types = ["Ingress", "Egress"]
}
}
`},
GoodExample: []string{`
resource "kubernetes_network_policy" "good_example" {
metadata {
name = "terraform-example-network-policy"
namespace = "default"
}
spec {
pod_selector {
match_expressions {
key = "name"
operator = "In"
values = ["webfront", "api"]
}
}
egress {
ports {
port = "http"
protocol = "TCP"
}
ports {
port = "8125"
protocol = "UDP"
}
to {
ip_block {
cidr = "10.0.0.0/16"
except = [
"10.0.0.0/24",
"10.0.1.0/24",
]
}
}
}
ingress {
ports {
port = "http"
protocol = "TCP"
}
ports {
port = "8125"
protocol = "UDP"
}
from {
ip_block {
cidr = "10.0.0.0/16"
except = [
"10.0.0.0/24",
"10.0.1.0/24",
]
}
}
}
policy_types = ["Ingress", "Egress"]
}
}
`},
Links: []string{
"https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/network_policy#spec.ingress.from.ip_block.cidr",
},
},
RequiredTypes: []string{
"resource",
},
RequiredLabels: []string{
"kubernetes_network_policy",
},
DefaultSeverity: severity.High,
CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context) {

egressBlock := resourceBlock.GetBlock("spec").GetBlock("egress")
if egressBlock.IsNil() || len(egressBlock.GetBlocks("to")) == 0 {
set.AddResult().
WithDescription("Resource '%s' allows all egress traffic by default", resourceBlock.FullName())
return
}

for _, to := range egressBlock.GetBlocks("to") {
if cidrAttr := to.GetBlock("ip_block").GetAttribute("cidr"); cidrAttr.IsString() {
if cidr.IsOpen(cidrAttr) {
set.AddResult().
WithDescription("Resource '%s' allows egress traffic to the internet", resourceBlock.FullName()).
WithAttribute(cidrAttr)
}
}
}
},
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package network

import (
"strings"
"testing"

"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
)

func Test_KubernetesNoPublicEgress_FailureExamples(t *testing.T) {
expectedCode := "kubernetes-network-no-public-egress"

rule, err := scanner.GetRuleById(expectedCode)
if err != nil {
t.Fatalf("Rule not found: %s", expectedCode)
}
for i, badExample := range rule.Documentation.BadExample {
t.Logf("Running bad example for '%s' #%d", expectedCode, i+1)
if strings.TrimSpace(badExample) == "" {
t.Fatalf("bad example code not provided for %s", rule.ID())
}
defer func() {
if err := recover(); err != nil {
t.Fatalf("Scan (bad) failed: %s", err)
}
}()
results := testutil.ScanHCL(badExample, t)
testutil.AssertCheckCode(t, rule.ID(), "", results)
}
}

func Test_KubernetesNoPublicEgress_SuccessExamples(t *testing.T) {
expectedCode := "kubernetes-network-no-public-egress"

rule, err := scanner.GetRuleById(expectedCode)
if err != nil {
t.Fatalf("Rule not found: %s", expectedCode)
}
for i, example := range rule.Documentation.GoodExample {
t.Logf("Running good example for '%s' #%d", expectedCode, i+1)
if strings.TrimSpace(example) == "" {
t.Fatalf("good example code not provided for %s", rule.ID())
}
defer func() {
if err := recover(); err != nil {
t.Fatalf("Scan (good) failed: %s", err)
}
}()
results := testutil.ScanHCL(example, t)
testutil.AssertCheckCode(t, "", rule.ID(), results)
}
}

0 comments on commit 628e15e

Please sign in to comment.