Slide 1

Slide 1 text

File descriptors, Unix sockets and other POSIX magic PyCon US 2016 Christian Heimes [email protected] @ChristianHeimes

Slide 2

Slide 2 text

File descriptors, PyCon US 2016 2 Who am I • from Hamburg / Germany • Python core contributor since 2008 • PEP 379, 452, 456 • ssl, hashlib • Python Security Response Team • I put bytes and b'' into Python 2.6

Slide 3

Slide 3 text

File descriptors, PyCon US 2016 3 Professional Life • Senior Software Engineer with Red Hat • Security Engineering & Identity Management • FreeIPA IdM • Dogtag PKI

Slide 4

Slide 4 text

File descriptors, PyCon US 2016 4 Custodia Secrets for Containers

Slide 5

Slide 5 text

File descriptors, PyCon US 2016 5 Recipes Concepts & Tools Linux / Unix only Python 3

Slide 6

Slide 6 text

File descriptors, PyCon US 2016 6 Agenda • File descriptor • Operating System 101 • File descriptors and processes • Networking and sockets • Unix sockets, containers and sandboxing • Bonus track

Slide 7

Slide 7 text

File descriptors, PyCon US 2016 7 Simplifications ahead

Slide 8

Slide 8 text

Introducing file descriptors

Slide 9

Slide 9 text

File descriptors, PyCon US 2016 9 Everything is a file

Slide 10

Slide 10 text

File descriptors, PyCon US 2016 10 Every I/O operating uses a file descriptor

Slide 11

Slide 11 text

File descriptors, PyCon US 2016 11 File descriptors are used for • reading / writing files • directories • devices • inter-process communication • network communication • I/O multiplexing • file system monitoring • …

Slide 12

Slide 12 text

File descriptors, PyCon US 2016 12

Slide 13

Slide 13 text

File descriptors, PyCon US 2016 13 Standard numbers • 0: standard input (sys.stdin) • 1: standard output (sys.stdout) • 2: standard error output (sys.stderr) • -1: error (Python raises an exception)

Slide 14

Slide 14 text

File descriptors, PyCon US 2016 14 Simple example with open('example.txt') as f: print(f.read()) with open('example.txt') as f: print(f.read())

Slide 15

Slide 15 text

File descriptors, PyCon US 2016 15 Reference file by descriptor with open('example.txt') as f: stat = os.stat(f.fileno()) os.chmod(f.fileno(), 0o640) with open('example.txt') as f: stat = os.stat(f.fileno()) os.chmod(f.fileno(), 0o640)

Slide 16

Slide 16 text

File descriptors, PyCon US 2016 16 Directory file descriptor flags = os.O_RDONLY | os.O_DIRECTORY etcdir = os.open('/etc', flags) # read /etc/passwd stat = os.stat('passwd', dir_fd=etcdir)) os.close(etcdir) flags = os.O_RDONLY | os.O_DIRECTORY etcdir = os.open('/etc', flags) # read /etc/passwd stat = os.stat('passwd', dir_fd=etcdir)) os.close(etcdir)

Slide 17

Slide 17 text

File descriptors, PyCon US 2016 17 Control hardware (silence please)

Slide 18

Slide 18 text

File descriptors, PyCon US 2016 18

Slide 19

Slide 19 text

File descriptors, PyCon US 2016 19 Open CD drive with Python import os, fcntl CDROMEJECT = 0x5309 # Linux specific! fd = os.open('/dev/cdrom', os.O_RDONLY | os.O_NONBLOCK) fcntl.ioctl(fd, CDROMEJECT, 0) os.close(fd) import os, fcntl CDROMEJECT = 0x5309 # Linux specific! fd = os.open('/dev/cdrom', os.O_RDONLY | os.O_NONBLOCK) fcntl.ioctl(fd, CDROMEJECT, 0) os.close(fd)

Slide 20

Slide 20 text

Operating System 101

Slide 21

Slide 21 text

File descriptors, PyCon US 2016 21 Dark Ages application application application application disk disk memory memory network network

Slide 22

Slide 22 text

File descriptors, PyCon US 2016 22 Modern Operating Systems application application application application disk disk memory memory network network Kernel Kernel

Slide 23

Slide 23 text

File descriptors, PyCon US 2016 23 syscall

Slide 24

Slide 24 text

File descriptors, PyCon US 2016 24 Simple example # read.py with open('example.txt') as f: print(f.read()) # read.py with open('example.txt') as f: print(f.read())

Slide 25

Slide 25 text

File descriptors, PyCon US 2016 25 Simple example $ strace ./read.py open("example.txt", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0664, st_size=19, …}) = 0 lseek(3, 0, SEEK_CUR) = 0 read(3, "Python is awesome!\n", 20) = 19 read(3, "", 1) = 0 write(1, "Python is awesome!\n", 19) = 19 write(1, "\n", 1) = 1 close(3) = 0 $ strace ./read.py open("example.txt", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0664, st_size=19, …}) = 0 lseek(3, 0, SEEK_CUR) = 0 read(3, "Python is awesome!\n", 20) = 19 read(3, "", 1) = 0 write(1, "Python is awesome!\n", 19) = 19 write(1, "\n", 1) = 1 close(3) = 0

Slide 26

Slide 26 text

File descriptors, PyCon US 2016 26 open file table (global)

Slide 27

Slide 27 text

File descriptors, PyCon US 2016 27 file descriptor table (process)

Slide 28

Slide 28 text

File descriptors, PyCon US 2016 28 open file / file descriptor tables example.txt example.txt process 1 file descriptor table fd 3 open file table entry 23 ... ... ... ... process 2 file descriptor table open 'example.txt'

Slide 29

Slide 29 text

File descriptors, PyCon US 2016 29 open file / file descriptor tables example.txt example.txt process 1 file descriptor table fd 3 fd 4 open file table entry 23 ... entry 117 ... ... process 2 file descriptor table open 'example.txt' a second time

Slide 30

Slide 30 text

File descriptors, PyCon US 2016 30 open file / file descriptor tables example.txt example.txt process 1 file descriptor table fd 3 fd 4 fd 5 open file table entry 23 ... entry 117 ... ... process 2 file descriptor table duplicate file descriptor 4

Slide 31

Slide 31 text

File descriptors, PyCon US 2016 31 open file / file descriptor tables example.txt example.txt process 1 file descriptor table fd 3 fd 4 fd 5 open file table ... ... entry 117 ... ... process 2 file descriptor table rename and close

Slide 32

Slide 32 text

File descriptors, PyCon US 2016 32 open file / file descriptor tables example.txt example.txt process 1 file descriptor table fd 3 fd 4 fd 5 open file table entry 23 ... entry 117 ... ... process 2 file descriptor table fd 7 send file descriptor to another process

Slide 33

Slide 33 text

File descriptors, PyCon US 2016 33 process and global state file descriptor table maps to open file table cloexec flags open file table position mode owner locks credentials reference count ...

Slide 34

Slide 34 text

File descriptors, PyCon US 2016 34

Slide 35

Slide 35 text

File descriptors, PyCon US 2016 35 Spawn a new process fork & exec

Slide 36

Slide 36 text

File descriptors, PyCon US 2016 36 fork() • creates a clone of the current process • inherits copy of file descriptor table • inherits copy of memory • shallow copy with Copy-on-Write

Slide 37

Slide 37 text

File descriptors, PyCon US 2016 37 fork() example import os f = open('example.txt', mode='rb', buffering=0) pid = os.fork() print("getpid {:>5}, fork {:>5}, fd {}: {}".format( os.getpid(), pid, f.fileno(), f.read(6))) f.close() $ python3 fork.py getpid 17364, fork 17365, fd 3: b'Python' getpid 17365, fork 0, fd 3: b' is aw' import os f = open('example.txt', mode='rb', buffering=0) pid = os.fork() print("getpid {:>5}, fork {:>5}, fd {}: {}".format( os.getpid(), pid, f.fileno(), f.read(6))) f.close() $ python3 fork.py getpid 17364, fork 17365, fd 3: b'Python' getpid 17365, fork 0, fd 3: b' is aw'

Slide 38

Slide 38 text

File descriptors, PyCon US 2016 38 exec() • replaces current program code • file descriptors are inherited by default • unless cloexec flag is set • potential security risk • PEP 446 -- Make newly created file descriptors non-inheritable (Victor Stinner)

Slide 39

Slide 39 text

File descriptors, PyCon US 2016 39 Summary • applications must go through the Kernel • syscalls call into kernel space • file descriptors point to global table • global tables contains mode and position • new process with fork() and exec()

Slide 40

Slide 40 text

subprocess.PIPE $ ls | grep py

Slide 41

Slide 41 text

File descriptors, PyCon US 2016 41

Slide 42

Slide 42 text

File descriptors, PyCon US 2016 43 pipe example import os readend, writeend = os.pipe() pid = os.fork() if pid != 0: # parent process os.close(writeend) with open(readend) as f: print(f.read()) else: # child process os.close(readend) os.dup2(writeend, 1) # 1: stdout os.execl('/bin/ls', # programm 'ls', '-l', 'example.txt') # argv 0, 1, 2 import os readend, writeend = os.pipe() pid = os.fork() if pid != 0: # parent process os.close(writeend) with open(readend) as f: print(f.read()) else: # child process os.close(readend) os.dup2(writeend, 1) # 1: stdout os.execl('/bin/ls', # programm 'ls', '-l', 'example.txt') # argv 0, 1, 2 1 2 3 4 6 5 7

Slide 43

Slide 43 text

network sockets

Slide 44

Slide 44 text

File descriptors, PyCon US 2016 45

Slide 45

Slide 45 text

File descriptors, PyCon US 2016 46

Slide 46

Slide 46 text

File descriptors, PyCon US 2016 47 socket parameters • addressing / routing • IPv4: AF_INET • IPv6: AF_INET6 • flow control • TCP: SOCK_STREAM • UDP: SOCK_DGRAM

Slide 47

Slide 47 text

File descriptors, PyCon US 2016 48 socket server from socket import socket, SOCK_STREAM,AF_INET server = socket(AF_INET, SOCK_STREAM) server.bind(('0.0.0.0', 443)) server.listen(1) while True: conn, addr = server.accept() from socket import socket, SOCK_STREAM,AF_INET server = socket(AF_INET, SOCK_STREAM) server.bind(('0.0.0.0', 443)) server.listen(1) while True: conn, addr = server.accept()

Slide 48

Slide 48 text

File descriptors, PyCon US 2016 49 socket client from socket import (socket, SOCK_STREAM, AF_INET, AF_INET6) cl4 = socket(AF_INET, SOCK_STREAM) cl4.connect(('104.130.43.121', 443)) cl6 = socket(AF_INET6, SOCK_STREAM) cl6.connect(('2001:4802:7901:0:e60a:1375:0:5', 443)) from socket import (socket, SOCK_STREAM, AF_INET, AF_INET6) cl4 = socket(AF_INET, SOCK_STREAM) cl4.connect(('104.130.43.121', 443)) cl6 = socket(AF_INET6, SOCK_STREAM) cl6.connect(('2001:4802:7901:0:e60a:1375:0:5', 443))

Slide 49

Slide 49 text

Unix Sockets (Unix Domain Sockets / Local Sockets)

Slide 50

Slide 50 text

File descriptors, PyCon US 2016 51

Slide 51

Slide 51 text

File descriptors, PyCon US 2016 52

Slide 52

Slide 52 text

File descriptors, PyCon US 2016 53 Unix sockets from socket import (socket, socketpair, SOCK_STREAM, AF_UNIX) server = socket(AF_UNIX, SOCK_STREAM) server.bind('/path/to/file') client = socket(AF_UNIX, SOCK_STREAM) client.connect('/path/to/file') a, b = socketpair() from socket import (socket, socketpair, SOCK_STREAM, AF_UNIX) server = socket(AF_UNIX, SOCK_STREAM) server.bind('/path/to/file') client = socket(AF_UNIX, SOCK_STREAM) client.connect('/path/to/file') a, b = socketpair()

Slide 53

Slide 53 text

File descriptors, PyCon US 2016 54 peer credentials import socket, struct def getpeercred(sock): fmt = "iII" size = struct.calcsize(fmt) raw = sock.getsockopt(socket.SOL_SOCKET, socket.SO_PEERCRED, size) pid, uid, gid = struct.unpack(fmt, raw) return pid, uid, gid >>> getpeercred(uds) (31362, 0, 0) import socket, struct def getpeercred(sock): fmt = "iII" size = struct.calcsize(fmt) raw = sock.getsockopt(socket.SOL_SOCKET, socket.SO_PEERCRED, size) pid, uid, gid = struct.unpack(fmt, raw) return pid, uid, gid >>> getpeercred(uds) (31362, 0, 0)

Slide 54

Slide 54 text

File descriptors, PyCon US 2016 55 peer security context import socket SO_PEERSEC = getattr(socket, 'SO_PEERSEC', 31) def getpeersec(sock): raw = sock.getsockopt(socket.SOL_SOCKET, SO_PEERSEC, 256) return raw.rstrip(b'\x00').decode('utf-8') >>> getpeersec(uds) 'system_u:system_r:svirt_lxc_net_t:s0:c560,c872' import socket SO_PEERSEC = getattr(socket, 'SO_PEERSEC', 31) def getpeersec(sock): raw = sock.getsockopt(socket.SOL_SOCKET, SO_PEERSEC, 256) return raw.rstrip(b'\x00').decode('utf-8') >>> getpeersec(uds) 'system_u:system_r:svirt_lxc_net_t:s0:c560,c872'

Slide 55

Slide 55 text

File descriptors, PyCon US 2016 56 Unix sockets and containers ● Unix sockets work between containers ● SELinux! ● Kernel translates PID name space • pid 31362 ● Multi-Category Security (MCS) Separation • system_u:system_r:svirt_lxc_net_t:s0:c560,c872

Slide 56

Slide 56 text

File descriptors, PyCon US 2016 57 Docker # cat /proc/31336/cgroup 7:pids:/system.slice/docker-de20f4cb....scope # docker inspect de20f4cb... ... "ProcessLabel": "system_u:system_r:svirt_lxc_net_t:s0:c560,c872", ... "Config": { "Labels": { "io.kubernetes.container.name": "custodia-pwmgr", "io.kubernetes.pod.name": "custodia-pwmgr-tqxtz", "io.kubernetes.pod.namespace": "default", ... }} # cat /proc/31336/cgroup 7:pids:/system.slice/docker-de20f4cb....scope # docker inspect de20f4cb... ... "ProcessLabel": "system_u:system_r:svirt_lxc_net_t:s0:c560,c872", ... "Config": { "Labels": { "io.kubernetes.container.name": "custodia-pwmgr", "io.kubernetes.pod.name": "custodia-pwmgr-tqxtz", "io.kubernetes.pod.namespace": "default", ... }}

Slide 57

Slide 57 text

File descriptors, PyCon US 2016 58 sending file descriptors # sender with open(filename) as f: send_fds(sender, b'\x01', [f.fileno()]) # receiver msg, fds = recv_fds(rcv, msglen=1, maxfds=1) with open(fds[0]) as f: f.read() # sender with open(filename) as f: send_fds(sender, b'\x01', [f.fileno()]) # receiver msg, fds = recv_fds(rcv, msglen=1, maxfds=1) with open(fds[0]) as f: f.read()

Slide 58

Slide 58 text

File descriptors, PyCon US 2016 59 seccomp sandboxing syscall filter OpenSSH, Tor, Firefox media plugins, Google Chrome sandbox disk disk network network broker broker ● read, write, close ● open, socket, unlink ● fork, exec

Slide 59

Slide 59 text

Beyond

Slide 60

Slide 60 text

File descriptors, PyCon US 2016 61 memory mapped I/0 • mmap • numpy.memmap

Slide 61

Slide 61 text

File descriptors, PyCon US 2016 62 memory fd • temporary file in memory • file sealing

Slide 62

Slide 62 text

File descriptors, PyCon US 2016 63 Efficient I/O with zero-copy • sendfile() • copy_file_range() • splice() / vmsplice() / tee() • AF_KTLS (Kernel space TLS)

Slide 63

Slide 63 text

File descriptors, PyCon US 2016 64 event-driven I/O • select • poll • epoll

Slide 64

Slide 64 text

File descriptors, PyCon US 2016 65 Open Space 4pm, room C120+C121 @ChristianHeimes / [email protected]

Slide 65

Slide 65 text

THANK YOU plus.google.com/+RedHat linkedin.com/company/red-hat youtube.com/user/RedHatVideos facebook.com/redhatinc twitter.com/RedHatNews

Slide 66

Slide 66 text

Images sources ● https://commons.wikimedia.org/wiki/File:Washington,_D.C._Miss_Helen_ Ringwald_works_with_the_pneumatic_tubes.jpg ( ● https://commons.wikimedia.org/wiki/File:Rohrpostbuechse.jpg (CC BY- SA 3.0) ● https://commons.wikimedia.org/wiki/File:Datasette_c2n.jpg (CC BY-SA 3.0)