Skip to content

Commit

Permalink
Merge pull request #24 from JonathonReinhart/inject-etc-passwd
Browse files Browse the repository at this point in the history
Inject /etc/passwd and friends instead of .scubainit
  • Loading branch information
JonathonReinhart committed Jan 8, 2016
2 parents c71040b + 2c7f3c0 commit 97cba4a
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 78 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Added
- Added `--verbose` and `--dry-run` options

### Removed
- umask is no longer set in the container. (See [#24])

### Fixed
- Problems with Ctrl+C in images are fixed. The user command now runs
as PID 1 again, as there is no more `.scubainit` script.


## [1.3.0] - 2016-01-07
### Added
- Set umask in container to the same as the host (local Docker only)
Expand Down Expand Up @@ -68,3 +76,4 @@ First versioned release
[1.0.0]: https://github.com/JonathonReinhart/scuba/compare/v0.1.0...v1.0.0

[issue 11]: https://github.com/JonathonReinhart/scuba/issues/11
[#24]: https://github.com/JonathonReinhart/scuba/pull/24
141 changes: 63 additions & 78 deletions src/scuba
Original file line number Diff line number Diff line change
Expand Up @@ -188,93 +188,85 @@ def group_entry(groupname, password, gid, users=[]):
gid = gid,
users = ','.join(users))

def shell_quote(s):
# http://stackoverflow.com/a/847800/119527
return pipes.quote(s)

def get_image_command(image):
'''Gets the default command for an image'''
args = ['docker', 'inspect', image]
try:
p = subprocess.Popen(args, stdout = subprocess.PIPE)
except OSError as e:
if e.errno == errno.ENOENT:
appmsg('Failed to execute docker. Is it installed?')
sys.exit(2)

stdout, _ = p.communicate()
if not p.returncode == 0:
appmsg('Failed to inspect image')
sys.exit(2)

info = json.loads(stdout)[0]
return info['Config']['Cmd']
def shadow_entry(username, **kw):
return '{username}:{password}:{lstchg}:{minchg}:{maxchg}:{warn}:{inact}:{expire}:{flag}'.format(
username = username,
password = kw.get('password', '*'),
lstchg = kw.get('lstchg', ''),
minchg = kw.get('minchg', ''),
maxchg = kw.get('maxchg', ''),
warn = kw.get('warn', ''),
inact = kw.get('inact', ''),
expire = kw.get('expire', ''),
flag = '',
)

def get_umask():
# Same logic as bash/builtins/umask.def
val = os.umask(022)
os.umask(val)
return val
def get_native_opts():
opts = []

def generate_scubainit(config, command):
'''Generate a .scubainit script
uid = os.getuid()
gid = os.getgid()

This script is executed by /bin/sh, as passed to "docker run".
It is responsible for setting up the environment, and running the
user-specified command. Normally, if the user provides no command to
"docker run", the image's default CMD is run. Because we force
"/bin/sh .scubainit" to be run, scuba must emulate the default behavior
itself.
'''
if len(command) == 0:
# No user-provided command; we want to run the image's default command
verbose_msg('No user command; getting command from image')
command = get_image_command(config['image'])
verbose_msg('{0} Cmd: "{1}"'.format(config['image'], command))
opts.append('--user={uid}:{gid}'.format(uid=uid, gid=gid))

# Turn command into a string which you would enter in a shell
command = ' '.join(shell_quote(c) for c in command)
def writeln(f, line):
f.write(line + '\n')

# Open a temporary file
with NamedTemporaryFile(prefix='scubainit', delete=False) as f:
# /etc/passwd
with NamedTemporaryFile(prefix='scuba', delete=False) as f:
cleanup.add(f.name)
opts.append(make_vol_opt(f.name, '/etc/passwd', 'z'))

def write_cmd(*args):
f.write(' '.join(c for c in args) + '\n')

write_cmd('#!/bin/sh')
writeln(f, passwd_entry(
username = 'root',
password = 'x',
uid = 0,
gid = 0,
gecos = 'root',
homedir = '/root',
shell = '/bin/sh',
))

# Add scubauser with current uid/gid
# BusyBox only has 'adduser', with arguments incompatible with
# that of the standard 'useradd'.
# Instead, we'll just write the entry ourselves.
entry = passwd_entry(
writeln(f, passwd_entry(
username = SCUBA_USER,
password = 'x',
uid = os.getuid(),
gid = os.getgid(),
uid = uid,
gid = gid,
gecos = 'Scuba User',
homedir = '/', # Docker sets $HOME=/
shell = '/bin/sh',
)
write_cmd('echo', shell_quote(entry), '>>', '/etc/passwd')
))

# /etc/group
with NamedTemporaryFile(prefix='scuba', delete=False) as f:
cleanup.add(f.name)
opts.append(make_vol_opt(f.name, '/etc/group', 'z'))

# Add scubauser group
entry = group_entry(
writeln(f, group_entry(
groupname = 'root',
password = 'x',
gid = 0,
))

writeln(f, group_entry(
groupname = SCUBA_GROUP,
password = 'x',
gid = os.getgid(),
users = [SCUBA_USER],
)
write_cmd('echo', shell_quote(entry), '>>', '/etc/group')
gid = gid,
))

# Set the umask
write_cmd('umask', oct(get_umask()))
# /etc/shadow
with NamedTemporaryFile(prefix='scuba', delete=False) as f:
cleanup.add(f.name)
opts.append(make_vol_opt(f.name, '/etc/shadow', 'z'))

# Execute the command indicated by the user, as the scuba user
write_cmd('su', SCUBA_USER, '-c', shell_quote(command))
writeln(f, shadow_entry(
username = 'root',
))
writeln(f, shadow_entry(
username = SCUBA_USER,
))

return f.name
return opts

def parse_args():
ap = argparse.ArgumentParser(description='Simple Container-Utilizing Build Apparatus')
Expand Down Expand Up @@ -328,19 +320,12 @@ def main():
We want files created inside the container (in scubaroot) to appear to the
host as if they were created there (owned by the same uid/gid, with same
umask, etc.) So, we use a .scubainit script to create a user with the same
uid/gid, su(1) to that user, and run the usercommand.
umask, etc.)
'''
verbose_msg('Docker running natively')

# Generate a .scubainit script
scubainit_path = generate_scubainit(config, usercmd)

# Mount scubainit script
docker_opts = [make_vol_opt(scubainit_path, '/.scubainit', 'z')]

# Run the .scubainit at startup
docker_cmd = ['/bin/sh', '/.scubainit']
docker_opts = get_native_opts()
docker_cmd = usercmd

# NOTE: This tells Docker to re-label the directory for compatibility
# with SELinux. See `man docker-run` for more information.
Expand Down

0 comments on commit 97cba4a

Please sign in to comment.