From: Matthias Kruk Date: Sat, 14 Aug 2021 07:23:11 +0000 (+0900) Subject: signbot: Add Debian package signer daemon and test script X-Git-Url: https://git.corax.cc/?a=commitdiff_plain;h=c840aab0a79520b1be55da39b9d2c3e2923b571f;p=foundry signbot: Add Debian package signer daemon and test script 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). --- diff --git a/sign.sh b/sign.sh new file mode 100755 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 index 0000000..1f668c3 --- /dev/null +++ b/signbot.sh @@ -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 "$?" +}