| tags: [ development linux shared work docker ] categories: [Development ]
Controlling Shared Processes
Controlling Shared Processes
Starting out a new year and I want to finish up what I started on team collaboration on unix systems. The last two posts have been dedicated to working in a shared directory using groups. When I started those, I had several use cases that I thought I would be able to knock off pretty quickly.
For reference, here are the use cases that I want to cover.
-
Share edit files: As a team of developers, we want to edit the same file (usually a configuration file).
-
Adding group perms: As a team of developers, we want to add new files and not have to always remember to change the group for each file.
-
Disallow intruders: As a team of developers, we do not want someone that is not part of the team to have write or delete permission to our working directory.
-
Modifying old files to add group: As a team of developers that have started out using ugo+rwx on all our files, we want to switch over to using a group and reduce our permissions down just the group (o-rwx)
-
Run a script as group: As a team of developers, we want anyone on the team to run a script and anyone else on the team is permitted to kill and restart the script
Run a script as a group
To get into this one, I need to set the stage and demonstrate the problem that Moss and Roy face as a team that is collaborating. To do that, I’m going to fire up my docker image, and write a quick little script that runs indefinitely and writes a file to a log directory.
Reminder and setup
To remind you where we started from, here is are our cast of characters, Moss, Roy and Jen:
[root /]$ id moss
uid=1000(moss) gid=1000(moss) groups=1000(moss),1003(stealth)
[root /]$ id roy
uid=1001(roy) gid=1001(roy) groups=1001(roy),1003(stealth)
[root /]$ id jen
uid=1002(jen) gid=1002(jen) groups=1002(jen)
[root /]$
Notice that Moss and Roy both are members of the stealth
group and
Jen is not a member of that group.
Here is the project directory setup:
[root /]$ ls -al /mnt/reynholm/
total 8
drwxrws--- 2 moss stealth 4096 Jan 5 17:15 .
drwxr-xr-x 3 root root 4096 Dec 31 19:38 ..
Notice that Mos owns the /tmn/reynholm
directory and currently there
isn’t anything in the directory – which we’ll change shortly.
Simple server script
Here is my very simple python server script. No this isn’t a real server, it doesn’t have all those bells, it’s just a process that should run indefinitely and emits some information to a log file.
#!/bin/python
import time
import datetime
import logging
logging.basicConfig(filename='./log/data.log', level=logging.DEBUG)
while(1):
logging.debug("CurrentTime:{} euid:{} egid:{}".format(datetime.datetime.now(),os.geteuid(),os.getegid()))
time.sleep(30)
Moss used vi
to create the file and he saved it in the
‘/mnt/reynholm` directory.
[moss /]$ ls -al /mnt/reynholm/
total 12
drwxrws--- 2 moss stealth 4096 Jan 5 17:24 .
drwxr-xr-x 3 root root 4096 Dec 31 19:38 ..
-rw-rw---- 1 moss stealth 223 Jan 5 17:24 TheInternet.py
Notice that the server TheInternet.py
is read write for user and
group. It does have a ‘shebang’ line at the top: #!/bin/python
so
it should be runnable from the command line if the execute bits are
set on the file. Both Moss and Roy don’t want to bother with running
python TheInternet.py
so Moss changes the name to TheInternet
and
sets the execute bits on the file. He also creates the log
directory that will contain the log files which demonstrate the
program is running.
[moss /]$ mv /mnt/reynholm/TheInternet.py /mnt/reynholm/TheInternet
[moss /]$ chmod ug+x /mnt/reynholm/TheInternet
[moss reynholm]$ mkdir /mnt/reynholm/log
[moss reynholm]$ ls -al /mnt/reynholm/
total 16
drwxrws--- 3 moss stealth 4096 Jan 5 17:34 .
drwxr-xr-x 3 root root 4096 Dec 31 19:38 ..
-rwxrwx--- 1 moss stealth 224 Jan 5 17:32 TheInternet
drwxrws--- 2 moss stealth 4096 Jan 5 17:34 log
Moss starts up the program and pushes it to the background using the
&
operator.
[moss reynholm]$ ./TheInternet &
[moss reynholm]$ pgrep -a TheInternet
2154 /bin/python ./TheInternet
[moss reynholm]$ tail ./log/data.log
DEBUG:root:CurrentTime:2019-01-06 20:10:52.913498 euid:1000 egid:1000
DEBUG:root:CurrentTime:2019-01-06 20:11:22.943739 euid:1000 egid:1000
DEBUG:root:CurrentTime:2019-01-06 20:11:52.974058 euid:1000 egid:1000
The process appears to be running in the background now and writing to the log file. It should not terminate if Moss logs off and Roy logs in.
[root reynholm]$ su roy
[roy reynholm]$ ps
PID TTY TIME CMD
2160 ? 00:00:00 bash
2173 ? 00:00:00 ps
[roy reynholm]$ pgrep -a TheInternet
2154 /bin/python ./TheInternet
Notice that Roy initially couldn’t see the process using just a ps
command. Because he does not own the process. However if he uses
pgrep
to search for the process using a know expression he is able
to see the processes that Moss is running.
Roy can also look at the log file because both Roy and Moss are in the same group and the log directory is readable by the group.
[roy reynholm]$ tail -f ./log/data.log
DEBUG:root:CurrentTime:2019-01-06 20:10:52.913498 euid:1000 egid:1000
DEBUG:root:CurrentTime:2019-01-06 20:11:22.943739 euid:1000 egid:1000
...
DEBUG:root:CurrentTime:2019-01-06 20:13:23.029303 euid:1000 egid:1000
<ctrl-c>
Stage is set
Ok, so now for the big question, can Roy control the process that Moss started? Let’s say that Moss is unavailable at the moment and Roy would like to make a code change to the script and start it backup.
I imagine he would do this by killing the process, editing the file and starting it backup:
[roy /]$ ps -u moss
PID TTY TIME CMD
2154 ? 00:00:00 TheInternet
[roy /]$ kill 2154
bash: kill: (2154) - Operation not permitted
Well, that didn’t work that way. On deeper inspection of the script
that Moss ran, I can see that the process is owned by Moss (uid:1000)
and that the group is the moss group (gid:1000) not the stealth group
(gid: 1003). So Roy (uid:1001) has no permission to control
TheInternet
even though the process was started in the group
directory /mnt/reynholm
.
[roy /]$ ps -o uid,gid,pid,cmd 2154
UID GID PID CMD
1000 1000 2154 /bin/python ./TheInternet
The wikipedia documentation indicates that this is, in fact, expected behavior because of a potential security hole. Modern unix systems do not allow a script to take on the group permission. However this wikipedia link indcates that: Although it is still possible for a binary executable to assume the group permission. However this assertion is rejected by other threads.
Binary Process Assume Group id
So which is it? Can a binary process be controlled using setguid and
group ownership or not. Roy decided to test this out. By writing a simple
go binary program that is very similar to the
python script TheInternet
. Moss and Roy will find out if they could
have shared control over the binary.
The small little docker linux system didn’t have Go so I installed it and added it to the path.
[root /]$ curl https://dl.google.com/go/go1.11.4.linux-amd64.tar.gz > /tmp/go1.11.4.linux-amd64.tar.gz
...
[root /]$ tar -C /usr/local -xzf /tmp/go1.11.4.linux-amd64.tar.gz
...
[root /]$ export PATH=$PATH:/usr/local/go/bin
[root /]$
[root /]$ go version
go version go1.11.4 linux/amd64
Roy fires up emacs1 and developed the following go program.
package main
import (
"log"
"time"
"os"
)
func main() {
log.SetPrefix("Reynholm:TheInternet:")
log.SetFlags(log.Llongfile | log.LUTC | log.Ldate | log.Ltime)
fh,err := os.Create("./log/godata.log")
if err != nil {
log.Fatal(err)
}
log.SetOutput(fh)
for {
log.Printf("euid:%d egid:%d",os.Geteuid(),os.Getegid())
time.Sleep(30 * time.Second)
}
}
He then compiles the go program into an executable
[roy reynholm]$ go build TheInternetGo.go
[roy reynholm]$ ls -al
total 2008
drwxrws--- 3 moss stealth 4096 Jan 6 20:31 .
drwxr-xr-x 3 root root 4096 Dec 31 19:38 ..
-rwxrwx--- 1 moss stealth 277 Jan 6 20:02 TheInternet
-rwxrwx--- 1 roy stealth 2034792 Jan 6 20:31 TheInternetGo
-rw-rw---- 1 moss stealth 381 Jan 6 17:07 TheInternetGo.go
drwxrws--- 2 moss stealth 4096 Jan 6 20:10 log
Just to make sure that the group id is responsible for the process,
Roy sets the setgid bit for TheInternetGo
binary.
[roy reynholm]$ chmod g+s TheInternetGo
[moss /]$ ls -l /mnt/reynholm/TheInternetGo
-rwxrws--- 1 roy stealth 2034792 Jan 6 20:31 /mnt/reynholm/TheInternetGo
The he fires up the process in the background.
[roy reynholm]$ ./TheInternetGo &
[1] 2334
[roy reynholm]$ tail -f log/godata.log
Reynholm:TheInternet:2019/01/06 20:34:13 /mnt/reynholm/TheInternetGo.go:19: euid:1001 egid:1003
Now Moss tries to kill the process.
[roy reynholm]$ exit
exit
[root /]$ su moss
[moss /]$ pgrep TheInternetGo
2334
[moss /]$ kill 2334
bash: kill: (2334) - Operation not permitted
Nope. The thread. asserting that no user can kill another users’ process appears to be correct.
The final assertion here is:
Using setguid group permissions, it is not possible for Moss to kill a process started by Roy and conversely, it is not possible for Roy to kill a process started by Moss.
Conclusions
Well, so what I know now is that although setguid bit is useful for group collaboration on files and directories, it does not enable members of the group to control processes started by other members of the group. Regardless of whether those processes are scripts or binaries.
Ok so my final use-case is still unsatisified.
- Run a script as group: As a team of developers, we want anyone on the team to run a script and anyone else on the team is permitted to kill and restart the script
Next post, I’ll head down a different path that should address this final use case.
Here are the related posts so far.
- Collaborative Work Simple and Exposed
- Using Groups for Collaborative Work
- Controlling Shared Processes
Reference
https://superuser.com/questions/137207/how-to-kill-a-process-started-with-a-different-user-without-being-root-or-sudoer https://en.wikipedia.org/wiki/Group_identifier https://wiki.centos.org/TipsAndTricks/BecomingRoot https://en.wikipedia.org/wiki/Setuid#Effects_of_setuid_and_setgid http://man7.org/linux/man-pages/man5/acl.5.html https://golangcode.com/while-true/ https://blog.scalyr.com/2018/06/go-logging/