草庐IT

【docker系列】使用非root用户安装及启动docker(rootless模式运行)

字母哥哥 2023-06-03 原文

通过我之前的文章已经可以验证,在root用户下安装启动的容器存在安全问题。究其原因是因为:

  • 容器内的root用户就是宿主机的root用户,容器内uid=1000的用户就是宿主机uid=1000的用户
  • docker的守护进程是root权限的

既然我们知道了原因,那么我们就来解决一下这两个问题。

文章目录

一、容器用户与宿主机用户映射

docker是使用--userns-remap容器用户映射宿主机用户的方式来解决问题,具体的方法描述如下:

用户和组的映射由两个配置文件来控制,分别是/etc/subuid/etc/subgid

echo "zimug:100000:65536" | tee /etc/subuid;
echo "zimug:100000:65536" | tee /etc/subgid;

subuid(sub子uid用户id):对于subuid的这一行表示,宿主机用户zimug的用户ip段为[100000,10000+65535]。也就是说,使用zimug这个宿主机启动容器,容器内的用户uid与宿主机内的用户uid存在关系。也就是说按照上面的配置

  • zimug这个用户的容器子用户id(subuid),只能在[100000-165535]之间进行分配。[0-99999]这个区间范围内的uid仍然保留给宿主机进行使用。
  • 使用zimug启动的第一个容器,容器用户root(uid=0)对应的宿主机用户应该是uid=100000(不是宿主机root用户)
  • 使用zimug启动的第二个容器,容器用户root(uid-0)对应的宿主机用户可能是uid=101000(也不是宿主机root用户)
  • subgid表示的是用户组id的映射关系,映射原理和uid是一致的。

二、在非root用户下运行docker守护进程

2.1.docker版本要求

既然root用户的提权问题解决了,我们就要解决下一个问题:docker的守护进程是root权限的,即运行docker守护进程的用户仍然是root。我们需要做如下修正:

也就是我们要在非root用户下安装docker,并启动docker守护进程,这种安装及运行模式被称为“RootLess”模式。可以安装但是存在先决条件:“RootLess”模式是在 Docker Engine v19.03 中作为实验性功能引入的,从 Docker Engine v20.10 开始提供正式使用。

2.2. 前置条件

需要安装newuidmapnewgidmap工具shadow-utils,即配置上文中的/etc/subuid/etc/subuid需要这两个工具的支持。安装之前使用yum list installed shadow-utils确认下是否已经安装过或者操作系统自带,如果存在就不要安装了,但第三步的配置是需要的。
第一步:添加一个软件包安装源,该源下面包含shadow-utils46-newxidmap

curl -o /etc/yum.repos.d/vbatts-shadow-utils-newxidmap-epel-7.repo https://copr.fedorainfracloud.org/coprs/vbatts/shadow-utils-newxidmap/repo/epel-7/vbatts-shadow-utils-newxidmap-epel-7.repo

第二步: yum install -y shadow-utils46-newxidmap
第三步:在/etc/sysctl.conf文件中修改系统参数user.max_user_namespaces = 28633,修改完成之后执行sysctl --system命令让参数生效。

echo user.max_user_namespaces=28633 >> /etc/sysctl.d/userns.conf;
sudo sysctl -p /etc/sysctl.d/userns.conf;

该参数默认值是0,即不允许操作系统用户存在subuid空间。上面的操作完成之后使用sysctl --all --pattern user_namespaces命令验证修改的结果。

2.3.开始rootless模式安装

root用户下确保已经执行下列命令进行用户id关系配置

echo "zimug:100000:65536" | tee /etc/subuid;
echo "zimug:100000:65536" | tee /etc/subgid;

我已经新建了一个linux用户zimug,使用su - zimug切换到该用户下,执行安装脚本,该安装脚本需要连网。

curl -fsSL https://get.docker.com/rootless | sh

脚本执行完成之后,显示的内容如下:

将上图中安装过程提示的export内容添加到 ~/.bashrc 文件中,添加完使用source ~/.bashrc命令使环境变量生效。注意:你的环境变量和我的一定不一致,要copy上图中红色部分。

export XDG_RUNTIME_DIR=/home/zimug/.docker/run
export PATH=/home/zimug/bin:$PATH
export DOCKER_HOST=unix:///home/zimug/.docker/run/docker.sock

2.4.启动守护进程运行容器

在zimug用户下使用下面的脚本启动docker守护进程,该脚本在上文中的环境变量PATH目录下,所以我们不用写全路径。注意:我们这里使用了 --experimental --storage-driver vfs参数启动,因为我的linux内核版本比较低所以需要加这个参数,后文我会说明原因。

dockerd-rootless.sh --experimental --storage-driver vfs

启动一个容器我们尝试一下,注意宿主机映射的端口不能小于1024,因为linux非root用户不能使用1024以下的端口。

docker run -d --name nginx-zimug -p  8080:80  nginx

访问nginx服务看到界面就证明nginx服务没有问题,同时注意宿主机防火墙开放8080端口访问,这个我就不说了。

三、存在若干已知的限制。

官方文档说的是known-limitations,也就是已知的受限因素是下面这些,未知的受限因素还不一定有多少呢。所以笔者针对rootless模式目前也只是研究,还没有在生产上能够正式使用(2022年4月6日)。虽然它很安全,但是如果坑太多的话,我也受不了啊。

  • 目前仅支持以下存储驱动程序:
    • overlay2(仅当使用内核 5.11 或更高版本或 Ubuntu 发行版本的内核运行时,才能够支持这个存储驱动)。上文中我的内核版本是3.10达不到要求。
    • fuse-overlayfs(仅当使用内核 4.18 或更高版本运行并fuse-overlayfs被安装时)
    • btrfs(仅当使用内核 4.18 或更高版本运行,或者~/.local/share/docker使用user_subvol_rm_allowed挂载选项挂载时)
    • vfs 兼容性最好,但这个存储驱动目前仅能用于实验,不能用于生产。
  • 只有在使用 cgroup v2 和 systemd 运行时才支持 Cgroup。
  • 不支持以下功能:
    • AppArmor
    • Checkpoint
    • Overlay网络模式
    • 暴漏SCTP 端口

四、卸载Rootless docker

在非root用户(我用的是zimug用户)下执行下列命令,完成docker rootless卸载
第一步:

rootlesskit rm -rf ~/.local/share/docker

第二步:

$ cd ~/bin 
$ rm -f containerd containerd-shim containerd-shim-runc-v2 ctr docker docker-init docker-proxy dockerd dockerd-rootless-setuptool.sh dockerd-rootless.sh rootlesskit rootlesskit-docker-proxy runc vpnkit

第三步: 把上文中在 ~/.bashrc 文件中添加的环境变量删掉。

有关【docker系列】使用非root用户安装及启动docker(rootless模式运行)的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  4. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  5. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  6. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  7. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  8. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  9. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  10. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

随机推荐