terrascan
https://github.com/tenable/terrascan
Terrascan 是基础架构即代码的静态代码分析器。Terrascan 允许:
- 将基础架构作为代码无缝扫描,以查找错误配置。
- 监控已配置的云基础架构,以查找引入终端安全评估漂移的配置更改,并启用恢复到安全终端
- 检测安全漏洞和违规行为。
- 在配置云原生基础架构之前降低风险。
- 提供在本地运行或与 CI\CD 集成的灵活性。
terrascan的基本原理是,通过内置的策略,对目标进行扫描。使用前需要下载策略库,而策略库是经常更新的。类似于扫描病毒需要下载病毒库。它还有一个特点是支持涵盖了IaC和容器领域的的文件扫描:
- Terraform (HCL2)
- AWS CloudFormation Templates (CFT)
- Azure Resource Manager (ARM)
- Kubernetes (JSON/YAML), Helm v3, and Kustomize
- Dockerfiles
可见IaC中常用的Cfn,Terraform,以及容器领域的K8S,Helm,Kustomize,Dockerfile都有支持,
一个产品搞定,不需要再部署其他的工具,简化了CICD的设计。
安装(Linux)
# 下载
aria2c https://github.com/tenable/terrascan/releases/download/v1.19.1/terrascan_1.19.1_Linux_x86_64.tar.gz
# 没有aria2c可以直接用wget
wget https://github.com/tenable/terrascan/releases/download/v1.19.1/terrascan_1.19.1_Linux_x86_64.tar.gz
# 解压
tar -xzvf terrascan.tar.gz
#安装
install terrascan /usr/local/bin && rm terrascan
命令帮助
Usage:
terrascan [command]
Available Commands:
init Initializes Terrascan and clones policies from the Terrascan GitHub repository.
scan Detect compliance and security violations across Infrastructure as Code.
server Run Terrascan as an API server
version Terrascan version
Flags:
-c, --config-path string config file path
-l, --log-level string log level (debug, info, warn, error, panic, fatal) (default "info")
--log-output-dir string directory path to write the log and output files
-x, --log-type string log output type (console, json) (default "console")
-o, --output string output type (human, json, yaml, xml, junit-xml, sarif, github-sarif) (default "human")
--temp-dir string temporary directory path to download remote repository,module and templates
init:下载策略库,即从策略库仓库 git clone到本地$HOME/.terrascan目录
scan:具体的scan命令,命令行方式使用
server:作为服务器,提供API供外部调用使用,方便和第三方系统整合
扫描参数
- 指定路径(默认为当前路径) -d
- 指定文件 -f
- 指定远程存储路径 -r
- git, s3, gcs, http, terraform-registry
- -u 指定具体的url
- 指定目标类型 -i
arm, cft, docker, helm, k8s, kustomize, terraform, tfplan
-i, --iac-type string iac type (arm, cft, docker, helm, k8s, kustomize, terraform, tfplan)
--iac-version string iac version (arm: v1, cft: v1, docker: v1, helm: v3, k8s: v1, kustomize: v2, v3, v4, terraform: v12, v13, v14, v15, tfplan: v1)
- 指定输出格式
- yaml, json, human(默认),xml
工作原理
初次执行时,先从github上下载对应的策略库,并根据策略库的要求检查目标文件。
也可以主动指定init命令更新策略库
命令行执行
- 测试用例文件
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
namespace: default
spec:
replicas: 2
revisionHistoryLimit: 10
selector:
matchLabels:
app: nginx
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
imagePullPolicy: Always
name: nginx
resources: {}
volumeMounts:
- mountPath: /data
name: mytest
restartPolicy: Always
volumes:
- name: mytest
persistentVolumeClaim:
claimName: my-test-pvc
- 执行命令行
terrascan scan -i k8s -f nginx.yaml
terrascan scan -i k8s -f nginx.yaml -o json
terrascan scan -i k8s -f nginx.yaml -o yaml
- 检查结果
k8s@k8s-devp-master:~/yaml$ terrascan scan -i k8s -f nginx.yaml
Violation Details -
Description : Memory Limits Not Set in config file.
File : nginx.yaml
Line : 1
Severity : MEDIUM
-----------------------------------------------------------------------
Description : Apply Security Context to Your Pods and Containers
File : nginx.yaml
Line : 1
Severity : MEDIUM
-----------------------------------------------------------------------
Description : No readiness probe will affect automatic recovery in case of unexpected errors
File : nginx.yaml
Line : 1
Severity : LOW
-----------------------------------------------------------------------
Description : CPU Limits Not Set in config file.
File : nginx.yaml
Line : 1
Severity : MEDIUM
-----------------------------------------------------------------------
Description : Container images with readOnlyRootFileSystem set as false mounts the container root file system with write permissions
File : nginx.yaml
Line : 1
Severity : MEDIUM
-----------------------------------------------------------------------
Description : Memory Request Not Set in config file.
File : nginx.yaml
Line : 1
Severity : MEDIUM
-----------------------------------------------------------------------
Description : No liveness probe will ensure there is no recovery in case of unexpected errors
File : nginx.yaml
Line : 1
Severity : LOW
-----------------------------------------------------------------------
Description : Image without digest affects the integrity principle of image security
File : nginx.yaml
Line : 1
Severity : MEDIUM
-----------------------------------------------------------------------
Description : CPU Request Not Set in config file.
File : nginx.yaml
Line : 1
Severity : MEDIUM
-----------------------------------------------------------------------
Description : Minimize Admission of Root Containers
File : nginx.yaml
Line : 1
Severity : HIGH
-----------------------------------------------------------------------
Description : Default Namespace Should Not be Used
File : nginx.yaml
Line : 1
Severity : HIGH
-----------------------------------------------------------------------
Description : Containers Should Not Run with AllowPrivilegeEscalation
File : nginx.yaml
Line : 1
Severity : HIGH
-----------------------------------------------------------------------
Description : Default seccomp profile not enabled will make the container to make non-essential system calls
File : nginx.yaml
Line : 1
Severity : MEDIUM
-----------------------------------------------------------------------
Description : AppArmor profile not set to default or custom profile will make the container vulnerable to kernel level threats
File : nginx.yaml
Line : 1
Severity : MEDIUM
-----------------------------------------------------------------------
Description : No tag or container image with :Latest tag makes difficult to rollback and track
File : nginx.yaml
Line : 1
Severity : LOW
-----------------------------------------------------------------------
Scan Summary -
File/Folder : /home/k8s/yaml/nginx.yaml
IaC Type : k8s
Scanned At : 2024-04-19 08:37:56.943717392 +0000 UTC
Policies Validated : 42
Violated Policies : 15
Low : 3
Medium : 9
High : 3
作为服务启动
- 启动
k8s@k8s-devp-master:~/yaml$ terrascan server -p 10888
2024-04-19T09:02:06.409Z info http-server/start.go:63 registering routes...
2024-04-19T09:02:06.409Z info http-server/start.go:75 Route GET - /health
2024-04-19T09:02:06.409Z info http-server/start.go:75 Route GET - /v1/providers
2024-04-19T09:02:06.409Z info http-server/start.go:75 Route POST - /v1/{iac}/{iacVersion}/{cloud}/local/file/scan
2024-04-19T09:02:06.409Z info http-server/start.go:75 Route POST - /v1/{iac}/{iacVersion}/{cloud}/remote/dir/scan
2024-04-19T09:02:06.409Z info http-server/start.go:75 Route POST - /v1/k8s/webhooks/{apiKey}/scan/validate
2024-04-19T09:02:06.409Z info http-server/start.go:109 http server listening at port 10888
- 执行
Server模式下,如何使用几乎没有任何说明,只能参考其源码猜测:
https://github.com/tenable/terrascan/blob/master/pkg/http-server/routes.go
https://github.com/tenable/terrascan/blob/master/pkg/http-server/file-scan_test.go
// Routes returns a slice of routes of API endpoints to be registered with
// http server
func (g *APIServer) Routes() []*Route {
h := NewAPIHandler()
routes := []*Route{
{verb: "GET", path: "/health", fn: h.Health},
{verb: "GET", path: versionedPath("/providers"), fn: h.iacProviders},
{verb: "POST", path: versionedPath("/{iac}/{iacVersion}/{cloud}/local/file/scan"), fn: h.scanFile},
{verb: "POST", path: versionedPath("/{iac}/{iacVersion}/{cloud}/remote/dir/scan"), fn: h.scanRemoteRepo},
// k8s webhook Routes
{verb: "POST", path: versionedPath("/k8s/webhooks/{apiKey}/scan/validate"), fn: h.validateK8SWebhook},
}
return routes
}
// http request of the type "/v1/{iacType}/{iacVersion}/{cloudType}/file/scan"
url := fmt.Sprintf("/v1/%s/%s/%s/local/file/scan", tt.iacType, tt.iacVersion, tt.cloudType)
可以看到,它支持如下功能:
- /providers:功能支持列表
- /{iac}/{iacVersion}/{cloud}/local/file/scan:本地文件扫描
- /{iac}/{iacVersion}/{cloud}/remote/dir/scan:远程目录扫描
- /k8s/webhooks/{apiKey}/scan/validate:和Kubernetes整合用的webhook
功能列表获取
k8s@k8s-devp-master:~/yaml$ curl -X GET -L http://localhost:10888/v1/providers
[
{
"type": "arm",
"versions": [
"v1"
],
"defaultVersion": "v1"
},
{
"type": "cft",
"versions": [
"v1"
],
"defaultVersion": "v1"
},
{
"type": "docker",
"versions": [
"v1"
],
"defaultVersion": "v1"
},
{
"type": "helm",
"versions": [
"v3"
],
"defaultVersion": "v3"
},
{
"type": "k8s",
"versions": [
"v1"
],
"defaultVersion": "v1"
},
{
"type": "kustomize",
"versions": [
"v2",
"v3",
"v4"
],
"defaultVersion": "v4"
},
{
"type": "terraform",
"versions": [
"v12",
"v13",
"v14",
"v15"
],
"defaultVersion": "v15"
},
{
"type": "tfplan",
"versions": [
"v1"
],
"defaultVersion": "v1"
}
根据以上结果可以拼接访问URL:
- kubernetes扫描:http://localhost:10888/v1/k8s/v1/k8s/local/file/scan
- Dockerfile扫描:http://localhost:10888/v1/docker/v1/docker/local/file/scan
另外文件扫描接口只支持multipart/form-data类型的文件上传,不能作为POST的数据直接上传。
命令行参考以下:
可以看到执行结果和命令行一样,json格式。file等信息是内部中间结果信息,可忽略。
curl -X POST -L http://localhost:10888/v1/k8s/v1/k8s/local/file/scan -F 'file=@./nginx.yaml'
{
"results": {
"violations": [
{
"rule_name": "privilegeEscalationCheck",
"description": "Containers Should Not Run with AllowPrivilegeEscalation",
"rule_id": "AC_K8S_0085",
"severity": "HIGH",
"category": "Compliance Validation",
"resource_name": "nginx",
"resource_type": "kubernetes_deployment",
"file": "terrascan-3209226987.yaml",
"line": 1
},
{
"rule_name": "secCompProfile",
"description": "Default seccomp profile not enabled will make the container to make non-essential system calls",
"rule_id": "AC_K8S_0080",
"severity": "MEDIUM",
"category": "Identity and Access Management",
"resource_name": "nginx",
"resource_type": "kubernetes_deployment",
"file": "terrascan-3209226987.yaml",
"line": 1
},
{
"rule_name": "appArmorProfile",
"description": "AppArmor profile not set to default or custom profile will make the container vulnerable to kernel level threats",
"rule_id": "AC_K8S_0073",
"severity": "MEDIUM",
"category": "Identity and Access Management",
"resource_name": "nginx",
"resource_type": "kubernetes_deployment",
"file": "terrascan-3209226987.yaml",
"line": 1
},
{
"rule_name": "imageWithoutDigest",
"description": "Image without digest affects the integrity principle of image security",
"rule_id": "AC_K8S_0069",
"severity": "MEDIUM",
"category": "Infrastructure Security",
"resource_name": "nginx",
"resource_type": "kubernetes_deployment",
"file": "terrascan-3209226987.yaml",
"line": 1
},
{
"rule_name": "securityContextUsed",
"description": "Apply Security Context to Your Pods and Containers",
"rule_id": "AC_K8S_0064",
"severity": "MEDIUM",
"category": "Infrastructure Security",
"resource_name": "nginx",
"resource_type": "kubernetes_deployment",
"file": "terrascan-3209226987.yaml",
"line": 1
},
{
"rule_name": "MemorylimitsCheck",
"description": "Memory Limits Not Set in config file.",
"rule_id": "AC_K8S_0100",
"severity": "MEDIUM",
"category": "Security Best Practices",
"resource_name": "nginx",
"resource_type": "kubernetes_deployment",
"file": "terrascan-3209226987.yaml",
"line": 1
},
{
"rule_name": "runAsNonRootCheck",
"description": "Minimize Admission of Root Containers",
"rule_id": "AC_K8S_0087",
"severity": "HIGH",
"category": "Identity and Access Management",
"resource_name": "nginx",
"resource_type": "kubernetes_deployment",
"file": "terrascan-3209226987.yaml",
"line": 1
},
{
"rule_name": "MemoryRequestsCheck",
"description": "Memory Request Not Set in config file.",
"rule_id": "AC_K8S_0099",
"severity": "MEDIUM",
"category": "Security Best Practices",
"resource_name": "nginx",
"resource_type": "kubernetes_deployment",
"file": "terrascan-3209226987.yaml",
"line": 1
},
{
"rule_name": "nolivenessProbe",
"description": "No liveness probe will ensure there is no recovery in case of unexpected errors",
"rule_id": "AC_K8S_0070",
"severity": "LOW",
"category": "Security Best Practices",
"resource_name": "nginx",
"resource_type": "kubernetes_deployment",
"file": "terrascan-3209226987.yaml",
"line": 1
},
{
"rule_name": "noReadinessProbe",
"description": "No readiness probe will affect automatic recovery in case of unexpected errors",
"rule_id": "AC_K8S_0072",
"severity": "LOW",
"category": "Security Best Practices",
"resource_name": "nginx",
"resource_type": "kubernetes_deployment",
"file": "terrascan-3209226987.yaml",
"line": 1
},
{
"rule_name": "readOnlyFileSystem",
"description": "Container images with readOnlyRootFileSystem set as false mounts the container root file system with write permissions",
"rule_id": "AC_K8S_0078",
"severity": "MEDIUM",
"category": "Identity and Access Management",
"resource_name": "nginx",
"resource_type": "kubernetes_deployment",
"file": "terrascan-3209226987.yaml",
"line": 1
},
{
"rule_name": "imageWithLatestTag",
"description": "No tag or container image with :Latest tag makes difficult to rollback and track",
"rule_id": "AC_K8S_0068",
"severity": "LOW",
"category": "Security Best Practices",
"resource_name": "nginx",
"resource_type": "kubernetes_deployment",
"file": "terrascan-3209226987.yaml",
"line": 1
},
{
"rule_name": "otherNamespace",
"description": "Default Namespace Should Not be Used",
"rule_id": "AC_K8S_0086",
"severity": "HIGH",
"category": "Security Best Practices",
"resource_name": "nginx",
"resource_type": "kubernetes_deployment",
"file": "terrascan-3209226987.yaml",
"line": 1
},
{
"rule_name": "CpulimitsCheck",
"description": "CPU Limits Not Set in config file.",
"rule_id": "AC_K8S_0098",
"severity": "MEDIUM",
"category": "Security Best Practices",
"resource_name": "nginx",
"resource_type": "kubernetes_deployment",
"file": "terrascan-3209226987.yaml",
"line": 1
},
{
"rule_name": "CpuRequestsCheck",
"description": "CPU Request Not Set in config file.",
"rule_id": "AC_K8S_0097",
"severity": "MEDIUM",
"category": "Security Best Practices",
"resource_name": "nginx",
"resource_type": "kubernetes_deployment",
"file": "terrascan-3209226987.yaml",
"line": 1
}
],
"skipped_violations": null,
"scan_summary": {
"file/folder": "/tmp/terrascan-3209226987.yaml",
"iac_type": "k8s",
"scanned_at": "2024-04-19 09:13:38.989530627 +0000 UTC",
"policies_validated": 42,
"violated_policies": 15,
"low": 3,
"medium": 9,
"high": 3
}
}