Dockerfile 配置
Dockerfile 配置
# Replace latest with a pinned version tag from https://hub.docker.com/_/alpine
#
# We suggest using the major.minor tag, not major.minor.patch.
FROM alpine:latest
# Non-root user for security purposes.
#
# UIDs below 10,000 are a security risk, as a container breakout could result
# in the container being ran as a more privileged user on the host kernel with
# the same UID.
#
# Static GID/UID is also useful for chown'ing files outside the container where
# such a user does not exist.
RUN addgroup -g 10001 -S nonroot && adduser -u 10000 -S -G nonroot -h /home/nonroot nonroot
# Install packages here with `apk add --no-cache`, copy your binary
# into /sbin/, etc.
# Tini allows us to avoid several Docker edge cases, see https://github.com/krallin/tini.
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--", "myapp"]
# Replace "myapp" above with your binary
# bind-tools is needed for DNS resolution to work in *some* Docker networks, but not all.
# This applies to nslookup, Go binaries, etc. If you want your Docker image to work even
# in more obscure Docker environments, use this.
RUN apk add --no-cache bind-tools
# Use the non-root user to run our application
USER nonroot
# Default arguments for your app (remove if you have none):
CMD ["--foo", "1", "--bar=2"]
多阶段构建
随着
# First stage: complete build environment
FROM maven:3.5.0-jdk-8-alpine AS builder
# add pom.xml and source code
ADD ./pom.xml pom.xml
ADD ./src src/
# package jar
RUN mvn clean package
# Second stage: minimal runtime environment
From openjdk:8-jre-alpine
# copy jar from the first stage
COPY --from=builder target/msb-1.0.jar msb.jar
# run jar
CMD ["java", "-jar", "msb.jar"]
对于
在前面阶段的
FROM image[:tag | @digest] AS stage
在后续阶段的
COPY --from=stage ...
同理,多阶段构建同样可以很方便地将多个彼此依赖的项目通过一个
安全配置
容器安全是一个广泛的问题空间,有很多低垂的果实可以收获来降低风险。一个好的出发点是在编写
Do not store secrets in environment variables
密钥分发是一个棘手的问题,而且很容易做错。对于容器化的应用,人们可以通过挂载卷从文件系统中浮出水面,或者通过环境变量更方便地浮出水面。使用
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])
}
仅使用可信镜像
容器化应用的供应链攻击也将来自用于构建容器本身的层级。主要的罪魁祸首显然是使用的基础映像。不受信任的基础镜像是一种高风险,应尽可能避免使用。
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])
}
这个规则是针对
避免使用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
从互联网上拉东西,然后用管道输送到
$ wget https://cloudberry.engineering/absolutely-trustworthy.sh | sh
供应链攻击的风险是一样的框架,归根结底是信任。如果你真的要使用
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])
}
不要升级系统包
这可能有点牵强,但道理如下:你要将软件依赖的版本固定下来,如果你进行
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 https://cloudberry.engineering/absolutely-trust-me.tar.gz
具有讽刺意味的是,官方文档建议使用
deny[msg] {
input[i].Cmd == "add"
msg = sprintf("Line %d: Use COPY instead of ADD", [i])
}
不要使用root
容器中的
USER hopefullynotroot
请注意,在
any_user {
input[i].Cmd == "user"
}
deny[msg] {
not any_user
msg = "Do not run as root, use USER instead"
}
不要使用sudo
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])
}