From 01f301966c851937f424ec0f9aa8ea5e7bbdf687 Mon Sep 17 00:00:00 2001 From: Damien DUPORTAL Date: Sat, 20 May 2017 12:56:25 +0200 Subject: [PATCH] Squashed 'alpine2docker/' content from commit 1600074 git-subtree-dir: alpine2docker git-subtree-split: 1600074c7f2d8fb33b503133ff12484ab42ecaf8 --- .gitattributes | 2 + .gitignore | 6 +++ CHANGELOG.md | 42 +++++++++++++++++ Makefile | 37 +++++++++++++++ README.md | 80 +++++++++++++++++++++++++++++++++ alpine2docker.json | 99 +++++++++++++++++++++++++++++++++++++++++ customize/example.tpl | 1 + customize/run.sh | 13 ++++++ http/answers | 16 +++++++ scripts/base.sh | 27 +++++++++++ scripts/clean.sh | 14 ++++++ scripts/customize.sh | 18 ++++++++ scripts/docker.sh | 37 +++++++++++++++ scripts/sshd.sh | 12 +++++ scripts/sudoers.sh | 11 +++++ scripts/vagrant.sh | 18 ++++++++ tests/00-box-tests.bats | 86 +++++++++++++++++++++++++++++++++++ vagrantfile-box.tpl | 21 +++++++++ 18 files changed, 540 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 Makefile create mode 100644 README.md create mode 100644 alpine2docker.json create mode 100644 customize/example.tpl create mode 100644 customize/run.sh create mode 100644 http/answers create mode 100644 scripts/base.sh create mode 100644 scripts/clean.sh create mode 100644 scripts/customize.sh create mode 100644 scripts/docker.sh create mode 100644 scripts/sshd.sh create mode 100644 scripts/sudoers.sh create mode 100644 scripts/vagrant.sh create mode 100644 tests/00-box-tests.bats create mode 100644 vagrantfile-box.tpl diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3ef7e82 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Force checkout as Unix endline style +text eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..769bb2a --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.vagrant +.DS_Store +*.box +output* +packer_cache +tests/Vagrantfile diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b9a51ba --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,42 @@ + +# CHANGELOG + + +## 26/03/2017 (1.2.0) + +- From GH-3: + - Moving Docker and Compose to "rolling" version based on Edge repository +- Docker upgraded (by rolling effect) to CE 17.03.0 +- Docker-Compose upgraded (by rolling effect) to 1.11.2 + +## 17/02/2017 (1.1.0) + +- From GH-2: + - Moving docker-compose version to 1.11.0 + - Adding Swap support for containers (cgroup swap and meme accounting) + - Making GRSec less aggressive to allow running non alpine linux JVM inside containers +- From GH-1: + - Introducing a customization endpoint to build your own machine + +Published on https://atlas.hashicorp.com/dduportal/boxes/alpine2docker/versions/1.1.0 + +Release hashes: +- SHA256: 0e6913f6dc14162b1cb04dd920b7f05494de37920fd68a8895eb287c0c485cb6 +- SHA1: f0e51cfa108d94ecdbd6f42f7d394a01ef9a3d03 +- MD5: 892f3acc90489d7be4d278da2fe8abcb + + +## 29/01/2017 (1.0.0) + +- Initial Release: + - Alpine Linux 3.5, virtual edition + - Docker 1.13.0 + - Docker-Compose 1.10.0 + - No VirtualBox additions + +Published on https://atlas.hashicorp.com/dduportal/boxes/alpine2docker/versions/1.0.0 + +Release hashes: +- SHA256: 8eb0bfb11efefb211ef508e08a685cf7f7f733b56670ba98bad6270b680a06a0 +- SHA1: a45a297bf892f6ee8f486ee385ee2a1bae583be0 +- MD5: 5b3913b726831c8aa2a9a51314e3a766 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..66bb58e --- /dev/null +++ b/Makefile @@ -0,0 +1,37 @@ + +export BOX_VERSION ?= 1.2.1 +export VM_CPUS ?= 1 +export VM_MEMORY ?= 1024 +BOX_BASENAME ?= alpine2docker +BOX_NAME ?= $(BOX_BASENAME)-$(BOX_VERSION) +export BOX_FILE ?= $(BOX_NAME).box +BOX_TEST ?= $(BOX_BASENAME)-test +TEST_DIR ?= ./tests + +all: clean box prepare-test test + +clean: clean-test clean-box + +box: $(BOX_FILE) + +$(BOX_FILE): + packer build -var 'BOX_VERSION=$(BOX_VERSION)' $(BOX_BASENAME).json + +prepare-test: + vagrant box add --force $(BOX_TEST) $(BOX_FILE) + cd $(TEST_DIR) && vagrant init -f -m $(BOX_TEST) + +test: + cd $(TEST_DIR) && bats ./*.bats + +clean-test: + cd $(TEST_DIR) && vagrant destroy -f || true + cd $(TEST_DIR) && rm -rf Vagrantfile .vagrant + vagrant box remove $(BOX_TEST) || true + vagrant global-status --prune + +clean-box: + rm -rf output* $(BOX_FILE) + rm -rf "$(HOME)/VirtualBox VMs/$(BOX_BASENAME)" + +.PHONY: box prepare-test test all clean clean-test clean-box diff --git a/README.md b/README.md new file mode 100644 index 0000000..1e76992 --- /dev/null +++ b/README.md @@ -0,0 +1,80 @@ +# alpine2docker Vagrant Box + +This repository contains the scripts necessary to create a lightweight Vagrant box for running docker, based on [Alpine Linux OS](https://alpinelinux.org/). + +If you work solely with Docker, this box lets you keep your Vagrant workflow and work in the most minimal Docker environment possible. + +## Usage + +The box is available on [Hashicorp's Atlas](https://atlas.hashicorp.com/dduportal/boxes/alpinedocker), making it very easy to use it: + +``` +$ vagrant init dduportal/alpine2docker +$ vagrant up +``` + +## [What's in the box ?](https://www.youtube.com/watch?v=1giVzxyoclE) + +* Guest OS: Alpine Linux 3.5 +* LVM root filesystem for any filesystem growing allocation +* 1 Gb swap enabled +* Default to NAT network +* Default to 2 vCPUs and 4 Gb memory +* OpenSSH server +* Bash, curl, sudo, rsync +* Administrative user *alpine* (password is the same) +* Docker Engine +* Docker Compose +* Docker Bash Completion +* *NOT in the box:* + - No VirtualBox addition (meaning: no shared filesystem with the host by default, unless using rsync) + - No USB + - No Audio + + +## Building the Box + +If you want to recreate the box, rather than using the binary, then +you can use the scripts and Packer template within this repository to +do so in seconds. + +### Requirements + +* [Make as workflow engine](http://www.gnu.org/software/make/) +* [Packer as vagrant basebox builder](http://www.packer.io) (at least version 1.0.0) +* [Vagrant](http://vagrantup.com) (at least version 1.9.4) +* [VirtualBox](http://www.virtualbox.org) (at least version 5.1.22) +* [bats for testing](https://github.com/sstephenson/bats) + +### Building the box + +Then run this command to build the box and run the test suite: + +``` +make all +``` + +You can also run the make targets independently +for a quick feedback loop: + +* `make box`: Only run the packer build +* `make clean-box`: Remove any packer final or intermediate artifacts +* `make prepare-test`: Copy the latest built box to the test environement +* `make test`: Run the test suite using vagrant +* `make clean-test`: Clean any test artifacts or VM +* `make clean`: Clean everything + +### Extension point + +If you want to tune the behavior to fit your needs, +but want to reuse all the build process, here is the workflow +for VM customization: + +* Add this repository as a +[git submodule](https://git-scm.com/docs/git-submodule) +of your repository +* Put in the `customize` folder the content you want to be uploaded to the VM + - You can overwrite existing content: it is for demo purpose + - The content will be uploaded inside /var/customize + - If there is a script `run.sh`, it will be run during box build time +* Build the VM with the previous instructions diff --git a/alpine2docker.json b/alpine2docker.json new file mode 100644 index 0000000..8af615e --- /dev/null +++ b/alpine2docker.json @@ -0,0 +1,99 @@ +{ + "description": "Build a minimalistic VM for Docker with Linux Alpine", + "variables": { + "BASE_USER": "alpine", + "BOX_FILE": "{{ env `BOX_FILE`}}", + "VM_CPUS": "{{ env `VM_CPUS`}}", + "VM_MEMORY": "{{ env `VM_MEMORY`}}" + }, + "builders": [ + { + "name": "vbox", + "type": "virtualbox-iso", + "headless": true, + "vboxmanage": [ + ["modifyvm","{{.Name}}","--cpus","{{user `VM_CPUS`}}"], + ["modifyvm","{{.Name}}","--memory","{{user `VM_MEMORY`}}"], + ["modifyvm","{{.Name}}","--cableconnected1","on"], + ["modifyvm","{{.Name}}","--audio","none"], + ["modifyvm","{{.Name}}","--usb","off"] + ], + "disk_size": 40960, + "guest_os_type": "Linux26_64", + "iso_urls": [ + "https://nl.alpinelinux.org/alpine/v3.5/releases/x86_64/alpine-virt-3.5.2-x86_64.iso" + ], + "iso_checksum": "2258ca975d7b829ea6229d314b2782d4070ec9b7f688392817e562f5a14a34b5", + "iso_checksum_type": "sha256", + "communicator": "ssh", + "http_directory": "./http", + "boot_wait": "20s", + "boot_command": [ + "root", + "ifconfig eth0 up && udhcpc -i eth0", + "wget http://{{ .HTTPIP }}:{{ .HTTPPort }}/answers", + "setup-alpine -f answers", + "root", + "root", + "", + "y", + "", + "", + "", + "", + "rc-service sshd stop", + "mount /dev/vg0/lv_root /mnt", + "echo 'PermitRootLogin yes' >> /mnt/etc/ssh/sshd_config", + "umount /mnt", + "reboot" + ], + "guest_additions_mode": "disable", + "virtualbox_version_file": ".vbox_version", + "ssh_username": "root", + "ssh_password": "root", + "ssh_wait_timeout": "10m", + "shutdown_command": "/sbin/poweroff" + } + ], + "provisioners": [ + { + "type": "shell", + "execute_command": "{{ .Vars }} /bin/sh '{{ .Path }}'", + "environment_vars": [ + "BASE_USER={{user `BASE_USER`}}" + ], + "scripts": [ + "./scripts/base.sh", + "./scripts/vagrant.sh", + "./scripts/sudoers.sh", + "./scripts/docker.sh" + ] + }, + { + "type": "file", + "source": "./customize/", + "destination": "/var/customize/", + "pause_before": "30s" + }, + { + "type": "shell", + "execute_command": "{{ .Vars }} /bin/sh '{{ .Path }}'", + "environment_vars": [ + "BASE_USER={{user `BASE_USER`}}" + ], + "scripts": [ + "./scripts/sshd.sh", + "./scripts/customize.sh", + "./scripts/clean.sh" + ] + } + ], + "post-processors": [ + { + "type": "vagrant", + "only": ["vbox"], + "vagrantfile_template": "vagrantfile-box.tpl", + "output": "{{user `BOX_FILE`}}" + } + ] +} diff --git a/customize/example.tpl b/customize/example.tpl new file mode 100644 index 0000000..0f5d909 --- /dev/null +++ b/customize/example.tpl @@ -0,0 +1 @@ +# Static file used for customization example diff --git a/customize/run.sh b/customize/run.sh new file mode 100644 index 0000000..da18f7c --- /dev/null +++ b/customize/run.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# +# This script is an example of customization script +# for https://github.com/dduportal/alpine2docker +# It will print the date to a file inside the VM root home + +MY_BASE_DIR="$(pwd -P)" + +# File comes from the packer copied content +cp "${MY_BASE_DIR}/example.tpl" /root/ + +# We append the date in the file +date +"%Y-%m-%d %H:%M:%S" | sudo tee -a /root/customized_build_date diff --git a/http/answers b/http/answers new file mode 100644 index 0000000..9c9e72b --- /dev/null +++ b/http/answers @@ -0,0 +1,16 @@ +KEYMAPOPTS="us us" +HOSTNAMEOPTS="-n alpine35" +INTERFACESOPTS="auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet dhcp + hostname alpine35 +" +DNSOPTS="-d local -n 8.8.8.8 8.8.4.4" +TIMEZONEOPTS="-z UTC" +PROXYOPTS="none" +APKREPOSOPTS="http://dl-cdn.alpinelinux.org/alpine/v3.5/main" +SSHDOPTS="-c openssh" +NTPOPTS="-c openntpd" +DISKOPTS="-s 1024 -L -m sys /dev/sda" diff --git a/scripts/base.sh b/scripts/base.sh new file mode 100644 index 0000000..0483dd6 --- /dev/null +++ b/scripts/base.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +set -eux -o pipefail + +uptime && date + +# Update system +apk upgrade -U --available --no-cache + +# Install base packages +apk --no-cache add curl bash bash-completion rsync + +# Configure root to use bash +sed -i 's#/ash#/bash#g' /etc/passwd + +# Trick vagrant by creating a dummy shutdown command +cat < /sbin/shutdown +#!/bin/sh +echo "INFO: Got the command: shutdown ${*}" +echo "INFO: Replacing by: poweroff" +/sbin/poweroff +EOF +chmod a+x /sbin/shutdown + +# Create a persisted folder for the customized resources +mkdir -p /var/customize +chmod -R 777 /var/customize diff --git a/scripts/clean.sh b/scripts/clean.sh new file mode 100644 index 0000000..f2e7f0a --- /dev/null +++ b/scripts/clean.sh @@ -0,0 +1,14 @@ +#!/bin/sh +set -ux # No -e flag for the dd case + +rm -rf /tmp/* /var/log/* /var/cache/apk/* + +dd if=/dev/zero of=/EMPTY bs=1M +rm -f /EMPTY +# Block until the empty file has been removed, otherwise, Packer +# will try to kill the box while the disk is still full and that's bad +sync +sync +sync + +exit 0 diff --git a/scripts/customize.sh b/scripts/customize.sh new file mode 100644 index 0000000..e971ab5 --- /dev/null +++ b/scripts/customize.sh @@ -0,0 +1,18 @@ +#!/bin/sh +set -uxe -o pipefail + +CUSTOMIZE_DIR="/var/customize" +SCRIPT_TO_RUN="run.sh" + +# Do we have a copy of customize resources ? +if [ -d "${CUSTOMIZE_DIR}" ] && [ -f "${CUSTOMIZE_DIR}/${SCRIPT_TO_RUN}" ] +then + ( + # Yes ? Make it executable and run it ! + cd "${CUSTOMIZE_DIR}" + chmod a+x "./${SCRIPT_TO_RUN}" + ./${SCRIPT_TO_RUN} + ) +fi + +exit 0 diff --git a/scripts/docker.sh b/scripts/docker.sh new file mode 100644 index 0000000..04b91a6 --- /dev/null +++ b/scripts/docker.sh @@ -0,0 +1,37 @@ +#!/bin/sh + +set -eux -o pipefail + +uptime && date + +#### Configure System to handle Docker capabilities + +# Make PAX in softmode - example for container with JVM +cat </etc/sysctl.d/10-docker.conf +kernel.pax.softmode=1 +EOF + +# Enable swap accounting for containers +sed -i 's/quiet/quiet cgroup_enable=memory swapaccount=1/' /boot/extlinux.conf + + +### Install Docker +echo 'http://dl-cdn.alpinelinux.org/alpine/edge/community' \ + | tee -a /etc/apk/repositories + +apk --no-cache add docker py-pip docker-bash-completion + +service docker stop +addgroup root docker +addgroup "${BASE_USER}" docker +service docker start +rc-update add docker boot + +### Install Docker-compose +pip install --no-cache-dir --upgrade pip +pip install --no-cache-dir "docker-compose" + +### Reboot now +reboot now +sleep 30 +exit 0 diff --git a/scripts/sshd.sh b/scripts/sshd.sh new file mode 100644 index 0000000..f4bb612 --- /dev/null +++ b/scripts/sshd.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +set -uxe -o pipefail + +uptime && date + +# add in order to allow packer ssh access to provision +# the system, remove here to make box more secure +sed -i '/^PermitRootLogin yes/d' /etc/ssh/sshd_config + +# make 'vagrant ssh' connections faster +echo "UseDNS no" >> /etc/ssh/sshd_config diff --git a/scripts/sudoers.sh b/scripts/sudoers.sh new file mode 100644 index 0000000..7f6c730 --- /dev/null +++ b/scripts/sudoers.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +set -eux -o pipefail + +uptime && date + +apk --no-cache add sudo +adduser "${BASE_USER}" wheel + +echo "Defaults exempt_group=wheel" > /etc/sudoers +echo "%wheel ALL=NOPASSWD:ALL" >> /etc/sudoers diff --git a/scripts/vagrant.sh b/scripts/vagrant.sh new file mode 100644 index 0000000..073b98b --- /dev/null +++ b/scripts/vagrant.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +set -eux -o pipefail + +uptime && date + +adduser -s /bin/bash -D "${BASE_USER}" +echo "${BASE_USER}:${BASE_USER}" | chpasswd + +SSH_USER_DIR="/home/${BASE_USER}/.ssh" + +mkdir -p 700 "${SSH_USER_DIR}" + +curl -Lso "${SSH_USER_DIR}/authorized_keys" \ + 'https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/vagrant.pub' + +chmod -R go-rwsx "${SSH_USER_DIR}" +chown -R "${BASE_USER}:${BASE_USER}" "${SSH_USER_DIR}" diff --git a/tests/00-box-tests.bats b/tests/00-box-tests.bats new file mode 100644 index 0000000..f792018 --- /dev/null +++ b/tests/00-box-tests.bats @@ -0,0 +1,86 @@ +#!/usr/bin/env bats + +BASE_USER=alpine + +execute_vagrant_ssh_command() { + vagrant ssh -c "${*}" -- -n -T +} + +@test "We can start the VM with vagrant" { + vagrant up +} + +@test "We can SSH inside the VM with vagrant" { + execute_vagrant_ssh_command "echo OK" +} + +@test "Default user of the VM is ${BASE_USER}" { + execute_vagrant_ssh_command "whoami" | grep "${BASE_USER}" +} + +@test "Default shell of default user ${BASE_USER} is bash" { + # Configured User shell + execute_vagrant_ssh_command 'echo ${SHELL}' | grep '/bin/bash' + # Effective shell + execute_vagrant_ssh_command 'echo ${0}' | grep 'bash' +} + +@test "We have the passwordless sudoers rights inside the VM" { + execute_vagrant_ssh_command 'sudo whoami' | grep root +} + +@test "SSH does not allow root login" { + [ "$(execute_vagrant_ssh_command \ + 'grep PermitRootLogin /etc/ssh/sshd_config' \ + | grep yes | wc -l )" -eq 0 ] +} + +@test "SSH does not use DNS resolution (faster vagrant ssh)" { + execute_vagrant_ssh_command "grep 'UseDNS no' /etc/ssh/sshd_config" +} + +@test "The root filesystem is located on a LVM volume" { + execute_vagrant_ssh_command 'sudo df -h | grep "/dev/vg0/lv_root" \ + | grep "/$" | wc -l' +} + +@test "Swap is enabled" { + [ $(execute_vagrant_ssh_command "free -m | grep Swap | awk '{print \$2}'") -ge 0 ] +} + +@test "Docker Client is in the PATH" { + execute_vagrant_ssh_command "which docker" +} + +@test "Docker Compose is in the PATH and executable" { + execute_vagrant_ssh_command "which docker-compose && docker-compose -v" +} + +@test "The default admin user ${BASE_USER} is in the docker group" { + execute_vagrant_ssh_command "grep docker /etc/group | grep ${BASE_USER}" +} + +@test "Docker Engine is started and respond correctly without sudo" { + execute_vagrant_ssh_command "docker info" +} + +@test "We have a customize folder where default user can write inside" { + execute_vagrant_ssh_command "[ -d /var/customize ] \ + && touch /var/customize/test" +} + +@test "We have a shutdown command" { + execute_vagrant_ssh_command "which shutdown" | grep '/sbin/shutdown' +} + +@test "We can restart the machine properly" { + vagrant reload +} + +@test "We can stop properly the machine" { + vagrant halt +} + +@test "We can destroy the vagrant" { + vagrant destroy -f +} diff --git a/vagrantfile-box.tpl b/vagrantfile-box.tpl new file mode 100644 index 0000000..809fbc0 --- /dev/null +++ b/vagrantfile-box.tpl @@ -0,0 +1,21 @@ +Vagrant.configure("2") do |config| + + config.ssh.username = "alpine" + config.ssh.shell = "bash -l" + + config.vm.provider "virtualbox" do |vm, override| + + # Custom VM configuration + vm.customize ["modifyvm", :id, "--memory", "4096"] + vm.customize ["modifyvm", :id, "--cpus", "2"] + vm.customize ["modifyvm", :id, "--cableconnected1", "on"] + vm.customize ["modifyvm", :id, "--audio", "none"] + vm.customize ["modifyvm", :id, "--usb", "off"] + # For secured workstations + vm.customize ["modifyvm", :id, "--natdnshostresolver1", "on"] + + # No FS share to allow any depds to the host + config.vm.synced_folder ".", "/vagrant", disabled: true + end + +end