Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gunicorn fail to bind abstract unix domain socket #2699

Open
horowity opened this issue Dec 9, 2021 · 5 comments
Open

Gunicorn fail to bind abstract unix domain socket #2699

horowity opened this issue Dec 9, 2021 · 5 comments
Assignees

Comments

@horowity
Copy link

horowity commented Dec 9, 2021

Abstract Unix domain sockets are not related to the file system and their path starts with \0.
When trying to bind gunicorn to such socket we get the following exception:

~/gunicorn$ cat gunicorn.conf.py 
bind = "unix:\0/foo/bar"
~/gunicorn$ /usr/bin/python3 /home/ubuntu/.local/bin/gunicorn -w 2 _--config gunicorn.conf.py_ echoapp:app
[2021-12-09 21:19:29 +0000] [1310108] [INFO] Starting gunicorn 20.1.0
Traceback (most recent call last):
  File "/home/ubuntu/.local/bin/gunicorn", line 8, in <module>
    sys.exit(run())
  File "/home/ubuntu/.local/lib/python3.8/site-packages/gunicorn/app/wsgiapp.py", line 67, in run
    WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]").run()
  File "/home/ubuntu/.local/lib/python3.8/site-packages/gunicorn/app/base.py", line 231, in run
    super().run()
  File "/home/ubuntu/.local/lib/python3.8/site-packages/gunicorn/app/base.py", line 72, in run
    Arbiter(self).run()
  File "/home/ubuntu/.local/lib/python3.8/site-packages/gunicorn/arbiter.py", line 198, in run
    self.start()
  File "/home/ubuntu/.local/lib/python3.8/site-packages/gunicorn/arbiter.py", line 155, in start
    self.LISTENERS = sock.create_sockets(self.cfg, self.log, fds)
  File "/home/ubuntu/.local/lib/python3.8/site-packages/gunicorn/sock.py", line 184, in create_sockets
    sock = sock_type(addr, conf, log)
  File "/home/ubuntu/.local/lib/python3.8/site-packages/gunicorn/sock.py", line 108, in __init__
    st = os.stat(addr)
ValueError: embedded null byte

2 questions:

  1. Is there any open issue for this? tried to look for such but couldn't find it.
  2. Would you accept PR with a fix for this bug?
@jamadden
Copy link
Collaborator

jamadden commented Dec 10, 2021

I'll just add that abstract Unix domain sockets "is a nonportable Linux extension".

@benoitc
Copy link
Owner

benoitc commented Dec 11, 2021

@horowity an abstract socket address is not (yet) supported by Gunicorn: https://github.com/benoitc/gunicorn/blob/master/gunicorn/sock.py#L101-L139

Support of it seems easy. You will need to check the pattern in init (https://github.com/benoitc/gunicorn/blob/master/gunicorn/sock.py#L105) and when Fd is none, ignore the filesystem check that use os.stat when this is an abstract socket.

Similarly in bind, ignore the chown and umask functions when it is an abstract socket : https://github.com/benoitc/gunicorn/blob/master/gunicorn/sock.py#L122-L126

Please add a test for it creating such socket and testing if launch is OK. Let me know if you have any other questions.

@horowity
Copy link
Author

@benoitc - could you please review the proposed PR - Abstract unix #2700 for adding such support?

@horowity
Copy link
Author

horowity commented Jan 4, 2022

@benoitc - kind reminder, we are waiting for your approval for the PR.

@insanitybit
Copy link

insanitybit commented Aug 24, 2024

I worked around this like so. In real code you'd likely want to do something a bit more robust. I haven't tested this much yet but it works so far.

class CustomServer(Server):
    def create_unix_server(self, sock, config):
        # Custom method to handle abstract socket
        if config.uds and config.uds.startswith("\0"):  # Check for abstract socket
            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
            sock.bind(config.uds)
            sock.listen(config.backlog)
        else:
            # Fall back to the default behavior for filesystem-based sockets
            sock = super().create_unix_server(sock, config)
        return sock

    async def startup(self, sockets):
        config = self.config
        uds = config.uds

        if uds and uds.startswith("\0"):
            sock = self.create_unix_server(None, config)
            await super().startup([sock])
        else:
            await super().startup(sockets)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants