Skip to content

Commit 5a97702

Browse files
authored
Merge pull request #308 from trz42/use_bot_name_as_namespace
use bot name as namespace
2 parents 03a57d4 + c16b5ff commit 5a97702

4 files changed

Lines changed: 177 additions & 61 deletions

File tree

.github/workflows/tests_scripts.yml

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,71 @@ on:
44
push:
55
paths:
66
- scripts/sign_verify_file_ssh.sh
7+
- .github/workflows/tests_scripts.yml
78
pull_request:
89
paths:
910
- scripts/sign_verify_file_ssh.sh
11+
- .github/workflows/tests_scripts.yml
1012
permissions:
1113
contents: read # to fetch code (actions/checkout)
1214
jobs:
1315
build:
1416
runs-on: ubuntu-24.04
1517
steps:
16-
- name: checkout
17-
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
18+
- name: Checkout repository
19+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
1820

19-
- name: test sign_verify_file_ssh.sh script
20-
run: |
21-
# Create a PEM format ssh identity
21+
- name: Prepare SSH key pair, file and signature
22+
run: |
2223
ssh-keygen -t rsa -b 4096 -m PEM -f id_rsa.pem -N ""
23-
# Create a file to sign
2424
echo "Very important stuff" > out.txt
2525
export FILE_TO_SIGN="out.txt"
26-
# Sign the file
27-
./scripts/sign_verify_file_ssh.sh sign id_rsa.pem "$FILE_TO_SIGN"
28-
# Create an allowed_signers file based on the public key
29-
echo -n "allowed_identity " > allowed_signers
26+
./scripts/sign_verify_file_ssh.sh --sign --private-key id_rsa.pem --file "$FILE_TO_SIGN" --namespace ci
27+
28+
- name: Create allowed signers file and verify
29+
run: |
30+
valid_before=$(date --date='today+3days' +%Y%m%d)
31+
echo -n 'allowed_identity namespaces="ci",valid-before="'$valid_before'" ' > allowed_signers
3032
cat id_rsa.pem.pub >> allowed_signers
31-
# Verify the signature
32-
./scripts/sign_verify_file_ssh.sh verify allowed_signers "$FILE_TO_SIGN"
33-
# Make a new signature that does not appear in the allowed signers file
33+
./scripts/sign_verify_file_ssh.sh --verify --allowed-signers-file allowed_signers --file out.txt
34+
35+
- name: Replace allowed signers with disallowed identity
36+
run: |
37+
valid_before=$(date --date='today+3days' +%Y%m%d)
3438
ssh-keygen -t rsa -b 4096 -m PEM -f id_rsa.alt.pem -N ""
35-
# Replace the allowed signers file
36-
echo -n "disallowed_identity " > allowed_signers
39+
echo -n 'disallowed_identity namespaces="ci",valid-before="'$valid_before'" ' > allowed_signers
3740
cat id_rsa.alt.pem.pub >> allowed_signers
38-
# Make sure signature checking fails in this case
39-
./scripts/sign_verify_file_ssh.sh verify allowed_signers "$FILE_TO_SIGN" && exit 1 || echo "Expected failure for unknown identity"
41+
42+
- name: Ensure verification fails for unknown identity
43+
run: |
44+
./scripts/sign_verify_file_ssh.sh --verify --allowed-signers-file allowed_signers --file out.txt && exit 1 || echo "Expected failure for unknown identity"
45+
46+
- name: Replace allowed signers with wrong namespace
47+
run: |
48+
valid_before=$(date --date='today+3days' +%Y%m%d)
49+
echo -n 'wrong_namespace_identity namespaces="CI",valid-before="'$valid_before'" ' > allowed_signers
50+
cat id_rsa.pem.pub >> allowed_signers
51+
52+
- name: Ensure verification fails for wrong namespace
53+
run: |
54+
./scripts/sign_verify_file_ssh.sh --verify --allowed-signers-file allowed_signers --file out.txt && exit 2 || echo "Expected failure for wrong namespace"
55+
56+
- name: Replace allowed signers with expired key
57+
run: |
58+
valid_expired=$(date --date='today-3days' +%Y%m%d)
59+
echo -n 'expired_key_identity namespaces="ci",valid-before="'$valid_expired'" ' > allowed_signers
60+
cat id_rsa.pem.pub >> allowed_signers
61+
62+
- name: Ensure verification fails for expired key
63+
run: |
64+
./scripts/sign_verify_file_ssh.sh --verify --allowed-signers-file allowed_signers --file out.txt && exit 3 || echo "Expected failure for expired key"
65+
66+
- name: Ensure verification when looping through allowed signers file
67+
run: |
68+
# Add the approved identity to the end
69+
valid_before=$(date --date='today+3days' +%Y%m%d)
70+
echo -n 'listed_identity namespaces="ci",valid-before="'$valid_before'" ' >> allowed_signers
71+
cat id_rsa.pem.pub >> allowed_signers
72+
./scripts/sign_verify_file_ssh.sh --verify --allowed-signers-file allowed_signers --file out.txt
73+
# Make sure we get exactly what we want in terse mode
74+
./scripts/sign_verify_file_ssh.sh --verify --allowed-signers-file allowed_signers --file out.txt --terse | grep -q '{\"identity\": \"listed_identity\", \"namespace\": \"ci\"}'

scripts/eessi-upload-to-staging

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ function display_help
7575
echo " expansion will be applied; arg '-l'" >&2
7676
echo " lists variables that are defined at" >&2
7777
echo " the time of expansion" >&2
78+
echo " -b | --bot-instance NAME - name of the bot instance that uploads" >&2
79+
echo " files to S3" >&2
7880
echo " -e | --endpoint-url URL - endpoint url (needed for non AWS S3)" >&2
7981
echo " -h | --help - display this usage information" >&2
8082
echo " -i | --pr-comment-id - identifier of a PR comment; may be" >&2
@@ -134,6 +136,7 @@ sign_key=
134136
sign_script=
135137

136138
# provided via options in the bot's config file app.cfg and/or command line argument
139+
bot_instance=
137140
metadata_prefix=
138141
artefact_prefix=
139142

@@ -147,6 +150,10 @@ while [[ $# -gt 0 ]]; do
147150
artefact_prefix="$2"
148151
shift 2
149152
;;
153+
-b|--bot-instance)
154+
bot_instance="$2"
155+
shift 2
156+
;;
150157
-e|--endpoint-url)
151158
endpoint_url="$2"
152159
shift 2
@@ -263,7 +270,7 @@ for file in "$*"; do
263270
# 1st sign artefact, and upload signature
264271
if [[ "${sign}" = "1" ]]; then
265272
# sign artefact
266-
${sign_script} sign ${sign_key} ${file}
273+
${sign_script} --sign --private-key ${sign_key} --file ${file} --namespace ${bot_instance}
267274
# TODO check if signing worked (just check exit code == 0)
268275
sig_file=${file}.sig
269276
aws_sig_file=${aws_file}.sig
@@ -314,7 +321,7 @@ for file in "$*"; do
314321
# 2nd sign metadata file, and upload signature
315322
if [[ "${sign}" = "1" ]]; then
316323
# sign metadata file
317-
${sign_script} sign ${sign_key} ${metadata_file}
324+
${sign_script} --sign --private-key ${sign_key} --file ${metadata_file} --namespace ${bot_instance}
318325
# TODO check if signing worked (just check exit code == 0)
319326
sig_metadata_file=${metadata_file}.sig
320327
aws_sig_metadata_file=${aws_metadata_file}.sig

scripts/sign_verify_file_ssh.sh

Lines changed: 113 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# Generates a signature file named `<file>.sig` in the same directory.
88
#
99
# Author: Alan O'Cais
10+
# Author: Thomas Roeblitz
1011
#
1112
# This program is free software: you can redistribute it and/or modify
1213
# it under the terms of the GNU General Public License as published by
@@ -23,25 +24,30 @@
2324

2425
# Usage message
2526
usage() {
27+
local exit_code=${1:-9}
2628
cat <<EOF
2729
Usage:
28-
$0 sign <private_key> <file>
29-
$0 verify <allowed_signers_file> <file> [signature_file]
30+
$0 --sign --private-key <private_key> --file <file> [--namespace <namespace>]
31+
$0 --verify --allowed-signers-file <allowed_signers_file> --file <file> [--signature-file <signature_file>] [--terse]
3032
3133
Options:
32-
sign:
33-
- <private_key>: Path to SSH private key (use KEY_PASSPHRASE env for passphrase)
34-
- <file>: File to sign
34+
--sign:
35+
--private-key <private_key>: Path to SSH private key (use KEY_PASSPHRASE env for passphrase)
36+
--file <file>: File to sign
37+
--namespace <namespace>: Optional, defaults to "file" if not specified
3538
36-
verify:
37-
- <allowed_signers_file>: Path to the allowed signers file
38-
- <file>: File to verify
39-
- [signature_file]: Optional, defaults to '<file>.sig'
39+
--verify:
40+
--allowed-signers-file <allowed_signers_file>: Path to the allowed signers file
41+
--file <file>: File to verify
42+
--signature-file <signature_file>: Optional, defaults to '<file>.sig'
43+
--terse: If set, output only matching identity and namespace for verification in JSON format
4044
4145
Example allowed signers format:
42-
identity_1 <public-key>
46+
identity_1 namespaces="namespace",valid-before="last-valid-day" <public-key>
47+
48+
If the private key has a passphrase, this can be provided via a 'KEY_PASSPHRASE' environment variable.
4349
EOF
44-
exit 9
50+
exit "$exit_code"
4551
}
4652

4753
# Error codes
@@ -50,19 +56,84 @@ CONVERSION_FAILURE=2
5056
VALIDATION_FAILED=3
5157

5258
# Ensure minimum arguments
53-
[ "$#" -lt 3 ] && usage
59+
if [ "$#" -lt 3 ]; then
60+
echo "Error: Missing required arguments."
61+
usage
62+
fi
5463

55-
MODE="$1"
56-
FILE_TO_SIGN="$3"
64+
# Parse options
65+
TERSE_MODE=false
66+
while [[ "$#" -gt 0 ]]; do
67+
case "$1" in
68+
--sign)
69+
MODE="sign"
70+
shift
71+
;;
72+
--verify)
73+
MODE="verify"
74+
shift
75+
;;
76+
--private-key)
77+
PRIVATE_KEY="$2"
78+
shift 2
79+
;;
80+
--file)
81+
FILE_TO_SIGN="$2"
82+
shift 2
83+
;;
84+
--namespace)
85+
NAMESPACE="$2"
86+
shift 2
87+
;;
88+
--allowed-signers-file)
89+
ALLOWED_SIGNERS_FILE="$2"
90+
shift 2
91+
;;
92+
--signature-file)
93+
SIG_FILE="$2"
94+
shift 2
95+
;;
96+
--terse)
97+
TERSE_MODE=true
98+
shift
99+
;;
100+
*)
101+
echo "Error: Invalid argument: $1"
102+
usage
103+
;;
104+
esac
105+
done
106+
107+
# Set default namespace if not provided
108+
if [ -z "$NAMESPACE" ]; then
109+
NAMESPACE="file"
110+
fi
111+
112+
# Ensure mode is set
113+
if [ -z "$MODE" ]; then
114+
echo "Error: Missing operation mode (either --sign or --verify)"
115+
usage
116+
fi
117+
118+
# Ensure required arguments
119+
if [ "$MODE" == "sign" ]; then
120+
[ -z "$PRIVATE_KEY" ] && { echo "Error: --private-key not specified."; usage $FILE_PROBLEM; }
121+
[ -z "$FILE_TO_SIGN" ] && { echo "Error: --file not specified."; usage $FILE_PROBLEM; }
122+
SIG_FILE="${FILE_TO_SIGN}.sig"
123+
elif [ "$MODE" == "verify" ]; then
124+
[ -z "$ALLOWED_SIGNERS_FILE" ] && { echo "Error: --allowed-signers-file not specified."; usage $FILE_PROBLEM; }
125+
[ -z "$FILE_TO_SIGN" ] && { echo "Error: --file not specified."; usage $FILE_PROBLEM; }
126+
SIG_FILE="${SIG_FILE:-${FILE_TO_SIGN}.sig}"
127+
fi
57128

58129
# Ensure the target file exists
59130
if [ ! -f "$FILE_TO_SIGN" ]; then
60131
echo "Error: File '$FILE_TO_SIGN' not found."
61132
exit $FILE_PROBLEM
62133
fi
63134

64-
# Use a very conservatuve umask throughout this script since we are dealing with sensitive things
65-
umask 077 || { echo "Error: Failed to set 0177 umask."; exit $FILE_PROBLEM; }
135+
# Use a very conservative umask throughout this script since we are dealing with sensitive things
136+
umask 0077 || { echo "Error: Failed to set 0077 umask."; exit $FILE_PROBLEM; }
66137

67138
# Create a restricted temporary directory and ensure cleanup on exit
68139
TEMP_DIR=$(mktemp -d) || { echo "Error: Failed to create temporary directory."; exit $FILE_PROBLEM; }
@@ -91,9 +162,7 @@ convert_private_key() {
91162

92163
# Sign mode
93164
if [ "$MODE" == "sign" ]; then
94-
PRIVATE_KEY="$2"
95165
TEMP_KEY="$TEMP_DIR/converted_key"
96-
SIG_FILE="${FILE_TO_SIGN}.sig"
97166

98167
# Check for key and existing signature
99168
[ ! -f "$PRIVATE_KEY" ] && { echo "Error: Private key not found."; exit $FILE_PROBLEM; }
@@ -102,55 +171,57 @@ if [ "$MODE" == "sign" ]; then
102171
convert_private_key "$PRIVATE_KEY" "$TEMP_KEY"
103172

104173
echo "Signing the file..."
105-
ssh-keygen -Y sign -f "$TEMP_KEY" -P "${KEY_PASSPHRASE:-}" -n file "$FILE_TO_SIGN"
106-
107-
[ ! -f "$SIG_FILE" ] && { echo "Error: Signing failed."; exit $FILE_PROBLEM; }
108-
echo "Signature created: $SIG_FILE"
174+
ssh-keygen -Y sign -f "$TEMP_KEY" -P "${KEY_PASSPHRASE:-}" -n "${NAMESPACE}" "$FILE_TO_SIGN"
109175

110176
cat <<EOF
111177
112178
For verification, your allowed signers file could contain:
113-
identity_1 $(cat "${TEMP_KEY}.pub")
179+
identity_1 namespaces="${NAMESPACE}",valid-before="LAST_VALID_DAY" $(cat "${TEMP_KEY}.pub")
114180
EOF
115181

182+
[ ! -f "$SIG_FILE" ] && { echo "Error: Signing failed."; exit $FILE_PROBLEM; }
183+
echo "Signature created: $SIG_FILE"
184+
116185
echo "Validating the signature..."
117-
ssh-keygen -Y check-novalidate -n file -f "${TEMP_KEY}.pub" -s "$SIG_FILE" < "$FILE_TO_SIGN" || {
186+
ssh-keygen -Y check-novalidate -n "${NAMESPACE}" -f "${TEMP_KEY}.pub" -s "$SIG_FILE" < "$FILE_TO_SIGN" || {
118187
echo "Error: Signature validation failed."
119188
exit $VALIDATION_FAILED
120189
}
121190

122191
# Verify mode
123192
elif [ "$MODE" == "verify" ]; then
124-
ALLOWED_SIGNERS_FILE="$2"
125-
SIG_FILE="${4:-${FILE_TO_SIGN}.sig}"
126-
127193
# Ensure required files exist
128194
for file in "$ALLOWED_SIGNERS_FILE" "$SIG_FILE"; do
129195
[ ! -f "$file" ] && { echo "Error: File '$file' not found."; exit $FILE_PROBLEM; }
130196
done
131197

132-
echo "Verifying the signature against allowed signers..."
133-
134198
# Iterate through each principal in the allowed signers file
135-
while IFS= read -r line || [[ -n "$line" ]]; do
136-
[[ -z "$line" || "$line" == \#* ]] && continue
137-
138-
# Extract and process each principal
139-
principals=$(echo "$line" | cut -d' ' -f1)
140-
IFS=',' read -ra principal_list <<< "$principals"
141-
142-
for principal in "${principal_list[@]}"; do
143-
echo "Checking principal: $principal"
144-
if ssh-keygen -Y verify -f "$ALLOWED_SIGNERS_FILE" -n file -I "$principal" -s "$SIG_FILE" < "$FILE_TO_SIGN"; then
145-
echo "Signature is valid for principal: $principal"
199+
while read -r principal options key
200+
do
201+
[[ -z "$principal" || "$principal" == \#* ]] && continue
202+
203+
namespaces=$(echo "$options" | grep -oP "namespaces=\"\K[^\"]+")
204+
205+
if [ "$TERSE_MODE" = true ]; then
206+
if ssh-keygen -Y verify -f "$ALLOWED_SIGNERS_FILE" -n "$namespaces" -I "$principal" -s "$SIG_FILE" < "$FILE_TO_SIGN" > /dev/null 2>&1; then
207+
# Output in JSON format
208+
echo "{\"identity\": \"$principal\", \"namespace\": \"$namespaces\"}"
146209
exit 0
147210
fi
148-
done
211+
else
212+
if ssh-keygen -Y verify -f "$ALLOWED_SIGNERS_FILE" -n "$namespaces" -I "$principal" -s "$SIG_FILE" < "$FILE_TO_SIGN"; then
213+
echo "Signature is valid for principal: $principal and namespace: $namespaces"
214+
exit 0
215+
else
216+
echo
217+
echo "Signature _not_ valid for principal: $principal and namespace: $namespaces"
218+
fi
219+
fi
149220
done < "$ALLOWED_SIGNERS_FILE"
150221

151222
echo "Error: No valid signature found."
152223
exit $VALIDATION_FAILED
153-
154224
else
225+
echo "Error: Invalid operation mode. Use --sign or --verify."
155226
usage
156227
fi

tasks/deploy.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@ def upload_artefact(job_dir, payload, timestamp, repo_name, pr_number, pr_commen
266266
metadata_prefix = deploycfg.get(config.DEPLOYCFG_SETTING_METADATA_PREFIX)
267267
artefact_prefix = deploycfg.get(config.DEPLOYCFG_SETTING_ARTEFACT_PREFIX)
268268
signing_str = deploycfg.get(config.DEPLOYCFG_SETTING_SIGNING) or ''
269+
github = cfg[config.SECTION_GITHUB]
270+
app_name = github.get(config.GITHUB_SETTING_APP_NAME)
269271
try:
270272
signing = json.loads(signing_str)
271273
except json.decoder.JSONDecodeError:
@@ -375,6 +377,7 @@ def upload_artefact(job_dir, payload, timestamp, repo_name, pr_number, pr_commen
375377
cmd_args.extend(['--pr-comment-id', str(pr_comment_id)])
376378
cmd_args.extend(['--pull-request-number', str(pr_number)])
377379
cmd_args.extend(['--repository', repo_name])
380+
cmd_args.extend(['--bot-instance', app_name])
378381
cmd_args.extend(sign_args)
379382
cmd_args.append(abs_path)
380383

0 commit comments

Comments
 (0)