【ArchSummit】如何通过AIOps推动可量化的业务价值增长和效率提升?>>> 了解详情
写点什么

Dockerfile 安全最佳实践

  • 2020-11-19
  • 本文字数:2376 字

    阅读完需:约 8 分钟

Dockerfile安全最佳实践

容器安全涉及问题很多,有许多“唾手可得”的方案能用来降低风险。不过,一个好的开始是编写 Dockerfile 文件时遵循一些规则。


在本文,我列出了一些常见的安全问题和如何规避它们。对于每一个问题,我还写了一个开放策略代理(Open Policy Agent,OPA)规则来使用conftest静态分析你的 Dockerfile 文件。


你可以在这个库找到.rego规则集。

不要在环境变量中存放密钥


密钥部署是一个很棘手的问题,而且很容易出错。对于容器化的应用程序,可以通过挂载卷从文件系统中显示它们,也可以更方便地通过环境变量显示。


使用ENV来存储密钥通常是不好的,因为 Dockerfile 文件通常与应用程序一起部署,因此这与在代码中硬编码密钥没有什么差别。


如何检测这一点:

secrets_env = [    "passwd",    "password",    "pass", #  "pwd", can't use this one       "secret",    "key",    "access",    "api_key",    "apikey",    "token",    "tkn"]
deny[msg] { input[i].Cmd == "env" val := input[i].Value contains(lower(val[_]), secrets_env[_]) msg = sprintf("Line %d: Potential secret in ENV key found: %s", [i, val])}
复制代码

只使用信任的根镜像


针对容器化应用程序的攻击链也来自构建容器本身所使用的层次结构。其中,主要的罪魁祸首明显是使用的根镜像。不受信的根镜像是一个高风险,任何时候都应该避免使用。


Docker 为大多数使用的操作系统和应用程序提供了一组官方根镜像。使用这些镜像,我们通过 Docker 自身分担的一些责任降低了协议风险。


如何检测这一点:

deny[msg] {    input[i].Cmd == "from"    val := split(input[i].Value[0], "/")    count(val) > 1    msg = sprintf("Line %d: use a trusted base image", [i])}
复制代码

这条规则针对的是 DockerHub 的官方镜像。由于我只检测到了 namespace 的缺失,这是非常愚蠢的。


信任的定义取决于你的上下文:可以相应地更改这条规则。


不要对根镜像使用'latest'标签


固定基础镜像的版本将使你对正在构建的容器的预期比较安心。


如果你依赖最新的(latest)版本,你可能会不知不觉地继承更新包,这在最好的坏情况下可能会影响你应用程序的可靠性,在最差的坏情况下可能会引入一个漏洞。


如何检测这一点:

deny[msg] {    input[i].Cmd == "from"    val := split(input[i].Value[0], ":")    contains(lower(val[1]), "latest"])    msg = sprintf("Line %d: do not use 'latest' tag for base images", [i])}
复制代码

避免 curl 命令


从互联网上拉取东西,并通过管道将它放到一个 shell 脚本中是非常糟糕的。不幸的是,这是一个比较广泛应用的方案来流式安装软件。


wget https://cloudberry.engineering/absolutely-trustworthy.sh | sh
复制代码


供应链攻击的风险与此相同,归根结底就是信任。如果你不得不使用 curl 命令,就请正确使用:


  • 使用可信来源

  • 使用安全连接

  • 验证下载内容的真实性和完整性


如何检测这一点:

deny[msg] {    input[i].Cmd == "run"    val := concat(" ", input[i].Value)    matches := regex.find_n("(curl|wget)[^|^>]*[|>]", lower(val), -1)    count(matches) > 0    msg = sprintf("Line %d: Avoid curl bashing", [i])}
复制代码

不要更新你的系统包


这可能有点儿牵强,但理由如下:你想要固定你的软件依赖的版本,如果你运行apt-get upgrade,你会将它们都更新到最新的版本。


如果你做了更新,而且你对根镜像使用latest标签,那么你就放大了你的依赖树的不确定性。


你要做的是固定根镜像的版本,并且只运行apt/apk update


如何检测这一点:

upgrade_commands = [    "apk upgrade",    "apt-get upgrade",    "dist-upgrade",]
deny[msg] { input[i].Cmd == "run" val := concat(" ", input[i].Value) contains(val, upgrade_commands[_]) msg = sprintf(“Line: %d: Do not upgrade your system packages", [i])}
复制代码

尽可能不要使用 ADD 命令


ADD命令的一个小功能是,将它指向一个远程 url,然后它会在构建时获取 url 的内容:

ADD https://cloudberry.engineering/absolutely-trust-me.tar.gz
复制代码

比较讽刺的是,官方文档建议使用 curl 命令来代替它。


从安全角度来看,不要这么做。事先获取你需要的内容,对其进行验证,然后COPY。但是,如果你真的需要,在安全连接上使用可靠信源


注意:如果你有一个奇特的构建系统,动态生成 Dockerfile 文件,那么ADD肯定会被使用到。


如何检测这一点:

deny[msg] {    input[i].Cmd == "add"    msg = sprintf("Line %d: Use COPY instead of ADD", [i])}
复制代码

不要使用 root 用户


容器中的 root 和主机上的 root 相同,但会受到 docker 守护程序配置的限制。无论有什么限制,如果一个人突破了容器,他也能够找到一种方法来获取访问主机的完整权限。


当然,这是不理想的,你的威胁模型不能忽视作为 root 用户运行所带来的风险。


因此,最好始终指定一个用户:

USER hopefullynotroot
复制代码


需要注意的是,在 dockerfile 中明确设定一个用户只是一层防线,不会解决所有以root用户运行所带来的问题


相反,我们可以——也应该采用层次防御方案并在整个堆栈中一步步缓解:严格配置 docker 守护进程或者使用一个非 root 容器方案,限制运行时配置(如果可能的话,禁止--privileged)等等。


如果检测这一点:

any_user {    input[i].Cmd == "user" }
deny[msg] { not any_user msg = "Do not run as root, use USER instead"}
复制代码

不要使用 sudo 命令


既然不能使用root用户,那你自然也不能使用 sudo 命令。


即使你作为一个普通用户运行,也要确保这个用户不属于sudoers

deny[msg] {    input[i].Cmd == "run"    val := concat(" ", input[i].Value)    contains(lower(val), "sudo")    msg = sprintf("Line %d: Do not use 'sudo' command", [i])}
复制代码

原文链接:


https://cloudberry.engineering/article/dockerfile-security-best-practices/


2020-11-19 12:422009
用户头像

发布了 165 篇内容, 共 71.5 次阅读, 收获喜欢 342 次。

关注

评论

发布
暂无评论
发现更多内容

ios新游上架

雪奈椰子

ios打包

「技术直播」分布式数据库订阅功能的原理及实现

TDengine

数据库 tdengine 时序数据库

2022年度回顾 | 这一年,LigaAI写了10万字

LigaAI

程序员 产品经理 敏捷开发 2022年终总结 企业号 1 月 PK 榜

软件测试/测试开发丨接口测试该怎么做?持证上岗的Charles,可以帮你做什么?

测试人

软件测试 自动化测试 接口测试 charles 测试发开

关于 Serverless 应用架构对企业价值的一些思考

阿里巴巴云原生

阿里云 Serverless 云原生

从0到1介绍一下开源大数据比对平台dataCompare

诸葛子房

大数据 开源 低代码

Java Agent 踩坑之 appendToSystemClassLoaderSearch 问题

阿里巴巴云原生

Java 阿里云 容器 云原生

喜报|3DCAT入选“灵境杯”深圳市最佳元宇宙案例!

3DCAT实时渲染

虚拟现实 元宇宙 增强现实 实时云渲染 元宇宙开发

分久必合?数据库进入“超”融合时代 | 爱分析调研

YMatrix 超融合数据库

数据库 数字化转型 案例分享 超融合数据库 YMatrix

上架苹果

雪奈椰子

ios打包

首汽约车驶向极速统一之路!出行平台如何基于StarRocks构建实时数仓?

StarRocks

数据库

湖南卫视携手华为云 打造跨年晚会“最炫科技风”

Geek_2d6073

软件测试/测试开发丨你以为Shell只是命令行?读懂这篇文,给你的工作赋能

测试人

Shell 软件测试 命令行 自动化测试 测试开发

iOS不想上架

雪奈椰子

ios打包

软件测试/测试开发丨接口管理工具YApi怎么用?颜值高、易管理、超好用

测试人

软件测试 接口测试 YAPI 测试开发

缓存与数据库双写时的数据如何保证一致性?

风铃架构日知录

Java 程序员 后端 开发工程师 后端开发工程师

“数据库内核从入门到精通 ”系列课开讲!

阿里云数据库开源

开源数据库 polarDB PolarDB-X 阿里云数据库 PolarDB for PostgreSQL

消息收发弹性——生产集群如何解决大促场景消息收发的弹性&降本诉求

阿里巴巴云原生

阿里云 RocketMQ 云原生

喜报 | 瑞云科技荣获“第四届天鸽奖十大创新企业”等两项大奖

3DCAT实时渲染

元宇宙 3DCAT 瑞云渲染

CodeArts TestPlan:一站式测试管理平台

华为云开发者联盟

云计算 后端 华为云 企业号 1 月 PK 榜

版本控制 | 一文了解虚拟制作,进入影视制作新赛道

龙智—DevSecOps解决方案

版本控制 版本控制软件 虚拟制作

浅析华为云基于HBase MTTR上的优化实践

华为云开发者联盟

大数据 后端 华为云 企业号 1 月 PK 榜

快速构造String对象及访问其内部成员的技巧

阿里技术

Java jdk FASTJSON2

广西首次!3DCAT实时云渲染助力南宁数字气象科普馆上线

3DCAT实时渲染

云计算 云渲染 元宇宙 3DCAT 虚拟数字气象馆

可观测性之Log4j2优雅日志打印原创

宋小生

日志 可观测性 log4j2

【架构设计】如何让你的应用做到高内聚、低耦合?

JAVA旭阳

Java 架构

ITSM | 权威指南发布,高速IT服务管理团队是什么样子的?

龙智—DevSecOps解决方案

数字化 ITSM IT服务管理中心

做产品质量“守护神” 华为云CodeArts TestPlan测试管理平台解读

科技热闻

可以一学的代码优化小技巧:减少if-else冗余

华为云开发者联盟

JavaScript 前端 代码 华为云 企业号 1 月 PK 榜

npm 包 chalk-next 被开发者投毒,导致 SRC 目录被删

墨菲安全

npm 投毒 npm chalk-next chalk-next 投毒

灵雀云入选2022 EDGE AWARDS「创新场景50」年度最佳场景实践榜单

York

云原生 数字化转型 业务思维 科技创新

Dockerfile安全最佳实践_文化 & 方法_Gianluca Brindisi_InfoQ精选文章