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 serverversion Terrascan versionFlags:-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: nginxname: nginxnamespace: default
spec:replicas: 2revisionHistoryLimit: 10selector:matchLabels:app: nginxstrategy:rollingUpdate:maxSurge: 25%maxUnavailable: 25%type: RollingUpdatetemplate:metadata:labels:app: nginxspec:containers:- image: nginximagePullPolicy: Alwaysname: nginxresources: {}volumeMounts:- mountPath: /dataname: mytestrestartPolicy: Alwaysvolumes:- name: mytestpersistentVolumeClaim: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.yamlViolation Details -Description : Memory Limits Not Set in config file.File : nginx.yamlLine : 1Severity : MEDIUM-----------------------------------------------------------------------Description : Apply Security Context to Your Pods and ContainersFile : nginx.yamlLine : 1Severity : MEDIUM-----------------------------------------------------------------------Description : No readiness probe will affect automatic recovery in case of unexpected errorsFile : nginx.yamlLine : 1Severity : LOW-----------------------------------------------------------------------Description : CPU Limits Not Set in config file.File : nginx.yamlLine : 1Severity : MEDIUM-----------------------------------------------------------------------Description : Container images with readOnlyRootFileSystem set as false mounts the container root file system with write permissionsFile : nginx.yamlLine : 1Severity : MEDIUM-----------------------------------------------------------------------Description : Memory Request Not Set in config file.File : nginx.yamlLine : 1Severity : MEDIUM-----------------------------------------------------------------------Description : No liveness probe will ensure there is no recovery in case of unexpected errorsFile : nginx.yamlLine : 1Severity : LOW-----------------------------------------------------------------------Description : Image without digest affects the integrity principle of image securityFile : nginx.yamlLine : 1Severity : MEDIUM-----------------------------------------------------------------------Description : CPU Request Not Set in config file.File : nginx.yamlLine : 1Severity : MEDIUM-----------------------------------------------------------------------Description : Minimize Admission of Root ContainersFile : nginx.yamlLine : 1Severity : HIGH-----------------------------------------------------------------------Description : Default Namespace Should Not be UsedFile : nginx.yamlLine : 1Severity : HIGH-----------------------------------------------------------------------Description : Containers Should Not Run with AllowPrivilegeEscalationFile : nginx.yamlLine : 1Severity : HIGH-----------------------------------------------------------------------Description : Default seccomp profile not enabled will make the container to make non-essential system callsFile : nginx.yamlLine : 1Severity : MEDIUM-----------------------------------------------------------------------Description : AppArmor profile not set to default or custom profile will make the container vulnerable to kernel level threatsFile : nginx.yamlLine : 1Severity : MEDIUM-----------------------------------------------------------------------Description : No tag or container image with :Latest tag makes difficult to rollback and trackFile : nginx.yamlLine : 1Severity : LOW-----------------------------------------------------------------------Scan Summary -File/Folder : /home/k8s/yaml/nginx.yamlIaC Type : k8sScanned At : 2024-04-19 08:37:56.943717392 +0000 UTCPolicies Validated : 42Violated Policies : 15Low : 3Medium : 9High : 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}}