Provide docker-based development environments to support
100-500 concurrent workspaces on a single server. The idea
is for developers to edit code and perform
light compilation or testing within the workspace.
- Serious testing is the duty of a continuous integration server.
- Serious compilation is the duty of a build server.
Please add any issues you find with this software
to the upstream wormhole.
User data lives in a data container and
persists across upgrades of the app container.
sshd is restrictive:
- ssh host key persists across upgrades of the app container.
Weak firewall allows:
- inbound from Internet to wormhole ephemeral TCP ports
- outbound from wormhole to Internet on all ports
Strong firewall allows:
- inbound from wormhole IP to internal TCP 22
- no outbound connections other than return traffic for inbound connections
Must be capable of running on CoreOS. Therefore no outside dependencies.
Internal infrastructure should use appropriate access control mechanisms
based on risk evaluation of the wormhole.
OVAL vulnerability scan
The Red Hat Security Response Team provides OVAL definitions
for all vulnerabilities (identified by CVE name) that affect RHEL or Fedora.
This enables users to perform a vulnerability scan and
diagnose whether the system is vulnerable.
The Dockerfile in this repo adds a script to download the latest
OVAL definitions from Red Hat and perform a vulnerability scan
against the image. If the image has one or more known vulnerabilies,
the script exits non-zero, and the
docker build fails.
We must resolve all known vulnerabilities
in order to successfully build an image.
The scan is time-dependent as of image build, so
we should rebuild the image when Red Hat updates the OVAL feed.
The vulnerability scan is distinct from the SCAP secure configuration scan
described by the test harness.
It is possible to scan an existing image:
docker run --rm -t jumanjiman/wormhole /usr/sbin/oval-vulnerability-scan.sh
The exact output of the vulnerability scan varies according to the
latest Red Hat OVAL feed, but it looks similar to this snapshot from August 2014:
-snip copious checks- RHSA-2014:1051: flash-plugin security update (Critical) oval-com.redhat.rhsa-def-20141051 CVE-2014-0538 CVE-2014-0540 CVE-2014-0541 CVE-2014-0542 CVE-2014-0543 CVE-2014-0544 CVE-2014-0545 pass RHSA-2014:1052: openssl security update (Moderate) oval-com.redhat.rhsa-def-20141052 CVE-2014-3505 CVE-2014-3506 CVE-2014-3507 CVE-2014-3508 CVE-2014-3509 CVE-2014-3510 CVE-2014-3511 pass RHSA-2014:1053: openssl security update (Moderate) oval-com.redhat.rhsa-def-20141053 CVE-2014-0221 CVE-2014-3505 CVE-2014-3506 CVE-2014-3508 CVE-2014-3510 pass vulnerability scan exit status 0
TODO: Implement some sort of CD system to poll the OVAL feed and rebuild
the image on any update. https://github.com/jumanjiman/docker-gocd may be
a candidate for the solution.
RSpec documents key behaviors and assures no regressions:
contributor friction there should not be any jumanjiman/wormhole should use correct docker API version image should be available image properties should expose ssh port and only ssh port should have volume /home/user should have volume /media/state/etc/ssh admin scripts given user handle="booga" everybody knows pubkey booga knows privkey `build.sh $handle "$pubkey"` creates wormhole from 2 containers "booga-data" is a persistent read-write container should exist should be stopped should be created from busybox should export /home/user volume read-write should export /media/state/etc/ssh volume read-write should not mount any volumes "booga" is a read-only app container should exist should be running should run unprivileged should be created from jumanjiman/wormhole should use volumes from booga-data should have hostname wormhole.example.com should be limited to 512 MiB RAM `docker logs` should show sshd running on sshd port should expose internal sshd port and only sshd port should map internal sshd port to an outside ephemeral port arcanist (phabricator client) `arc` is in user path `arc version` is functional uses bash autocompletion BZ1099206 (slow test) home directory should exist go get should work user convenience man -k returns results locate returns the path for issue.net shell-shock should resolve CVE-014-6271 should resolve CVE-014-7169 eiffelstudio has command-line eiffel compiler in path has estudio in path ldc D compiler compiles a D program (PENDING: https://bugzilla.redhat.com/show_bug.cgi?id=1176361) locale archive supports C supports POSIX supports en_US.utf8 supports en_US.UTF-8 SCAP secure configuration checks (slow test) should pass all tests /etc/securetty should be a zero-size file prohibited packages should not have at installed should not have prelink installed should not have sudo installed prohibited commands should not have the at command should not have the crond command should not have the crontab command should not have the /usr/sbin/prelink command rhc (openshift client) `rhc` is in user path `rhc --version` is functional sshd config auth should use privilege separation should use pam should allow pubkeyauthentication should deny passwordauthentication should deny gssapiauthentication should deny kerberosauthentication should deny challengeresponseauthentication tunnels and forwarding should deny ssh tunnels should deny TCP forwarding should deny X11 forwarding should deny gateway ports Common Configuration Enumeration (CCE) CCE-3660-8 Disable remote ssh from accounts with empty passwords CCE-3845-5 idle timeout interval should be set appropriately CCE-4325-7 Disable SSH protocol version 1 CCE-4370-3 Disable SSH host-based authentication CCE-4387-7 Disable root login via SSH CCE-4431-3 SSH warning banner should be enabled CCE-4475-0 Disable emulation of rsh command through sshd CCE-14061-6 "keep alive" msg count should be set appropriately CCE-14491-5 Use appropriate ciphers for SSH CCE-14716-5 Users should not be allowed to set env options obscurity should hide patch level users with interactive shells should only include "root" and "user" su "user" cannot su Finished in 1 minute 6.35 seconds (files took 0.45472 seconds to load) 61 examples, 0 failures
The OpenSCAP secure configuration test shown above uses a
to adjust the upstream checks.
It expands to this inside the container:
Title gpgcheck Enabled In Main Yum Configuration Rule ensure_gpgcheck_globally_activated Result pass Title gpgcheck Enabled For All Yum Package Repositories Rule ensure_gpgcheck_never_disabled Result pass Title Shared Library Files Have Restrictive Permissions Rule file_permissions_library_dirs Result pass Title Shared Library Files Have Root Ownership Rule file_ownership_library_dirs Result pass Title System Executables Have Restrictive Permissions Rule file_permissions_binary_dirs Result pass Title System Executables Have Root Ownership Rule file_ownership_binary_dirs Result pass Title Direct root Logins Not Allowed Rule no_direct_root_logins Result notchecked Title Virtual Console Root Logins Restricted Rule securetty_root_login_console_only Result pass Title Serial Port Root Logins Restricted Rule restrict_serial_port_logins Result pass Title Only Root Has UID 0 Rule no_uidzero_except_root Result pass Title Log In to Accounts With Empty Password Impossible Rule no_empty_passwords Result pass Title Password Hashes For Each Account Shadowed Rule no_hashes_outside_shadow Result pass Title netrc Files Do Not Exist Rule no_netrc_files Result pass Title SSH Root Login Disabled Rule sshd_disable_root_login Result pass Title SSH Access via Empty Passwords Disabled Rule sshd_disable_empty_passwords Result pass Title SSH Idle Timeout Interval Used Rule sshd_set_idle_timeout Result pass Title SSH Client Alive Count Used Rule sshd_set_keepalive Result pass
New containers begin life with a git-suitable
This is only the initial bashrc; you can modify it at any time.
Inside the container, your user account is literally named
That means, with default build options, your prompt inside the container is:
Connect to your container with info provided by admin:
ssh -i path/to/privkey -p <your port> user@<ip>
Edit global configuration
Clone this repo, then inspect and optionally modify
When you build a user box for the first time, the build script
Edit duo configuration
This step is optional. The default configuration does not use Duo.
If you want to use Duo Security for multi-factor authentication,
you must edit both
When you build your first user box (see below), these two files
are copied into
If you modify
/etc/wormhole/*.conf, you must restart the user
Build a user box
Use the build script with a unique user id (such as github handle)
and the user ssh pubkey to create a runtime container:
user=jumanjiman pubkey="ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEArSm80/8UD/eMolUXU3j6geyNrxthnQxbE4dpwRhXU+F6fbQG+wk9SdWev9NcLLWg9a4zBUSMJUXrrU/8ik3WshSpZpqQary4ZiFFQKgSfYriouchc20S3wwFQZcbOJgH5t5wgGeNaDMzc2GRFhqbuuBiBBF+W5llk0X9CGE1o1iAlyVPAn4UfrJ4//5OXMhYwmU+fO9df3y5Kpn/0SY/lRwWuZeVVIXC+nZcFYXNzPyBVTNEooOXLVXivddtU82jfp65ggTMdLfUafZqia1/smfWQP23lU8F4ySayAOa1lhXXvrGtpxl3lu7vaSvdEg7+F4YaIhxnWZqt769joDraw== pmorgan@github" ./build.sh $user "$pubkey"
:warning: The build script attempts to fetch the github user's
ssh pubkeys and place them into the data container as authorized keys.
This is really bad if you create a devenv for somebody based on
a name that is not their github handle and the name is a valid
github handle for somebody else!
A runtime container should be up on a random ssh port:
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 689479673e8e jumanjiman:latest /bin/sh -c /usr/sbin About an hour ago Up About an hour 0.0.0.0:49153->22/tcp jumanjiman
The build script depends on systemd and enables a "wormhole@<username>"
service to start at boot-time for the container you just created and
persists the port as an environment variable in
For example: If the username is jumanjiman, you can run
sudo systemctl status wormhole@jumanjiman to see:
● firstname.lastname@example.org - jumanjiman app container Loaded: loaded (/etc/systemd/system/wormhole@.service; enabled) Active: active (running) since Wed 2014-06-18 14:08:05 UTC; 3h 18min ago Main PID: 701 (docker) CGroup: /email@example.com └─701 /usr/bin/docker run --rm -t -m 512m --volumes-from jumanjiman-data -p 49153:22 -h wormhole.example.com --name jumanjiman... Jun 18 14:08:05 ip-192-168-254-21 bash: jumanjiman Jun 18 14:08:05 ip-192-168-254-21 systemd: Started jumanjiman app container. Jun 18 14:08:09 ip-192-168-254-21 docker: Server listening on 0.0.0.0 port 22. Jun 18 14:08:09 ip-192-168-254-21 docker: Server listening on :: port 22.
:warning: The default
global.conf limits each app container to 512 MiB memory.
You can override this for a single user in
You can view the current limit for a container via the sys filesystem.
For example, here is a container that was started with a 1 MiB limit:
$ cat /sys/fs/cgroup/memory/lxc/<hash>/memory.limit_in_bytes 1048576
If a PID inside a container gets killed due to the memory limit,
you can view details in
Create a local file called
jumanjiman-data.tar with contents of
user data container.
Restore user data container from backup
Upgrade user app container
jumanjiman/wormhole image as described above, then...
Destroy a user app+data container
Backup, then discard both the app and data containers for a user.
Build an image for the app container
This image serves as a template for an app container.<br/>
You can build the image locally:
Trigger a rebuild-and-test cycle to get latest updates:
cd wormhole/ date > REBUILD git add REBUILD git commit -m 'test build with latest package updates' # Open pull request.
:warning: Use CoreOS to build image.
Fedora kernel on DigitalOcean
has an older LXC implementation that leads to inconsistent builds.
For example, it sometimes builds the base image with bad perms on
/var and other directories that must be