]> git.corax.cc Git - foundry/commitdiff
signbot: Add Debian package signer daemon and test script
authorMatthias Kruk <m@m10k.eu>
Sat, 14 Aug 2021 07:23:11 +0000 (16:23 +0900)
committerMatthias Kruk <m@m10k.eu>
Sat, 14 Aug 2021 07:23:11 +0000 (16:23 +0900)
This commit adds signbot, a daemon for automatic signing of Debian
packages. This commit further adds a script that can be used to
manually inject contexts with artifacts into the build system to
test the functionality of signbot (or manually sign packages).

sign.sh [new file with mode: 0755]
signbot.sh [new file with mode: 0755]

diff --git a/sign.sh b/sign.sh
new file mode 100755 (executable)
index 0000000..9c12513
--- /dev/null
+++ b/sign.sh
@@ -0,0 +1,157 @@
+#!/bin/bash
+
+check_result() {
+       local endpoint="$1"
+       local context="$2"
+
+       local start
+       local timelimit
+
+       start=$(date +"%s")
+       timelimit=$((5 * 60))
+
+       while (( ( $(date +"%s") - start ) < timelimit )); do
+               local msg
+               local fmsg
+               local ctx
+               local artifact
+               local ret
+
+               ret=0
+
+               if ! msg=$(ipc_endpoint_recv "$endpoint" 5); then
+                       continue
+               fi
+
+               if ! fmsg=$(ipc_msg_get_data "$msg"); then
+                       log_warn "Dropping invalid message (not a foundry message)"
+                       continue
+               fi
+
+               if ! ctx=$(foundry_msg_sign_get_context "$fmsg"); then
+                       log_warn "Dropping invalid message (no context in message)"
+                       continue
+               fi
+
+               if [[ "$ctx" != "$context" ]]; then
+                       # Unrelated
+                       continue
+               fi
+
+               while read -r artifact; do
+                       if [[ "$artifact" != *".deb" ]]; then
+                               continue
+                       fi
+
+                       if ! dpkg-sig --verify "$artifact"; then
+                               log_error "Invalid signature on $artifact"
+                               ret=1
+                       fi
+               done < <(foundry_context_get_files "$context")
+
+               return "$ret"
+       done
+
+       return 1
+}
+
+test_signbot() {
+       local project="$1"
+       local artifacts=("${@:2}")
+
+       local signreq
+       local endpoint
+       local context
+       local artifact
+
+       if ! context=$(foundry_context_new "$project"); then
+               log_error "Could not create context for $project"
+               return 1
+       fi
+
+       log_info "Created context $context"
+
+       for artifact in "${artifacts[@]}"; do
+               log_info "Adding $artifact to context $context"
+               if ! foundry_context_add_file "$context" "build" "$artifact"; then
+                       log_error "Could not add $artifact to context $context"
+                       return 1
+               fi
+       done
+
+       if ! signreq=$(foundry_msg_signrequest_new "$context"); then
+               log_error "Could not make sign request"
+               return 1
+       fi
+
+       if ! endpoint=$(ipc_endpoint_open); then
+               log_error "Could not open IPC endpoint"
+               return 1
+       fi
+
+       if ! ipc_endpoint_subscribe "$endpoint" "signs"; then
+               log_error "Could not subscribe to topic \"signs\""
+               return 1
+       fi
+
+       if ! ipc_endpoint_send "$endpoint" "pub/signbot" "$signreq"; then
+               log_error "Could not send sign request to pub/signbot"
+               return 1
+       fi
+
+       if ! check_result "$endpoint" "$context"; then
+               return 1
+       fi
+
+       return 0
+}
+
+_add_artifact() {
+       local name="$1"
+       local value="$2"
+
+       if [[ "$name" == "artifact" ]]; then
+               artifacts+=("$value")
+       fi
+
+       return 0
+}
+
+main() {
+       local artifacts
+       local project
+
+       artifacts=()
+
+       opt_add_arg "a" "artifact" "rv" "" "An artifact to be signed" "" _add_artifact
+       opt_add_arg "p" "project"  "rv" "" "Artifact project name"
+
+       if ! opt_parse "$@"; then
+               return 1
+       fi
+
+       project=$(opt_get "project")
+
+       log_info "Signing project $project"
+       array_to_lines "${artifacts[@]}" | log_highlight "artifacts" | log_info
+
+       if ! test_signbot "$project" "${artifacts[@]}"; then
+               echo "Signing failed"
+               return 1
+       fi
+
+       return 0
+}
+
+{
+       if ! . toolbox.sh; then
+               exit 1
+       fi
+
+       if ! include "log" "opt" "ipc" "array" "foundry/msg" "foundry/context"; then
+               exit 1
+       fi
+
+       main "$@"
+       exit "$?"
+}
diff --git a/signbot.sh b/signbot.sh
new file mode 100755 (executable)
index 0000000..1f668c3
--- /dev/null
@@ -0,0 +1,148 @@
+#!/bin/bash
+
+publish_results() {
+       local endpoint="$1"
+       local topic="$2"
+       local key="$3"
+       local context="$4"
+       local result="$5"
+
+       local sign
+
+       if ! sign=$(foundry_msg_sign_new "$context" "$key"); then
+               log_error "Could not make sign message"
+               return 1
+       fi
+
+       if ! ipc_endpoint_publish "$endpoint" "$topic" "$sign"; then
+               log_error "Could not publish sign message"
+               return 1
+       fi
+
+       return 0
+}
+
+handle_sign_request() {
+       local endpoint="$1"
+       local topic="$2"
+       local request="$3"
+       local signer_key="$4"
+
+       local context
+       local artifact
+       local signlog
+       local result
+
+       if ! context=$(foundry_msg_signrequest_get_context "$request"); then
+               log_warn "No context in sign request. Dropping."
+               return 1
+       fi
+
+       inst_set_status "Signing $context"
+
+       signlog=""
+       result=0
+
+       while read -r artifact; do
+               if [[ "$artifact" != *".deb" ]]; then
+                       continue
+               fi
+
+               if ! signlog+=$(dpkg-sig --sign "builder" \
+                                        -k "$signer_key" \
+                                        "$artifact" 2>&1); then
+                       log_error "Could not sign $artifact with key $signer_key"
+                       result=1
+               fi
+       done < <(foundry_context_get_files "$context" "build")
+
+       if ! foundry_context_log "$context" "sign" <<< "$signlog"; then
+               log_error "Could not log to context $context"
+       fi
+
+       if ! publish_results "$endpoint" "$topic" "$signer_key" \
+                            "$context" "$result"; then
+               log_error "Could not publish results at $topic"
+               return 1
+       fi
+
+       return 0
+}
+
+dispatch_tasks() {
+       local endpoint_name="$1"
+       local topic="$2"
+       local signer_key="$3"
+
+       local endpoint
+
+       if ! endpoint=$(ipc_endpoint_open "$endpoint_name"); then
+               log_error "Could not open IPC endpoint $endpoint_name"
+               return 1
+       fi
+
+       while inst_running; do
+               local msg
+               local data
+               local msgtype
+
+               inst_set_status "Awaiting sign requests"
+
+               if ! msg=$(ipc_endpoint_recv "$endpoint" 5); then
+                       continue
+               fi
+
+               if ! data=$(ipc_msg_get_data "$msg"); then
+                       log_warn "Received message without data. Dropping."
+                       continue
+               fi
+
+               if ! msgtype=$(foundry_msg_get_type "$data") ||
+                  [[ "$msgtype" != "signrequest" ]]; then
+                       log_warn "Received message with unexpected type. Dropping."
+                       continue
+               fi
+
+               inst_set_status "Sign request received"
+               handle_sign_request "$endpoint" "$topic" "$data" "$signer_key"
+       done
+
+       return 0
+}
+
+main() {
+       local endpoint
+       local topic
+       local key
+
+       opt_add_arg "e" "endpoint" "v"  "pub/signbot" "The IPC endpoint to listen on"
+       opt_add_arg "t" "topic"    "v"  "signs"       "The topic to publish signs under"
+       opt_add_arg "k" "key"      "rv" ""            "Fingerprint of the key to sign with"
+
+       if ! opt_parse "$@"; then
+               return 1
+       fi
+
+       endpoint=$(opt_get "endpoint")
+       topic=$(opt_get "topic")
+       key=$(opt_get "key")
+
+       if ! inst_start dispatch_tasks "$endpoint" "$topic" "$key"; then
+               return 1
+       fi
+
+       return 0
+}
+
+{
+       if ! . toolbox.sh; then
+               exit 1
+       fi
+
+       if ! include "log" "opt" "inst" "ipc" "foundry/context" "foundry/msg"; then
+               exit 1
+       fi
+
+       main "$@"
+       exit "$?"
+}