[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[dak/master] Allow a safe deployment of code on our hosts from salsa



---
 config/debian/deploy.functions | 75 ++++++++++++++++++++++++++++++++++++++++++
 config/debian/deploy.tasks     |  7 ++++
 config/debian/deploy.variables | 43 ++++++++++++++++++++++++
 scripts/debian/gpgverify       | 30 +++++++++++++++++
 4 files changed, 155 insertions(+)
 create mode 100644 config/debian/deploy.functions
 create mode 100644 config/debian/deploy.tasks
 create mode 100644 config/debian/deploy.variables
 create mode 100755 scripts/debian/gpgverify

diff --git a/config/debian/deploy.functions b/config/debian/deploy.functions
new file mode 100644
index 00000000..34c8d1a9
--- /dev/null
+++ b/config/debian/deploy.functions
@@ -0,0 +1,75 @@
+# -*- mode:sh -*-
+
+function prepare_playground() {
+    if [[ -d ${DEPLOYDIR}/.git ]]; then
+        # Git clone exists, lets get latest and hopefully greatest content
+        cd ${DEPLOYDIR}
+        # Don't be bad, don't play in here manually, we get rid of that
+        git reset --hard
+        git clean -fd
+        git pull
+    else
+        # Not yet there, or not a git yet, lets see
+        if [[ -d ${DEPLOYDIR} ]]; then
+            # Hey fun, our DEPLOYDIR exists, but is not a git checkout?
+            # Complain loudly, let a human master see who fucked up here,
+            # so they can be properly kicked.
+            log_error "BIG BAD PROBLEM, ${DEPLOYDIR} EXISTS BUT IS NOT A CHECKOUT. SOMEONE FUCKED UP, PLEASE KICK THEM"
+            exit 42
+        fi
+
+        # Get it fresh
+        git clone ${GITURL} ${DEPLOYDIR}
+        git config --local gpg.program "${scriptsdir}/gpgverify"
+    fi
+}
+
+function check_for_db_update() {
+    # Check: Do we have a DB Upgrade?
+    if [[ -n ${NEEDDB} ]]; then
+        NEWDBVER=$(ls -1 ${deploydir}/dak/dakdb/update*py|sort -V|tail -n 1)
+        NEWDBVER=${NEWDBVER##*update}
+        declare -r NEWDBVER=${NEWDBVER%%.py}
+    fi
+
+    if [[ ${OLDDBVER} -ne ${NEWDBVER} ]] && [[ -z ${DODB:-""} ]]; then
+        # Differing versions and no DODB variable, break
+        log_error "Database update from ${OLDDBVER} to ${NEWDBVER} required, will not update dak code on ${HOSTNAME} unless told with DODB=1 in the environment"
+        exit 21
+    fi
+}
+
+function find_commitids() {
+    OLDHEAD=$(GIT_DIR=${masterdir}/.git git rev-parse HEAD)
+    NEWHEAD=$(GIT_DIR=${DEPLOYDIR}/.git git rev-parse HEAD)
+}
+
+function check_ancestor() {
+    cd ${DEPLOYDIR}
+    if ! git merge-base --is-ancestor ${OLDHEAD} ${NEWHEAD}; then
+        log_error "Running code HEAD ${OLDHEAD} is not an ancestor of newly-to-deploy HEAD ${NEWHEAD}, refusing to update"
+        exit 2
+    fi
+}
+
+function check_commit_signature() {
+    cd ${DEPLOYDIR}
+    if ! SIGNKEY=$(git verify-commit --raw ${NEWHEAD} 2>&1 | awk '/VALIDSIG/ {print $NF}'); then
+        log_error "{NEWHEAD} is not signed correctly"
+        exit 3
+    fi
+    if [[ -z ${DEPLOY_KEYS[${SIGNKEY}]} ]]; then
+        log_error "{NEWHEAD} signed by ${SIGNKEY} which is not allowed to deploy code"
+        exit 4
+    fi
+}
+
+function update_masterdir() {
+    cd ${masterdir}
+    # We do not want local changes
+    git stash save --include-untracked --all "Update for commitid ${NEWHEAD}"
+    # Now fetch stuff from remote
+    git fetch origin
+    # And switch to the commit we just verified
+    git checkout ${NEWHEAD}
+}
diff --git a/config/debian/deploy.tasks b/config/debian/deploy.tasks
new file mode 100644
index 00000000..2df5ddfe
--- /dev/null
+++ b/config/debian/deploy.tasks
@@ -0,0 +1,7 @@
+# FUNC                 ARGS                       TIME           ERR     BG
+prepare_playground     none                       none           true    false
+NOSTAGE                find_commitids             none           true    false
+check_commit_signature none                       none           true    false
+NOSTAGE                check_for_db_update        none           true    false
+check_ancestor         none                       none           true    false
+update_masterdir       none                       none           true    false
diff --git a/config/debian/deploy.variables b/config/debian/deploy.variables
new file mode 100644
index 00000000..1326fa43
--- /dev/null
+++ b/config/debian/deploy.variables
@@ -0,0 +1,43 @@
+# -*- mode:sh -*-
+
+# Checking for DB updates, only really done on some hosts
+case ${HOSTNAME} in
+    fasolo|seger)
+        declare -r NEEDDB="yes, we need to check for DB updates"
+        ;;
+    *)
+        declare -r NEEDDB=""
+esac
+# Whats the last db update revision before update
+OLDDBVER=0
+# Whats the last db update revision after update
+NEWDBVER=0
+
+# Where is our code
+declare -r GITURL="https://salsa.debian.org/ftp-team/dak.git";
+
+# Where to send error log messages to
+declare -r MAILTO=${MAILTO:-"ftpmaster@debian.org"}
+
+# Store at which DB update we are in live system
+if [[ -n ${NEEDDB} ]]; then
+    OLDDBVER=$(ls -1 ${masterdir}/dak/dakdb/update*py|sort -V|tail -n 1)
+    OLDDBVER=${OLDDBVER##*update}
+    declare -r OLDDBVER=${OLDDBVER%%.py}
+fi
+
+# Used for checking new commits before deploying them
+declare -r DEPLOYDIR=${scriptdir}/deploy
+
+# GPG options for verifying stuff
+DEFGPGOPT="--no-default-keyring --batch --no-tty --no-options --exit-on-status-write-error --no-greeting --with-colons --keyring /srv/keyring.debian.org/keyrings/debian-keyring.gpg"
+
+# Which key is allowed to sign commits for a deploy
+# Only keys count, values are just for fun.
+declare -A DEPLOY_KEYS=(
+  ["309911BEA966D0613053045711B4E5FF15B0FD82"]="mhy"
+  ["80E976F14A508A48E9CA3FE9BC372252CA1CF964"]="ansgar"
+  ["FBFABDB541B5DC955BD9BA6EDB16CF5BB12525C4"]="joerg"
+  ["C74F6AC9E933B3067F52F33FA459EC6715B0705F"]="alteholz"
+  ["8C823DED10AA8041639E12105ACE8D6E0C14A470"]="lfaraone"
+)
diff --git a/scripts/debian/gpgverify b/scripts/debian/gpgverify
new file mode 100755
index 00000000..c15738f6
--- /dev/null
+++ b/scripts/debian/gpgverify
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+set -e
+set -u
+
+TMPDIR=${TMPDIR:-"/tmp"}
+
+GPGSTATUS=$(mktemp -p "${TMPDIR}" GPGSTATUS.XXXXXX)
+trap 'rm -f -- "${GPGSTATUS:?}"' EXIT
+GPGLOGS=$(mktemp -p "${TMPDIR}" GPGLOGS.XXXXXX)
+trap 'rm -f -- "${GPGLOGS:?}"' EXIT
+
+DEFGPGOPT="--no-default-keyring --batch --no-tty --no-options --exit-on-status-write-error --no-greeting --with-colons --keyring /srv/keyring.debian.org/keyrings/debian-keyring.gpg"
+/usr/bin/gpg ${DEFGPGOPT} --status-file ${GPGSTATUS} --logger-file ${GPGLOGS} $3 $4 -
+
+declare -A TOKENS
+while read gpgtag TOKEN something; do
+    TOKENS[$TOKEN]=1
+done < <(cat ${GPGSTATUS})
+
+# Heyho, we just verified, but git verify-commit is silly and wants to redo our work...
+cat ${GPGSTATUS}
+rm -f ${GPGSTATUS}
+rm -f ${GPGLOGS}
+
+if [[ ${TOKENS[VALIDSIG]} == 1 ]] && [[ ${TOKENS[GOODSIG]} == 1 ]]; then
+    exit 0
+else
+    exit 1
+fi
-- 
2.11.0


Reply to: