2

So I have this server:

import socket
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.bind('/tmp/sock.sock')
sock.listen(1)
while True:
    conn, ca = sock.accept()
    print(conn.recv(1024))

And this client:

import socket
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect('/tmp/sock.sock')
sock.send('Hello, World')

The actual sending and receiving is a bit more complex, but works perfectly, as long as both the server and client is run by root. It also works when the client is run as root and the server is run by a normal user. But when I try to run the server with root and client by normal user I get this:

$ python3 client.py
connecting to /tmp/sock.sock
[Errno 13] Permission denied

How can I solve this? The server need to be run as root and the client as an unprivileged user.

I have understood as much as that it has to do with file permissions for the socket file, but I cannot see how I should fix it. I could not see any options to pass to the socket constructor that would fix this.

klutt
  • 30,332
  • 17
  • 55
  • 95

3 Answers3

1

Change file mode to allow anyone to access it. Add this lines in your server

import os
os.chmod("/tmp/sock.sock", 0o777)
klutt
  • 30,332
  • 17
  • 55
  • 95
P. Dmitry
  • 1,123
  • 8
  • 26
  • `0777` it's an octal number. If it doesn't work for in for some reason try to use `511` it's `0777` as decemal number – P. Dmitry Oct 08 '19 at 07:54
  • 1
    Using `0` for octal does not work in Python3. There you have to use `0o`. I changed your answer and now it works. Thanks. – klutt Oct 08 '19 at 07:55
1

This is not a good idea, if you have your whole server designed to run as root, there's a design error somewhere.

What happens here is that the root user creates the /tmp/sock.sock file with its own privileges, and that file is accessible only to the user that created it.

The problem is that even if the bind method on Linux (but not on Mac OS!) has been patched not to follow symlinks, you're still creating a security issue. Suppose that the file is not there, and the server is not running. A regular user can then create a symlink from /tmp/sock.sock to /etc/passwd and when the server tries to start, it will try to bind, set the permissions and then fail because of the symlink, but the permissions will be set. So you'll have your passwd file set as writable.

Even if there were no permission issues, suppose that someone sends a really big amount of data and it fills the disk making the server crash. If you run the server with a regular user you can set a maximum quota, but you don't want to set that on root.

My suggestion is: create a new user whose only task is to run the server and the client and has the minimum set of permissions required.

ChatterOne
  • 3,381
  • 1
  • 18
  • 24
  • In the real service I do an `unlink` before. But the service does need to perform certain things with root privs. – klutt Oct 08 '19 at 08:52
0

Thanks to P.Dmitry for pointing me in the right direction. I ended up with this modification of the same idea, which suits my needs a little bit better:

import pwd
import grp
...
# Change the owner of the socket
os.chown('/tmp/sock.sock', 
         pwd.getpwnam('user').pw_uid, 
         grp.getgrnam('group').gr_gid)
# Restrict access to only this user (and root of course)
os.chmod('/tmp/sock.sock', 0o600)

Note:

0o is the octal prefix in Python3. In Python2 it's just 0 like in C.

klutt
  • 30,332
  • 17
  • 55
  • 95