四会建设局网站,太原编程课哪个机构最好,电商美工的工作内容是什么,新浪博客网站原文作者#xff1a;Artem Konev - Senior Technical Writer 原文链接#xff1a;使用 NGINX Unit 实施应用隔离 转载来源#xff1a;NGINX 中文官网 NGINX 唯一中文官方社区 #xff0c;尽在 nginx.org.cn NGINX Unit 特性集的最新动态之一是支持应用隔离#xff0c;该特… 原文作者Artem Konev - Senior Technical Writer 原文链接使用 NGINX Unit 实施应用隔离 转载来源NGINX 中文官网 NGINX 唯一中文官方社区 尽在 nginx.org.cn NGINX Unit 特性集的最新动态之一是支持应用隔离该特性于 1.11.0 版中引入通过 Linux 命名空间实施。
下面我们先简单回顾一下 Linux 命名空间它本质上是一种内核机制支持一个进程组将多种系统资源与其他进程组共享的资源分开共享。内核能够确保命名空间中的进程仅访问分配给该命名空间的资源。尽管两个不同命名空间中的进程可以共享一些资源但其他资源对于另一个命名空间中的进程“不可见”。可在命名空间中隔离的资源类型因操作系统而异包括进程和用户 ID、进程间通信实体、文件系统中的挂载点、网络对象等。
听起来有点乏味也许如此特别是在您不了解操作系统技术的情况下。但命名空间是容器化变革背后的关键因素之一在单个操作系统实例中隔离应用进程可实现在容器中运行应用所需的关键安全和扩展机制。
想法
现在我们已经确定命名空间可能是个好东西但 NGINX Unit 拿它有何用呢在进一步阐释前我们先概括介绍相关背景信息了解一下 Tiago 本人的想法
“我正在研究更好的方法以有效监控和拦截来自应用的流量。闲暇之余我一直在研究 NGINX Unit 的内部机制并认为进程隔离可能非常适用。但是我还不确定是否是最佳方案。之前我曾考虑 eBPF并研究它如何在内核级别重定向数据包但后来我有了不同的想法。由于 NGINX Unit 以类似于容器运行时的方式运行并管理应用那么如果我们为 NGINX Unit 添加应用隔离支持并使用它代替运行时将会怎样这一想法与 NGINX Unit 团队未来构思设计之一不谋而合。
在集群中容器运行时启动和停止应用因此我们了解集群中运行的一切。NGINX Unit 架构不仅做了同样的事情而且还默认实施流量监控和拦截到达应用的唯一途径是 NGINX Unit 的共享内存模型。值得注意的是我们甚至能够隔离网络类似于跳过容器内的接口设置但应用仍可通过与 NGINX Unit 共享内存来与外界通信而不会遭遇任何代价高昂的网络攻击。”
配置
从配置的角度来看一切都离不开全新 isolation 对象它定义了应用对象中的命名空间相关设置。
isolation 对象中的命名空间选项取决于系统因为可隔离到命名空间的资源类型因操作系统而异。下面是为应用创建单独的用户 ID 和挂载点命名空间的基本示例
{applications: { isolation_app: { type: external,executable: /tmp/go-app,isolation: { namespaces: { credential: true,mount: true}}}}
}
目前NGINX Unit 支持配置 Linux 内核支持的 7 种命名空间隔离类型中的 6 种。相应配置选项为 cgroup、credential、pid、mount、network 及 uname。暂不支持最后一个类型 ipc。
默认情况下禁用所有隔离类型选项设置为 false这意味着应用驻留在 NGINX Unit 命名空间中。当您通过将其选项设置为 true 来为应用启用特定隔离类型时NGINX Unit 将为该应用创建这一类型的单独命名空间。例如除了自身有一个单独的 mount 或 credential 命名空间外应用还可与 NGINX Unit 位于同一命名空间。
有关 isolation 对象中选项的更多详细信息请参阅 NGINX Unit 文档。
注在撰写本文时所有应用均需使用与 NGINX Unit 相同的 ipc 命名空间此为共享内存机制要求。您可将 ipc 选项添加至配置中但其设置无效。这一要求可能会在未来版本中发生变化。
用户和组 ID 映射
NGINX Unit 中的应用隔离包括支持 UID 和 GID 映射如果启用 credential 隔离这意味着您的应用在单独的凭证命名空间中运行则可对其进行配置。您可以将应用命名空间我们称之为 “container容器命名空间”中的一系列 ID 映射至该应用父进程凭证命名空间我们称之为 “host主机命名空间”中的相同长度 ID 范围。
例如假设您的一款应用采用非特权用户凭证运行并启用了 credential 隔离以便为该应用创建一个容器命名空间。NGINX Unit 支持您将主机命名空间中非特权用户的 UID 映射到容器命名空间中的 UID 0 (root)。根据设计任何命名空间中取值为 0 的 UID 在该命名空间中拥有全部权限而其在主机命名空间中映射对应 UID 的权限仍然受限。因此该应用似乎拥有 root 功能但仅可用于其命名空间内的资源。GID 映射也是如此。
此处我们将主机命名空间中从 UID 500 开始的 10 项 UID 范围值映射到容器命名空间中从 UID 0 开始的 UID 范围值主机500-509容器0-9。同样我们将主机命名空间中从 GID 1000 开始的 20 项 GID 范围值映射到容器命名空间中从 GID 0 开始的范围值主机1000-1019容器0-19
{applications: { isolation_app: { type: external,executable: /bin/app,isolation: { namespaces: { credential: true},uidmap: [ { container: 0,host: 500,size: 10}],gidmap: [ { container: 0,host: 1000,size: 20}]}}}
}
如果您未创建显式 UID 和 GID 映射默认情况下主机命名空间中非特权 NGINX Unit 进程的当前有效 UID (EUID) 将映射到容器命名空间中的 root UID。另请注意仅当主机操作系统支持用户命名空间时UID/GID 映射才可用。说到这里让我们继续了解一下应用隔离对 NGINX Unit 中运行的应用的影响。
入门基本应用隔离
下面我们从基础开始了解该特性运行时的行为。为此我们将采用我们官方存储库中的一个 Go 应用在测试新版本时运行
package mainimport (encoding/jsonfmtnet/httpnginx/unitosstrconv
)type (NS struct {USER uint64PID uint64IPC uint64CGROUP uint64UTS uint64MNT uint64NET uint64}Output struct {PID intUID intGID intNS NS}
)func abortonerr(err error) {if err ! nil {panic(err)}
}func getns(nstype string) uint64 {// readlink returns: [nstype]:[4026531835]str, err : os.Readlink(fmt.Sprintf(/proc/self/ns/%s, nstype))if err ! nil {return 0}str str[len(nstype)2:]str str[:len(str)-1]val, err : strconv.ParseUint(str, 10, 64)abortonerr(err)return val
}func handler(w http.ResponseWriter, r *http.Request) {pid : os.Getpid()out : Output{PID: pid,UID: os.Getuid(),GID: os.Getgid(),NS: NS{PID: getns(pid),USER: getns(user),MNT: getns(mnt),IPC: getns(ipc),UTS: getns(uts),NET: getns(net),CGROUP: getns(cgroup),},}data, err : json.Marshal(out)if err ! nil {w.WriteHeader(http.StatusInternalServerError)return}w.Write(data)
}func main() {http.HandleFunc(/, handler)unit.ListenAndServe(: 7080, nil)
}
这段代码使用应用进程和命名空间 ID 的 JSON 格式清单响应请求枚举 /proc/self/ns/ 目录的内容。下面我们在 NGINX Unit 中配置应用暂时忽略 isolation 对象
{ listeners: { *:8080: { pass: applications/go-app}},applications: { go-app: { type: external,executable: /tmp/go-app}}
}
来自运行中应用实例的 HTTP 响应
$ curl -X GET http://localhost:8080{ PID: 5778,UID: 65534,GID: 65534,NS: { USER: 4026531837,PID: 4026531836,IPC: 4026531839,CGROUP: 4026531835,UTS: 4026531838,MNT: 4026531840,NET: 4026531992}
}
现在我们添加 isolation 对象以启用应用隔离。隔离机制需要重启应用才可生效。NGINX Unit 将在幕后执行这一任务因此从最终用户的角度来看更新非常透明。
{ listeners: { *:8080: { pass: applications/go-app}},applications: { go-app: { type: external,user: root,executable: /tmp/go-app,isolation: { namespaces: { cgroup: true,credential: true,mount: true,network: true,pid: true,uname: true},uidmap: [ { host: 1000,container: 0,size: 1000}],gidmap: [ { host: 1000,container: 0,size: 1000}]}}}
}
请注意user 选项设置为 root。启用映射到容器命名空间中的 UID/GID 0 需要执行这一设置。
我们再次发出命令
$ curl -X GET http://localhost:8080{ PID: 1,UID: 0,GID: 0,NS: { USER: 4026532180,PID: 4026532184,IPC: 4026531839,CGROUP: 4026532185,UTS: 4026532183,MNT: 4026532181,NET: 4026532187}
}
我们现已启用应用隔离命名空间 ID 已变更 — 它们现在是容器命名空间中的 ID而非主机命名空间中的 ID。唯一保持不变的是 IPC原因如上所述。
深入探究网络应用隔离
为进行深入了解下面我们将探讨应用隔离对网络的实际影响这对 Web 应用而言非常重要。我们为此选择的工具是 nsenter它适用于 NGINX Unit 支持的许多操作系统发行版。该实用程序允许我们在进程命名空间内运行任意命令我们将使用它来演示由前面配置的同一 Go 应用 isolation 对象中的不同设置引起的变化。首先我们找出主机 PID
更多代码详情查看NGINX社区官网
确定 PID 后我们可以进入容器命名空间并查看其内部组成
请注意仅环回接口可用但该应用完全能够通过 NGINX Unit 处理外部 HTTP 请求。接下来我们将从配置的命名空间列表中删除 network 选项以查看禁用网络隔离的应用的最终网络接口配置
然后我们重复执行上述相同步骤
现在还有应用进程在启动时沿用 NGINX Unit 的接口 (eth0)。 NGINX 唯一中文官方社区 尽在 nginx.org.cn
更多 NGINX 相关的技术干货、互动问答、系列课程、活动资源 开源社区官网 | 微信公众号