在这篇文章中,我们将为您详细介绍如何使用Distroless让你的容器更加安全的内容。此外,我们还会涉及一些关于10个技巧让你的RESTfulWeb服务更加实用、Alpine?Distroless?B
在这篇文章中,我们将为您详细介绍如何使用 Distroless 让你的容器更加安全的内容。此外,我们还会涉及一些关于10 个技巧让你的 RESTful Web 服务更加实用、Alpine?Distroless?Busybox?到底谁才是容器镜像的瑞士军刀?、Beego中的权限控制——让你的Web应用更加安全、Distroless加固容器安全的知识,以帮助您更全面地了解这个主题。
本文目录一览:- 如何使用 Distroless 让你的容器更加安全
- 10 个技巧让你的 RESTful Web 服务更加实用
- Alpine?Distroless?Busybox?到底谁才是容器镜像的瑞士军刀?
- Beego中的权限控制——让你的Web应用更加安全
- Distroless加固容器安全
如何使用 Distroless 让你的容器更加安全
使用 Distroless
镜像来保护 Kubernetes
上的容器。
容器改变了我们看待技术基础设施的方式。这是我们运行应用程序方式的一次巨大飞跃。容器编排和云服务一起为我们提供了一种近乎无限规模的无缝扩展能力。
根据定义,容器应该包含 应用程序 及其 运行时依赖项。然而,在现实中,它们包含的远不止这些。标准容器基础映像包含标准 Linux
发行版中可以找到的包管理器、shell
和其他程序。
虽然这些都是构建容器镜像所必需的,但它们不应该成为最终镜像的一部分。例如,一旦你把包安装好了,就不再需要在容器中使用 apt
等包管理工具了。
这不仅使你的容器里充满了不必要的软件包和程序,而且还为网络罪犯提供了攻击特定程序漏洞的机会。
你应该始终了解容器运行时中存在什么,并且应该精确地限制其只包含应用程序所需的依赖项。
除了那些必要的,你不应该安装任何东西。一些领先的科技巨头,如谷歌,有多年在生产中运行容器的经验,已经采用了这种方法。
谷歌现在通过提供 Distroless
镜像向全世界开放这种能力。谷歌构建的这些镜像的目标是只包含你的应用程序及其依赖项,同时它们将没有常规 Linux
发行版的所有特性,包括 shell
。
这意味着虽然可以想以前一样运行应用程序的容器,但不能在容器运行的时候进入容器内。这是一个重大的安全改进,因为你现在已经为黑客通过 shell
进入你的容器关上了大门。
Distroless 基础镜像
谷歌为大多数流行的编程语言和平台提供了 Distroless
的基础镜像。
以下基础镜像是正式发布的版本:
- gcr.io/distroless/static-debian10
- gcr.io/distroless/base-debian10
- gcr.io/distroless/java-debian10
- gcr.io/distroless/cc-debian10
- gcr.io/distroless/nodejs-debian10
下面的基础镜像仍在实验阶段,不推荐用于生产环境:
- gcr.io/distroless/python2.7-debian10
- gcr.io/distroless/python3-debian10
- gcr.io/distroless/java/jetty-debian10
- gcr.io/distroless/dotnet
构建 Distroless 镜像
谷歌在内部使用 Bazel 来构建容器映像,但是我们可以使用 Docker
来做同样的事情。关于使用 Distroless
镜像的一个有争议的问题是:当我们有一个 Distroless
镜像时,我们如何使用 Dockerfile
来构建我们的应用程序呢?
通常,Dockerfile
以一个标准的 OS 基础镜像开始,然后是创建适当的运行时构建所需执行的多个步骤。这包括包的安装,为此需要像 apt
或 yum
这样的包管理器。
有两种方法:
- 先在
Docker
外部构建好你的应用程序,然后使用Dockerfile
中的 ADD 或 COPY 指令将二进制包复制到容器中。 - 使用多阶段
Docker
构建。这是 Docker 17.05 及以后版本的一个新特性,它允许你将构建分为不同的阶段。第一阶段可以从标准的 OS 基础镜像开始,可以帮助你构建应用程序;第二阶段可以简单地从第一阶段获取构建的文件并使用Distroless
作为基础镜像。
为了理解它是如何工作的,让我们使用多阶段构建流程进行一个实际操作练习。
必要条件
你需要具备以下内容:
- Docker 版本大于等于 17.05,用于构建镜像
- 可选的
Kubernetes
集群用于实践练习的第二部分。如果你想在Docker
中运行你的容器,你可以使用等价的docker
命令。
GitHub 代码仓
作为实践练习,将 此代码仓 Fork 到你的 GitHub 帐号下,然后克隆 GitHub 代码仓并使用 cd
进入到项目目录下。
该代码仓包含一个 Python
的 Flask
应用程序,当你调用 API 时,该应用程序会响应 Hello World!
。
app.py
文件如下所示:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == ''__main__'':
app.run(host=''0.0.0.0'', debug=True)
Dockerfile 包含两个阶段:
FROM python:2.7-slim AS build
ADD . /app
WORKDIR /app
RUN pip install --upgrade pip
RUN pip install -r ./requirements.txt
FROM gcr.io/distroless/python2.7
COPY --from=build /app /app
COPY --from=build /usr/local/lib/python2.7/site-packages /usr/local/lib/python2.7/site-packages
WORKDIR /app
ENV PYTHONPATH=/usr/local/lib/python2.7/site-packages
EXPOSE 5000
CMD ["app.py"]
构建阶段:
- 从 python:2.7-slim 的基础镜像开始
- 将应用程序复制到 /app 目录下
- 升级
pip
并安装依赖
Distroless 阶段:
- 从 gcr.io/distroless/python2.7 的基础镜像开始
- 将应用程序从构建阶段的 /app 目录复制到当前阶段的 /app 目录
- 将 python 的
site-packages
从构建阶段复制到当前阶段的site-packages
目录 - 设置工作目录到 /app,将 python PATH 设置为
site-packages
目录,并暴露 5000 端口 - 使用
CMD
指令运行app.py
由于 Disroless
镜像不包含 shell
,所以应该在最后使用 CMD
指令。如果不这样做,Docker 将认为它是一个 shell CMD,并试图这样执行它,但这是不工作的。
构建镜像:
$ docker build -t <your_docker_repo>/flask-hello-world-distroless .
Sending build context to Docker daemon 95.74kB
Step 1/12 : FROM python:2.7-slim AS build
---> eeb27ee6b893
Step 2/12 : ADD . /app
---> a01dc81df193
Step 3/12 : WORKDIR /app
---> Running in 48ccf6b990e4
Removing intermediate container 48ccf6b990e4
---> 2e5e335be678
Step 4/12 : RUN pip install --upgrade pip
---> Running in 583be3d0b8cc
Collecting pip
Downloading pip-20.1.1-py2.py3-none-any.whl (1.5 MB)
Installing collected packages: pip
Attempting uninstall: pip
Found existing installation: pip 20.0.2
Uninstalling pip-20.0.2:
Successfully uninstalled pip-20.0.2
Successfully installed pip-20.1.1
Removing intermediate container 583be3d0b8cc
...................................
Successfully installed Jinja2-2.11.2 MarkupSafe-0.23 click-7.1.2 flask-1.1.2 itsdangerous-0.24 werkzeug-1.0.1
Removing intermediate container c4d00b1abf4a
---> 01cbadcc531f
Step 6/12 : FROM gcr.io/distroless/python2.7
---> 796952c43cc4
Step 7/12 : COPY --from=build /app /app
---> 92657682cdcc
Step 8/12 : COPY --from=build /usr/local/lib/python2.7/site-packages /usr/local/lib/python2.7/site-packages
---> faafd06edeac
Step 9/12 : WORKDIR /app
---> Running in 0cf545aa0e62
Removing intermediate container 0cf545aa0e62
---> 4c4af4333209
Step 10/12 : ENV PYTHONPATH=/usr/local/lib/python2.7/site-packages
---> Running in 681ae3cd51cc
Removing intermediate container 681ae3cd51cc
---> 564f48eff90a
Step 11/12 : EXPOSE 5000
---> Running in 7ff5c073d568
Removing intermediate container 7ff5c073d568
---> ccc3d211d295
Step 12/12 : CMD ["app.py"]
---> Running in 2b2c2f111423
Removing intermediate container 2b2c2f111423
---> 76d13d2f61cd
Successfully built 76d13d2f61cd
Successfully tagged <your_docker_repo>/flask-hello-world-distroless:latest
登录到 DockerHub 并推送镜像:
docker login
docker push <your_docker_repo>/flask-hello-world-distroless:latest
登录到 DockerHub(或者你的私有镜像仓),你应该会看到容器镜像可以使用:
如果你看一下压缩后的大小,它只有 23.36 MB。如果你使用 slim
发行版作为基础镜像,它将占用 56 MB。
你已经减少了超过一半的容器占用空间。That’s amazing!
在 Kubernetes 中运行容器
为了测试构建是否有效,让我们在 Kubernetes 集群中运行容器。如果你没有 Kubernetes,你可以运行等价的 Docker 命令来做相同的活动,因为 Kubectl 和 Docker 命令是相似的。
我在代码仓中创建了一个 kubernetes.yaml 文件,该文件包含使用我们构建的镜像的 Deployment
和 负载均衡的 Service
。
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-deployment
spec:
selector:
matchLabels:
app: flask
replicas: 2
template:
metadata:
labels:
app: flask
spec:
containers:
- name: flask
image: bharamicrosystems/flask-hello-world-distroless
ports:
- containerPort: 5000
---
apiVersion: v1
kind: Service
metadata:
name: flask-service
spec:
selector:
app: flask
ports:
- port: 80
targetPort: 5000
type: LoadBalancer
这是一个非常简单的设置。负载均衡器监听端口 80 并映射到目标端口 5000。这些 Pods 在默认的 5000 端口上监听 Flask 应用程序。
应用:
$ kubectl apply -f kubernetes.yaml
deployment.apps/flask-deployment created
service/flask-service created
我们查看一下所有的资源,看看我们已经创建了什么:
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/flask-deployment-576496558b-hnbxt 1/1 Running 0 47s
pod/flask-deployment-576496558b-hszpq 1/1 Running 0 73s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/flask-service LoadBalancer 10.8.9.163 35.184.113.120 80:31357/TCP 86s
service/kubernetes ClusterIP 10.8.0.1 <none> 443/TCP 26m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/flask-deployment 2/2 2 2 88s
NAME DESIRED CURRENT READY AGE
replicaset.apps/flask-deployment-576496558b 2 2 2 89s
我们看到存在两个 Pods
、一个 Deployment
、一个带有外部 IP 的 LoadBalancer
服务和一个 ReplicaSet
。
让我们访问应用程序:
$ curl http://35.184.113.120
Hello World!
我们得到了 Hello World!
。这表明 Flask 应用程序在正常工作。
使用 Shell 对应用程序进行访问
正如我在引言中所描述的,Disroless
容器中没有 shell
,因此不可能进入到容器内。然而,让我们试着在容器中执行 exec:
$ kubectl exec -it flask-deployment-576496558b-hnbxt /bin/bash
OCI runtime exec failed: exec failed: container_linux.go:349: starting container process caused "exec: \"/bin/bash\": stat /bin/bash: no such file or directory": unknown
command terminated with exit code 126
我们无法连接到容器上。
容器日志呢?如果拿不到容器日志,我们就失去了调试应用程序的方法。
让我们试着去拿日志:
$ kubectl logs flask-deployment-576496558b-hnbxt
* Running on http://0.0.0.0:5000/
* Restarting with reloader
10.128.0.4 - - [31/May/2020 13:40:27] "GET / HTTP/1.1" 200 -
10.128.0.3 - - [31/May/2020 13:42:01] "GET / HTTP/1.1" 200 -
所以容器日志是可以被获取到的!
结论
使用 Distroless
作为基础镜像是一种令人兴奋的保护容器安全的方式。由于镜像小并且仅包含应用程序和依赖项,因此它为应用程序提供了最小的攻击面。它在更大程度上提高了应用程序的安全性,所以它是保护容器安全的好方法。
谢谢阅读!我希望你喜欢这篇文章。
原文链接
本文翻译自 How to Harden Your Containers With Distroless Docker Images</none></your_docker_repo></your_docker_repo></your_docker_repo>
10 个技巧让你的 RESTful Web 服务更加实用
提示:随着RESTful Web services的流行程度不断地上升,开发人员需要知道如何避免开发中的陷阱以及让开发出来的Web service达到自己能做到的最好程度。
过去的几年里,我们看到RESTful Web services变得流行起来是有好些原因的。这里有十个技巧你应该要做的,它们能让你的RESTful Web Service更加高水准并且被其他开发人员使用起来更加简易。
- 不要寻找一个官方的“REST 标准”
- 还是坚持一些标准
- 确保你的文档是完美无瑕疵的
- 提供 JSON 输出
- 不要漏掉 XML
- 理解 HTTP 动词
- 理解 URI 路由的重要性
- 在版本管理下进行更新维护
- 与你的用户保持联系
- 提供示例代码
REST是一个概念,不是一个标准。因此没有任何的要求让你的 RESTful Web service在一个特定的方式下运行。话虽如此,但......
......你的 RESTful Web services 应该遵循至少一些标准!例如用户认证的OAuth协议、数据交换的JSON和XML、网络传输和自控制的HTTP协议还有URI标准。如果你想要一个更加完整的包,开放数据协议OData是一个有效的选择(因为它够大?)。因为没有人说“REST必须坚持这些标准”这样的话,并不意味着你就应该按照自己的意愿来。
使用 SOAP 协议的WSDL(Web Service 定义语言)系统,如果你有一个基于WSDL、可以自动生成代码的工具,那么开发Web Service是相当简单的。但如果用 REST ,由于services不需要严格的定义,并且它们运行在被称为适当地正确工作的理念上。这意味着service的文档必须要非常严谨。如果你要开发一个Web Service一定要确保你的文档百分百的正确。
JSON 已经迅速的变成web上的重要标准。首先,它很方便,因为它可以很容易地让JavaScript以最少的编码量来使用Web Service。现在有很多库可以让服务端与客户端的JSON交互工作的非常好。
说到输出,XML 依然像往常一样非常重要。为什么要同时支持 XML 和 JSON 呢?因为并不是所有的系统都能使用 JSON 的,但如果一个系统能被称为 Web Service ,那么它就一定会被规定去处理XML。现在有成堆的遗留系统,举个例子,它是用XML工作而不是JSON。并且不是所有开发人员都想去mixing和匹配JSON跟XML的。因此要确保你的Web service系统支持这两种格式。通过HTTP请求头的“Accepts”参数来做这种支持而不是通过不同的包含参数的 service URL 来做。
REST Web services 其中一个很核心关键的是HTTP协议已经定义好的一大块功能。而这其中最基本的一部分就是 HTTP 动词,例如 GET、POST 。而这些基本功能在REST中已经被很好地、充分地理解,一些想法仍然不断地涌现,例如使用打补丁的方式更新实体中一些特定属性而不是整个实体。
RESTful Web services 在很大程度上是通过URI来决定干什么的。举个例子,在一次 GET 请求中,典型的URI路径会包含一个实体的主键值(或者其他标识键值)来检索获取实体的数据。例如 “http://www.example.com/service/entityname/76″ 将检索名字为 entityname, 主键值为 76 的实体。使用 REST Web services,URI 不仅是一种访问service的方式,还是控制service和作为传达你的需求的信号载体。
一件很诱人的事是你只需对唯一一个版本的service做更新维护。但真的不要这样做!确保你每一次发布更新后都新开一个分支版本来维护。最简单、最常见的办法是让你的service URI 带上版本号,通常是路径的一部分。人们最需要的一件事就是使用更新后的软件没有任何新的问题或警告出现。
因为用户不会主动发现你的更新,因此你和你的用户保持联系就显得十分重要了。例如,当你为你的service发布一个新的版本时,你应该给每一个用户发一封邮件让他们知道,以及提供旧版本的缺陷信息。
对你的用户来说你要做的最好的一件事之一就是给他们提供示例代码。确保你给出的代码至少包含以下几个主要的开发语言:Java、.NET、JavaScript、Ruby还有Python。如果有必要的话,雇佣一个顾问将这些代码放在一起。因为它对于你的 service 能否被采用是绝对重要的。还要确保你的许可协议可以让你的用户没有任何风险影响地使用你的示例代码,例如可以使用 MIT 或 BSD 许可协议。
一些跟 RESTful 相关的开源软件请看这里。
英文原文,OSChina原创翻译
Alpine?Distroless?Busybox?到底谁才是容器镜像的瑞士军刀?
大多数情况下,我们构建容器镜像时选择的基础镜像无外乎是 busybox
、alpine
和 google/distroless
这几种,这几个基础镜像在云原生的世界很吃香,被广泛应用于各个应用的容器化。
那么问题来了,为什么这几个基础镜像如此受欢迎呢?
我们先来看下这几个基础镜像的大小:
→ podman image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/library/alpine latest 14119a10abf4 6 days ago 5.87 MB
docker.io/library/busybox latest 42b97d3c2ae9 13 days ago 1.46 MB
gcr.io/distroless/static latest e0851a4aa136 51 years ago 3.06 MB
可以看到这些镜像的体积都非常小,几乎可以忽略不计。
Busybox
先启动一个 Busybox 容器进去一探究竟:

这个镜像的大小只有 1.24MB
,缺容纳了这么多 GNU 命令,麻雀虽小五脏俱全啊,这到底是怎么做到的?
事实上这一切都要归功于 Multi-Call binary
。什么是 Multi-Call binary
呢?
顾名思义,Multi-Call binary 就是多重调用二进制文件,是一个用C语言编写的程序,它允许多次调用来执行二进制文件。它包含了很多函数,每个执行独特动作的函数都可以通过一个名字来调用,这个名字同时也是 Multi-Call binary 的一个符号链接。Multi-Call binary 最好的应用范例便是 Busybox。
Busybox 里面的函数可以通过两种方式来调用:
-
busybox ls
-
ls
例如:

很明显,这些不是我们所熟知的 GNU 二进制文件,因为所有的二进制文件都具有相同的属性,比如大小、日期等。这些都不是独立的二进制文件,而是 Multi-Call binary 每个调用函数的别名。这个 Multi-Call binary 就叫 Busybox
。
遗憾的是,这些 Busybox 命令并不完全等同于 GNU 命令,某些命令的某些参数是无法执行的,相当于阉割版。
Alpine
看完了 Busybox,我们再来看看 Alpine 是怎么做的。

巧了,Alpine 的二进制文件竟然是指向 busybox 二进制文件的,这就很明显了,Alpine 镜像的底层使用了 busybox 二进制文件。除此之外,Alpine 还包含了 apk
包管理器和一些额外的可执行文件,所以 Alpine 镜像的体积才会比 Busybox 大。
Distroless
Distroless
就不用说了,它来自 Google[1]。该镜像几乎就是空的,只包含应用程序及其运行时所需的依赖,不包含软件包管理器、shell 和其他 GNU 二进制文件,当然还包含一些时区配置和部分 ca-certificates。

可以看到这个镜像中既没有 shell
也没有 bash
,为了一探究竟,可以先把镜像保存为 tar 包,然后把 rootfs
解压出来:
→ mkdir image
→ tar xvf distroless.tar.gz -C image/
16679402dc206c982b5552ab8de7d898547100e5468be29d4f67d393c0eadfdb.tar
e0851a4aa13657fc8dcd01e0e5e08cb817123ccb82e2c604b34f9ec9c1755e3f.json
2e18de03719583329b7fa8374130e57cc7cddf2b5a487fe4a4988622ca60575c/layer.tar
2e18de03719583329b7fa8374130e57cc7cddf2b5a487fe4a4988622ca60575c/VERSION
2e18de03719583329b7fa8374130e57cc7cddf2b5a487fe4a4988622ca60575c/json
manifest.json
repositories
→ cd image
→ ls -lh
total 3.0M
-r--r--r--. 1 root root 3.0M Jan 1 1970 16679402dc206c982b5552ab8de7d898547100e5468be29d4f67d393c0eadfdb.tar
drwxr-xr-x. 2 root root 50 Sep 3 17:42 2e18de03719583329b7fa8374130e57cc7cddf2b5a487fe4a4988622ca60575c
-r--r--r--. 1 root root 462 Jan 1 1970 e0851a4aa13657fc8dcd01e0e5e08cb817123ccb82e2c604b34f9ec9c1755e3f.json
-r--r--r--. 1 root root 213 Jan 1 1970 manifest.json
-r--r--r--. 1 root root 106 Jan 1 1970 repositories
→ mkdir rootfs
→ tar xf 16679402dc206c982b5552ab8de7d898547100e5468be29d4f67d393c0eadfdb.tar -C rootfs
→ tree rootfs
rootfs
├── bin
├── boot
├── dev
├── etc
│ ├── debian_version
│ ├── default
│ ├── dpkg
│ │ └── origins
│ │ └── debian
│ ├── group
│ ├── host.conf
│ ├── issue
│ ├── issue.net
│ ├── nsswitch.conf
│ ├── os-release
│ ├── passwd
│ ├── profile.d
│ ├── protocols
│ ├── rpc
│ ├── services
│ ├── skel
│ ├── ssl
│ │ └── certs
│ │ └── ca-certificates.crt
│ └── update-motd.d
│ └── 10-uname
├── home
│ └── nonroot
├── lib
├── proc
├── root
├── run
├── sbin
├── sys
├── tmp
├── usr
│ ├── bin
│ ├── games
│ ├── include
│ ├── lib
│ │ └── os-release
│ ├── sbin
│ │ └── tzconfig
│ ├── share
│ │ ├── base-files
│ │ │ ├── dot.bashrc
│ │ │ ├── dot.profile
│ │ │ ├── dot.profile.md5sums
│ │ │ ├── info.dir
│ │ │ ├── motd
│ │ │ ├── profile
│ │ │ ├── profile.md5sums
│ │ │ └── staff-group-for-usr-local
...
...
该镜像只有一层,大小为 3MB,也没有二进制文件,只有一些证书文件和目录。如果向下滚动,还能看到许可证和时区配置。看来 Distroless 采取的是非常极端的手段,直接把不需要的二进制文件全部抛弃了,只留下一个空镜像和部分必需品。
总结
由此看来,这几个基础镜像如此受欢迎的主要原因就是体积小。镜像越小,漏洞就越少,可攻击面也会大幅减少,而且很容易维护。所以大家构建镜像时尽量选择这些镜像作为基础镜像。
引用链接
Google: https://github.com/GoogleContainerTools/distroless
你可能还喜欢
点击下方图片即可阅读
云原生是一种信仰
关注公众号
后台回复◉k8s◉获取史上最方便快捷的 Kubernetes 高可用部署工具,只需一条命令,连 ssh 都不需要!

点击 "阅读原文" 获取更好的阅读体验!
发现朋友圈变“安静”了吗?
本文分享自微信公众号 - 云原生实验室(cloud_native_yang)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
Beego中的权限控制——让你的Web应用更加安全
beego是一个基于go语言的web框架,它提供了一些方便快捷的工具来开发高效、安全的web应用。在开发web应用时,安全是非常重要的一个方面。这篇文章将介绍如何使用beego实现权限控制,来保护你的web应用,让它更加安全。
什么是权限控制?
权限控制是一种在Web应用中对用户进行身份认证和授权的技术。它可以限制用户访问某些敏感页面或执行某些敏感操作,如添加、修改或删除数据。权限控制是保护Web应用安全的一种方式,它可以防止未经授权的用户做出不良的行动。对于一些特别敏感的操作,需要特定用户角色的授权才能进行操作,如系统管理员或高级用户。权限控制技术可以使你在Web应用中实现这种要求。
Beego中的权限控制模块
在Beego中,你可以使用beego.Acl模块来实现权限控制。这个模块提供了一个多层次的权限控制系统,它允许你对不同的用户角色进行授权,从而控制用户能够访问的不同级别的页面和资源。它有以下特点:
- 多层次权限控制:支持用户角色的多级别授权,如普通用户和管理员。
- 简单易用:只需要在你的应用中定义好用户角色和对应的权限就可以使用。
- 同时适用于代码和模板:可以通过代码和模板来控制用户权限。
- 可扩展性:可以自由定义新的用户角色和权限。
Beego中的权限控制实现
让我们通过一个简单的示例来演示如何使用Beego实现权限控制。假设我们有一个用户信息管理系统,其中有两个角色:管理员和普通用户。管理员可以添加、修改和删除用户信息,而普通用户只能查看信息。
首先,我们需要在应用的初始化代码中定义用户角色、权限和授权。通过Beego的Init函数来定义。代码如下:
func init() { //admin role beego.Acl.AddRole("admin") //normal role beego.Acl.AddRole("normal") //user info resource beego.Acl.AddResource("/admin/user", "GET", "POST", "DELETE") //set role auth beego.Acl.AddRoleForUser("admin", "admin") beego.Acl.AddRoleForUser("normal", "normal") //set auth for role and resource beego.Acl.Allow("admin", "/admin/user", "*") beego.Acl.Deny("normal", "/admin/user", "POST", "DELETE") }
在这段代码中,我们定义了两个用户角色:admin和normal。我们还定义了一个资源,即用户信息(/admin/user),并限制了它的访问方式:GET、POST和DELETE。接着,我们分别为admin和normal设置了对应的角色,然后进行授权。我们允许admin角色拥有对用户信息资源的全部权限,但禁止normal角色对于资源的POST和DELETE请求。在这里,我们使用了*符号来表示拥有全部权限。
接下来,在我们的控制器中使用Beego的ac接口来控制用户权限。代码如下:
func (c *UserController) List() { if beego.Acl.HasRole(c.GetSession("username").(string), "admin") { // get userlist } else { c.Data["error"] = "permission denied" c.TplName = "error.html" } } func (c *UserController) Add() { if beego.Acl.HasPermission(c.GetSession("username").(string), "/admin/user", "POST") { // add user } else { c.Data["error"] = "permission denied" c.TplName = "error.html" } } func (c *UserController) Delete() { if beego.Acl.HasPermission(c.GetSession("username").(string), "/admin/user", "DELETE") { // delete user } else { c.Data["error"] = "permission denied" c.TplName = "error.html" } }
实际上,Controller实现了beego.ACLer接口,所以就可以直接使用beego.Acl来进行权限控制。在这个示例中,我们检查当前用户是否有相应的权限。如果当前用户拥有管理员角色,则允许它们访问/api/user/,否则返回一个错误信息。
最后,我们需要在相应的模板(如user.tpl)中进行权限判断的渲染。代码如下:
{{if beego.Acl.HasPermission .username "/admin/user" "POST"}} <a href="#">Add User</a> {{end}} {{if beego.Acl.HasPermission .username "/admin/user" "DELETE"}} <a href="#">Delete User</a> {{end}}
在这个示例中,我们使用beego.Acl.HasPermission函数来检查当前用户是否有POST或DELETE操作的访问权限。如果有,则渲染相应的操作按钮。注意,在模板中使用ac函数需要在控制器中传递当前用户的用户名(username)。
总结
在这个示例中,我们演示了如何使用Beego实现权限控制,从而保护我们的Web应用,使其更加安全。Beego提供了一个非常简单易用的API,让你可以很轻松地定义用户角色、权限和授权,并在你的控制器和模板中使用它们。当然,这只是一个简单的例子,你可以依据你的实际需求来使用它。
以上就是Beego中的权限控制——让你的Web应用更加安全的详细内容,更多请关注php中文网其它相关文章!
Distroless加固容器安全
谷歌现在通过提供 distroless 镜像向全世界开放这种能力。谷歌构建的这些镜像的目标是只包含你的应用程序及其依赖项,同时它们将没有常规 Linux 发行版的所有特性,包括 shell。 |
使用distroless镜像来保护Kubernetes上的容器。容器改变了我们看待技术基础设施的方式。这是我们运行应用程序方式的一次巨大飞跃。容器编排和云服务一起为我们提供了一种近乎无限规模的无缝扩展能力。根据定义,容器应该包含「应用程序」及其「运行时依赖项」。然而,在现实中,它们包含的远不止这些。标准容器基础映像包含标准Linux发行版中可以找到的包管理器、shell和其他程序。虽然这些都是构建容器镜像所必需的,但它们不应该成为最终镜像的一部分。例如,一旦你把包安装好了,就不再需要在容器中使用apt等包管理工具了。这不仅使你的容器里充满了不必要的软件包和程序,而且还为网络罪犯提供了攻击特定程序漏洞的机会。你应该始终了解容器运行时中存在什么,并且应该精确地限制其只包含应用程序所需的依赖项。除了那些必要的,你不应该安装任何东西。一些领先的科技巨头,如谷歌,有多年在生产中运行容器的经验,已经采用了这种方法。
谷歌现在通过提供distroless镜像向全世界开放这种能力。谷歌构建的这些镜像的目标是只包含你的应用程序及其依赖项,同时它们将没有常规Linux发行版的所有特性,包括shell。「这意味着虽然可以像以前一样运行应用程序的容器,但不能在容器运行的时候进入容器内」。这是一个重大的安全改进,因为你现在已经为黑客通过shell进入你的容器关上了大门。
distroless 基础镜像谷歌为大多数流行的编程语言和平台提供了 distroless 的基础镜像。
以下基础镜像是正式发布的版本:
- gcr.io/distroless/static-debian10
- gcr.io/distroless/base-debian10
- gcr.io/distroless/java-debian10
- gcr.io/distroless/cc-debian10
- gcr.io/distroless/nodejs-debian10
下面的基础镜像仍在实验阶段,不推荐用于生产环境:
- gcr.io/distroless/python2.7-debian10
- gcr.io/distroless/python3-debian10
- gcr.io/distroless/java/jetty-debian10
- gcr.io/distroless/dotnet
谷歌在内部使用 Bazel 来构建容器映像,但是我们可以使用 Docker 来做同样的事情。关于使用 distroless 镜像的一个有争议的问题是:当我们有一个 distroless 镜像时,我们如何使用 Dockerfile 来构建我们的应用程序呢?
通常,Dockerfile 以一个标准的 OS 基础镜像开始,然后是创建适当的运行时构建所需执行的多个步骤。这包括包的安装,为此需要像 apt 或 yum 这样的包管理器。
有两种方法:
- 先在 Docker 外部构建好你的应用程序,然后使用 Dockerfile 中的 ADD 或 copY 指令将二进制包复制到容器中。
- 使用多阶段 Docker 构建。这是 Docker 17.05 及以后版本的一个新特性,它允许你将构建分为不同的阶段。第一阶段可以从标准的 OS 基础镜像开始,可以帮助你构建应用程序;第二阶段可以简单地从第一阶段获取构建的文件并使用 distroless 作为基础镜像。
为了理解它是如何工作的,让我们使用多阶段构建流程进行一个实际操作练习。
必要条件你需要具备以下内容:
- Docker 版本大于等于 17.05,用于构建镜像
- 可选的Kubernetes集群用于实践练习的第二部分。如果你想在 Docker 中运行你的容器,你可以使用等价的docker
命令
- 。
作为实践练习,将 此代码仓 Fork 到你的 GitHub 帐号下,然后克隆 GitHub 代码仓并使用 cd 进入到项目目录下。
该代码仓包含一个 Python 的 Flask 应用程序,当你调用API时,该应用程序会响应 Hello World!。
app.py 文件如下所示:
from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello World!" if __name__ == '__main__': app.run(host='0.0.0.0', debug=True)
Dockerfile 包含两个阶段:
FROM python:2.7-slim AS build ADD . /app workdir /app RUN pip install --upgrade pip RUN pip install -r ./requirements.txt FROM gcr.io/distroless/python2.7 copY --from=build /app /app copY --from=build /usr/local/lib/python2.7/site-packages /usr/local/lib/python2.7/site-packages workdir /app ENV PYTHONPATH=/usr/local/lib/python2.7/site-packages EXPOSE 5000 CMD ["app.py"]
构建阶段:
- 从python:2.7-slim的基础镜像开始
- 将应用程序复制到/app目录下
- 升级pip并安装依赖
distroless 阶段:
- 从 gcr.io/distroless/python2.7 的基础镜像开始
- 将应用程序从构建阶段的 /app 目录复制到当前阶段的 /app 目录
- 将 python 的 site-packages 从构建阶段复制到当前阶段的 site-packages 目录
- 设置工作目录到 /app,将 python PATH 设置为 site-packages 目录,并暴露 5000 端口
- 使用 CMD 指令运行 app.py
由于 disroless 镜像不包含 shell,所以应该在最后使用 CMD 指令。如果不这样做,Docker 将认为它是一个 shell CMD,并试图这样执行它,但这是不工作的。
构建镜像:
$ docker build -t /flask-hello-world-distroless . Sending build context to Docker daemon 95.74kB Step 1/12 : FROM python:2.7-slim AS build ---> eeb27ee6b893 Step 2/12 : ADD . /app ---> a01dc81df193 Step 3/12 : workdir /app ---> Running in 48ccf6b990e4 Removing intermediate container 48ccf6b990e4 ---> 2e5e335be678 Step 4/12 : RUN pip install --upgrade pip ---> Running in 583be3d0b8cc Collecting pip Downloading pip-20.1.1-py2.py3-none-any.whl (1.5 MB) Installing collected packages: pip Attempting uninstall: pip Found existing installation: pip 20.0.2 Uninstalling pip-20.0.2: Successfully uninstalled pip-20.0.2 Successfully installed pip-20.1.1 Removing intermediate container 583be3d0b8cc ................................... Successfully installed Jinja2-2.11.2 MarkupSafe-0.23 click-7.1.2 flask-1.1.2 itsdangerous-0.24 werkzeug-1.0.1 Removing intermediate container c4d00b1abf4a ---> 01cbadcc531f Step 6/12 : FROM gcr.io/distroless/python2.7 ---> 796952c43cc4 Step 7/12 : copY --from=build /app /app ---> 92657682cdcc Step 8/12 : copY --from=build /usr/local/lib/python2.7/site-packages /usr/local/lib/python2.7/site-packages ---> faafd06edeac Step 9/12 : workdir /app ---> Running in 0cf545aa0e62 Removing intermediate container 0cf545aa0e62 ---> 4c4af4333209 Step 10/12 : ENV PYTHONPATH=/usr/local/lib/python2.7/site-packages ---> Running in 681ae3cd51cc Removing intermediate container 681ae3cd51cc ---> 564f48eff90a Step 11/12 : EXPOSE 5000 ---> Running in 7ff5c073d568 Removing intermediate container 7ff5c073d568 ---> ccc3d211d295 Step 12/12 : CMD ["app.py"] ---> Running in 2b2c2f111423 Removing intermediate container 2b2c2f111423 ---> 76d13d2f61cd Successfully built 76d13d2f61cd Successfully tagged /flask-hello-world-distroless:latest
登录到 dockerhub 并推送镜像:
docker login docker push /flask-hello-world-distroless:latest
登录到 dockerhub(或者你的私有镜像仓),你应该会看到容器镜像可以使用:
如果你看一下压缩后的大小,它只有 23.36 MB。如果你使用 slim 发行版作为基础镜像,它将占用 56 MB。
你已经减少了超过一半的容器占用空间。That’s amazing!
在 Kubernetes 中运行容器为了测试构建是否有效,让我们在 Kubernetes 集群中运行容器。如果你没有 Kubernetes,你可以运行等价的 Docker 命令来做相同的活动,因为 Kubectl 和 Docker 命令是相似的。
我在代码仓中创建了一个 kubernetes.yaml 文件,该文件包含使用我们构建的镜像的 Deployment 和 负载均衡的 Service。
--- apiVersion: apps/v1 kind: Deployment Metadata: name: flask-deployment spec: selector: matchLabels: app: flask replicas: 2 template: Metadata: labels: app: flask spec: containers: - name: flask image: bharamicrosystems/flask-hello-world-distroless ports: - containerPort: 5000 --- apiVersion: v1 kind: Service Metadata: name: flask-service spec: selector: app: flask ports: - port: 80 targetPort: 5000 type: LoadBalancer
这是一个非常简单的设置。负载均衡器监听端口 80 并映射到目标端口 5000。这些 Pods 在默认的 5000 端口上监听 Flask 应用程序。
应用:
$ kubectl apply -f kubernetes.yaml deployment.apps/flask-deployment created service/flask-service created
我们查看一下所有的资源,看看我们已经创建了什么:
$ kubectl get all NAME READY STATUS RESTARTS AGE pod/flask-deployment-576496558b-hnbxt 1/1 Running 0 47s pod/flask-deployment-576496558b-hszpq 1/1 Running 0 73s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/flask-service LoadBalancer 10.8.9.163 35.184.113.120 80:31357/TCP 86s service/kubernetes ClusterIP 10.8.0.1 443/TCP 26m NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/flask-deployment 2/2 2 2 88s NAME DESIRED CURRENT READY AGE replicaset.apps/flask-deployment-576496558b 2 2 2 89s
我们看到存在两个 Pods、一个 Deployment、一个带有外部 IP 的 LoadBalancer 服务和一个 replicaset。
让我们访问应用程序:
$ curl http://35.184.113.120 Hello World!
我们得到了 Hello World!。这表明 Flask 应用程序在正常工作。
使用 Shell 对应用程序进行访问正如我在引言中所描述的,disroless 容器中没有 shell,因此不可能进入到容器内。然而,让我们试着在容器中执行 exec:
$ kubectl exec -it flask-deployment-576496558b-hnbxt /bin/bash OCI runtime exec Failed: exec Failed: container_linux.go:349: starting container process caused "exec: \"/bin/bash\": stat /bin/bash: no such file or directory": unkNown command terminated with exit code 126
我们无法连接到容器上。
容器日志呢?如果拿不到容器日志,我们就失去了调试应用程序的方法。
让我们试着去拿日志:
$ kubectl logs flask-deployment-576496558b-hnbxt * Running on http://0.0.0.0:5000/ * Restarting with reloader 10.128.0.4 - - [31/May/2020 13:40:27] "GET / HTTP/1.1" 200 - 10.128.0.3 - - [31/May/2020 13:42:01] "GET / HTTP/1.1" 200 -
所以容器日志是可以被获取到的!
结论使用 distroless 作为基础镜像是一种令人兴奋的保护容器安全的方式。由于镜像小并且仅包含应用程序和依赖项,因此它为应用程序提供了最小的攻击面。它在更大程度上提高了应用程序的安全性,所以它是保护容器安全的好方法。
本文地址:https://www.linuxprobe.com/distroless-container-security.html
今天关于如何使用 Distroless 让你的容器更加安全的介绍到此结束,谢谢您的阅读,有关10 个技巧让你的 RESTful Web 服务更加实用、Alpine?Distroless?Busybox?到底谁才是容器镜像的瑞士军刀?、Beego中的权限控制——让你的Web应用更加安全、Distroless加固容器安全等更多相关知识的信息可以在本站进行查询。
本文标签: