diff --git a/.github/workflows/verify-split-files.yml b/.github/workflows/verify-split-files.yml index 5909a40..80e5dba 100644 --- a/.github/workflows/verify-split-files.yml +++ b/.github/workflows/verify-split-files.yml @@ -72,6 +72,7 @@ on: - "tests/db-helper-context.sh" - "tests/forge-tag-spelling.sh" - "tests/forge-deb-services.sh" + - "tests/self-signed-cert.sh" - "tests/cs-clear.sh" - "tests/forge-appversion-scope.sh" - "tests/forge-singlefile-compressed.sh" @@ -143,6 +144,7 @@ on: - "tests/db-helper-context.sh" - "tests/forge-tag-spelling.sh" - "tests/forge-deb-services.sh" + - "tests/self-signed-cert.sh" - "tests/cs-clear.sh" - "tests/forge-appversion-scope.sh" - "tests/forge-singlefile-compressed.sh" @@ -305,6 +307,8 @@ jobs: run: bash tests/forge-tag-spelling.sh - name: Services after a .deb install tests run: bash tests/forge-deb-services.sh + - name: Self-signed certificate tests + run: bash tests/self-signed-cert.sh - name: Screen clear without a terminal tests run: bash tests/cs-clear.sh - name: App version pin and .deb stamp tests diff --git a/lib/system.func b/lib/system.func index fc56a65..de5ef0a 100644 --- a/lib/system.func +++ b/lib/system.func @@ -2167,7 +2167,9 @@ create_self_signed_cert() { local CERT_CRT="${CERT_DIR}/${APP_NAME_LC}.crt" if [[ -f "$CERT_CRT" && -f "$CERT_KEY" ]]; then - return 0 + if ! command -v openssl >/dev/null 2>&1 || openssl x509 -checkend 2592000 -noout -in "$CERT_CRT" >/dev/null 2>&1; then + return 0 + fi fi # Use ensure_dependencies for cleaner handling @@ -2180,7 +2182,8 @@ create_self_signed_cert() { [[ -n "$EXTRA_SAN" ]] && san="${san},${EXTRA_SAN}" mkdir -p "$CERT_DIR" - $STD openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ + # 825 days is the most macOS and iOS accept, even for a certificate the user trusts. + $STD openssl req -new -newkey rsa:2048 -days 825 -nodes -x509 \ -subj "/CN=${HOSTNAME}" \ -addext "subjectAltName=${san}" \ -keyout "$CERT_KEY" \ diff --git a/tests/self-signed-cert.sh b/tests/self-signed-cert.sh new file mode 100644 index 0000000..8614625 --- /dev/null +++ b/tests/self-signed-cert.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -uo pipefail + +CORE_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" +command -v openssl >/dev/null 2>&1 || { + echo "SKIP: no openssl" + exit 0 +} + +TEST_DIR="$(mktemp -d)" +trap 'rm -rf -- "$TEST_DIR"' EXIT +eval "$(awk '/^create_self_signed_cert\(\) \{/,/^}/' "$CORE_DIR/lib/system.func" | sed "s#/etc/ssl#${TEST_DIR}#g")" +passed=0 failed=0 + +check() { + if [[ "$3" == "$2" ]]; then + echo "PASS $1" + passed=$((passed + 1)) + else + echo "FAIL $1: expected <$2>, got <$3>" >&2 + failed=$((failed + 1)) + fi +} + +# shellcheck disable=SC2034 # read by the eval'd helper +STD="" +msg_error() { echo "error: $*" >&2; } +ensure_dependencies() { :; } +crt="$TEST_DIR/demo/demo.crt" +days_left() { local d=$((($(date -d "$(openssl x509 -enddate -noout -in "$crt" | cut -d= -f2)" +%s) - $(date +%s)) / 86400)); ((d >= 824 && d <= 825)) && echo 825 || echo "$d"; } +serial() { openssl x509 -serial -noout -in "$crt"; } + +create_self_signed_cert demo +check "a new certificate is valid for 825 days" "825" "$(days_left)" + +before="$(serial)" +create_self_signed_cert demo +check "a valid certificate is kept" "$before" "$(serial)" + +openssl req -new -newkey rsa:2048 -days 10 -nodes -x509 -subj "/CN=old" \ + -keyout "$TEST_DIR/demo/demo.key" -out "$crt" >/dev/null 2>&1 +create_self_signed_cert demo +check "an expiring certificate is renewed" "825" "$(days_left)" +check "and the key stays private" "600" "$(stat -c %a "$TEST_DIR/demo/demo.key")" + +echo "${passed} passed, ${failed} failed" +[[ "$failed" -eq 0 ]]