Commit 5d7f12dd authored by Lars Beckers's avatar Lars Beckers
Browse files

nullmailer: add enqueueing daemon for restricted processes

parent 368bbc57
---
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
  • Ich bin mir ziemlich sicher, dass das so nicht funktioniert. Wenn müsste man eher alle php-fpm@.service in ein Target stopfen und das machen, aber systemd kann afaik keine Wildcards.

  • Es gibt eine Einschränkung der Art für [Install] WantedBy=, aber für [Unit] Before= oder andere Ordering- oder Dependency-Direktiven steht nichts dergleichen in der Dokumentation. Hätte es nicht funktioniert, hätte ich die Direktive aber auch eher weggelassen statt mir Targets für das optionale Ordering über verschiedene Rollen hinweg zu basteln.

Please register or sign in to reply
[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
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 @@
tags:
- 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
- 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
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment