Skip to content

Commit c8f535f

Browse files
committed
ALLOWED_PORT_MAPPINGS config option to make run -p args configurable
1 parent 8ab638c commit c8f535f

3 files changed

Lines changed: 58 additions & 1 deletion

File tree

CHANGELOG

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ New features:
2626
- Use of DOCKER_HOST env var will raise an ERROR.
2727
- Users can now re-attach to their previously started containers (in case of
2828
connection loss for example).
29+
- ALLOWED_PORT_MAPPINGS config var to allow configurable explicit user port
30+
publishing (docker run -p). Defaults to selectable container ports that are
31+
mappable to random host port (like in -P), but user can decide if host local
32+
or world accessible.
2933
- Improved support for nvidia-docker's NV_GPU env var, which is now checked
3034
against admin config options:
3135

userdocker/config/default.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,37 @@
162162
CAPS_DROP = ['ALL']
163163
CAPS_ADD = []
164164

165+
# User ability to map ports explicitly:
166+
# Unlike the probably safe `-P` run arg (which maps all exposed container ports
167+
# to random free host ports (world accessible)), giving users explicit control
168+
# over port mappings is probably not the best idea, as they are likely to
169+
# collide on frequently used ports such as 5000 or 8080.
170+
# Also it might unintentionally allow them to bind ports in the root range if
171+
# you are not careful.
172+
# For flexibility, we allow you to specify regexps that each -p arg is matched
173+
# against one by one. Only if each of the user's -p args matches at least one of
174+
# these, the docker run command is executed. If the list is empty, the -p
175+
# argument is neither allowed nor shown to the user.
176+
ALLOWED_PORT_MAPPINGS = [
177+
# useful defaults: most similar to -P, but allows users to select
178+
# ports instead of mapping all exposed publicly. Also might allow them to
179+
# bind them local to host only:
180+
r'^127\.0\.0\.1::[0-9]+$', # local access from host (via random free port)
181+
'^[0-9]+$', # public access (via random free host port)
182+
183+
# more examples:
184+
185+
# allow `-p 127.0.0.1:5000-6000:80`, so user can map container 80 to
186+
# random host port in range of 5000-6000 that is only accessible from host:
187+
# r'^127\.0\.0\.1:5000-6000:80$'
188+
189+
# allow `-p 8080:80`, so user can map container 80 to host 8080 (if free):
190+
# '^8080:80$' # probably useful in user-specific configs
191+
192+
# allow all (probably bad idea!):
193+
# '^.*$', # allows all, probably bad idea!
194+
]
195+
165196
# Environment vars to set for the container:
166197
ENV_VARS = [
167198
# sets HOME env var to user's home

userdocker/subcommands/run.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from .. import __version__
99
from ..config import ALLOWED_IMAGE_REGEXPS
10+
from ..config import ALLOWED_PORT_MAPPINGS
1011
from ..config import CAPS_ADD
1112
from ..config import CAPS_DROP
1213
from ..config import ENV_VARS
@@ -60,6 +61,16 @@ def parser_run(parser):
6061
help="Working directory inside the container",
6162
)
6263

64+
if ALLOWED_PORT_MAPPINGS:
65+
sub_parser.add_argument(
66+
"-p", "--publish",
67+
help="Publish a container's ports to the host (see docker help). "
68+
"Allowed: " + ', '.join(ALLOWED_PORT_MAPPINGS),
69+
action="append",
70+
dest="port_mappings",
71+
default=[],
72+
)
73+
6374
sub_parser.add_argument(
6475
"image",
6576
help="the image to run",
@@ -144,6 +155,17 @@ def prepare_nvidia_docker_run(args):
144155
def exec_cmd_run(args):
145156
cmd = init_cmd(args)
146157

158+
# check port mappings
159+
for pm in getattr(args, 'port_mappings', []):
160+
for pm_pattern in ALLOWED_PORT_MAPPINGS:
161+
if re.match(pm_pattern, pm):
162+
cmd += ['-p', pm]
163+
break
164+
else:
165+
raise UserDockerException(
166+
"ERROR: given port mapping not allowed: %s" % pm
167+
)
168+
147169
mounts = []
148170
mounts_available = \
149171
VOLUME_MOUNTS_ALWAYS + VOLUME_MOUNTS_DEFAULT + VOLUME_MOUNTS_AVAILABLE
@@ -153,7 +175,7 @@ def exec_cmd_run(args):
153175
if not args.no_default_mounts:
154176
mounts += VOLUME_MOUNTS_DEFAULT
155177

156-
for user_mount in args.volumes:
178+
for user_mount in getattr(args, 'volumes', []):
157179
if user_mount in mounts:
158180
continue
159181
if user_mount in mounts_available:

0 commit comments

Comments
 (0)