Skip to content
Snippets Groups Projects
Commit 5d7f12dd authored by Lars Beckers's avatar Lars Beckers
Browse files

nullmailer: add enqueueing daemon for restricted processes

parent 368bbc57
No related branches found
No related tags found
No related merge requests found
---
nullmailer_enable_queued: false
#!/usr/bin/env python3
import socket
import sys
SERVER_SOCKET = '/run/nullmailer/queued.sock'
# reference implementation from docs.python.org; will be in v3.9
if not hasattr(socket.socket, 'send_fds'):
import array
def send_fds(sock, msg, fds):
ancdata = [(socket.SOL_SOCKET, socket.SCM_RIGHTS, array.array("i", fds).tobytes())]
return sock.sendmsg([msg], ancdata)
socket.socket.send_fds = send_fds
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
sock.connect(SERVER_SOCKET)
except socket.error as msg:
print(msg)
sys.exit(255)
ret = 255
try:
sock.send_fds((1).to_bytes(4, 'big'), [sys.stdin.fileno()])
ret = int.from_bytes(sock.recv(1024), 'big')
finally:
sock.close()
sys.exit(ret)
#!/usr/bin/env python3
import os
import socket
import socketserver
import struct
import subprocess
import sys
SERVER_SOCKET = '/run/nullmailer/queued.sock'
QUEUE_BINARY = '/usr/sbin/nullmailer-queue'
# from: https://github.com/ganeti/ganeti/blob/4d8c16a0739c93400471023b3cf9adf73a037b8f/lib/netutils.py#L51
_STRUCT_UCRED = "iII" # pid, uid, gid
_STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
def get_peer_credentials(sock):
peercred = sock.getsockopt(socket.SOL_SOCKET, socket.SO_PEERCRED, _STRUCT_UCRED_SIZE)
return struct.unpack(_STRUCT_UCRED, peercred)
# reference implementation from docs.python.org; will be in v3.9
if not hasattr(socket.socket, 'recv_fds'):
import array
def recv_fds(sock, msglen, maxfds):
fds = array.array("i") # Array of ints
msg, ancdata, flags, addr = sock.recvmsg(msglen, socket.CMSG_LEN(maxfds * fds.itemsize))
for cmsg_level, cmsg_type, cmsg_data in ancdata:
if cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS:
# Append data, ignoring any truncated integers at the end.
fds.frombytes(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)])
return msg, list(fds)
socket.socket.recv_fds = recv_fds
def drop_privs(uid, gid):
try:
os.setgid(gid)
os.setuid(uid)
except OSError as e:
print(f'could not drop privs to user {uid}, group {gid}, because {e}')
sys.exit(1)
class QueueingHandler(socketserver.BaseRequestHandler):
def handle(self):
print(f'request incoming')
try:
fd = self.request.recv_fds(1024, 1)[1][0]
except IndexError:
print(f'did not receive fd')
try:
self.request.send((255).to_bytes(4, 'big'))
except:
pass
return
print(f'received fd')
(uid, gid) = get_peer_credentials(self.request)[1:]
print(f'by user: {uid}, {gid}')
try:
if sys.version_info.minor >= 9:
proc = subprocess.run([QUEUE_BINARY], stdin=fd, group=gid, user=uid)
else:
# This is not strictly safe with a threading server due to preexec_fn.
proc = subprocess.run([QUEUE_BINARY], stdin=fd, preexec_fn=lambda: drop_privs(uid, gid))
except Exception as e:
print(f'could not execute {QUEUE_BINARY}, because {e}')
try:
self.request.send((255).to_bytes(4, 'big'))
except:
pass
return
print(f'ran {QUEUE_BINARY}, returns {proc.returncode}')
self.request.send(proc.returncode.to_bytes(4, 'big'))
if __name__ == '__main__':
try:
os.unlink(SERVER_SOCKET)
except OSError:
if os.path.exists(SERVER_SOCKET):
raise
with socketserver.ThreadingUnixStreamServer(SERVER_SOCKET, QueueingHandler) as server:
os.chmod(SERVER_SOCKET, 0o0777)
server.serve_forever()
[Unit]
Description=Enqueueing facility for restricted clients of nullmailer
After=networking.target
Before=php-fpm@.service
[Service]
ExecStart=/usr/local/sbin/nullmailer-queued
RuntimeDirectory=nullmailer
ReadWritePaths=/var/spool/nullmailer
ProtectSystem=strict
ProtectControlGroups=yes
ProtectHome=yes
PrivateTmp=yes
--- ---
# file: roles/nullmailer/handlers/main.yml
- name: restart nullmailer - name: restart nullmailer
service: name=nullmailer state=restarted service:
name: nullmailer
state: restarted
- name: restart nullmailer-queued
service:
name: nullmailer-queued
state: restarted
- name: reload systemd service files
systemd:
daemon_reload: true
...@@ -95,4 +95,33 @@ ...@@ -95,4 +95,33 @@
tags: tags:
- nullmailer - nullmailer
- name: ensure there is a custom enqueueing service
copy:
src: nullmailer-queued.service
dest: /etc/systemd/system/
notify:
- reload systemd service files
when: nullmailer_enable_queued
- meta: flush_handlers - meta: flush_handlers
- name: ensure there is a custom enqueueing daemon
copy:
src: "nullmailer-{{ item }}.py"
dest: "/usr/local/sbin/nullmailer-{{ item }}"
owner: root
group: root
mode: '0755'
with_items:
- queuec
- queued
notify:
- restart nullmailer-queued
when: nullmailer_enable_queued
- name: ensure the custom enqueueing service is enabled and running
service:
name: nullmailer-queued
state: started
enabled: true
when: nullmailer_enable_queued
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment