Skip to content
This repository has been archived by the owner on Sep 3, 2024. It is now read-only.

Preliminary support for Azure Resource Manager #188

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions pkg/loader/arm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2021 Fugue, Inc.
//
// 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 loader

import (
"encoding/json"
"fmt"
)

var validArmExts map[string]bool = map[string]bool{
".json": true,
}

type ArmDetector struct{}

func (c *ArmDetector) DetectFile(i InputFile, opts DetectOptions) (IACConfiguration, error) {
if !opts.IgnoreExt && !validArmExts[i.Ext()] {
return nil, fmt.Errorf("File does not have .json extension: %v", i.Path())
}
contents, err := i.Contents()
if err != nil {
return nil, err
}

template := &armTemplate{}
if err := json.Unmarshal(contents, &template.Contents); err != nil {
return nil, fmt.Errorf("Failed to parse file as JSON %v: %v", i.Path(), err)
}
_, hasSchema := template.Contents["$schema"]
_, hasResources := template.Contents["resources"]

if !hasSchema && !hasResources {
return nil, fmt.Errorf("Input file is not an ARM template: %v", i.Path())
}
path := i.Path()

return &armConfiguration{
path: path,
template: *template,
}, nil
}

func (c *ArmDetector) DetectDirectory(i InputDirectory, opts DetectOptions) (IACConfiguration, error) {
return nil, nil
}

type armConfiguration struct {
path string
template armTemplate
source *CfnSourceInfo
}

func (l *armConfiguration) RegulaInput() RegulaInput {
return RegulaInput{
"filepath": l.path,
"content": l.template.Contents,
}
}

func (l *armConfiguration) Location(attributePath []string) (LocationStack, error) {
if l.source == nil {
return nil, nil
}
loc, err := l.source.Location(attributePath)
if loc == nil || err != nil {
return nil, err
}
return []Location{*loc}, nil
}

func (l *armConfiguration) LoadedFiles() []string {
return []string{l.path}
}

type armTemplate struct {
Contents map[string]interface{}
}
7 changes: 6 additions & 1 deletion pkg/loader/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ const (
// Tf means that regula will load the HCL in the directory in a similar
// way to terraform plan, or it can also load individual files.
Tf
// Azure Resource Manager JSON
Arm
)

// InputTypeIDs maps the InputType enums to string values that can be specified in
Expand All @@ -50,6 +52,7 @@ var InputTypeIDs = map[InputType][]string{
TfPlan: {"tf-plan", "tf_plan"},
Cfn: {"cfn"},
Tf: {"tf"},
Arm: {"arm"},
}

// InputTypeForString is a utility function to translate the string name of an input
Expand All @@ -64,6 +67,8 @@ func InputTypeForString(typeStr string) (InputType, error) {
return TfPlan, nil
case "tf":
return Tf, nil
case "arm":
return Arm, nil
default:
return -1, fmt.Errorf("Unrecognized input type %v", typeStr)
}
Expand Down Expand Up @@ -114,7 +119,7 @@ type Location struct {
//
// These are stored as a call stack, with the most specific location in the
// first position, and the "root of the call stack" at the last position.
type LocationStack = []Location;
type LocationStack = []Location

func (l Location) String() string {
return fmt.Sprintf("%s:%d:%d", l.Path, l.Line, l.Col)
Expand Down
3 changes: 3 additions & 0 deletions pkg/loader/loadpaths.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,16 @@ func DetectorByInputType(inputType InputType) (ConfigurationDetector, error) {
&CfnDetector{},
&TfPlanDetector{},
&TfDetector{},
&ArmDetector{},
), nil
case Cfn:
return &CfnDetector{}, nil
case TfPlan:
return &TfPlanDetector{}, nil
case Tf:
return &TfDetector{}, nil
case Arm:
return &ArmDetector{}, nil
default:
return nil, fmt.Errorf("Unsupported input type: %v", inputType)
}
Expand Down
8 changes: 8 additions & 0 deletions rego/lib/fugue/input_type.rego
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package fugue.input_type_internal
# - "tf_plan"
# - "tf_runtime"
# - "cfn"
# - "arm"
#
# To check the current resource type, use `input_type`.
# To check if a rule applies for this input type, use `compatibility`.
Expand All @@ -37,6 +38,8 @@ input_type = "tf" {
_ = input.Resources
} else = "cfn" {
_ = input.AWSTemplateFormatVersion
} else = "arm" {
_ = input.contentVersion
} else = "unknown" {
true
}
Expand All @@ -51,6 +54,10 @@ cloudformation_input_type {
input_type == "cfn"
}

arm_input_type {
input_type == "arm"
}

rule_input_type(pkg) = ret {
# This is a workaround for an issue in fregot, where the next line will fail
# the typechecker when there isn't a single `input_type` defined, which is
Expand All @@ -69,4 +76,5 @@ compatibility := {
"tf_runtime": {"tf_runtime"},
"cfn": {"cfn"},
"cloudformation": {"cfn"}, # Backwards-compatibility
"arm": {"arm"},
}
7 changes: 7 additions & 0 deletions rego/lib/fugue/resource_view.rego
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package fugue.resource_view
import data.fugue.input_type_internal
import data.fugue.resource_view.cloudformation
import data.fugue.resource_view.terraform
import data.fugue.resource_view.arm

resource_view = ret {
# If we are already given a resource view, just pass it through.
Expand All @@ -31,6 +32,9 @@ resource_view = ret {
} else = ret {
input_type_internal.cloudformation_input_type
ret = cloudformation.resource_view
} else = ret {
input_type_internal.arm_input_type
ret = arm.resource_view
}

resource_view_input = ret {
Expand All @@ -42,4 +46,7 @@ resource_view_input = ret {
} else = ret {
input_type_internal.cloudformation_input_type
ret = {"resources": resource_view, "_template": input}
} else = ret {
input_type_internal.arm_input_type
ret = {"resources": resource_view, "_template": input}
}
23 changes: 23 additions & 0 deletions rego/lib/fugue/resource_view/arm.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2021 Fugue, Inc.
#
# 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 fugue.resource_view.arm

resource_view[id] = ret {
resource := input.resources[id]
ret := json.patch(resource, [
{"op": "add", "path": ["id"], "value": resource.name},
{"op": "add", "path": ["_type"], "value": resource.type},
{"op": "add", "path": ["_provider"], "value": "arm"},
])
}
47 changes: 47 additions & 0 deletions rego/rules/arm/sql/auditing.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright 2020-2021 Fugue, Inc.
#
# 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 rules.arm_sql_auditing

import data.fugue

__rego__metadoc__ := {
"custom": {
"controls": {
"CIS-Azure_v1.1.0": [
"CIS-Azure_v1.1.0_4.1"
],
"CIS-Azure_v1.3.0": [
"CIS-Azure_v1.3.0_4.1.1"
]
},
"severity": "Medium"
},
"description": "SQL Server auditing should be enabled. The Azure platform allows a SQL server to be created as a service. Enabling auditing at the server level ensures that all existing and newly created databases on the SQL server instance are audited. Auditing policy applied on the SQL database does not override auditing policy and settings applied on the particular SQL server where the database is hosted.",
"id": "FG_R00282",
"title": "SQL Server auditing should be enabled"
}

input_type = "arm"

resource_type = "Microsoft.Sql/servers/databases/auditingPolicies"

default allow = false

allow {
{
lower(input.properties.auditingState) == "enabled"
} {
lower(input.properties.state) == "enabled"
}
}
48 changes: 48 additions & 0 deletions rego/rules/arm/sql/auditing_retention_90days.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Copyright 2020-2021 Fugue, Inc.
#
# 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 rules.arm_sql_auditing_retention_90days

import data.fugue

__rego__metadoc__ := {
"custom": {
"controls": {
"CIS-Azure_v1.1.0": [
"CIS-Azure_v1.1.0_4.3"
],
"CIS-Azure_v1.3.0": [
"CIS-Azure_v1.3.0_4.1.3"
]
},
"severity": "Medium"
},
"description": "SQL Server auditing retention should be greater than 90 days. Audit Logs can be used to check for anomalies and give insight into suspected breaches or misuse of information and access.",
"id": "FG_R00283",
"title": "SQL Server auditing retention should be greater than 90 days"
}

input_type = "arm"

resource_type = "Microsoft.Sql/servers/databases/auditingPolicies"

default allow = false

allow {
{
lower(input.properties.auditingState) == "enabled"
} {
lower(input.properties.state) == "enabled"
}
input.properties.retentionDays >= 90
}
Loading