20171124 How do groups work on Linux.md

This commit is contained in:
David Chen 2018-09-19 10:39:40 +08:00
parent f0e225c40c
commit 41f4e466e8
2 changed files with 152 additions and 143 deletions

View File

@ -1,143 +0,0 @@
Translating by DavidChen
How do groups work on Linux?
============================================================
Hello! Last week, I thought I knew how users and groups worked on Linux. Here is what I thought:
1. Every process belongs to a user (like `julia`)
2. When a process tries to read a file owned by a group, Linux a) checks if the user `julia` can access the file, and b) checks which groups `julia` belongs to, and whether any of those groups owns & can access that file
3. If either of those is true (or if the any bits are set right) then the process can access the file
So, for example, if a process is owned by the `julia` user and `julia` is in the `awesome` group, then the process would be allowed to read this file.
```
r--r--r-- 1 root awesome 6872 Sep 24 11:09 file.txt
```
I had not thought carefully about this, but if pressed I would have said that it probably checks the `/etc/group` file at runtime to see what groups youre in.
### that is not how groups work
I found out at work last week that, no, what I describe above is not how groups work. In particular Linux does **not** check which groups a processs user belongs to every time that process tries to access a file.
Here is how groups actually work! I learned this by reading Chapter 9 (“Process Credentials”) of [The Linux Programming Interface][1] which is an incredible book. As soon as I realized that I did not understand how users and groups worked, I opened up the table of contents with absolute confidence that it would tell me whats up, and I was right.
### how users and groups checks are done
They key new insight for me was pretty simple! The chapter starts out by saying that user and group IDs are **attributes of the process**:
* real user ID and group ID;
* effective user ID and group ID;
* saved set-user-ID and saved set-group-ID;
* file-system user ID and group ID (Linux-specific); and
* supplementary group IDs.
This means that the way Linux **actually** does group checks to see a process can read a file is:
* look at the processs group IDs & supplementary group IDs (from the attributes on the process, **not** by looking them up in `/etc/group`)
* look at the group on the file
* see if they match
Generally when doing access control checks it uses the **effective** user/group ID, not the real user/group ID. Technically when accessing a file it actually uses the **file-system** ids but those are usually the same as the effective uid/gid.
### Adding a user to a group doesnt put existing processes in that group
Heres another fun example that follows from this: if I create a new `panda` group and add myself (bork) to it, then run `groups` to check my group memberships Im not in the panda group!
```
bork@kiwi~> sudo addgroup panda
Adding group `panda' (GID 1001) ...
Done.
bork@kiwi~> sudo adduser bork panda
Adding user `bork' to group `panda' ...
Adding user bork to group panda
Done.
bork@kiwi~> groups
bork adm cdrom sudo dip plugdev lpadmin sambashare docker lxd
```
no `panda` in that list! To double check, lets try making a file owned by the `panda`group and see if I can access it:
```
$ touch panda-file.txt
$ sudo chown root:panda panda-file.txt
$ sudo chmod 660 panda-file.txt
$ cat panda-file.txt
cat: panda-file.txt: Permission denied
```
Sure enough, I cant access `panda-file.txt`. No big surprise there. My shell didnt have the `panda` group as a supplementary GID before, and running `adduser bork panda` didnt do anything to change that.
### how do you get your groups in the first place?
So this raises kind of a confusing question, right if processes have groups baked into them, how do you get assigned your groups in the first place? Obviously you cant assign yourself more groups (that would defeat the purpose of access control).
Its relatively clear how processes I **execute** from my shell (bash/fish) get their groups my shell runs as me, and it has a bunch of group IDs on it. Processes I execute from my shell are forked from the shell so they get the same groups as the shell had.
So there needs to be some “first” process that has your groups set on it, and all the other processes you set inherit their groups from that. That process is called your **login shell** and its run by the `login` program (`/bin/login`) on my laptop. `login` runs as root and calls a C function called `initgroups` to set up your groups (by reading `/etc/group`). Its allowed to set up your groups because it runs as root.
### lets try logging in again!
So! Lets say I am running in a shell, and I want to refresh my groups! From what weve learned about how groups are initialized, I should be able to run `login` to refresh my groups and start a new login shell!
Lets try it:
```
$ sudo login bork
$ groups
bork adm cdrom sudo dip plugdev lpadmin sambashare docker lxd panda
$ cat panda-file.txt # it works! I can access the file owned by `panda` now!
```
Sure enough, it works! Now the new shell that `login` spawned is part of the `panda` group! Awesome! This wont affect any other shells I already have running. If I really want the new `panda` group everywhere, I need to restart my login session completely, which means quitting my window manager and logging in again.
### newgrp
Somebody on Twitter told me that if you want to start a new shell with a new group that youve been added to, you can use `newgrp`. Like this:
```
sudo addgroup panda
sudo adduser bork panda
newgrp panda # starts a new shell, and you don't have to be root to run it!
```
You can accomplish the same(ish) thing with `sg panda bash` which will start a `bash` shell that runs with the `panda` group.
### setuid sets the effective user ID
Ive also always been a little vague about what it means for a process to run as “setuid root”. It turns out that setuid sets the effective user ID! So if I (`julia`) run a setuid root process (like `passwd`), then the **real** user ID will be set to `julia`, and the **effective** user ID will be set to `root`.
`passwd` needs to run as root, but it can look at its real user ID to see that `julia`started the process, and prevent `julia` from editing any passwords except for `julia`s password.
### thats all!
There are a bunch more details about all the edge cases and exactly how everything works in The Linux Programming Interface so I will not get into all the details here. That book is amazing. Everything I talked about in this post is from Chapter 9, which is a 17-page chapter inside a 1300-page book.
The thing I love most about that book is that reading 17 pages about how users and groups work is really approachable, self-contained, super useful, and I dont have to tackle all 1300 pages of it at once to learn helpful things :)
--------------------------------------------------------------------------------
via: https://jvns.ca/blog/2017/11/20/groups/
作者:[Julia Evans ][a]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]:https://jvns.ca/
[1]:http://man7.org/tlpi/

View File

@ -0,0 +1,152 @@
"组"在 Linux 上到底是怎么工作的?
============================================================
你好!就在上周,我还自认为对 Linux 上的用户和组的工作机制了如指掌。我认为它们的关系是这样的:
1. 每个进程都属于一个用户( 比如用户`julia`)
2. 当这个进程试图读取一个被某个组所拥有的文件时, Linux 会 a先检查用户`julia` 是否有权限访问文件。(LCTT译注检查文件的所有者是否就是`julia`) b检查`julia` 属于哪些组,并进一步检查在这些组里是否有某个组拥有这个文件或者有权限访问这个文件。
3. 如果上述a,b任一为真 或者`其他`位设为有权限访问),那么这个进程就有权限访问这个文件。
比如说,如果一个进程被用户`julia`拥有并且`julia` 在`awesome`组,那么这个进程就能访问下面这个文件。
```
r--r--r-- 1 root awesome 6872 Sep 24 11:09 file.txt
```
然而上述的机制我并没有考虑得非常清楚,如果你硬要我阐述清楚,我会说进程可能会在**运行时**去检查`/etc/group` 文件里是否有某些组拥有当前的用户。
### 然而这并不是Linux 里“组”的工作机制
我在上个星期的工作中发现了一件有趣的事事实证明我前面的理解错了我对组的工作机制的描述并不准确。特别是Linux**并不会**在进程每次试图访问一个文件时就去检查这个进程的用户属于哪些组。
我在读了[The Linux Programming
Interface][1]这本书的第九章后才恍然大悟(这本书真是太棒了。)这才是组真正的工作方式!我意识到之前我并没有真正理解用户和组是怎么工作的,我信心满满的尝试了下面的内容并且验证到底发生了什么,事实证明现在我的理解才是对的。
### 用户和组权限检查是怎么完成的
现在这些关键的知识在我看来非常简单! 这本书的第九章上来就告诉我如下事实用户和组ID是**进程的属性**,它们是:
* 真实用户ID和组ID
* 有效用户ID和组ID
* 被保存的set-user-ID和被保存的set-group-ID;
* 文件系统用户ID和组ID特定于 Linux);
* 增补的组ID
这说明Linux**实际上**检查一个进程能否访问一个文件所做的组检查是这样的:
* 检查一个进程的组ID和补充组ID这些ID就在进程的属性里**并不是**实时在`/etc/group`里查找这些ID
* 检查要访问的文件的访问属性里的组设置
* 确定进程对文件是否有权限访问LCTT 译注:即文件的组是否是以上的组之一)
通常当访问控制的时候使用的是**有效**用户/组ID而不是**真实**用户/组ID。技术上来说当访问一个文件时使用的是**文件系统**ID,他们实际上和有效用户/组ID一样。LCTT译注这句话针对 Linux 而言。)
### 将一个用户加入一个组并不会将一个已存在的进程(的用户)加入那个组
下面是一个有趣的例子:如果我创建了一个新的组:`panda` 组并且将我自己(bork)加入到这个组,然后运行`groups` 来检查我是否在这个组里结果是我bork竟然不在这个组
```
bork@kiwi~> sudo addgroup panda
Adding group `panda' (GID 1001) ...
Done.
bork@kiwi~> sudo adduser bork panda
Adding user `bork' to group `panda' ...
Adding user bork to group panda
Done.
bork@kiwi~> groups
bork adm cdrom sudo dip plugdev lpadmin sambashare docker lxd
```
`panda`并不在上面的组里!为了再次确定我们的发现,让我们建一个文件,这个文件被`panda`组拥有,看看我能否访问它。
```
$ touch panda-file.txt
$ sudo chown root:panda panda-file.txt
$ sudo chmod 660 panda-file.txt
$ cat panda-file.txt
cat: panda-file.txt: Permission denied
```
好吧,确定了,我(bork)无法访问`panda-file.txt`。这一点都不让人吃惊,我的命令解释器并没有`panda` 组作为补充组ID运行`adduser bork panda`并不会改变这一点。
### 那进程一开始是怎么得到用户的组的呢?
这真是个非常令人困惑的问题对吗如果进程会将组的信息预置到进程的属性里面进程在初始化的时候怎么取到组的呢很明显你无法给你自己指定更多的组否则就会和Linux访问控制的初衷相违背了。。。
有一点还是很清楚的:一个新的进程是怎么从我的命令行解释器(/bash/fish里被**执行**而得到它的组的。(新的)进程将拥有我的用户 IDbork并且进程属性里还有很多组ID。从我的命令解释器里执行的所有进程是从这个命令解释器里`复刻`而来的,所以这个新进程得到了和命令解释器同样的组。
因此一定存在一个“第一个”进程来把你的组设置到进程属性里,而所有由此进程而衍生的进程将都设置这些组。而那个“第一个”进程就是你的**登录命令**,在我的笔记本电脑上,它是由‘登录’程序(`/bin/login`)实例化而来。` 登录程序` 以root身份运行,然后调用了一个 C 的库函数-`initgroups`来设置你的进程的组(具体来说是通过读取`/etc/group` 文件),因为登录程序是以root运行的所以它能设置你的进程的组。
### 让我们再登录一次
好了!既然我们的`login shell`正在运行,而我又想刷新我的进程的组设置从我们前面所学到的进程是怎么初始化组ID的我应该可以通过再次运行`login` 程序来刷新我的进程组并启动一个新的`login shell`
让我们试试下边的方法:
```
$ sudo login bork
$ groups
bork adm cdrom sudo dip plugdev lpadmin sambashare docker lxd panda
$ cat panda-file.txt # it works! I can access the file owned by `panda` now!
```
当然,成功了!现在由登录程序衍生的程序的用户是组`panda`的一部分了!太棒了!这并不会影响我其他的已经在运行的登录程序(及其子进程),如果我真的希望“所有的”进程都能对`panda`
组有访问权限。我必须完全的重启我的登陆会话,这意味着我必须退出我的窗口管理器然后再重新`login`。(LCTT译注即更新进程树的树根进程这里是窗口管理器进程。)
### newgrp命令
在 Twitter 上有人告诉我如果只是想启动一个刷新了组信息的命令解释器的话,你可以使用`newgrp`LCTT译注不启动新的命令解释器,如下:
```
sudo addgroup panda
sudo adduser bork panda
newgrp panda # starts a new shell, and you don't have to be root to run it!
```
你也可以用`sg panda bash` 来完成同样的效果,这个命令能启动一个`bash` 登录程序,而这个程序就有`panda` 组。
### seduid 将设置有效用户 ID
其实我一直对一个进程如何以`setuid root`的权限来运行意味着什么有点似是而非。现在我知道了事实上所发生的是setuid 设置了`有效用户ID`! 如果我('julia')运行了一个`setuid root` 的进程( 比如`passwd`,那么进程的**真实**用户 ID 将为`julia`,而**有效**用户 ID 将被设置为`root`。
`passwd` 需要以root权限来运行但是它能看到进程的真实用户ID是`julia` ,是`julia`启动了这个进程,`passwd`会阻止这个进程修改除了`julia`之外的用户密码。
### 就是这些了!
在 Linux Programming Interface 这本书里有很多Linux上一些功能的罕见使用方法以及Linux上所有的事物到底是怎么运行的详细解释这里我就不一一展开了。那本书棒极了我上面所说的都在该书的第九章这章在1300页的书里只占了17页。
我最爱这本书的一点是我只用读17页关于用户和组是怎么工作的内容而这区区17页就能做到内容完备详实有用。我不用读完所有的1300页书就能得到有用的东西太棒了
--------------------------------------------------------------------------------
via: https://jvns.ca/blog/2017/11/20/groups/
作者:[Julia Evans ][a]
译者:[DavidChen](https://github.com/DavidChenLiang)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]:https://jvns.ca/
[1]:http://man7.org/tlpi/