Skip to content

Instantly share code, notes, and snippets.

@juztas
Created December 8, 2022 16:40
Show Gist options
  • Select an option

  • Save juztas/00cf0e654d68213c5def1b54bcf69aac to your computer and use it in GitHub Desktop.

Select an option

Save juztas/00cf0e654d68213c5def1b54bcf69aac to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
"""
Wrapper for docker binary
- If we are docker-run'ing a trusted image, add capabilities
To enable, copy the program and set the following Condor config:
DOCKER = /usr/local/libexec/condor-docker
"""
import argparse
import contextlib
import socket
import subprocess
import sys
import syslog
DOCKER_BIN = '/usr/bin/docker'
TRUSTED_IMAGES = [
'swarm-01.ultralight.org:5000/condor-wn:latest',
]
TRUSTED_CAPABILITIES = [
# '--cap-add=SYS_ADMIN', # priv singularity
# '--cap-add=SYS_PTRACE', # priv singularity
# '--cap-add=IPC_LOCK', # priv singularity
'--security-opt', 'seccomp=unconfined', # unpriv singularity
'--security-opt', 'systempaths=unconfined', # unpriv singularity
'--device=/dev/fuse', # unpriv singularity
'--security-opt', 'no-new-privileges', # unpriv singularity
]
class ArgumentParser(argparse.ArgumentParser):
"""Don't exit on argparse error"""
# https://www.python.org/dev/peps/pep-0389/#discussion-sys-stderr-and-sys-exit
def error(self, message):
pass
def main():
"""Main"""
parser = ArgumentParser(add_help=False)
parser.add_argument('exe')
args, _ = parser.parse_known_args()
# By default, we just pass on the command
cmd = [DOCKER_BIN] + sys.argv[1:]
# Are we handling 'docker run' or 'docker create'?
if args.exe == 'run' or args.exe == 'create':
# Yes, parse the options to 'docker run'
parser = ArgumentParser(add_help=False)
parser.add_argument('exe')
parser.add_argument('image')
parser.add_argument('command', nargs=argparse.REMAINDER)
# We have to tell argparse about all the --parameters with an argument
# which are passed from Condor to 'docker run'.
# Otherwise we cannot identify the Docker image because it is a
# positional argument.
parser.add_argument('--hostname', action='append')
parser.add_argument('--name', action='append')
parser.add_argument('--env', '-e', action='append')
parser.add_argument('--volume', action='append')
parser.add_argument('--device', action='append')
parser.add_argument('--workdir', action='append')
parser.add_argument('--user', action='append')
parser.add_argument('--group-add', dest='group-add', action='append')
parser.add_argument('--publish', '-p', action='append')
parser.add_argument('--network', action='append')
args, docker_opts = parser.parse_known_args()
# Docker (occasionally) doesn't reserve the same port for both IPv4 and IPv6
# - https://github.com/moby/libnetwork/issues/2639
# If no host port is specified, reserve one ourselves
if args.publish:
for i, port in enumerate(args.publish):
if port.isdigit():
args.publish[i] = str(port_reserve()) + ':' + port
# Put our parsed options back into the list
for arg, vals in vars(args).items():
if arg in ['exe', 'image', 'command', 'network']:
continue
for val in vals or []:
docker_opts += ['--' + arg, val]
# Did we successfully parse the docker run command?
if args.image and args.command:
# Is the Docker image in the trusted list?
if args.image in TRUSTED_IMAGES:
docker_opts += TRUSTED_CAPABILITIES
# Build the new 'docker run' command
cmd = [DOCKER_BIN, args.exe] + docker_opts + [args.image] + args.command
syslog.syslog('"%s" => "%s"' % (' '.join(sys.argv), ' '.join(cmd)))
ret = subprocess.call(cmd)
sys.exit(ret)
# Assign an ephemeral host port
# - Copied from ephemeral-port-reserve (MIT license)
# - https://github.com/Yelp/ephemeral-port-reserve/
def port_reserve(ip='127.0.0.1', port=0):
port = int(port)
with contextlib.closing(socket.socket()) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((ip, port))
# the connect below deadlocks on kernel >= 4.4.0 unless this arg is greater than zero
s.listen(1)
sockname = s.getsockname()
# these three are necessary just to get the port into a TIME_WAIT state
with contextlib.closing(socket.socket()) as s2:
s2.connect(sockname)
sock, _ = s.accept()
with contextlib.closing(sock):
return sockname[1]
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment