Skip to content

Commit

Permalink
changes for argocd integration (#724)
Browse files Browse the repository at this point in the history
* changes for argocd integration

* incorporate review changes
  • Loading branch information
patilpankaj212 authored May 5, 2021
1 parent 0c7f4ca commit 881417a
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 106 deletions.
4 changes: 4 additions & 0 deletions build/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ RUN addgroup --gid 101 terrascan && \
adduser -S --uid 101 --ingroup terrascan terrascan && \
apk add --no-cache git openssh

# create ~/.ssh & ~/bin folder and change owner to terrascan
RUN mkdir -p /home/terrascan/.ssh /home/terrascan/bin && \
chown -R terrascan:terrascan /home/terrascan

# run as non root user
USER terrascan

Expand Down
39 changes: 30 additions & 9 deletions pkg/http-server/remote-repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ import (
"path/filepath"
"strings"

"github.com/accurics/terrascan/pkg/config"
"github.com/accurics/terrascan/pkg/downloader"
admissionwebhook "github.com/accurics/terrascan/pkg/k8s/admission-webhook"
"github.com/accurics/terrascan/pkg/runtime"
"github.com/accurics/terrascan/pkg/utils"
"github.com/gorilla/mux"
Expand Down Expand Up @@ -69,11 +71,12 @@ func (g *APIHandler) scanRemoteRepo(w http.ResponseWriter, r *http.Request) {
// scan remote repo
s.d = downloader.NewDownloader()
var results interface{}
var isAdmissionDenied bool
if g.test {
results, err = s.ScanRemoteRepo(iacType, iacVersion, cloudType, []string{"./testdata/testpolicies"})
results, isAdmissionDenied, err = s.ScanRemoteRepo(iacType, iacVersion, cloudType, []string{"./testdata/testpolicies"})

} else {
results, err = s.ScanRemoteRepo(iacType, iacVersion, cloudType, getPolicyPathFromConfig())
results, isAdmissionDenied, err = s.ScanRemoteRepo(iacType, iacVersion, cloudType, getPolicyPathFromConfig())
}
if err != nil {
apiErrorResponse(w, err.Error(), http.StatusBadRequest)
Expand All @@ -90,17 +93,23 @@ func (g *APIHandler) scanRemoteRepo(w http.ResponseWriter, r *http.Request) {
}

// return with results
// if result contain violations denied by admission controller return 403 status code
if isAdmissionDenied {
apiResponse(w, string(j), http.StatusForbidden)
return
}
apiResponse(w, string(j), http.StatusOK)
}

// ScanRemoteRepo is the actual method where a remote repo is downloaded and
// scanned for violations
func (s *scanRemoteRepoReq) ScanRemoteRepo(iacType, iacVersion string, cloudType []string, policyPath []string) (interface{}, error) {
func (s *scanRemoteRepoReq) ScanRemoteRepo(iacType, iacVersion string, cloudType []string, policyPath []string) (interface{}, bool, error) {

// return params
var (
output interface{}
err error
output interface{}
err error
isAdmissionDenied bool
)

// temp destination directory to download remote repo
Expand All @@ -112,23 +121,23 @@ func (s *scanRemoteRepoReq) ScanRemoteRepo(iacType, iacVersion string, cloudType
if err != nil {
errMsg := fmt.Sprintf("failed to download remote repo. error: '%v'", err)
zap.S().Error(errMsg)
return output, err
return output, isAdmissionDenied, err
}

// create a new runtime executor for scanning the remote repo
executor, err := runtime.NewExecutor(iacType, iacVersion, cloudType,
"", iacDirPath, policyPath, s.ScanRules, s.SkipRules, s.Categories, s.Severity)
if err != nil {
zap.S().Error(err)
return output, err
return output, isAdmissionDenied, err
}

// evaluate policies IaC for violations
results, err := executor.Execute()
if err != nil {
errMsg := fmt.Sprintf("failed to scan uploaded file. error: '%v'", err)
zap.S().Error(errMsg)
return output, err
return output, isAdmissionDenied, err
}

if !s.ShowPassed {
Expand All @@ -139,9 +148,21 @@ func (s *scanRemoteRepoReq) ScanRemoteRepo(iacType, iacVersion string, cloudType
if s.ConfigOnly {
output = results.ResourceConfig
} else {
isAdmissionDenied = hasK8sAdmissionDeniedViolations(results)
output = results.Violations
}

// succesful
return output, nil
return output, isAdmissionDenied, nil
}

// hasK8sAdmissionDeniedViolations checks if violations have denied by k8s admission controller
func hasK8sAdmissionDeniedViolations(o runtime.Output) bool {
denyRuleMatcher := admissionwebhook.WebhookDenyRuleMatcher{}
for _, v := range o.Violations.ViolationStore.Violations {
if denyRuleMatcher.Match(*v, config.GetK8sAdmissionControl()) {
return true
}
}
return false
}
62 changes: 61 additions & 1 deletion pkg/http-server/remote-repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"path/filepath"
"reflect"
"testing"

"github.com/accurics/terrascan/pkg/config"
"github.com/accurics/terrascan/pkg/downloader"
"github.com/accurics/terrascan/pkg/policy"
"github.com/accurics/terrascan/pkg/results"
"github.com/accurics/terrascan/pkg/runtime"
"github.com/gorilla/mux"
)

Expand Down Expand Up @@ -69,7 +74,7 @@ func TestScanRemoteRepo(t *testing.T) {

for _, tt := range table {
t.Run(tt.name, func(t *testing.T) {
gotOutput, gotErr := tt.s.ScanRemoteRepo(tt.iacType, tt.iacVersion, tt.cloudType, []string{})
gotOutput, _, gotErr := tt.s.ScanRemoteRepo(tt.iacType, tt.iacVersion, tt.cloudType, []string{})
if !reflect.DeepEqual(gotErr, tt.wantErr) {
t.Errorf("error got: '%v', want: '%v'", gotErr, tt.wantErr)
}
Expand Down Expand Up @@ -203,3 +208,58 @@ func TestScanRemoteRepoHandler(t *testing.T) {
})
}
}

func TestHasK8sAdmissionDeniedViolations(t *testing.T) {
k8sTestData := "k8s_testdata"
configFileWithCategoryDenied := filepath.Join(k8sTestData, "config-deny-category.toml")

type args struct {
o runtime.Output
}
tests := []struct {
name string
args args
want bool
conigFile string
}{
{
name: "result with no violations",
args: args{
o: runtime.Output{
Violations: policy.EngineOutput{
ViolationStore: &results.ViolationStore{},
},
},
},
want: false,
},
{
name: "result contains denied violations",
args: args{
o: runtime.Output{
Violations: policy.EngineOutput{
ViolationStore: &results.ViolationStore{
Violations: []*results.Violation{
{
Category: "Identity and Access Management",
},
},
},
},
},
},
want: true,
conigFile: configFileWithCategoryDenied,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := config.LoadGlobalConfig(tt.conigFile); err != nil {
t.Errorf("error while loading the config file '%s'", tt.conigFile)
}
if got := hasK8sAdmissionDeniedViolations(tt.args.o); got != tt.want {
t.Errorf("hasK8sAdmissionDeniedViolations() = %v, want %v", got, tt.want)
}
})
}
}
47 changes: 8 additions & 39 deletions pkg/iac-providers/kubernetes/v1/normalize.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,47 +159,16 @@ func readSkipRulesFromAnnotations(annotations map[string]interface{}, resourceID
return nil
}

skipRules := make([]output.SkipRule, 0)
if rules, ok := skipRulesFromAnnotations.([]interface{}); ok {
for _, rule := range rules {
if value, ok := rule.(map[string]interface{}); ok {
skipRule := getSkipRuleObject(value)
if skipRule != nil {
skipRules = append(skipRules, *skipRule)
}
} else {
zap.S().Debugf("each rule in %s must be of map type", terrascanSkip)
}
}
} else {
zap.S().Debugf("%s must be an array of {rule: ruleID, comment: reason for skipping}", terrascanSkip)
}

return skipRules
}

func getSkipRuleObject(m map[string]interface{}) *output.SkipRule {
var skipRule output.SkipRule
var rule, comment interface{}
var ok bool

// get rule, if rule not found return nil
if rule, ok = m[terrascanSkipRule]; ok {
if _, ok = rule.(string); ok {
skipRule.Rule = rule.(string)
} else {
if rules, ok := skipRulesFromAnnotations.(string); ok {
skipRules := make([]output.SkipRule, 0)
err := json.Unmarshal([]byte(rules), &skipRules)
if err != nil {
zap.S().Debugf("json string %s cannot be unmarshalled to []output.SkipRules struct schema", rules)
return nil
}
} else {
return nil
}

// get comment
if comment, ok = m[terrascanSkipComment]; ok {
if _, ok = comment.(string); ok {
skipRule.Comment = comment.(string)
}
return skipRules
}

return &skipRule
zap.S().Debugf("%s must be a string containing an json array like [{rule: ruleID, comment: reason for skipping}]", terrascanSkip)
return nil
}
Loading

0 comments on commit 881417a

Please sign in to comment.