mirror of
https://github.com/trailofbits/algo.git
synced 2025-04-16 22:27:20 +02:00
Creates a Docker container to run algo (#331)
* Creates a Docker container to run algo * Simplistic testing of the Docker image This simply uses the same LXC system that was just tested. It's functional, but minimal. * More thorough tests against Docker This doubles the number of LXC containers in use, but does provide a more thorough test of the Docker image.
This commit is contained in:
parent
0fda81f12d
commit
62fc22ab59
7 changed files with 190 additions and 2 deletions
13
.dockerignore
Normal file
13
.dockerignore
Normal file
|
@ -0,0 +1,13 @@
|
|||
.dockerignore
|
||||
.git
|
||||
.github
|
||||
.gitignore
|
||||
.travis.yml
|
||||
CONTRIBUTING.md
|
||||
Dockerfile
|
||||
README.md
|
||||
config.cfg
|
||||
configs
|
||||
docs
|
||||
logo.png
|
||||
tests
|
10
.travis.yml
10
.travis.yml
|
@ -4,6 +4,9 @@ python: "2.7"
|
|||
sudo: required
|
||||
dist: trusty
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
|
@ -32,6 +35,10 @@ before_cache:
|
|||
env:
|
||||
- LXC_NAME=ubuntu1604 LXC_DISTRO=ubuntu LXC_RELEASE=xenial
|
||||
- LXC_NAME=ubuntu1710 LXC_DISTRO=ubuntu LXC_RELEASE=artful
|
||||
- LXC_NAME=docker LXC_DISTRO=ubuntu LXC_RELEASE=artful
|
||||
|
||||
before_install:
|
||||
- test "${LXC_NAME}" != "docker" || docker build -t travis/algo .
|
||||
|
||||
install:
|
||||
- sudo tar xf $HOME/lxc/cache.tar -C / || echo "Didn't extract cache."
|
||||
|
@ -41,6 +48,7 @@ install:
|
|||
- export LXC_IP="$(sudo lxc-info -Hin $LXC_NAME)"
|
||||
- sudo /bin/bash -c "printf '\n$LXC_IP test.lxc\n' >> /etc/hosts"
|
||||
- ssh-keygen -f ~/.ssh/id_rsa -t rsa -N ''
|
||||
- chmod 0644 ~/.ssh/config
|
||||
- sudo mkdir -vm 0700 $LXC_ROOTFS/root/.ssh/
|
||||
- sudo cp -v ~/.ssh/id_rsa.pub $LXC_ROOTFS/root/.ssh/authorized_keys
|
||||
- sudo apt-get install build-essential libssl-dev libffi-dev python-dev && sudo pip install -r requirements.txt
|
||||
|
@ -52,7 +60,7 @@ script:
|
|||
# - shellcheck algo
|
||||
# - ansible-lint deploy.yml users.yml deploy_client.yml
|
||||
- ansible-playbook deploy.yml --syntax-check
|
||||
- ansible-playbook deploy.yml -t local,vpn,dns,ssh_tunneling,tests -e "server_ip=$LXC_IP server_user=root IP_subject_alt_name=$LXC_IP local_dns=Y"
|
||||
- ./tests/local-deploy.sh
|
||||
|
||||
after_script:
|
||||
- ./tests/update-users.sh
|
||||
|
|
36
Dockerfile
Normal file
36
Dockerfile
Normal file
|
@ -0,0 +1,36 @@
|
|||
FROM python:2-alpine
|
||||
|
||||
ARG VERSION="git"
|
||||
ARG PACKAGES="bash libffi openssh-client openssl rsync tini"
|
||||
ARG BUILD_PACKAGES="gcc libffi-dev linux-headers make musl-dev openssl-dev"
|
||||
|
||||
LABEL name="algo" \
|
||||
version="${VERSION}" \
|
||||
description="Set up a personal IPsec VPN in the cloud" \
|
||||
maintainer="Trail of Bits <http://github.com/trailofbits/algo>"
|
||||
|
||||
RUN apk --no-cache add ${PACKAGES}
|
||||
RUN adduser -D -H -u 19857 algo
|
||||
RUN mkdir -p /algo && mkdir -p /algo/configs
|
||||
|
||||
WORKDIR /algo
|
||||
COPY requirements.txt .
|
||||
RUN apk --no-cache add ${BUILD_PACKAGES} && \
|
||||
python -m pip --no-cache-dir install -U pip && \
|
||||
python -m pip --no-cache-dir install virtualenv && \
|
||||
python -m virtualenv env && \
|
||||
source env/bin/activate && \
|
||||
python -m pip --no-cache-dir install -r requirements.txt && \
|
||||
apk del ${BUILD_PACKAGES}
|
||||
COPY . .
|
||||
RUN chmod 0755 /algo/algo-docker.sh
|
||||
|
||||
# Because of the bind mounting of `configs/`, we need to run as the `root` user
|
||||
# This may break in cases where user namespacing is enabled, so hopefully Docker
|
||||
# sorts out a way to set permissions on bind-mounted volumes (`docker run -v`)
|
||||
# before userns becomes default
|
||||
# Note that not running as root will break if we don't have a matching userid
|
||||
# in the container. The filesystem has also been set up to assume root.
|
||||
USER root
|
||||
CMD [ "/algo/algo-docker.sh" ]
|
||||
ENTRYPOINT [ "/sbin/tini", "--" ]
|
44
algo-docker.sh
Normal file
44
algo-docker.sh
Normal file
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -eEo pipefail
|
||||
|
||||
ALGO_DIR="/algo"
|
||||
DATA_DIR="/data"
|
||||
|
||||
umask 0077
|
||||
|
||||
usage() {
|
||||
retcode="${1:-0}"
|
||||
echo "To run algo from Docker:"
|
||||
echo ""
|
||||
echo "docker run --cap-drop ALL -it -v <path to configurations>:"${DATA_DIR}" trailofbits/algo:latest"
|
||||
echo ""
|
||||
exit ${retcode}
|
||||
}
|
||||
|
||||
if [ ! -f "${DATA_DIR}"/config.cfg ] ; then
|
||||
echo "Looks like you're not bind-mounting your config.cfg into this container."
|
||||
echo "algo needs a configuration file to run."
|
||||
echo ""
|
||||
usage -1
|
||||
fi
|
||||
|
||||
if [ ! -e /dev/console ] ; then
|
||||
echo "Looks like you're trying to run this container without a TTY."
|
||||
echo "If you don't pass `-t`, you can't interact with the algo script."
|
||||
echo ""
|
||||
usage -1
|
||||
fi
|
||||
|
||||
# To work around problems with bind-mounting Windows volumes, we need to
|
||||
# copy files out of ${DATA_DIR}, ensure appropriate line endings and permissions,
|
||||
# then copy the algo-generated files into ${DATA_DIR}.
|
||||
|
||||
tr -d '\r' < "${DATA_DIR}"/config.cfg > "${ALGO_DIR}"/config.cfg
|
||||
test -d "${DATA_DIR}"/configs && rsync -qLktr --delete "${DATA_DIR}"/configs "${ALGO_DIR}"/
|
||||
|
||||
"${ALGO_DIR}"/algo ${ALGO_ARGS}
|
||||
retcode=${?}
|
||||
|
||||
rsync -qLktr --delete "${ALGO_DIR}"/configs "${DATA_DIR}"/
|
||||
exit ${retcode}
|
69
docs/Docker.md
Normal file
69
docs/Docker.md
Normal file
|
@ -0,0 +1,69 @@
|
|||
# Docker Support
|
||||
|
||||
While it is not possible to run your Algo server from within a Docker container, it is possible to use Docker to provision your Algo server.
|
||||
|
||||
## Limitations
|
||||
|
||||
1. [Advanced](ADVANCED.md) installations are not currently supported; you must use the interactive `algo` script.
|
||||
2. This has not yet been tested with user namespacing enabled.
|
||||
3. If you're running this on Windows, take care when editing files under `configs/` to ensure that line endings are set appropriately for Unix systems.
|
||||
|
||||
## Deploying an Algo Server with Docker
|
||||
|
||||
1. Install [Docker](https://www.docker.com/community-edition#/download) -- setup and configuration is not covered here
|
||||
2. Create a local directory to hold your VPN configs (e.g. `C:\Users\trailofbits\Documents\VPNs\`)
|
||||
3. Create a local copy of [config.cfg](https://github.com/trailofbits/algo/blob/master/config.cfg), with required modifications (e.g. `C:\Users\trailofbits\Documents\VPNs\config.cfg`)
|
||||
4. Run the Docker container, mounting your configurations appropriately:
|
||||
- From Windows:
|
||||
```powershell
|
||||
C:\Users\trailofbits> docker run --cap-drop=all -it \
|
||||
-v C:\Users\trailofbits\Documents\VPNs:/data \
|
||||
trailofbits/algo:latest
|
||||
```
|
||||
- From Linux:
|
||||
```bash
|
||||
$ docker run --cap-drop-all -it \
|
||||
-v /home/trailofbits/Documents/VPNs:/data \
|
||||
trailofbits/algo:latest
|
||||
```
|
||||
5. When it exits, you'll be left with a fully populated `configs` directory, containing all appropriate configuration data for your clients, and for future server management
|
||||
|
||||
### Providing Additional Files
|
||||
f
|
||||
If you need to provide additional files -- like authorization files for Google Cloud Project -- you can simply specify an additional `-v` parameter, and provide the appropriate path when prompted by `algo`.
|
||||
|
||||
For example, you can specify `-v C:\Users\trailofbits\Documents\VPNs\gce_auth.json:/algo/gce_auth.json`, making the local path to your credentials JSON file `/algo/gce_auth.json`.
|
||||
|
||||
## Managing an Algo Server with Docker
|
||||
|
||||
Even though the container itself is transient, because you've persisted the configuration data, you can use the same Docker image to manage your Algo server. This is done by setting the environment variable `ALGO_ARGS`.
|
||||
|
||||
If you want to use Algo to update the users on an existing server, specify `-e "ALGO_ARGS=update-users"` in your `docker run` command:
|
||||
```powershell
|
||||
$ docker run --cap-drop=all -it \
|
||||
-e "ALGO_ARGS=update-users" \
|
||||
-v C:\Users\trailofbits\Documents\VPNs:/data \
|
||||
trailofbits/algo:latest
|
||||
```
|
||||
|
||||
## Building Your Own Docker Image
|
||||
|
||||
You can use the Dockerfile provided in this repository as-is, or modify it to suit your needs. Further instructions on building an image can be found in the [Docker engine](https://docs.docker.com/engine/) documents.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
Using Docker is largely no different from running Algo yourself, with a couple of notable exceptions: we run as root within the container, and you're retrieving your content from Docker Hub.
|
||||
|
||||
To work around the limitations of bind mounts in docker, we have to run as root within the container. To mitigate concerns around doing this, we pass the `--cap-drop=all` parameter to `docker run`, which effectively removes all privileges from the root account, reducing it to a generic user account that happens to have a userid of 0. Further steps can be taken by applying `seccomp` profiles to the container; this is being considered as a future improvement.
|
||||
|
||||
Docker themselves provide a concept of [Content Trust](https://docs.docker.com/engine/security/trust/content_trust/) for image management, which helps to ensure that the image you download is, in fact, the image that was uploaded. Content trust is still under development, and while we may be using it, its implementation, limitations, and constraints are documented with Docker.
|
||||
|
||||
## Future Improvements
|
||||
|
||||
1. Even though we're taking care to drop all capabilities to minimize the impact of running as root, we can probably include not only a `seccomp` profile, but also AppArmor and/or SELinux profiles as well.
|
||||
2. The Docker image doesn't natively support [advanced](ADVANCED.md) Algo deployments, which is useful for scripting. This can be done by launching an interactive shell and running the commands yourself.
|
||||
3. The way configuration is passed into and out of the container is a bit kludgy. Hopefully future improvements in Docker volumes will make this a bit easier to handle.
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
If you want to poke around the Docker container yourself, you can do so by changing your `entrypoint`. Pass `--entrypoint=/bin/ash` as a parameter to `docker run`, and you'll be dropped into a full Linux shell in the container.
|
12
tests/local-deploy.sh
Executable file
12
tests/local-deploy.sh
Executable file
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
DEPLOY_ARGS="server_ip=$LXC_IP server_user=root IP_subject_alt_name=$LXC_IP local_dns=Y"
|
||||
|
||||
if [ "${LXC_NAME}" == "docker" ]
|
||||
then
|
||||
docker run -it -v $(pwd)/config.cfg:/algo/config.cfg -v ~/.ssh:/root/.ssh -e "DEPLOY_ARGS=${DEPLOY_ARGS}" travis/algo /bin/sh -c "chown -R 0:0 /root/.ssh && source env/bin/activate && ansible-playbook deploy.yml -t local,vpn,dns,ssh_tunneling,security,tests -e \"${DEPLOY_ARGS}\""
|
||||
else
|
||||
ansible-playbook deploy.yml -t local,vpn,dns,ssh_tunneling,tests -e "${DEPLOY_ARGS}"
|
||||
fi
|
|
@ -3,10 +3,16 @@
|
|||
set -ex
|
||||
|
||||
CAPW=`cat /tmp/ca_password`
|
||||
USER_ARGS="server_ip=$LXC_IP server_user=root ssh_tunneling_enabled=y IP_subject=$LXC_IP easyrsa_CA_password=$CAPW"
|
||||
|
||||
sed -i 's/- jack$/- jack_test/' config.cfg
|
||||
|
||||
ansible-playbook users.yml -e "server_ip=$LXC_IP server_user=root ssh_tunneling_enabled=y IP_subject=$LXC_IP easyrsa_CA_password=$CAPW"
|
||||
if [ "${LXC_NAME}" == "docker" ]
|
||||
then
|
||||
docker run -it -v $(pwd)/config.cfg:/algo/config.cfg -v ~/.ssh:/root/.ssh -e "USER_ARGS=${USER_ARGS}" travis/algo /bin/sh -c "chown -R 0:0 /root/.ssh && source env/bin/activate && ansible-playbook users.yml -e \"${USER_ARGS}\""
|
||||
else
|
||||
ansible-playbook users.yml -e "${USER_ARGS}"
|
||||
fi
|
||||
|
||||
cd configs/$LXC_IP/pki/
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue