Linux 身份认证系统 -- 从哪儿学起?
前言
我一直对操作系统的内部实现非常感兴趣。最近,我在家里的内网部署了 kanidm,在过程中也对
在本文的开头,我将讲述一小段我了解 sudo
是怎么工作的流程,这些内容以
Forewords
I’ve always been curious about the internal implementations of operating systems. I recently deployed kanidm in my home lab, and I’ve leaned a lot about how does linux authentication works during this process. These paragraph is licensed under CC-BY-NC-SA 4.0. I’m writing this post to share my thoughts and share my translation of this fantastic blog from Firstyear in Chinese.
SUDO 是怎么工作的?
在我学习操作系统课程的时候,我突然想到
- 应该有一个用于提权的系统调用
sudo
程序在用户态收集用户的密码,并调用这个系统调用提升到root
但是,仔细一想便会发现,这个假设有很大的问题:
sudo
接受的是当前用户的密码,而不是root
的密码,而哪些用户可以执行sudo
是在sudoers
配置文件里的- 所以,这个『用于提权的系统调用』应该需要知道哪些用户可以执行
sudo
- 但是不太可能是读那个配置文件?
- 所以,这个『用于提权的系统调用』应该需要知道哪些用户可以执行
- 同时,
sudo
还支持更细颗粒度的权限限制,比如要求某个用户只能以root
执行某个特定的指令。
经过一番搜索,我找到了 setuid
这个系统调用,发现它可以设置当前进程的
『降低权限CAP_SETUID
能力的进程才可以调用。那么,在sudo
这种场景下,权限的『提升』是什么时候完成的呢?
经过我的尝试,我发现在调用sudo
输入密码之前,sudo
这个进程的uid
就已经是sudo
这个二进制程序本身有
特殊的权限,让他能直接提升到root
。思考到了这一步,结论就十分显然:这个特殊的权限只能是存在文件系统里的。经过一番搜索,我发现文件系统除了读写执行等权限之外,还维护了几个特殊的权限:sudo
用的是一个叫做setuid
的权限:
The Unix access rights flags setuid and setgid (short for set user identity and set group identity) allow users to run an executable with the file system permissions of the executable’s owner or group respectively and to change behaviour in directories.
这样,任何用户在运行 sudo
的时候,权限都会临时的提升到 root
(也就是 sudo
这个文件的 owner
sudo
自然可以自己判断用户是否有权限保留 root
权限,或者切换到其他用户。这也是为什么如果对 /usr/bin
运行 chmod -R 755
会导致 sudo
没法用了的原因: setuid
权限被清除了。
这就是我对
How does sudo works?
While I was learning Operating Systems at my school, it suddenly occurred to me that I don’t know how sudo
works. Based on my little knowledge of Linux, I made the following assumption:
- There should be a syscall for elevating privileges
sudo
collects user’s password, and pass it to the syscall
However, almost immediately, I found some problems of my assumption:
sudo
asks for the password of the current user, not the root user; and who is allowed tosudo
is stored insudoers
- So the syscall for elevating privileges should be able to know who can do that
- But it is unlikely that the syscall reads the
sudoers
file
sudo
supports controlling which command a user is allowed to run- This is too complicated to be integrated into the kernel.
After some digging, I found the syscall setuid
. It is able to set the uid
of the current process. After some further searching, I found that this syscall is only for lowering the permissions: You have to have CAP_SETUID
to run it. Then, in a scenario like sudo
, when does the permission “elevation” happens?
After some trial, I discovered that when sudo
is asking for password, the uid
of it’s process is already 0, which means that the binary file sudo
have something unique which makes it can directly be run as root
. At this point, the answer is quite obvious: the only reasonable answer is that the permission is stored in the file system. Again, after some searching, I found that the file system maintains some special permission other than regular rwx
. sudo
uses setuid
:
The Unix access rights flags setuid and setgid (short for set user identity and set group identity) allow users to run an executable with the file system permissions of the executable’s owner or group respectively and to change behaviour in directories.
So, the permission is temporarily elevated to root
(who is the owner of sudo
). sudo
itself can determin whether the user is able to maintain the root
permission or switch to other users. That is also why sudo
breaks if you run chmod -R 755 /usr/bin
.
This my first understanding of linux authentication system.
以下内容翻译自
The following content is translation of this fantastic blog from Firstyear. Copyright remains to original author.
Linux 身份认证系统 – 从哪儿学起?
最近有一个人问我,应该如何学习
你…是谁?
一个 nsswitch.conf
的例子如下:
1 | passwd: compat sss |
这个文件使用 服务: 模组1 模组2...
的格式。一个简单的例子是:当一个程序使用gethostbyname
方法来进行host
服务,先通过files
模组解析/etc/hosts
,再通过mdns
模组(也叫做avahi
bonjour
dns
模组解析。
passwd
group
shadow
是有关身份的三行。最常见的情况下,你会使用files
模组。它会查询/etc/passwd
和/etc/shadow
来返回响应。compat
模组和files
类似,只是增加了对于sss
,它会访问kanidm
这个
你可以用getent
命令来测试
1 | # getent passwd william |
注意到,
这些模组都是动态链接库,你可以用以下命令找到他们:
1 | # ls -al /usr/lib[64]/libnss_* |
当一个进程想要通过/etc/passwd
或者访问网络来解析身份。有些模组(比如
证明你自己
如果/etc/pam.d
文件夹(和/etc/pam.conf
有一点点语法上的区别
然后,/etc/pam.d/服务名称
,在我们这个例子中是/etc/pam.d/ssh
。以下是一个从
1 | # cat /etc/pam.d/ssh |
注意 “include” 分别对于system-auth
1 | # cat /etc/pam.d/system-auth |
所以,首先我们在『验证』阶段。这个阶段是pam_env.so
开始,它返回『通过但未结束faildelay
,以此类推。这些模组一个一个被访问,它们的结果与前面的『规则pam_unix.so
和 pam_sss.so
。所以,如果这两个都没有返回『成功,验证结束pam_deny.so
就会最终返回一个 『失败但是结束
第二个阶段是『账户阶段
第三个阶段是『会话阶段pam_limit.so
,它负责设置新会话的
第四个阶段是『密码阶段passwd
命令来修改这个密码的。每个模块依次被询问:你可以修改这个用户的密码吗?如果最终失败了,你会得到一个『authentication token manipulation error
这些模块都是动态链接库,一般可以在 /usr/lib64/security
找到。就像libpam.so
,它会在运行时加载 /usr/lib64/security
中的动态链接库。鉴于/etc/shadow
只能被root
用户读取,同时任何需要验证密码的东西都需要来读取这个文件,这基本意味着任何root
的地址空间中。这就是发行版仔细审计和控制哪个模块可以添加一个
那么,网络验证呢?
现在,我们已经覆盖了进程和守护进程如何找到用户、验证凭据的基础。现在,我们来看看
正如之前提到的,pam_ldap.so
可能在root
的地址空间运行,同时需要访问网络的权限以及需要解析asn.1
1 | ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ |
1 | ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ |
另一个很大的好处是
这就是pam_ldap
和pam_krb5
。
巨兽之内
1 | # /etc/sssd/sssd.conf |
现在我们知道了一个新的概念:一个
在大部分情况下,如果你在使用
每个
1 | [domain/default] |
id_procider
负责解析名字和uid/gid
到身份。
auth_provider
负责验证密码。
access_provider
负责判断这个身份是否有权限访问这台系统。
chpass_provider
负责更改密码。
正如你可以看到的,在这个设计中有很大的灵活性:比如,你可以使用krb5
来验证身份,但是使用ldap
来修改密码。
正因为这个设计,
常见问题
性能
在某些情况下,
如果要避免这一点,你可以设置:
1 | ignore_group_members = False |
这样能避免组加载他们的成员。这样,所有用户组看上去都是没有成员的,不过所有用户都会展示他们所在的用户组。鉴于绝大部分程序都使用『是
清除缓存
这样有两个问题:在某些情况下,清除缓存看起来没有作用,失效的记录被继续使用;同时,-E
选项并不总是会使全部记录失效。
在这样的情况下,一个通常的建议是关闭/var/lib/sss/db
文件夹内的所有东西(但是不要删掉文件夹)然后重启
调试Kerberos
1 | KRB5_TRACE=/dev/stderr kinit user@domain |
这个
总结
以上就是全部内容了,我可能会持续更新这个博文!
(翻译于