From 81dc8132d05121442ac1396d094b67e793ab38b0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Felix=20D=C3=B6rre?= Date: Tue, 21 Mar 2017 10:41:48 +0100 Subject: [PATCH] add: script and instructions for automated setup Change-Id: I0757795270b97d00dd7cf1f1f5414e0b3b796939 --- bootstrap-user | 0 manager/.gitignore | 5 ++ manager/README.md | 128 ++++++++++++++++++++++++++++++++ manager/fetch | 20 +++++ manager/installNRE | 32 ++++++++ manager/setup | 180 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 365 insertions(+) mode change 100644 => 100755 bootstrap-user create mode 100644 manager/.gitignore create mode 100644 manager/README.md create mode 100644 manager/fetch create mode 100755 manager/installNRE create mode 100755 manager/setup diff --git a/bootstrap-user b/bootstrap-user old mode 100644 new mode 100755 diff --git a/manager/.gitignore b/manager/.gitignore new file mode 100644 index 0000000..e2d7f3b --- /dev/null +++ b/manager/.gitignore @@ -0,0 +1,5 @@ +/nre-results/ +/tricks +*/config +*~ +\#*\# diff --git a/manager/README.md b/manager/README.md new file mode 100644 index 0000000..03730d1 --- /dev/null +++ b/manager/README.md @@ -0,0 +1,128 @@ +VM-Setup (for CI) +================= + +In the following instruction "this directory" refers to the directory (outside of the VM) where this README.md resides. + +1. create a VM with network adapter + - recommended more than 16GB HD and 1GB RAM +2. install debian (stretch) into this VM + - with SSHD + - recommended: default user, one partition, mostly default settings +3. create configuration for connecting to this vm + - create an ssh-key (may have a passphrase) named "vm-key" in this directory + - install the public key into the VMs authorized_keys-file (e.g. with `ssh-copy-id`) + - create a file named `/config` containing `to=user@vm-connection-info` + (steps 4 and 5 can be executed by script init-vm) +4. connect to this VM and install CI-SSH-Key (generate one named vm-key in this directory) + - (optional) disable SSH-Password Auth (in the VM) + - (optional; required for CI) disable sudo requiring a password. (in the VM) +5. (optional) install some packages for CI speedup (in the VM) +6. (optional) Now may be a good time to poweroff the VM, take a snapshot and power it back on. +7. run `./setup [fresh]` + - `fresh` instructs the script to reset the VM before installing + + +Creating a tricks script +------ + +You may create a script named `tricks` in this directory. This script will be executed after initial bootstrap is completed but before bootstrap-user is invoked. This script may therefore apply some site-local patches to the system to get everything ready for initializing gigi's database. Things to do here would be: +- setting up additional tunnels +- patching gigi's public suffix list to accept your domain +- other stuff your site requires + +Here are some useful snippets that you might want to include in your tricks file: + + +### Adding your own domain as public suffix + +The bootstrap procedure requires Gigi’s domain to be exactly one level below a *public suffix*, +for example `someca.de` or `someca.co.uk`. +If you do not have many such domains to spare, +you can edit Gigi’s public suffix list to add a domain controlled by you to it (for example `gigi.yourname.com`), +and then run Gigi instances under `test1.gigi.yourname.com`, `test2.gigi.yourname.com` etc. +In this example, `YOUR.PUBLIC.SUFFIX` below would be `gigi.yourname.com`. + +``` +if ! sudo lxc-attach -n gigi -- unzip -c /usr/share/java/gigi.jar club/wpia/gigi/util/effective_tld_names.dat | grep "YOUR.PUBLIC.SUFFIX" > /dev/null; then + echo "patching public suffixes" + sudo lxc-attach -n gigi -- apt-get update + sudo lxc-attach -n gigi -- apt-get install --no-install-recommends -y unzip zip + sudo lxc-attach -n gigi bash <> club/wpia/gigi/util/effective_tld_names.dat +zip /usr/share/java/gigi.jar club/wpia/gigi/util/effective_tld_names.dat +rm -fR club +EOF +fi +``` + +### Forwarding IPv6 traffic to nginx + +All containers inside the VM have an IP address in the private 10.0.0.0/8 block, +and we set up iptables rules to NAT IPv4 traffic from the VM to the nginx container +(which then proxies to Gigi). +This doesn't work if your VM only has a public IPv6 address, for example because its host system is on a residential connection. +In this case, you can set up another server (with an IPv4 address) to proxy to your system via IPv6, +and then run services in the VM that forward that IPv6 traffic back to nginx' IPv4 address. +(You will have to use that server's domain name in your configuration, +and possibly adjust the public suffix list as described elsewhere in this document.) + +``` +if ! [[ -f /etc/systemd/system/forward-to-nginx@.socket ]]; then + sudo tee /etc/systemd/system/forward-to-nginx@.socket > /dev/null << 'EOF' +[Unit] +Description=Listen on port %i and forward it to the nginx container + +[Socket] +ListenStream=%i + +[Install] +WantedBy=sockets.target +EOF + sudo systemctl daemon-reload +fi +if ! [[ -f /etc/systemd/system/forward-to-nginx@.service ]]; then + sudo tee /etc/systemd/system/forward-to-nginx@.service > /dev/null << 'EOF' +[Unit] +Description=Forward port %i to the nginx container +Documentation=man:systemd-socket-proxyd(8) + +[Service] +ExecStart=/lib/systemd/systemd-socket-proxyd 10.0.3.13:%i + +DynamicUser=yes +PrivateUsers=yes +PrivateTmp=yes +PrivateDevices=yes +ProtectSystem=strict +ProtectHome=yes +NoNewPrivileges=yes +SystemCallArchitectures=native +RestrictAddressFamilies=AF_INET +ProtectKernelModules=yes +MemoryDenyWriteExecute=yes +RestrictRealtime=yes +EOF + sudo systemctl daemon-reload +fi +sudo systemctl enable forward-to-nginx@{80,443}.socket +sudo systemctl start forward-to-nginx@{80,443}.socket +``` + + +Other snippets +-------------- + +### Restarting the user-bootstrap procedure + +If you entered wrong bootstrap users or something went wrong during the final phase of bootstrapping +(the actual initialization of Gigi’s database with the first administrative accounts) +you can kill the script and then wipe the database with the following one-liner (run in the VM): + +```bash +sudo lxc-attach -n gigi -- systemctl stop gigi-proxy.{service,socket} cassiopeia-client && sudo lxc-attach -n postgres-primary -- su -c "psql" postgres <<< "DROP DATABASE gigi; CREATE DATABASE gigi;" +``` + +Afterwards, you can run `./bootstrap-user` in the VM again. diff --git a/manager/fetch b/manager/fetch new file mode 100644 index 0000000..809b1bf --- /dev/null +++ b/manager/fetch @@ -0,0 +1,20 @@ +#!/bin/bash +function ssh_target { + ssh -i vm-key "$to" "$@" +} +source "$1/config" + +echo "To: $to" +echo "Agent: $SSH_AGENT_PID" +oldpid=$SSH_AGENT_PID +if [[ $oldpid == "" ]]; then + eval $(ssh-agent) + ssh-add vm-key +fi + +git fetch "$to:" "HEAD:refs/remotes/$1" + +if [[ $oldpid == "" ]]; then + eval $(ssh-agent -k) +fi + diff --git a/manager/installNRE b/manager/installNRE new file mode 100755 index 0000000..2116eaf --- /dev/null +++ b/manager/installNRE @@ -0,0 +1,32 @@ +#!/bin/bash +signerLocation=self +function ssh_target { + ssh -i vm-key "$to" "$@" +} + +source "$1/config" + +function extract { + pattern=$1 + folder=$2 + echo "On $to" + echo $folder + for i in $pattern; do + cat $i | ssh_target "mkdir -p $folder && tar xzv -C $folder" + done +} +shopt -s nullglob +extract "nre-results/signer-server-*.tar.gz" modules/cassiopeia_signer/files +extract "nre-results/gigi-*.tar.gz" modules/nre/files +extract "nre-results/signer-client-*.tar.gz" modules/cassiopeia_client/files + +#if [[ "$1" == "signer" ]]; then +# extract "generated/signer-server-*.tar.gz" modules/cassiopeia_signer/files +#elif [[ "$1" == "both" ]]; then +#else +# echo "installing on $target" +# extract "generated/gigi-*.tar.gz" modules/nre/files +# extract "generated/signer-client-*.tar.gz" modules/cassiopeia_client/files +# #todo generate this with mkcassiopeia +# extract "generated/signer.tar.gz" modules/cassiopeia/files +#fi diff --git a/manager/setup b/manager/setup new file mode 100755 index 0000000..9481c85 --- /dev/null +++ b/manager/setup @@ -0,0 +1,180 @@ +#!/bin/bash +signerLocation=self +devPkgs="" +targetHost="$1" + +function ssh_target { + ssh -i vm-key "$to" "$@" +} + +function install_nre { + ./installNRE "$targetHost" +} +function read_activation_link { + read -rp "Link: " link + printf '%s\n' "$link" +} +function configure { + ssh_target -t 'bash conf-puppet --force' + ssh_target 'sed -i "s%\$signerLocation = '".*'"'%\$signerLocation = '"'$signerLocation'"'%" environments/production/manifests/ip.pp' +} +function do_reset_vm { + read -rp "Please reset your VM and confirm by pressing enter." _ +} +function execute-bootstrap-user { + ssh_target -t 'bash bootstrap-user' +} + +source "$targetHost/config" + +eval $(ssh-agent) +trap "eval \$(ssh-agent -k)" EXIT +ssh-add vm-key +function title { + printf '\e]0;%s\007' "$1" +} + +function reset_vm { + if ! [[ -f reset-vm-key ]]; then + ssh-keygen -f reset-vm-key -t ed25519 -N "" + fi + title 'S0: VM-reset' + printf "command=\"bash reset1\" " + cat reset-vm-key.pub + + echo "Powering off" + ssh_target -t 'ip a show dev enp0s3; sudo poweroff' + + echo "Resetting VM" + do_reset_vm + echo "Waiting for VM to respond: " + while ! ping -c 1 -W 1 "${to#*@}" > /dev/null; do + printf "." + done + printf "\n" + while ! ssh_target 'echo Got into VM'; do + sleep 5 + echo "Please open the VM for my key" + done +} +function update { + title 'S1: apt-get; clone' + ssh_target -t "sudo apt-get update && DEBIAN_FRONTEND=noninteractive sudo -E apt-get upgrade -o Dpkg::Options::=\"--force-confold\" -y && sudo apt-get install -y git curl $devPkgs" + echo "Git init" + ssh_target -t '[[ -d .git ]] || git init;' + echo "Git push" + git bundle create .fullBundle HEAD + cat .fullBundle | ssh_target 'cat > .fullBundle' + ssh_target 'git fetch .fullBundle HEAD:refs/remotes/origin/master; rm .fullBundle' + rm .fullBundle + echo "Git update" + ssh_target -t 'if ! git rev-parse --verify master &> /dev/null; then git checkout origin/master; else git merge --ff-only origin/master; fi' +} + +function ensure_nre { + first=true + while true; do + if [[ $first == "true" ]]; then + first=false + else + echo "Please provide the NRE archives in 'nre-results/*.tar.gz' or install them manually into the target." + read -p "press enter to continue" tmp + fi + install_nre + if ssh_target '[ -d modules/cassiopeia_client/files/profiles ] && [ -d modules/nre/files/config/profiles ]'; then + echo cassiopeia-client and nre-conf files found + else + echo cassiopeia-client or nre-conf files not found + continue + fi + if [[ "$signerLocation" == "self" ]]; then + if ssh_target '[ -d modules/cassiopeia_signer/files/profiles ]'; then + echo cassiopeia-signer files found + else + echo cassiopeia-signer files not found + continue + fi + if ssh_target '[ -d modules/cassiopeia/files ]'; then + echo external keys with self-signer?? wrong! + #continue + #TODO make more intelligent... only allow if key-pair exists + fi + else + if ssh_target '[ -d modules/cassiopeia/files ]'; then + echo external keys with self-signer found + else + echo external keys with self-signer not found + continue + fi + fi + break + done +} +function execute-bootstrap-user-auto { + + coproc { + ssh_target -t -t 'bash bootstrap-user' + } + echo "waiting for bootstrap-user..." + if [[ $need_sudo == "true" ]]; then + read -sp "sudo: " sudo + if [[ $sudo != "" ]]; then + printf '%s\n' "$sudo" >&${COPROC[1]} + fi + fi + line="" + while [[ $line != "We need a first "* ]]; do + if ! read -r line <&${COPROC[0]}; then + echo "Error, bootstrap terminated early" + exit -1 + fi + echo "Line: $line" + done + bootstrapper_details >&${COPROC[1]} + + while [[ $line != "You should now have been sent an activation link to the email"* ]]; do + if ! read -r line <&${COPROC[0]}; then + echo "Error, bootstrap terminated early" + exit -1 + fi + printf '%s\n' "$line" + done + read_activation_link >&${COPROC[1]} + cat <&${COPROC[0]} +} + +if [[ "$2" == "fresh" ]]; then + reset_vm +fi +need_sudo=true +if ssh_target "sudo -n whoami" > /dev/null; then + echo "Sudo does not need a password, great!" + need_sudo=false +fi + +update +if [[ "$2" == "update" ]]; then + exit 0; +fi +configure +ensure_nre +title 'S3: puppet-1' +ssh_target -t 'sudo ./bootstrap' +title 'S3: puppet-2' +ssh_target -t 'sudo ./bootstrap' + +[[ -f ../../migrate ]] && ( cd ../.. && bash migrate "$targetHost") +if [[ -f tricks ]]; then + cat tricks | ssh_target 'cat > tricks && chmod +x tricks' + ssh_target -t 'bash tricks' +fi + +if [[ $signerLocation == "self" ]] && [[ $(ssh_target 'ps -ef | grep tcpseria[l] | wc -l') != "2" ]]; then + echo "Error some services weren't started" + exit 1 +fi + +execute-bootstrap-user + +title 'bash' +eval $(ssh-agent -k) -- 2.39.2