GVKun编程网logo

在 10 分钟内实现安全的 React + Docker(react docker部署)

25

对于想了解在10分钟内实现安全的React+Docker的读者,本文将提供新的信息,我们将详细介绍reactdocker部署,并且为您提供关于04.Docker安全与Docker底层实现、167doc

对于想了解在 10 分钟内实现安全的 React + Docker的读者,本文将提供新的信息,我们将详细介绍react docker部署,并且为您提供关于04 . Docker安全与Docker底层实现、167 docker docker构建nginx容器系列问题 docker registry docker run docker toolbo、com.facebook.react.views.picker.ReactDropdownPickerManager的实例源码、Docker in Docker(实际上是 Docker outside Docker): /var/run/docker.sock的有价值信息。

本文目录一览:

在 10 分钟内实现安全的 React + Docker(react docker部署)

在 10 分钟内实现安全的 React + Docker(react docker部署)

每日前端夜话第336篇

翻译:疯狂的技术宅

作者:Matt Raible

来源:scotch

正文共:8941 字

预计阅读时间:15 分钟

假如你已经构建了一个 React 应用,但是现在需要部署它。应该怎么做?首先,最好选择一个云提供商,因为它们一般成本低而且部署容易。

大多数云提供商都提供了一种部署静态站点的方法。用 React 构建应用只是 JavaScript、HTML 和 CSS。它们是静态文件,几乎可以在任何 Web 服务器上使用。但实际上,如果你使用了 JSX(JS 中的 HTML)和样式化组件,那么这些可以说只有 JavaScript

Docker 是用于构建和共享容器化应用的事实标准。你可以使用它打包你的应用程序,并包含多种开源 Web 服务器来为你的应用程序提供服务。另外,你还可以通过配置网络服务器来发送安全标头,这样使你的程序更安全。

前提条件:

  • Node 12 https://nodejs.org/)

  • Docker https://docs.docker.com/install/)

  • Okta 开发者帐户https://developer.okta.com/signup/)

创建 React 应用

为了集中精力,我用了一位同事已经构建的程序。首先克隆存储库。

1git clone https://github.com/oktadeveloper/okta-react-styled-components-example.git react-docker
2cd react-docker
3npm install

这是一个使用样式化组件的 React 应用,并由 OpenID Connect(aka OIDC)保护。你可以在使用样式化组件构建 React 应用(https://developer.okta.com/blog/2020/03/16/react-styled-components) 一文中了解其创建方式。

登录你的 Okta 开发者帐户(你已经创建了一个(https://developer.okta.com/signup/),对吗?)注册此应用并启用 OIDC 身份验证。

  1. 转到顶部菜单中的 Applications

  2. 选择  Add Application > Single-Page App ,然后单击 Next

  3. 在设置屏幕上,为你的应用命名,例如 React Docker

  4. 确保端口设置为 3000,并且 Login redirect URI 为  http://localhost:3000/callback

  5. 点击 Done

出现的界面将为你提供一个客户端 ID。

将客户端 ID 复制并粘贴到应用程序的 src/App.js 中。<yourIssuerURI> 的值可以在 Okta 仪表板的 API > Authorization Servers 下找到。例如我的是 https://dev-133320.okta.com/oauth2/default

 1function App({
2  return (
3    <Router>
4      <Security issuer=''<yourIssuerURI>''
5                clientId=''<yourClientId>''
6                redirectUri={window.location.origin + ''/callback''}
7                pkce={true}>
8        <SecureRoute path=''/'' exact={true} component={Calendar}/>
9        <Route path=''/callback'' component={LoginCallback}/>
10      </Security>
11    </Router>
12  );
13}

<> 括号只是占位符,请确保将其删除!

npm start 启动你的应用。你将被重定向到 Okta 进行身份验证,然后返你的应用。如果你没有重定向,那是因为你已经登录。请在 private 窗口中重试来查看登录过程。

你会看到一个简单、干净的日历,并选择了今天的日期。

我承认这是一个非常简单的应用,但我们会用它来演示如何用 Docker 进行容器化。

为什么要使用Docker?

你可能会问:“为什么要用 Docker?这不会使事情复杂化吗?”

是的我同意。用 Docker 进行操作比用 Heroku 进行 firebase deploygit push 处理更为复杂。但是如果你真的要使事情复杂化,并用 Kubernetes 去管理你的应用,那么它可以给你更多的控制权。

创建Dockerfile和Nginx配置

在你的根目录中创建一个 Dockerfile

 1FROM node:14.1-alpine AS builder
2
3WORKDIR /opt/web
4COPY package.json package-lock.json ./
5RUN npm install
6
7ENV PATH="./node_modules/.bin:$PATH"
8
9COPY . ./
10RUN npm run build
11
12FROM nginx:1.17-alpine
13RUN apk --no-cache add curl
14RUN curl -L https://github.com/a8m/envsubst/releases/download/v1.1.0/envsubst-`uname -s`-`uname -m` -o envsubst && \
15    chmod +x envsubst && \
16    mv envsubst /usr/local/bin
17COPY ./nginx.config /etc/nginx/nginx.template
18CMD ["/bin/sh""-c""envsubst < /etc/nginx/nginx.template > /etc/nginx/conf.d/default.conf && nginx -g ''daemon off;''"]
19COPY --from=builder /opt/web/build /usr/share/nginx/html

这将会构建你的项目并把 Nginx 添加为 Web服务器。它还将安装 envsubst 版本,该版本允许你用环境变量去替换变量,并设置默认值。

在同一目录中创建一个 nginx.config

 1server {
2    listen       ${PORT:-80};
3    server_name  _;
4
5    root /usr/share/nginx/html;
6    index index.html;
7
8    location / {
9        try_files $$uri /index.html;
10    }
11}

这个文件把 Nginx 配置为将你的 React 应用作为 SPA(其中所有路由都转到 index.html)并在 80 端口上运行。在 uri 前面有两个 $$,以防止 $uri 被替换为空白值。

用 React 应用构建 Docker 镜像

先执行  docker ps  确保你的 Docker 守护进程正在运行。然后运行以下命令来构建你的 Docker 镜像。命令中的 react-docker 可以是你想要为镜像命名的任何名字。

1docker build -t react-docker .

该过程完成后,你将会看到以下消息的内容:

1Successfully built 3211a1255527
2Successfully tagged react-docker:latest

运行你的 Docker + React 应用

现在,你可以用 docker run 命令通过 Docker 在端口 3000 上运行 React 应用。

1docker run -p 3000:80 react-docker

如果你发现这些 docker 命令很难记住,也可以在 package.json文件中添加几个脚本 。

1"docker""docker build -t react-docker .",
2"react-docker""docker run -p 3000:80 react-docker"

然后就可以用 npm run dockernpm run react-docker 运行了。

很漂亮吧?在短短几分钟内就把你的 React 应用做了 docker 化。

把将你的 React App 部署到 Heroku

你的应用要直到正式投入生产时才会真正的存在,所以让我们把它部署到 Heroku。首先我将向你展示怎样不用 Docker 做到这一点。

首先,你需要 一个 Heroku 帐户(https://signup.heroku.com/login)。然后,安装 Heroku CLI (https://devcenter.heroku.com/articles/heroku-cli)

打开终端,登录你的 Heroku 帐户,然后创建一个新应用。

1heroku login
2heroku create

现在,你应该有了一个新的 heroku Git 远程存储库。可以用 git remote -v 来确认。

在带有安全标头的根目录中创建一个 static.json 文件,并把所有 HTTP 请求重定向到 HTTPS。

 1{
2  "headers": {
3    "/**": {
4      "Content-Security-Policy""default-src ''self''; script-src ''self'' ''unsafe-inline''; style-src ''self'' ''unsafe-inline''; img-src ''self'' data:; font-src ''self'' data:; connect-src ''self'' https://*.okta.com;",
5      "Referrer-Policy""no-referrer, strict-origin-when-cross-origin",
6      "Strict-Transport-Security""max-age=63072000; includeSubDomains",
7      "X-Content-Type-Options""nosniff",
8      "X-Frame-Options""DENY",
9      "X-XSS-Protection""1; mode=block",
10      "Feature-Policy""accelerometer ''none''; camera ''none''; microphone ''none''"
11    }
12  },
13  "https_only"true,
14  "root""build/",
15  "routes": {
16    "/**""index.html"
17  }
18}

要读取 “static.json”,你必须用 Heroku static buildpack (https://github.com/heroku/heroku-buildpack-static)

把你的更改提交到 Git,添加 Node.js + static buildpack,然后部署 React 应用。

1git commit -am "Configure secure headers and static buildpacks"
2heroku buildpacks:set heroku/nodejs
3heroku buildpacks:add https://github.com/heroku/heroku-buildpack-static.git
4git push heroku master

该过程完成后,使用以下方法在浏览器中打开你的应用程序:

1heroku open

你将会被重定向到 Okta,可能会看到以下错误:

1The ''redirect_uri'' parameter must be an absolute URI that is whitelisted in the client app settings.

要解决这个问题,需要修改 Okta 应用,以将你的 Heroku URL 添加为“登录重定向 URI”。例如https://gentle-peak-37809.herokuapp.com/callback

现在,你应该可以登录并看到你的应用在 Heroku 上运行了!你可以在 https://securityheaders.com 上验证其安全标头是否正确。

在这个部署示例中,buildpacks 为你完成了所有工作。但是并非每个云提供商都提供 buildpack。这就是需要 Docker 的地方。

把 Docker + React App 部署到 Heroku

当涉及到 Docker 镜像时,Heroku 具有一些出色的功能。如果你的项目有一个 Dockerfile,则可以用 Heroku Container Registry (https://devcenter.heroku.com/articles/container-registry-and-runtime)直接部署你的应用。

首先,登录到Container Registry。

1heroku container:login

然后,创建一个新的应用。

1heroku create

把 Git URL 作为新的 remote 添加到你的应用。

1git remote add docker https://git.heroku.com/<your-app-name>.git

然后,把将你的 Docker 镜像 push 到 Heroku 的 Container Registry。

1heroku container:push web --remote docker

该过程完成后,release 你的应用程序镜像:

1heroku container:release web --remote docker

然后,在浏览器中打开该应用:

1heroku open --remote docker

你需要先在 Okta 中添加应用的 URI,然后才能登录。

改善 Docker 中 Nginx 的安全标头

如果在 securityheaders.com 上的 Docker 站点中测试新的 Nginx,你的得分应该是 F

为了解决这个问题,修改你的 nginx.config 添加安全头。

 1server {
2    listen       ${PORT:-80};
3    server_name  _;
4
5    root /usr/share/nginx/html;
6    index index.html;
7
8    location / {
9        try_files $$uri /index.html;
10    }
11
12    add_header Content-Security-Policy "default-src ''self''; script-src ''self'' ''unsafe-inline''; style-src ''self'' ''unsafe-inline''; img-src ''self'' data:; font-src ''self'' data:; connect-src ''self'' https://*.okta.com;";
13    add_header Referrer-Policy "no-referrer, strict-origin-when-cross-origin";
14    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
15    add_header X-Content-Type-Options nosniff;
16    add_header X-Frame-Options DENY;
17    add_header X-XSS-Protection "1; mode=block";
18    add_header Feature-Policy "accelerometer ''none''; camera ''none''; microphone ''none''";
19}

更新文件后,运行以下命令:

1heroku container:push web --remote docker
2heroku container:release web --remote docker

现在你应该得到 A

用 Cloud Native Buildpacks 创建你的 React + Docker 镜像

在本文中,我们学习了把 React 应用部署到 Heroku 的两种方法。首先是利用 buildpack 和 git push。第二个是使用 Heroku 的 Container Registry 和 heroku container:push + heroku push:release

Cloud Native Buildpacks (https://buildpacks.io/) 是 Pivotal 和 Heroku 在 2018 年初发起的一项举措。它具有 pack CLI (https://github.com/buildpacks/pack),可让你用 buildpacks 构建 Docker 映像。

我的好朋友 Joe Kutner是 Heroku 的一名软件架构师,在实现 Cloud Native Buildpacks 中发挥了重要的作用。Joe 是 JHipster 项目的积极提交者,其作者 The Healthy Programmer 是 Cloud Native Buildpacks 核心团队的创始成员 。他对 Docker 的建议是:“如果不需要,请不要使用 Dockerfile”。

Joe 对我在弄清楚如何使用 buildpacks 创建 Docker 映像的技术上提供了很大的帮助,所以下面的说明应该归功于他。

首先,请 install pack (https://buildpacks.io/docs/install-pack/)。如果你使用的是 Mac 或 Linux,可以使用 Homebrew。如果用的是 Windows,可以[安装其可执行文件 (https://github.com/buildpacks/pack/releases/download/v0.10.0/pack-v0.10.0-windows.zip)

1brew tap buildpack/tap
2brew install pack

在前面的 buildpacks 示例中,我用了 Heroku 的 Node.js 和静态 buildpacks。

Heroku 静态构建包不是 “Cloud Native” 构建包。它使用旧的(原生云)API。这意味着它与开箱即用的 pack 不兼容。

幸运的是,Heroku 确实提供了 cnb-shim (https://github.com/heroku/cnb-shim),你可以用它来使其工作。在用 cnb-shim 转换后,Joe 为 Heroku 的静态 buildpack 创建了一个 URL (https://cnb-shim.herokuapp.com/v1/heroku-community/static) 。

在本地构建和运行 Docker 镜像之前,必须先进行一项更改。从 static.json 中删除 "https_only":true 这一行。

然后用以下命令通过 Node.js 和静态 buildpack(也就是你在 Heroku 上使用的相同 buildpack)构建 Docker 镜像。

1pack build react-pack --builder heroku/buildpacks --buildpack \
2  heroku/nodejs,https://cnb-shim.herokuapp.com/v1/heroku-community/static

提示:如果你想摆脱 --builder 参数,可以用  pack set-default-builder heroku/buildpacks

该过程完成后,你应该可以运行它。

1docker run --rm -it --init -p 3000:3000 --env PORT=3000 okta

如果你发现这些 pack 命令很难被记住,那么可以把它们添加到 package.json 中。

1"pack""pack build react-pack --builder heroku/buildpacks --buildpack heroku/nodejs,https://cnb-shim.herokuapp.com/v1/heroku-community/static",
2"react-pack""docker run --rm -it --init -p 3000:3000 --env PORT=3000 react-pack"

然后可以使用 npm run packnpm run react-pack 来运行它们。

把将你的 React + Docker 镜像部署到 Docker Hub

通过把它们部署到 Docker Hub 等注册表中,可以轻松共享 Docker 容器。如果你还没有 Docker Hub 帐户,那就先创建一个 (https://hub.docker.com/signup)

拥有帐户之后,登录并 push 你的镜像。在下面的示例中,我正在使用 react-docker,但你也可以使用 react-pack 来部署 buildpacks 版本。

1docker login
2docker image tag react-docker <your-username>/react-docker
3docker push <your-username>/react-docker

默认情况下,这会将其标记为 latest。如果要标记和推送特定版本,可以用:

1docker image tag react-docker <your-username>/react-docker:1.0
2docker push <your-username>/react-docker

然后其他人就可以用以下命令 pull 并运行:

1docker run -p 3000:80 <your-username>/react-docker

把 React + Docker 镜像部署到 Heroku

要把现有映像部署到 Heroku,可以用 docker push。你必须用以下命名约定来标记和推送镜像。

1docker tag <image> registry.heroku.com/<app>/<process-type>
2docker push registry.heroku.com/<app>/<process-type>

要部署 react-pack 镜像,你可以执行以下操作:

1docker tag react-pack registry.heroku.com/fierce-eyrie-08414/web
2docker push registry.heroku.com/fierce-eyrie-08414/web
3heroku container:release web --remote docker

我尝试了一下,发现没有强制使用 HTTPS。必须将 "https_only":true 添加到  static.json 中,然后重新push。

了解有关 React 和 Docker 的更多信息

在本教程中,我们学习了如何用 Docker 容器化你的 React 应用。你可以用 docker build 手动进行这项操作,也可以用 Heroku 的 Container Registry 通过 Dockerfile 推送和发布项目。在构建容器时,还可以用 pack 命令来利用 Cloud-Native + Heroku 构建包。

如果你用的是 Heroku,它的 buildpack 比 Docker 更容易使用。通过简单的 git push,你可以在 Heroku 的服务器上部署代码并构建。

可以在 GitHub上 的 oktadeveloper/okta-react-docker-example (https://github.com/oktadeveloper/okta-react-docker-example) 上找到本示例的源代码。


原文链接



https://scotch.io/tutorials/react-docker-with-security-in-10-minutes


 

2020年

京程一灯课程体系上新,这是我们第一次将全部课程列表对外开放。

愿你有个好前程,愿你月薪30K。我们是认真的 !

点击文末 阅读全文 查看细节。


长按二维码,加大鹏老师微信好友

拉你加入前端技术交流群

唠一唠怎样才能拿高薪

往期精彩回顾

怎样为你的 Vue.js 单页应用提速

10 个实用的 JavaScript 小技巧

它改变了 JavaScript 的体系结构——Webpack 5 Module Federation

与 JavaScript 模块相关的所有知识点

我们是怎样优化 V8 中的指针压缩的

当一个模块被导入两次时,会发生什么?

ReactJS 与 VueJS:两种流行前端 JS 框架之战

十分钟搞定 TypeScript + webpack 配置

18 个漂亮的 Bootstrap 模板

前端程序员要懂的 UI 设计知识


本文分享自微信公众号 - 前端先锋(jingchengyideng)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

04 . Docker安全与Docker底层实现

04 . Docker安全与Docker底层实现

Docker安全

Docker安全性时,主要考虑三个方面

# 1. 由内核的名字空间和控制组机制提供的容器内在安全
# 2. Docker程序(特别是服务端)本身的抗攻击性
# 3. 内核安全性的加强机制对容器安全性的影响
内核命名空间

Docker容器和LXC容器很相似,所提供的安全特性也差不多。当用docker run启动一个容器时,在后台Docker为容器创建了一个独立的命名空间和控制组集合。

命名空间提供了最基础也是最直接的隔离,在容器中运行的进程不会被运行在主机上的进程和其它容器发 现和作用。

每个容器都有自己独有的网络栈,意味着它们不能访问其他容器的sockets或接口。不过,如果主机系统上做了相应的设置,容器可以像跟主机交互一样的和其他容器交互。当指定公共端口或使用links来连接2个容器时,容器就可以相互通信了(可以根据配置来限制通信的策略).

从网络架构的角度来看,所有的容器通过本地主机的网桥接口相互通信,就像物理机器通过物理交换机通信一样。

那么,内核中实现命名空间和私有网络的代码是否足够成熟?

内核名字空间从2.6.15版本(2008年7月发布)之后被引入,数年间,这些机制的可靠性在诸多大型生产系统中被实践验证。

实际上,名字空间的想法和设计提出的时间要更早,最初是为了在内核中引入一种机制来实现 OpenVZ 的特性。而OpenVZ项目早在2005年就发布了,其设计和实现都已经十分成熟。

控制组

控制组是Linux容器机制的另外一个关键组件,负责实现资源的审计和限制.

它提供了很多有用的特性;以及确保各个容器可以公平地分享主机的内存、cpu、磁盘IO等资源;当然,更重要的是,控制组确保了当容器内的资源使用产生压力时不会连累主机系统。

尽管控制组不负责隔离容器之间相互访问、处理数据和进程,它在防止拒绝服务(DDOS)攻击方面是必不可少的。尤其是在多用户的平台(比如公有或私有的PaaS)上,控制组十分重要。例如,当某些应用程序表现异常的时候,可以保证一致地正常运行和性能。

控制组始于2006年,内核从2.6.24版本开始被引入.

Docker服务端的防护

运行一个容器或应用程序的核心是通过Docker服务端。Docker服务的运行目前需要root权限,因此其安全性十分关键。

首先,确保只有可信的用户才可以访问Docker服务。Docker允许用户在主机和容器间共享文件夹,同时不需要限制容器的访问权限,这就容易让容器突破资源限制。例如,恶意用户启动容器的时候将主机的根目录/映射到容器的 /host目录中,那么容器理论上就可以对主机的文件系统进行任意修改了。这听起来很疯狂?但是事实上几乎所有虚拟化系统都允许类似的资源共享,而没法禁止用户共享主机根文件系统到虚拟机系统

这将会造成很严重的安全后果。因此,当提供容器创建服务时(例如通过一个web服务器),要更加注意进行参数的安全检查,防止恶意的用户用特定参数来创建一些破坏性的容器

为了加强对服务端的保护,Docker的REST API(客户端用来跟服务端通信)在0.5.2之后使用本地的Unix套接字机制替代了原先绑定在 127.0.0.1 上的TCP套接字,因为后者容易遭受跨站脚本攻击。现在用户使用Unix权限检查来加强套接字的访问安全。

用户仍可以利用HTTP提供REST API访问。建议使用安全机制,确保只有可信的网络或VPN,或证书保 护机制(例如受保护的stunnel和ssl认证)下的访问可以进行。此外,还可以使用HTTPS和证书来加强 保护。

最近改进的Linux名字空间机制将可以实现使用非root用户来运行全功能的容器。这将从根本上解决了容器和主机之间共享文件系统而引起的安全问题。

终极目标是改进 2 个重要的安全特性:

  • 将容器的root用户映射到本地主机上的非root用户,减轻容器和主机之间因权限提升而引起的安全问题;
  • 允许Docker服务端在非root权限下运行,利用安全可靠的子进程来代理执行需要特权权限的操作。这些子进程将只允许在限定范围内进行操作,例如仅仅负责虚拟网络设定或文件系统管理、配置操作等。
  • 最后,建议采用专用的服务器来运行Docker 和相关的管理服务(例如管理服务比如ssh监控和进程监控、管理工具nrpe、collectd等)。其它的业务服务都放到容器中去运行。
内核能力机制

能力机制(Capability)是Linux内核一个强大的特性,可以提供细粒度的权限访问控制。 Linux内核自2.2版本起就支持能力机制,它将权限划分为更加细粒度的操作能力,既可以作用在进程上,也可以作用在文件上。

例如,一个Web服务进程只需要绑定一个低于1024的端口的权限,并不需要root权限。那么它只需要被授权 net_bind_service能力即可。此外,还有很多其他的类似能力来避免进程获取root权限。

默认情况下,Docker启动的容器被严格限制只允许使用内核的一部分能力.

使用能力机制对加强Docker容器的安全有很多好处。通常,在服务器上会运行一堆需要特权权限的进程,包括有ssh、cron、syslogd、硬件管理工具模块(例如负载模块)、网络配置工具等等。容器跟这些进程是不同的,因为几乎所有的特权进程都由容器以外的支持系统来进行管理。

# 1. ssh访问被主机上ssh服务来管理;
# 2. cron通常应该作为用户进程执行执行,权限交给使用他服务的应用来处理;
# 3. 日志系统可由Docker或第三方服务管理;
# 4. 硬件管理无关紧要,容器中也就无需执行udevd以及类似服务;
# 5. 网络管理也都在主机上设置,除非特殊需求,容器不需要对网络进行配置.

从上面的例子可以看出,大部分情况下,容器并不需要真正的root权限,容器只需要少数的能力即可.为了加强安全,容器可以禁用一些没必要的权限:

# 1. 完全禁止任何mount操作.
# 2. 禁止直接访问本地主机的套接字.
# 3. 禁止访问一些文件系统的操作,比如创建新的设备,修改文件属性等.
# 4. 禁止模块加载.

这样,就算攻击者在容器中取得了root权限,也不能获得本地主机的较高权限,能进行的破坏也有限.

默认认情况下,Docker采用 白名单 机制,禁用必需功能之外的其它权限。 当然,用户也可以根据自身需求来为Docker容器启用额外的权限。

其他安全特性.

除了能力机制之外,还可以利用一些现有的安全机制来增强docker的安全性,例如TOMOYO,AppArmor,SELinux,GRSEC等.

Docker 当前默认只开启了能力机制,用户可以采用多种方案来加强Docker主机的安全,例如:

  1. 在内核中启用GRSEC和pax,这将增加很多编译和运行时的安全检查,通过地址随机化避免恶意探测等,并且,启用该特性不需要Docker进行任何配置.

  2. 使用一些有增强安全特性的容器模板,比如带AppArmor的模板和Redhat带Selinux策略的模板.这些模板提供了额外的安全特性.

  3. 用户可以自定义访问控制机制来定制安全策略.

跟其他添加Docker容器的第三方工具一样(比如网络拓扑和文件系统共享),有很多类似的机制,在不改变Docker内核情况下就可以加固现有的容器.

小结

总体来说,Docker容器还是十分安全的,特别是在容器不使用root权限来运行进程的话.

另外,用户可以使用现有工具,比如Apparmor,GRSEC来增强安全性,甚至自己在内核中实现更复杂的安全机制.

Docker底层实现

Docker底层的核心技术包括Linux上的命名空间(Namespaces)、控制组(ControlGroups),Union文件系统(Union file systems)和容器格式(Container format).

我们知道,传统的虚拟机通过在宿主主机中运行hypervisor来模拟一套完整的硬件环境提供给虚拟机的操作系统.虚拟机系统看到的环境是可限制的,也是彼此隔离的,这种直接的做法实现了对资源完整的封装,但很多时候往往意味着系统资源的浪费,例如,以宿主机和虚拟机系统都为linux系统为例,虚拟机中运行的应用其实是可以利用宿主机系统中的运行环境。

我们知道,在操作系统中,包括内核、文件系统、网络、PID、UID、IPC、内存、硬盘、cpu等等,所有的资源都是应用进程直接共享的,要想实现虚拟化,除了要实现对内存、cpu、网络IO、硬盘IO、存储空间等的限制外,还要实现文件系统、网络、PID、UID、IPC等等的相互隔离,前者相对容易实现一些,后者则需要宿主机系统的深入支持.

随着Linux系统对于命名空间功能的完善实现,程序员可以实现上面的所有需要,让某些进程在彼此隔离的命名空间中运行,大家虽然都共用一个内核和某些运行时环境(l例如一些系统命令和系统等),但是彼此却看不到,大家都以为系统中只有自己的存在,这种机制就是容器,利用命名空间来做权限的隔离控制,利用cgroups来做资源分配.

容器的基本架构

Dcoker采用了c/s架构,包括客户端和服务端,Docker守护进程(Daemon)作为服务端接受来自客户端的请求,并处理这些请求(创建、运行、分发容器).

客户端和服务端既可以运行在一个机器上,也可以通过socket或者RESTful API来进行通信.

Docker守护进程一般在宿主主机后台运行,等待来自客户端的消息.

Docker客户端则为用户提供一系列可执行的命令,用户用这些命令实现跟Docker守护进程交互.

命名空间

命名空间是Linux内核一个强大的特性,每个容器都有自己单独的命名空间,运行在其中的应用都像是在独立的操作系统运行一样,命名空间保证了容器之间彼此互不影响.

pid命名空间

不同用户的进程就是通过pid命名空间隔离开的,且不同命名空间可以有相同的pid,所有的LXC进程在Docker中的父进程为Docker进程,每个LXC进程具有不同的命名空间,同时由于嵌套,因此可以很方便的实现嵌套的Docker容器.

net命名空间

有了pid命名空间,每个命名空间的pid能够实现相互隔离,但是网络端口还是共享host的端口,网络隔离是通过net命名空间实现的,每个net命名空间有单独的网络设备,IP地址,路由表,/proc/net目录,这样每个容器的网络就能隔离开来,Docker默认采用veth的方式,将容器中的虚拟网卡host上的一个Docker网桥docker0连接在一起.

IPC命名空间

容器中进程交互采用了Linux常见的进程交互方法,包括信号量,消息队列和共享内存等,然而同VM不同的是,容器的进程交互实际山还是host上具有相同pid命名空间的进程交互,因此需要在IPC资源中申请加入命名空间信息,每个IPC资源有一个唯一的32位id。

mnt命名空间

类似chroot,将一个进程放到一个特定的目录执行,mnt命名空间允许不同命名空间的进程看到的文件结构不同,这样每个命名空间中的进程所看到的文件目录就被隔离开了,同chroot不同,每个命名空间的容器在/proc/mounts的信息只包含所在命名空间的mount point。

uts命名空间

UTS命名空间允许每个容器拥有独立的hostname和domain name,使其在网络上可以被视作一个独立的节点而非主机上的一个进程.

每个容器可以有不同的用户和组id,也就是说可以在容器内用容器内部的用户执行程序而非主机上的用户.

控制组

控制组(cgroups)是一个Linux内核的一个特性,主要用来对资源进行隔离、限制、审计等,只有能控制分配到容器的资源,才能避免当多个容器同时运行时对系统资源的竞争.

控制组技术最早由Google的程序员在2006年提出,Linux内核从2.6.24开始支持.

控制组可以提供对容器的内存、cpu、磁盘IO等资源的限制和审计管理.

联合文件系统

联合文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,他支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统.

联合文件系统是Docker镜像的基础,镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像.

另外,不同Docker容器就可以共享一些基础的文件系统层,同时加上自己独有的改动层,大大提高了存储的效率.

Docker中使用的AUFS就是一种联合文件系统,AUFS支持为每一个成员目录(类似Git的分支)设定只读(readonly)、读写(readwrite)和写出(whiteout-able)权限,同时AUFS里有一个类似分层的概念,对只读权限的分支可以逻辑上进行增量的修改(不影响只读部分的).

Docker目前支持的联合文件系统包括OverlayFS,AUFS,BtrFS,VFS,ZFS和Device Mapper。

有可能的情况下,推荐使用overlay2存储驱动,overlay2是目前Docker默认的存储驱动,以前是aufs,可以通过配置以上提到的其他类型存储驱动.

容器格式

最初,Docker采用了LXC中的容器格式,从0.7版本开始以后去除LXC,转而使用自行开发的libcontainer,从1.11开始,进一步演进为runc和containerd。

网络实现

Docker的网络实现其实就是利用了Linux上的网络命名空间和虚拟网络设备(特别是vethpair).

基本原理

首先,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)来收发数据包,此外,如果不同子网之间要进行通信,需要路由机制.

Docker中的网络接口默认都是虚拟的接口,虚拟接口的优势之一就是转发效率较高,Linux通过在内核中进行数据复制来实现虚拟接口之间的数据转发,发送接口的发送缓存中的数据包直接复制到接收接口的接受缓存中,对于本地系统和容器内系统来看就像是一个正常的以太网卡,只是他不需要真正同外部网络设备通信,速度要快很多.

Docker容器网络就是利用了这项技术,他在本地主机和容器内分别创建一个虚拟接口,并让他们彼此连通(这样的一对接口叫做veth pair).

总结

以上是小编为你收集整理的04 . Docker安全与Docker底层实现全部内容。

如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。

167 docker docker构建nginx容器系列问题 docker registry docker run docker toolbo

167 docker docker构建nginx容器系列问题 docker registry docker run docker toolbo

docker构建nginx容器系列问题


background : 最近为小伙伴们筹划docker系列的技术分享,研究了一会docker相关技术, 在此记录一下构建nginx容器时候的坑

1.nginx服务器根目录问题

docker 官方镜像提供的nginx基于debian/jessie平台,其文件结构和ubuntu中的nginx中并不相同

eg:

run一个niginx容器

<span>//80端口被占用,so...</span>
$ sudo docker run <span>-it</span><span>-p</span><span>800</span>:<span>800</span> nginx
$ sudo docker ps 

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                   NAMES
<span>1801</span>a32aab54        nginx               <span>"nginx -g ''daemon off"</span><span>2</span> minutes ago       Up <span>2</span> minutes        <span>80</span>/tcp, <span>443</span>/tcp, <span>0.0</span><span>.0</span><span>.0</span>:<span>800</span><span>-&gt;</span><span>800</span>/tcp   berserk_kare
登录后复制

进入容器内部

<span>$ </span>sudo docker exec -it <span>1801</span>a32aab54 /bin/bash
root<span>@1801a32aab54</span><span>:/</span><span># </span>
登录后复制

查看nginx目录

<span># cd /etc/nginx/</span>
conf<span>.d</span>/         koi-utf         mime<span>.types</span>      nginx<span>.conf</span>      uwsgi_params    
fastcgi_params  koi-win         modules/        scgi_params     win-utf  
登录后复制

可以看到不仅没有熟悉的 /sites-available,也没有 /sites-enabled

继续查看nginx配置

<span># cat /conf.d/default.conf</span><span>server</span> {
    listen       <span>80</span>;
    server_name  localhost;

    <span>#charset koi8-r;</span><span>#access_log  /var/log/nginx/log/host.access.log  main;</span>    location / {
        root   /usr/share/nginx/html;
        <span>index</span><span>index</span>.html <span>index</span>.htm;
    }

    <span>#error_page  404              /404.html;</span><span># redirect server error pages to the static page /50x.html</span><span>#</span>
    error_page   <span>500</span><span>502</span><span>503</span><span>504</span>  /<span>50</span>x.html;
    location = /<span>50</span>x.html {
        root   /usr/share/nginx/html;
    }
    <span>#...省略php-fpm配置,好长..</span>
}
登录后复制

根目录配置: root /usr/share/nginx/html;

测试

<span># cd /usr/share/nginx/html</span><span># touch index.html</span><span># echo "test nginx in docker" &gt;index.html</span>
登录后复制

php-fpm配置相关

'').addClass(''pre-numbering'').hide(); $(this).addClass(''has-numbering'').parent().append($numbering); for (i = 1; i '').text(i)); }; $numbering.fadeIn(1700); }); });

以上就介绍了167 docker docker构建nginx容器系列问题,包括了docker,nginx方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

com.facebook.react.views.picker.ReactDropdownPickerManager的实例源码

com.facebook.react.views.picker.ReactDropdownPickerManager的实例源码

项目:ReactNativeSignatureExample    文件:MainReactPackage.java   
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
  return Arrays.<ViewManager>asList(
    ARTRenderableViewManager.createARTGroupViewManager(),ARTRenderableViewManager.createARTShapeViewManager(),ARTRenderableViewManager.createARTTextViewManager(),new ARTSurfaceViewManager(),new ReactDialogPickerManager(),new ReactDrawerLayoutManager(),new ReactDropdownPickerManager(),new ReacthorizontalscrollviewManager(),new ReactimageManager(),new ReactProgressBarViewManager(),new ReactRawTextManager(),new ReactScrollViewManager(),new ReactSwitchManager(),new FrescoBasedReactTextInlineImageViewManager(),new ReactTextInputManager(),new ReactTextViewManager(),new ReactToolbarManager(),new ReactViewManager(),new ReactViewPagerManager(),new ReactVirtualTextViewManager(),new ReactWebViewManager(),new RecyclerViewBackedScrollViewManager(),new SwipeRefreshLayoutManager());
}
项目:react-native-ibeacon-android    文件:MainReactPackage.java   
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
  return Arrays.<ViewManager>asList(
    ARTRenderableViewManager.createARTGroupViewManager(),new SwipeRefreshLayoutManager());
}
项目:react-native-Box-loaders    文件:MainReactPackage.java   
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
  return Arrays.<ViewManager>asList(
    ARTRenderableViewManager.createARTGroupViewManager(),new ReactModalHostManager(),new ReactSliderManager(),new SwipeRefreshLayoutManager());
}
项目:Ironman    文件:MainReactPackage.java   
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
  return Arrays.<ViewManager>asList(
    ARTRenderableViewManager.createARTGroupViewManager(),new SwipeRefreshLayoutManager());
}
项目:RNLearn_Project1    文件:MainReactPackage.java   
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
  List<ViewManager> viewManagers = new ArrayList<>();

  viewManagers.add(ARTRenderableViewManager.createARTGroupViewManager());
  viewManagers.add(ARTRenderableViewManager.createARTShapeViewManager());
  viewManagers.add(ARTRenderableViewManager.createARTTextViewManager());
  viewManagers.add(new ARTSurfaceViewManager());
  viewManagers.add(new ReactDialogPickerManager());
  viewManagers.add(new ReactDrawerLayoutManager());
  viewManagers.add(new ReactDropdownPickerManager());
  viewManagers.add(new ReacthorizontalscrollviewManager());
  viewManagers.add(new ReactimageManager());
  viewManagers.add(new ReactModalHostManager());
  viewManagers.add(new ReactProgressBarViewManager());
  viewManagers.add(new ReactRawTextManager());
  viewManagers.add(new ReactScrollViewManager());
  viewManagers.add(new ReactSliderManager());
  viewManagers.add(new ReactSwitchManager());
  viewManagers.add(new FrescoBasedReactTextInlineImageViewManager());
  viewManagers.add(new ReactTextInputManager());
  viewManagers.add(new ReactTextViewManager());
  viewManagers.add(new ReactToolbarManager());
  viewManagers.add(new ReactViewManager());
  viewManagers.add(new ReactViewPagerManager());
  viewManagers.add(new ReactVirtualTextViewManager());
  viewManagers.add(new ReactWebViewManager());
  viewManagers.add(new SwipeRefreshLayoutManager());

  SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(reactContext);
  if (preferences.getBoolean("flat_uiimplementation",false)) {
    viewManagers.addAll(Arrays.asList(
      new RCTViewManager(),new RCTTextManager(),new RCTRawTextManager(),new RCTVirtualTextManager(),new RCTTextInlineImageManager(),new RCtimageViewManager(),new RCTTextInputManager(),new RCTViewPagerManager(),new FlatARTSurfaceViewManager(),new RCTModalHostManager()));
  }

  return viewManagers;
}

Docker in Docker(实际上是 Docker outside Docker): /var/run/docker.sock

Docker in Docker(实际上是 Docker outside Docker): /var/run/docker.sock

在 Docker 容器里面使用 docker run/docker build

Docker 容器技术目前是微服务/持续集成/持续交付领域的第一选择。而在 DevOps 中,我们需要将各种后端/前端的测试/构建环境打包成 Docker 镜像,然后在需要的时候,Jenkins 会使用这些镜像启动容器以执行 Jenkins 任务。

为了方便维护,我们的 CI 系统如 Jenkins,也会使用 Docker 方式部署。 Jenkins 任务中有些任务需要将微服务构建成 Docker 镜像,然后推送到 Harbor 私有仓库中。 或者我们所有的 Jenkins Master 镜像和 Jenkins Slave 镜像本身都不包含任何额外的构建环境,执行任务时都需要启动包含对应环境的镜像来执行任务。

我们的 Jenkins Master、Jenkins Slaves 都是跑在容器里面的,该如何在这些容器里面调用 docker run 命令启动包含 CI 环境的镜像呢? 在这些 CI 镜像里面,我们从源码编译完成后,又如何通过 docker build 将编译结果打包成 Docker 镜像,然后推送到内网仓库呢?

答案下面揭晓。

一、原理说明:/var/run/docker.sock

Docker 采取的是 Client/Server 架构,我们常用的 docker xxx 命令工具,只是 docker 的 client,我们通过该命令行执行命令时,实际上是在通过 client 与 docker engine 通信。

我们通过 apt/yum 安装 docker-ce 时,会自动生成一个 systemd 的 service,所以安装完成后,需要通过 sudo systemctl enable docker.service 来启用该服务。 这个 Docker 服务启动的,就是 docker engine,查看 /usr/lib/systemd/system/docker.service,能看到有这样一条语句:

ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

默认情况下,Docker守护进程会生成一个 socket(/var/run/docker.sock)文件来进行本地进程通信,因此只能在本地使用 docker 客户端或者使用 Docker API 进行操作。 sock 文件是 UNIX 域套接字,它可以通过文件系统(而非网络地址)进行寻址和访问。

因此只要以数据卷的形式将 docker 客户端和上述 socket 套接字挂载到容器内部,就能实现 "Docker in Docker",在容器内使用 docker 命令了。具体的命令见后面的「示例」部分。

要记住的是,真正执行我们的 docker 命令的是 docker engine,而这个 engine 跑在宿主机上。所以这并不是真正的 "Docker in Docker".

二、示例

在容器内部使用宿主机的 docker,方法有二:

  1. 命令行方式:将 /usr/bin/docker 映射进容器内部,然后直接在容器内部使用这个命令行工具 docker
    • 需要的时候,也可以将 /etc/docker 文件夹映射到容器内,这样容器内的 docker 命令行工具也会使用与宿主机同样的配置。
  2. 编程方式:在容器内部以编程的方式使用 docker
    • 通过 python 使用 docker: 在 Dockerfile 中通过 pip install docker 将 docker client 安装到镜像中来使用

容器的启动方式也有两种,如下:

1. 直接通过 docker 命令启动

示例命令如下:

docker run --name <name> \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /usr/bin/docker:/usr/bin/docker \
    --user root \
    <image-name>:<tag>

**必须以 root 用户启动!(或者其他有权限读写 /var/run/docker.sock 的用户)**然后,在容器内就能正常使用 docker 命令,或者访问宿主机的 docker api 了。

2. 使用 docker-compose 启动

docker-compose.yml 文件内容如下:

version: ''3.3''
services:
  jenkins-master:
    image: jenkinsci/blueocean:latest
    container_name: jenkins-master
    environment:
      - TZ=Asia/Shanghai  # 时区
    ports:
      - "8080:8080"
      - "50000:50000"
    volumes:
      - ./jenkins_home:/var/jenkins_home  # 将容器中的数据映射到宿主机
      - /usr/bin/docker:/usr/bin/docker  # 为容器内部提供 docker 命令行工具(这个随意)
      - /var/run/docker.sock:/var/run/docker.sock  # 容器内部通过 unix socket 使用宿主机 docker engine
    user: root  # 必须确保容器以 root 用户启动!(这样它才有权限读写 docker.socket)
    restart: always

然后通过 docker-compose up -d 即可后台启动容器。

Docker 中的 uid 与 gid

通过上面的操作,我们在容器内执行 docker ps 时,还是很可能会遇到一个问题:权限问题

如果你容器的默认用户是 root,那么你不会遇到这个问题,因为 /var/run/docker.sock 的 onwer 就是 root.

但是一般来说,为了限制用户的权限,容器的默认用户一般都是 uid 和 gid 都是 1000 的普通用户。这样我们就没有权限访问 /var/run/docker.sock 了。

解决办法:

方法一(不一定有效):在构建镜像时,最后一层添加如下内容:

# docker 用户组的 id,通常都是 999
RUN groupadd -g 999 docker \
    && usermod -aG docker <your_user_name>

这样我们的默认用户,就能使用 docker 命令了。

P.S. 999 不一定是 docker 用户组,所以上述方法某些情况下可能失效。这时还是老老实实通过 docker run -u root 启动容器吧。(或者在 docker-compose.yml 中添加 user: root 属性)

参考

  • Docker in Docker - 王柏元

今天关于在 10 分钟内实现安全的 React + Dockerreact docker部署的讲解已经结束,谢谢您的阅读,如果想了解更多关于04 . Docker安全与Docker底层实现、167 docker docker构建nginx容器系列问题 docker registry docker run docker toolbo、com.facebook.react.views.picker.ReactDropdownPickerManager的实例源码、Docker in Docker(实际上是 Docker outside Docker): /var/run/docker.sock的相关知识,请在本站搜索。

本文标签:

上一篇Windows Kernel Exploitation Notes(一)——HEVD Stack Overflow(windows-kernel-explorer)

下一篇C#串口开发之SerialPort类封装(c# 串口类用法)