summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2020-10-21 05:30:09 +0000
committerLars Wirzenius <liw@liw.fi>2020-10-21 05:30:09 +0000
commitec80623d7cae7f3c5028a5337520bc642e6bb1aa (patch)
tree1c31c8884e447b89578d4a4c1323a86c2f151aa9
parent876d6370e1e0b63eca488bd85639b8b0db659cd0 (diff)
parent515aa564410e068c3ed9d7f882a49e10077d62c2 (diff)
downloadick-contractor-ec80623d7cae7f3c5028a5337520bc642e6bb1aa.tar.gz
Merge branch 'rust' into 'master'
Rust See merge request larswirzenius/contractor!21
-rwxr-xr-xcheck2
-rwxr-xr-xcontractor146
-rw-r--r--contractor.md24
-rwxr-xr-xcreate-vm148
-rw-r--r--heippa.yaml8
-rw-r--r--hosts1
-rwxr-xr-xmanager21
-rw-r--r--manager-config.yaml2
-rw-r--r--manager-ssh-config1
-rw-r--r--manager-vm.yaml7
-rw-r--r--manager.yml107
-rw-r--r--nop.yaml2
-rwxr-xr-xremove-vm26
-rw-r--r--toy.yaml7
-rwxr-xr-xtry.sh6
-rw-r--r--vm.vmdb50
-rw-r--r--vm.yml79
-rw-r--r--worker-scap.vmdb49
-rw-r--r--worker-scap.yml110
-rw-r--r--worker-subplot.vmdb49
-rw-r--r--worker-subplot.yml54
-rw-r--r--worker-vmdb2.vmdb49
-rw-r--r--worker-vmdb2.yml95
23 files changed, 457 insertions, 586 deletions
diff --git a/check b/check
index bc57b7a..dc38406 100755
--- a/check
+++ b/check
@@ -3,7 +3,7 @@
set -eu
rm -f test.py
-black --check contractor *.py
+black --check contractor ./*.py
echo "$1" > test.address
echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK" > test.env
sp-codegen contractor.md -o test.py --run
diff --git a/contractor b/contractor
index 66693ed..75d809f 100755
--- a/contractor
+++ b/contractor
@@ -10,7 +10,6 @@ import sys
import time
import subprocess
from subprocess import PIPE, STDOUT
-
import yaml
@@ -18,8 +17,14 @@ import yaml
DEFAULT_CONFIGS = {os.path.expanduser("~/.config/contractor/config.yaml")}
-# The device in the manager VM for the workspace disk.
-WS_DEV = "/dev/vdb"
+# The disk image file on the manager VM for the workspace disk.
+WS_IMG = "/home/manager/workspace.img"
+WS_SIZE = "20G"
+WS_MNT = "/mnt"
+
+
+# The device on the worker for the workspace disk.
+WORKER_WS_DEV = "vdb"
# The worker VM image file on manager VM.
@@ -30,14 +35,6 @@ WORKER_IMG = "worker.img"
TEMP_IMG = "temp.img"
-# The UID of the worker account, on the worker VM.
-WORKER_UID = 1000
-
-
-# The GID of the worker account, on the worker VM.
-WORKER_GID = 1000
-
-
class ExecResult:
def __init__(self, stdout, stderr, exit_code):
self.stdout = stdout
@@ -248,7 +245,7 @@ class CopyWorkerImage(RemoteExecution):
def argv(self):
return ["sh", "-c", "rm -f temp.img; cp worker.img temp.img"]
self.ssh(["rm", "-f", TEMP_IMG])
- ret = self.ssh(["cp", WORKER_IMG, TEMP_IMG])
+ self.ssh(["cp", WORKER_IMG, TEMP_IMG])
class StartGuestNetworking(MayFail):
@@ -259,12 +256,52 @@ class StartGuestNetworking(MayFail):
return virsh("net-start", "default")
+class GetUID(RemoteExecution):
+ def msg(self):
+ return "get UID on manager"
+
+ def argv(self):
+ return ["id", "-u"]
+
+
+class GetGID(RemoteExecution):
+ def msg(self):
+ return "get GID of on manager"
+
+ def argv(self):
+ return ["id", "-g"]
+
+
+class RemoveWS(RemoteExecution):
+ def msg(self):
+ return "remove workspace image on manager"
+
+ def argv(self):
+ return ["rm", "-f", WS_IMG]
+
+
+class CreateWS(RemoteExecution):
+ def msg(self):
+ return "creating workspace on manager"
+
+ def argv(self):
+ return ["qemu-img", "create", "-q", "-f", "raw", WS_IMG, WS_SIZE]
+
+
+class MkfsWS(RemoteExecution):
+ def msg(self):
+ return "mkfs workspace on manager"
+
+ def argv(self):
+ return ["sudo", "mkfs", "-t", "ext4", "-q", WS_IMG]
+
+
class MountWS(RemoteExecution):
def msg(self):
return "mounting workspace on manager"
def argv(self):
- return ["sudo", "mount", WS_DEV, "/mnt"]
+ return ["sudo", "mount", "-oloop", WS_IMG, WS_MNT]
class MountWSonWorker(RemoteExecution):
@@ -272,7 +309,7 @@ class MountWSonWorker(RemoteExecution):
return "mounting workspace on worker"
def argv(self):
- return ["sudo", "mount", "/dev/vdb", "/workspace"]
+ return ["sudo", "mount", "/dev/{}".format(WORKER_WS_DEV), "/workspace"]
class TryUnmountWS(MayFail):
@@ -280,7 +317,7 @@ class TryUnmountWS(MayFail):
return "trying to unmount workspace on manager"
def argv(self):
- return ["sudo", "umount", "--quiet", WS_DEV]
+ return ["sudo", "umount", "--quiet", WS_IMG]
class UnmountWS(RemoteExecution):
@@ -288,15 +325,19 @@ class UnmountWS(RemoteExecution):
return "unmounting workspace on manager"
def argv(self):
- return ["sudo", "umount", "--quiet", WS_DEV]
+ return ["sudo", "umount", "--quiet", WS_IMG]
class ChownWS(RemoteExecution):
+ def __init__(self, uid, gid):
+ self.uid = uid
+ self.gid = gid
+
def msg(self):
return "set ownerships on workspace"
def argv(self):
- return ["sudo", "chown", "-R", "{}:{}".format(WORKER_UID, WORKER_GID), "/mnt"]
+ return ["sudo", "chown", "-R", "{}:{}".format(self.uid, self.gid), WS_MNT]
class Mkdir(RemoteExecution):
@@ -368,8 +409,8 @@ class AttachWS(RemoteExecution):
"--quiet",
"attach-disk",
"worker",
- WS_DEV,
- "vdb",
+ WS_IMG,
+ WORKER_WS_DEV,
"--targetbus",
"virtio",
"--live",
@@ -510,19 +551,19 @@ def upload_worker_image(vrb, filename, dest, port):
def sync_to_workspace(vrb, frm, dest, port, subdir):
- destdir = "/mnt/{}".format(subdir)
+ destdir = "{}/{}".format(WS_MNT, subdir)
vrb("syncing local {} to manager {}".format(frm, destdir))
- er = rsync("{}/.".format(frm), "{}:{}/.".format(dest, destdir), port)
+ er = rsync(f"{frm}/.", f"{dest}:{destdir}/.", port)
if er.failed():
error("Failed to rsync saved workspace to worker")
sys.exit(1)
def sync_from_workspace(vrb, dest, port, ws):
- vrb("syncing manager /mnt to local {}".format(ws))
+ vrb("syncing manager {!r} to local {!r} (port {!r})".format(WS_MNT, ws, port))
if not os.path.exists(ws):
os.makedirs(ws)
- er = rsync("{}:/mnt/.".format(dest), "{}/.".format(ws), port)
+ er = rsync(f"{dest}:{WS_MNT}/.", f"{ws}/.", port)
if er.failed():
error("Failed to rsync workspace from worker")
sys.exit(1)
@@ -549,6 +590,28 @@ def cmd_dump(args):
sys.stdout.write("{}\n".format(json.dumps(bs.as_dict(), indent=4)))
+def cmd_provision(args):
+ ssh_opts = [
+ "ControlMaster=auto",
+ "ControlPersist=60s",
+ "StrictHostKeyChecking=accept-new",
+ "UserKnownHostsFile=/dev/null",
+ ]
+
+ env = dict(os.environ)
+ env["ANSIBLE_SSH_ARGS"] = " ".join(f"-o{opt}" for opt in ssh_opts)
+
+ argv = [
+ "ansible-playbook",
+ "-i",
+ "hosts",
+ "manager.yml",
+ f"-eansible_ssh_host={args.manager_address}",
+ f"-eansible_ssh_port={args.manager_port}",
+ ]
+ subprocess.check_call(argv, env=env)
+
+
def cmd_status(args):
dest, port = manager_destination(args)
verbose(args, "manager VM is {}:{}".format(dest, port))
@@ -560,7 +623,9 @@ def cmd_status(args):
def cmd_build(args):
- vrb = lambda msg: verbose(args, msg)
+ def vrb(msg):
+ verbose(args, msg)
+
vrb("building according to {}".format(args.spec))
bs = load_build_spec(args.spec)
dest, port = manager_destination(args)
@@ -582,9 +647,27 @@ def cmd_build(args):
CopyWorkerImage(),
StartGuestNetworking(),
CreateWorkerVM(),
+ ]
+ exec_quietly(manager, *execs)
+
+ with Timer(vrb, "start-worker"):
+ execs = [GetUID()]
+ er = exec_quietly(manager, *execs)
+ manager_uid = int(er.stdout)
+
+ with Timer(vrb, "start-worker"):
+ execs = [GetGID()]
+ er = exec_quietly(manager, *execs)
+ manager_gid = int(er.stdout)
+
+ with Timer(vrb, "start-worker"):
+ execs = [
TryUnmountWS(),
+ RemoveWS(),
+ CreateWS(),
+ MkfsWS(),
MountWS(),
- ChownWS(),
+ ChownWS(manager_uid, manager_gid),
]
exec_quietly(manager, *execs)
@@ -596,7 +679,10 @@ def cmd_build(args):
sync_to_workspace(vrb, ws, dest, port, ".")
with Timer(vrb, "upload-source"):
- exec_quietly(manager, Mkdir("/mnt/src", owner=WORKER_UID, group=WORKER_GID))
+ exec_quietly(
+ manager,
+ Mkdir("{}/src".format(WS_MNT), owner=manager_uid, group=manager_gid),
+ )
src = bs.source()
sync_to_workspace(vrb, src, dest, port, "src")
@@ -659,6 +745,9 @@ def load_default_config(args):
def load_config(filename, args):
+ def identity(x):
+ return x
+
with open(filename) as f:
config = yaml.safe_load(f)
@@ -673,7 +762,7 @@ def load_config(filename, args):
if key in config:
func = keys[key]
if func is None:
- func = lambda x: x
+ func = identity
setattr(args, key, func(config[key]))
@@ -696,6 +785,9 @@ def main():
dump.add_argument("spec")
dump.set_defaults(func=cmd_dump)
+ provision = sub.add_parser("provision", help="provision manager VM")
+ provision.set_defaults(func=cmd_provision, **manager_defaults)
+
status = sub.add_parser("status", help="check status of manager VM")
status.add_argument("-m", "--manager-address", help="address of manager VM")
status.add_argument("-p", "--manager-port", help="SSH port of manager VM")
diff --git a/contractor.md b/contractor.md
index 3b18b0b..430bd87 100644
--- a/contractor.md
+++ b/contractor.md
@@ -15,7 +15,8 @@ fundamentally the same. The process is roughly as follows:
* run the software, perhaps as part of unit testing
When the software is run, even if only a small unit of it, it can do
-anything that the person running the build can do, in principle:
+anything that the person running the build can do. For example, it can
+do any and all of the following, unless guarded against:
* delete files
* modify files
@@ -34,8 +35,6 @@ they work with don't do any of that. In both cases, they may be wrong:
mistakes happen. It's a well-guarded secret among programmers that
they sometimes, even if rarely, make catastrophic mistakes.
-**FIXME**: reference the bug in Debian that removed the ld.so symlink
-
Accidents aside, mayhem and chaos may be intentional. Your own project
may not have malware, and you may have vetted all your dependencies,
and you trust them. But your dependencies have dependencies, which
@@ -43,7 +42,7 @@ have further dependencies, which have dependencies of their own. You'd
need to vet the whole dependency tree. Even decades ago, in the 1990s,
this could easily be hundreds of thousands of lines of code, and
modern systems a much larger. Note that build tools are themselves
-dependencies, as is the whole operating system. Any code that is used
+dependencies, as is the whole operating system. Any code that is invoked
in the build process is a dependency.
How certain are you that you can spot malicious code that's
@@ -60,7 +59,7 @@ This risk affects every operating system and every programming
language. The degree in which it exists varies, a lot. Some
programming language ecosystems seem more vulnerable than others: the
nodejs/npm one, for example, values tiny and highly focused packages,
-which leads to immense dependency trees. The direct or indirect
+which leads to immense dependency trees. The more direct or indirect
dependencies there are, the higher the chance that one of them turns
out to be bad.
@@ -165,7 +164,7 @@ These requirements stem from the threat model above.
* **DefaultBuilder**: The Contractor SHOULD be easy to set up and to
use. It should not require extensive configuration. Running a build
- should be as easy as running **make**(1) on the commadnd line. It
+ should be as easy as running **make**(1) on the command line. It
should be feasible to expect developers to use the Contractor for
their normal development work.
@@ -244,9 +243,6 @@ This high-level design is chosen for the following reasons:
technologies, although it doesn't do much to protect against
virtualisation or hardware vulnerabilities (**HostProtection**)
-**HOWEVER**, this architecture needs improvements, which will happen
-soon. The current implementation is a proof of concept only.
-
## Build process
The architecture leads to a build process that would work roughly like
@@ -268,7 +264,7 @@ this:
* command line tool reports to the developer build success or failure
and where build log and build artifacts are
-## Implementation sketch
+## Implementation sketch (FIXME: update)
This is the current status, to be improved upon.
@@ -455,20 +451,16 @@ abstract: |
further things. It is becoming infeasible to vet the whole set of
software running during a build. If a build includes running local
tests (unit tests, some integration tests), the problem gets worse
- in magintude, if not quality.
+ in magnitude, if not quality.
Some software ecosystems are especially vulnerable to this (nodejs,
Python, Ruby, Go, Rust), but it's true for anything that has
dependencies on any code from outside its own code base, and even if
all the dependencies come from a trusted source, such as the
- operating system vendor.
+ operating system vendor or a Linux distribution.
The Contractor is an attempt to be able to build software securely,
by leveraging virtual machine technology. It attempts to be
secure, convenient, and reasonably efficient.
- The Contractor is not a replacement for a Continuous Integration
- engine, but its technology will hopefully one day become part of the
- Ick CI engine.
-
...
diff --git a/create-vm b/create-vm
new file mode 100755
index 0000000..00242fc
--- /dev/null
+++ b/create-vm
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+
+import os
+import shutil
+import socket
+import subprocess
+import sys
+import tempfile
+import time
+import yaml
+
+
+def cloud_init_iso(iso, hostname, pubkey):
+ tmp = tempfile.mkdtemp()
+
+ with open(os.path.join(tmp, "meta-data"), "w") as f:
+ f.write(
+ f"""\
+# Amazon EC2 style metadata
+local-hostname: {hostname}
+"""
+ )
+
+ with open(os.path.join(tmp, "user-data"), "w") as f:
+ f.write(
+ f"""\
+#cloud-config
+ssh_authorized_keys:
+- {pubkey}
+"""
+ )
+
+ subprocess.check_call(
+ [
+ "genisoimage",
+ "-quiet",
+ "-volid",
+ "cidata",
+ "-joliet",
+ "-rock",
+ "-output",
+ iso,
+ tmp,
+ ]
+ )
+ shutil.rmtree(tmp)
+
+
+def create_vm(vm, image, iso, memory=1024, cpus=1):
+ subprocess.check_call(
+ [
+ "virt-install",
+ "--name",
+ vm,
+ "--memory",
+ str(memory),
+ "--vcpus",
+ str(cpus),
+ f"--disk=path={image},cache=none",
+ f"--disk=path={iso},readonly=on",
+ "--network=network=default",
+ "--connect",
+ "qemu:///system",
+ "--cpu=host-passthrough",
+ "--os-variant=debian9",
+ "--import",
+ "--graphics=spice",
+ "--noautoconsole",
+ "--quiet",
+ ]
+ )
+
+
+def wait_for_ssh(hostname):
+ ssh_port = 22
+ while True:
+ time.sleep(5)
+ try:
+ conn = socket.create_connection((hostname, ssh_port), timeout=1)
+ except Exception:
+ continue
+ conn.close()
+ break
+
+
+def provision(vm, pubkey):
+ ssh_opts = [
+ "ControlMaster=auto",
+ "ControlPersist=60s",
+ "StrictHostKeyChecking=accept-new",
+ "UserKnownHostsFile=/dev/null",
+ ]
+
+ env = dict(os.environ)
+ env["ANSIBLE_SSH_ARGS"] = " ".join(f"-o{opt}" for opt in ssh_opts)
+
+ vars_file = {"user_pub": pubkey}
+
+ fd, filename = tempfile.mkstemp()
+ os.write(fd, yaml.safe_dump(vars_file).encode("UTF-8"))
+ os.close(fd)
+
+ argv = [
+ "ansible-playbook",
+ "-i",
+ "hosts",
+ "manager.yml",
+ f"-eansible_ssh_host={vm}",
+ f"-e@{filename}",
+ ]
+ subprocess.check_output(argv, env=env)
+
+ os.remove(filename)
+
+
+def main():
+ config = yaml.safe_load(open(sys.argv[1]))
+
+ base = config["base_image"]
+ vm = config["name"]
+ img = config["image_file"]
+ size = config["image_size"]
+ pubkey = config["public_key"]
+ memory = config.get("memory", 1024)
+ cpus = config.get("cpus", 1)
+
+ memory = int(memory)
+ cpus = int(cpus)
+ pubkey = open(pubkey).read().rstrip()
+ iso = f"{vm}.iso"
+
+ cloud_init_iso(iso, vm, pubkey)
+
+ if os.path.exists(img):
+ os.remove(img)
+ shutil.copy(base, img)
+
+ subprocess.check_call(["qemu-img", "resize", "-q", img, size])
+
+ create_vm(vm, img, iso, memory=memory, cpus=cpus)
+ wait_for_ssh(vm)
+ os.remove(iso)
+
+
+# provision(vm, pubkey)
+
+
+main()
diff --git a/heippa.yaml b/heippa.yaml
index 4ec5f9b..22a98e8 100644
--- a/heippa.yaml
+++ b/heippa.yaml
@@ -9,10 +9,6 @@ ansible:
- build-essential
vars:
ansible_python_interpreter: /usr/bin/python3
-source: /tmp/heippa
-workspace: /tmp/heippa-workspace
+source: .
build: |
- rm -rf .git
- rm -f heippa
- make
- ./heippa
+ echo hello, world
diff --git a/hosts b/hosts
new file mode 100644
index 0000000..737929f
--- /dev/null
+++ b/hosts
@@ -0,0 +1 @@
+manager
diff --git a/manager b/manager
new file mode 100755
index 0000000..2e24d63
--- /dev/null
+++ b/manager
@@ -0,0 +1,21 @@
+#!/usr/bin/env python3
+
+import os
+import subprocess
+
+env = dict(os.environ)
+env[
+ "ANSIBLE_SSH_ARGS"
+] = "-o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null"
+
+subprocess.check_call(
+ [
+ "ansible-playbook",
+ "-i",
+ "hosts",
+ "manager.yml",
+ "-e@manager-vars.yml",
+ "-eansible_ssh_host=manager",
+ ],
+ env=env,
+)
diff --git a/manager-config.yaml b/manager-config.yaml
new file mode 100644
index 0000000..9b0e014
--- /dev/null
+++ b/manager-config.yaml
@@ -0,0 +1,2 @@
+manager_address: manager
+log: ~/contractor.log
diff --git a/manager-ssh-config b/manager-ssh-config
index 7569c07..0921845 100644
--- a/manager-ssh-config
+++ b/manager-ssh-config
@@ -1,3 +1,4 @@
Host 192.168.*
StrictHostKeyChecking accept-new
+ UserKnownHostsFile /dev/null
IdentityFile ~/.ssh/manager.key
diff --git a/manager-vm.yaml b/manager-vm.yaml
new file mode 100644
index 0000000..d06f57a
--- /dev/null
+++ b/manager-vm.yaml
@@ -0,0 +1,7 @@
+name: manager
+image_file: /mnt/manager.qcow2
+image_size: 30G
+memory: 8192
+cpus: 4
+base_image: /home/liw/debian-10-openstack-amd64.qcow2
+public_key: /home/liw/.ssh/liw-openpgp.pub
diff --git a/manager.yml b/manager.yml
new file mode 100644
index 0000000..c6241c6
--- /dev/null
+++ b/manager.yml
@@ -0,0 +1,107 @@
+- hosts: manager
+ remote_user: debian
+ become: true
+ tasks:
+ - name: "configure modprobe to enable nested VMs"
+ copy:
+ content: |
+ options kvm-intel nested=1
+ options kvm-intel enable_shadow_vmcs=1
+ options kvm-intel enable_apicv=1
+ options kvm-intel ept=1
+ dest: /etc/modprobe.d/kvm-nested.conf
+
+ - name: "install needed packages"
+ apt:
+ name:
+ - ssh
+ - sudo
+ - qemu-system-x86
+ - virtinst
+ - libvirt-daemon-system
+ - libvirt-clients
+ - locales-all
+ - jq
+ - rsync
+ - kpartx
+ - python3-lxml
+ - ansible
+
+ - name: change IP block in default virtual network
+ replace:
+ path: /etc/libvirt/qemu/networks/default.xml
+ regexp: '192\.168\.122\.'
+ replace: '192.168.88.'
+
+ - name: configure default virtual network
+ command: /usr/bin/virsh net-define /etc/libvirt/qemu/networks/default.xml
+
+ - name: start default network now
+ virt_net:
+ state: active
+ name: default
+
+ - name: start default network at boot
+ virt_net:
+ autostart: yes
+ name: default
+
+ - name: "create manager user"
+ user:
+ comment: "Manager"
+ name: manager
+ shell: /bin/bash
+ groups:
+ - libvirt
+
+ - name: "create ~manager/.ssh"
+ file:
+ state: directory
+ path: /home/manager/.ssh
+ owner: manager
+ group: manager
+ mode: 0700
+
+ - name: "copy root authorized_keys to manager user"
+ shell: |
+ install -o manager -g manager /root/.ssh/authorized_keys /home/manager/.ssh/authorized_keys
+ sed -i 's/^.* ssh-rsa /ssh-rsa /' /home/manager/.ssh/authorized_keys
+
+ - name: "give manager sudo"
+ copy:
+ content: |
+ manager ALL=(ALL:ALL) NOPASSWD: ALL
+ dest: /etc/sudoers.d/manager
+ owner: root
+ group: root
+ mode: 0600
+
+ - name: "create ~manager/.ssh"
+ file:
+ state: directory
+ path: /home/manager/.ssh
+ owner: manager
+ group: manager
+ mode: 0700
+
+ - name: "add SSH keys to manager"
+ copy:
+ src: "{{ item }}"
+ dest: "/home/manager/.ssh/{{ item }}"
+ owner: manager
+ group: manager
+ mode: 0600
+ with_items:
+ - manager.key
+ - manager.key.pub
+
+ - name: "configure manager's ssh"
+ copy:
+ src: manager-ssh-config
+ dest: /home/manager/.ssh/config
+ owner: manager
+ group: manager
+ mode: 0600
+
+ vars:
+ ansible_python_interpreter: /usr/bin/python3
diff --git a/nop.yaml b/nop.yaml
index 4ecef9d..0a84568 100644
--- a/nop.yaml
+++ b/nop.yaml
@@ -1,4 +1,4 @@
worker-image: ~/tmp/contractor/worker.img
-source: nop
+source: .
build: |
echo hello, world
diff --git a/remove-vm b/remove-vm
new file mode 100755
index 0000000..67a078d
--- /dev/null
+++ b/remove-vm
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+set -eu -o pipefail
+
+running()
+{
+ virsh domstate "$1" | grep -q -Fx running
+}
+
+shutoff()
+{
+ virsh domstate "$1" | grep -q -Fx "shut off"
+}
+
+for vm in "$@"
+do
+ if running "$vm"
+ then
+ virsh shutdown "$vm" > /dev/null
+ while ! shutoff "$vm"
+ do
+ sleep 1
+ done
+ fi
+ virsh undefine "$vm" > /dev/null
+done
diff --git a/toy.yaml b/toy.yaml
new file mode 100644
index 0000000..d2fdafd
--- /dev/null
+++ b/toy.yaml
@@ -0,0 +1,7 @@
+name: toy2
+image_file: toy.qcow2
+image_size: 10G
+memory: 2048
+cpus: 4
+base_image: /home/liw/debian-10-openstack-amd64.qcow2
+public_key: /home/liw/.ssh/liw-openpgp.pub
diff --git a/try.sh b/try.sh
new file mode 100755
index 0000000..38e1030
--- /dev/null
+++ b/try.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+set -eu -o pipefail
+
+./contractor -c manager-config.yaml build nop.yaml
+rm -f ~/contractor.log
diff --git a/vm.vmdb b/vm.vmdb
deleted file mode 100644
index 45def5e..0000000
--- a/vm.vmdb
+++ /dev/null
@@ -1,50 +0,0 @@
-# An image for running the Ick contractor outer VM.
-
-steps:
- - mkimg: "{{ output }}"
- size: 20G
-
- - mklabel: msdos
- device: "{{ output }}"
-
- - mkpart: primary
- device: "{{ output }}"
- start: 0%
- end: 100%
- tag: /
-
- - kpartx: "{{ output }}"
-
- - mkfs: ext4
- partition: /
-
- - mount: /
-
- - unpack-rootfs: /
-
- - debootstrap: buster
- mirror: http://deb.debian.org/debian
- target: /
- unless: rootfs_unpacked
-
- - apt: install
- packages:
- - linux-image-amd64
- - locales-all
- - python3
- - sudo
- - ssh
- - ansible
- tag: /
- unless: rootfs_unpacked
-
- - cache-rootfs: /
- unless: rootfs_unpacked
-
- - fstab: /
-
- - ansible: /
- playbook: vm.yml
-
- - grub: bios
- tag: /
diff --git a/vm.yml b/vm.yml
deleted file mode 100644
index 783685b..0000000
--- a/vm.yml
+++ /dev/null
@@ -1,79 +0,0 @@
-- hosts: image
- tasks:
- - shell: |
- echo "{{ host }}" > /etc/hostname
- sed -i '/^127\.0\.0.*localhost.*/s/.*/127.0.0.1 localhost {{ host }}/' \
- /etc/hosts
- sed -i '/^root:[^:]:/s//root::/' /etc/passwd
- - copy:
- content: |
- auto lo
- iface lo inet loopback
-
- auto eth0
- iface eth0 inet dhcp
- dest: /etc/network/interfaces
- - copy:
- content: |
- options kvm-intel nested=1
- options kvm-intel enable_shadow_vmcs=1
- options kvm-intel enable_apicv=1
- options kvm-intel ept=1
- dest: /etc/modprobe.d/kvm-nested.conf
- - apt:
- name:
- - ssh
- - sudo
- - qemu-system-x86
- - virtinst
- - libvirt-daemon-system
- - libvirt-clients
- - locales-all
- - jq
- - rsync
- - kpartx
- - name: "modify libvirt to use a non-standard IP range for guests"
- shell:
- sed -i 's/192\.168\.122\./192.168.99\./g' /etc/libvirt/qemu/networks/default.xml
- - user:
- comment: "Manager"
- name: manager
- shell: /bin/bash
- groups:
- - libvirt
- - authorized_key:
- user: manager
- key: "{{ user_pub }}"
- - copy:
- content: |
- manager ALL=(ALL:ALL) NOPASSWD: ALL
- dest: /etc/sudoers.d/manager
- owner: root
- group: root
- mode: 0600
- - file:
- state: directory
- path: /home/manager/.ssh
- owner: manager
- group: manager
- mode: 0700
- - copy:
- src: "{{ item }}"
- dest: "/home/manager/.ssh/{{ item }}"
- owner: manager
- group: manager
- mode: 0600
- with_items:
- - manager.key
- - manager.key.pub
- - copy:
- src: manager-ssh-config
- dest: /home/manager/.ssh/config
- owner: manager
- group: manager
- mode: 0600
- vars:
- host: contractor
- ansible_python_interpreter: /usr/bin/python3
- user_pub: |
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDAlECa3tbFGXhB3Zh/4/GhM11THOThVfiuLqqJ2dpWHEClzpKJHpzzwWt7g9z/MMQNMsUJLy+okz+De6hdjjmYJ9kG9Sr3H4YKq6itGQMj7L/cH3WS3ynp0uy0oW3hf932vDZKQ8iy9vczXH+ERYl+4TYae1Jp4Hyf4/2IYxEfuhKctvSvqySST3Qk9JNZ71HFGOWhjH/MmoCLoT1v+HkqmHdYf/GMKGRo3gqCEGgCgNErYYIyKm3OF3dHXK+hyGLE/cZNu6fU5woW3rvtUCFt08Ri2pm0cnXXJn9jQIMxfS5Kkf64svwgzKmPqgX1f4flopYPlsBXduCgzbJvj+lpgauAk/i1A5B01CFa9sI4C6pHZmwk1qxRwN+4IXL2CQt+tDgYC84ZDDd8R7cNyL22a3KhMQmdHtvog1beAa3Ab+J+cafkXXN+Es9f1wQjzk7DiHupmJIVofBvPP+cRcB46rwha6ati8Fa5QkT9rXFNqQsKk7jq8TIi54Bm15OOa0jInGG3TM17b9Ftu2WTJSAaqgBnDfZiInK7HEvC6K/IBljrN3oGagmFZPrAvzw7d6C2/nKFAQtfoMcE5oWVDrJyjsmJ8oaru0E8rwj7mMvyKPgEMnXTGXLWDgEo50+i291m4bkCxVwiOPbPRvdMll1Y8qfBAPT76sY4Ikgcw/2iw== openpgp:0xBBE80E50
diff --git a/worker-scap.vmdb b/worker-scap.vmdb
deleted file mode 100644
index a0b7a68..0000000
--- a/worker-scap.vmdb
+++ /dev/null
@@ -1,49 +0,0 @@
-steps:
- - mkimg: "{{ output }}"
- size: 4G
-
- - mklabel: msdos
- device: "{{ output }}"
-
- - mkpart: primary
- device: "{{ output }}"
- start: 0%
- end: 100%
- tag: /
-
- - kpartx: "{{ output }}"
-
- - mkfs: ext4
- partition: /
-
- - mount: /
-
- - virtual-filesystems: /
-
- - unpack-rootfs: /
-
- - debootstrap: buster
- mirror: http://deb.debian.org/debian
- target: /
- unless: rootfs_unpacked
-
- - apt: install
- packages:
- - linux-image-amd64
- - locales-all
- - openssh-server
- - python3
- - sudo
- tag: /
- unless: rootfs_unpacked
-
- - cache-rootfs: /
- unless: rootfs_unpacked
-
- - fstab: /
-
- - ansible: /
- playbook: worker-scap.yml
-
- - grub: bios
- tag: /
diff --git a/worker-scap.yml b/worker-scap.yml
deleted file mode 100644
index 1003b77..0000000
--- a/worker-scap.yml
+++ /dev/null
@@ -1,110 +0,0 @@
-- hosts: image
- tasks:
- - shell: |
- echo "{{ host }}" > /etc/hostname
- sed -i '/^127\.0\.0.*localhost.*/s/.*/127.0.0.1 localhost {{ host }}/' \
- /etc/hosts
- sed -i '/^root:[^:]:/s//root::/' /etc/passwd
- - copy:
- content: |
- auto lo
- iface lo inet loopback
-
- auto eth0
- iface eth0 inet dhcp
- dest: /etc/network/interfaces
- - copy:
- content: "{{ ci_prod_signing_key }}"
- dest: /etc/apt/trusted.gpg.d/ci_prod.asc
- - apt_repository:
- repo: "deb http://ci-prod-controller.vm.liw.fi/debian unstable-ci main"
- - apt:
- name:
- - bash-completion
- - build-essential
- - ca-certificates
- - debhelper
- - dh-python
- - flake8
- - git
- - git-buildpackage
- - libjpeg-dev
- - locales-all
- - openssh-server
- - openssl
- - pandoc
- - php
- - python-all-dev
- - python-concurrent.futures
- - python-configparser
- - python-flake8
- - python-jinja2
- - python-openssl
- - python-psutil
- - python-pygments
- - python-requests
- - python-setuptools
- - python-six
- - python-yaml
- - python3-all-dev
- - rsync
- - subplot
- - sudo
- - tox
- - zlib1g-dev
- - user:
- comment: "Worker"
- name: worker
- shell: /bin/bash
- - file:
- state: directory
- path: /home/worker/.ssh
- owner: worker
- group: worker
- mode: 0700
- - copy:
- src: manager.key.pub
- dest: /home/worker/.ssh/authorized_keys
- owner: worker
- group: worker
- mode: 0600
- - copy:
- content: |
- worker ALL=(ALL:ALL) NOPASSWD: ALL
- dest: /etc/sudoers.d/worker
- owner: root
- group: root
- mode: 0600
- vars:
- host: worker
- ansible_python_interpreter: /usr/bin/python3
- ci_prod_signing_key: |
- -----BEGIN PGP PUBLIC KEY BLOCK-----
-
- mQINBFrLO7kBEADdz6mHstYmKU5Dp6OSjxWtWaqTDOX1sJdmmaIK/9EKVIH0Maxp
- 5kvVO5G6mULLAjv/kLG0MxasHPrq8I2A/y8AqKAGVL8QelwLjQMIFZ30/VbGQPHS
- +T5TZXEnoQtNce1GUhFwJ38ZyjjwHBFV9tSec7rZ2Q3YeM3nNnGPf6DacXGfEOPO
- HIN4sXAN2hzNXNjKRzTIvxQseb6nr7afUh/SlZ3yhQOCrIzmYlD7tP9WJe7ofL0p
- JY4pDQYw8rT6nC2BE/ioemh84kERCT1vCe+OVFlSRuMlqfEv+ZpKQ+itOmPDQ/lM
- jpUm1K2hrW/lWpxT/ZxHKo/w1K36J5WshgMZxfUu5BMCL9LMqMcrXNhNjDMfxDMM
- 3yBPOvQ4ls6fecOZ/bsFo1p8VzMk/w/eG8vPs5yuNa5XxN95yFMXoOHGb5Xbu8D4
- 6yiW+Af70LbiSNpGdmNdneiGB2fY38NxBukPw5u3S5qG8HedSmMr1RvSr5kHoAAe
- UbOY+BYaaKsTAT7+1skUW1o3FJSqoRKCHAzTsMWC6zzhR8hRn7jVrrguH1hGbqq5
- TZSCFQZExuTJ7uXrTLG0WoBXIjB5wWNcSeXn8myUWYB51nJNF4tJBouZOz9JwWGl
- kiAQkrHnBttLQWdW9FyjbIoTZMtpvVx+m6ObGTGdGL1cNlLAvWprMXGc+QARAQAB
- tDJJY2sgQVBUIHJlcG9zaXRvcnkgc2lnbmluZyBrZXkgKDIwMTgpIDxsaXdAbGl3
- LmZpPokCTgQTAQgAOBYhBKL1uyDoXyxUH3O717Wr+TZVS6PGBQJayzu5AhsDBQsJ
- CAcCBhUICQoLAgQWAgMBAh4BAheAAAoJELWr+TZVS6PGB5QQANTcikhRUHwt9N4h
- dGc/Hp6CbqdshMoWlwpFskttoVDxQG5OAobuZl5XyzGcmja1lT85RGkZFfbca0IZ
- LnXOLLSAu51QBkXNaj4OhjK/0uQ+ITrvL6RQSXNgHiUTR/W2XD1GIUq6nBqe2GSN
- 31S1baYKKVj5QIMsi7Dq8ls3BBXuPCE+xTSaNmGWjes2t9pPidcRvxsksCLY1qgw
- P1GFXBeMkBQ29kBP87SUL15SIk7OiQLlEURCy5iRls5rt/YEsdEpRWIb0Tm5Nrjv
- 2M3VM+iBhfNXTwj0rJ34mlycF1qQmA7YcTEobT7z587GPY0VWzBpQUnEQj7rQWPM
- cDYY0b+I6kQ8VKOaL4wVAtE98d7HzFIrIrwhTKufnrWrVDPYsmLZ+LPC1jiF7JBD
- SR6Vftb+SdDR9xoE1yRuXbC6IfoW+5/qQNrdQ2mm9BFw5jOonBqchs18HTTf3441
- 6SWwP9fY3Vi+IZphPPi0Gf85oMStgnv/Wnw6LacEL32ek39Desero/D8iGLZernK
- Q2mC9mua5A/bYGVhsNWyURNFkKdbFa+/wW3NfdKYyZnsSfo+jJ2luNewrhAY7Kod
- GWXTer9RxzTGA3EXFGvNr+BBOOxSj0SfWTl0Olo7J5dnxof+jLAUS1VHpceHGHps
- GSJSdir7NkZidgwoCPA7BTqsb5LN
- =dXB0
- -----END PGP PUBLIC KEY BLOCK-----
diff --git a/worker-subplot.vmdb b/worker-subplot.vmdb
deleted file mode 100644
index 3ff1d1c..0000000
--- a/worker-subplot.vmdb
+++ /dev/null
@@ -1,49 +0,0 @@
-steps:
- - mkimg: "{{ output }}"
- size: 4G
-
- - mklabel: msdos
- device: "{{ output }}"
-
- - mkpart: primary
- device: "{{ output }}"
- start: 0%
- end: 100%
- tag: /
-
- - kpartx: "{{ output }}"
-
- - mkfs: ext4
- partition: /
-
- - mount: /
-
- - virtual-filesystems: /
-
- - unpack-rootfs: /
-
- - debootstrap: buster
- mirror: http://deb.debian.org/debian
- target: /
- unless: rootfs_unpacked
-
- - apt: install
- packages:
- - linux-image-amd64
- - locales-all
- - openssh-server
- - python3
- - sudo
- tag: /
- unless: rootfs_unpacked
-
- - cache-rootfs: /
- unless: rootfs_unpacked
-
- - fstab: /
-
- - ansible: /
- playbook: worker-subplot.yml
-
- - grub: bios
- tag: /
diff --git a/worker-subplot.yml b/worker-subplot.yml
deleted file mode 100644
index 8cf09ec..0000000
--- a/worker-subplot.yml
+++ /dev/null
@@ -1,54 +0,0 @@
-- hosts: image
- tasks:
- - shell: |
- echo "{{ host }}" > /etc/hostname
- sed -i '/^127\.0\.0.*localhost.*/s/.*/127.0.0.1 localhost {{ host }}/' \
- /etc/hosts
- sed -i '/^root:[^:]:/s//root::/' /etc/passwd
- - copy:
- content: |
- auto lo
- iface lo inet loopback
-
- auto eth0
- iface eth0 inet dhcp
- dest: /etc/network/interfaces
- - apt:
- name:
- - build-essential
- - cargo
- - graphviz
- - librsvg2-bin
- - locales-all
- - pandoc
- - pandoc-citeproc
- - plantuml
- - texlive-fonts-recommended
- - texlive-latex-base
- - texlive-latex-recommended
- - user:
- comment: "Worker"
- name: worker
- shell: /bin/bash
- - file:
- state: directory
- path: /home/worker/.ssh
- owner: worker
- group: worker
- mode: 0700
- - copy:
- src: manager.key.pub
- dest: /home/worker/.ssh/authorized_keys
- owner: worker
- group: worker
- mode: 0600
- - copy:
- content: |
- worker ALL=(ALL:ALL) NOPASSWD: ALL
- dest: /etc/sudoers.d/worker
- owner: root
- group: root
- mode: 0600
- vars:
- host: worker
- ansible_python_interpreter: /usr/bin/python3
diff --git a/worker-vmdb2.vmdb b/worker-vmdb2.vmdb
deleted file mode 100644
index be9ab2d..0000000
--- a/worker-vmdb2.vmdb
+++ /dev/null
@@ -1,49 +0,0 @@
-steps:
- - mkimg: "{{ output }}"
- size: 4G
-
- - mklabel: msdos
- device: "{{ output }}"
-
- - mkpart: primary
- device: "{{ output }}"
- start: 0%
- end: 100%
- tag: /
-
- - kpartx: "{{ output }}"
-
- - mkfs: ext4
- partition: /
-
- - mount: /
-
- - virtual-filesystems: /
-
- - unpack-rootfs: /
-
- - debootstrap: buster
- mirror: http://deb.debian.org/debian
- target: /
- unless: rootfs_unpacked
-
- - apt: install
- packages:
- - linux-image-amd64
- - locales-all
- - openssh-server
- - python3
- - sudo
- tag: /
- unless: rootfs_unpacked
-
- - cache-rootfs: /
- unless: rootfs_unpacked
-
- - fstab: /
-
- - ansible: /
- playbook: worker-vmdb2.yml
-
- - grub: bios
- tag: /
diff --git a/worker-vmdb2.yml b/worker-vmdb2.yml
deleted file mode 100644
index 112839d..0000000
--- a/worker-vmdb2.yml
+++ /dev/null
@@ -1,95 +0,0 @@
-- hosts: image
- tasks:
- - shell: |
- echo "{{ host }}" > /etc/hostname
- sed -i '/^127\.0\.0.*localhost.*/s/.*/127.0.0.1 localhost {{ host }}/' \
- /etc/hosts
- sed -i '/^root:[^:]:/s//root::/' /etc/passwd
- - copy:
- content: |
- auto lo
- iface lo inet loopback
-
- auto eth0
- iface eth0 inet dhcp
- dest: /etc/network/interfaces
- - copy:
- content: "{{ ci_prod_signing_key }}"
- dest: /etc/apt/trusted.gpg.d/ci_prod.asc
- - apt_repository:
- repo: "deb http://ci-prod-controller.vm.liw.fi/debian unstable-ci main"
- - apt:
- name:
- - ansible
- - build-essential
- - cmdtest
- - debootstrap
- - dosfstools
- - git
- - kpartx
- - locales-all
- - moreutils
- - pandoc
- - parted
- - python3-all
- - python3-cliapp
- - python3-coverage-test-runner
- - python3-jinja2
- - python3-yaml
- - qemu-utils
- - user:
- comment: "Worker"
- name: worker
- shell: /bin/bash
- - file:
- state: directory
- path: /home/worker/.ssh
- owner: worker
- group: worker
- mode: 0700
- - copy:
- src: manager.key.pub
- dest: /home/worker/.ssh/authorized_keys
- owner: worker
- group: worker
- mode: 0600
- - copy:
- content: |
- worker ALL=(ALL:ALL) NOPASSWD: ALL
- dest: /etc/sudoers.d/worker
- owner: root
- group: root
- mode: 0600
- vars:
- host: worker
- ansible_python_interpreter: /usr/bin/python3
- ci_prod_signing_key: |
- -----BEGIN PGP PUBLIC KEY BLOCK-----
-
- mQINBFrLO7kBEADdz6mHstYmKU5Dp6OSjxWtWaqTDOX1sJdmmaIK/9EKVIH0Maxp
- 5kvVO5G6mULLAjv/kLG0MxasHPrq8I2A/y8AqKAGVL8QelwLjQMIFZ30/VbGQPHS
- +T5TZXEnoQtNce1GUhFwJ38ZyjjwHBFV9tSec7rZ2Q3YeM3nNnGPf6DacXGfEOPO
- HIN4sXAN2hzNXNjKRzTIvxQseb6nr7afUh/SlZ3yhQOCrIzmYlD7tP9WJe7ofL0p
- JY4pDQYw8rT6nC2BE/ioemh84kERCT1vCe+OVFlSRuMlqfEv+ZpKQ+itOmPDQ/lM
- jpUm1K2hrW/lWpxT/ZxHKo/w1K36J5WshgMZxfUu5BMCL9LMqMcrXNhNjDMfxDMM
- 3yBPOvQ4ls6fecOZ/bsFo1p8VzMk/w/eG8vPs5yuNa5XxN95yFMXoOHGb5Xbu8D4
- 6yiW+Af70LbiSNpGdmNdneiGB2fY38NxBukPw5u3S5qG8HedSmMr1RvSr5kHoAAe
- UbOY+BYaaKsTAT7+1skUW1o3FJSqoRKCHAzTsMWC6zzhR8hRn7jVrrguH1hGbqq5
- TZSCFQZExuTJ7uXrTLG0WoBXIjB5wWNcSeXn8myUWYB51nJNF4tJBouZOz9JwWGl
- kiAQkrHnBttLQWdW9FyjbIoTZMtpvVx+m6ObGTGdGL1cNlLAvWprMXGc+QARAQAB
- tDJJY2sgQVBUIHJlcG9zaXRvcnkgc2lnbmluZyBrZXkgKDIwMTgpIDxsaXdAbGl3
- LmZpPokCTgQTAQgAOBYhBKL1uyDoXyxUH3O717Wr+TZVS6PGBQJayzu5AhsDBQsJ
- CAcCBhUICQoLAgQWAgMBAh4BAheAAAoJELWr+TZVS6PGB5QQANTcikhRUHwt9N4h
- dGc/Hp6CbqdshMoWlwpFskttoVDxQG5OAobuZl5XyzGcmja1lT85RGkZFfbca0IZ
- LnXOLLSAu51QBkXNaj4OhjK/0uQ+ITrvL6RQSXNgHiUTR/W2XD1GIUq6nBqe2GSN
- 31S1baYKKVj5QIMsi7Dq8ls3BBXuPCE+xTSaNmGWjes2t9pPidcRvxsksCLY1qgw
- P1GFXBeMkBQ29kBP87SUL15SIk7OiQLlEURCy5iRls5rt/YEsdEpRWIb0Tm5Nrjv
- 2M3VM+iBhfNXTwj0rJ34mlycF1qQmA7YcTEobT7z587GPY0VWzBpQUnEQj7rQWPM
- cDYY0b+I6kQ8VKOaL4wVAtE98d7HzFIrIrwhTKufnrWrVDPYsmLZ+LPC1jiF7JBD
- SR6Vftb+SdDR9xoE1yRuXbC6IfoW+5/qQNrdQ2mm9BFw5jOonBqchs18HTTf3441
- 6SWwP9fY3Vi+IZphPPi0Gf85oMStgnv/Wnw6LacEL32ek39Desero/D8iGLZernK
- Q2mC9mua5A/bYGVhsNWyURNFkKdbFa+/wW3NfdKYyZnsSfo+jJ2luNewrhAY7Kod
- GWXTer9RxzTGA3EXFGvNr+BBOOxSj0SfWTl0Olo7J5dnxof+jLAUS1VHpceHGHps
- GSJSdir7NkZidgwoCPA7BTqsb5LN
- =dXB0
- -----END PGP PUBLIC KEY BLOCK-----