Skip to content

Instantly share code, notes, and snippets.

@catchdave
Last active November 5, 2024 01:02
Show Gist options
  • Save catchdave/ff9c7d7a396a3201cfb14f912d3e5cda to your computer and use it in GitHub Desktop.
Save catchdave/ff9c7d7a396a3201cfb14f912d3e5cda to your computer and use it in GitHub Desktop.
Find all .PEM certificates on Synology
#!/bin/bash
#
# Finds all .pem certs on synology under /usr.
#
# Usage: ./find_all_certs.sh [--valid-only | --invalid-only]
# --valid-only - Only show valid certificates found
# --invalid-only - Only show invalid certificates found
# --no-color - Don't display ANSII Color output
if [ "$EUID" -ne 0 ]; then
echo "This script must be run as root or with sudo."
exit 1
fi
# Color codes
GREEN="\033[1;32m"
RED="\033[0;31m"
YELLOW="\033[0;33m"
BLUE="\033[0;95m"
NC="\033[0m" # No Color
BOLD="\033[1m"
HEAD_COL="\033[0;97m\033[0;100m"
# Initialize variables
NOW_TS=$(date +%s)
BASE_DIR=/usr/
WARNINGS=()
PREV_DIR=""
valid_count=0
count=0
total_count=0
total_valid=0
total_invalid=0
total_dirs_no_valid=0
total_dirs=0
valid_status=
mode=all
color=1
main() {
parse_args "$@"
print_header
while read -r cert; do
cert_dir=$(dirname "$cert")
cert_file=$(basename "$cert")
if parse_cert_info "$cert"; then # Sets vars: $from, $to, $subject, $issuer, $validity_color & $valid_status
process_dir_change "$cert_dir" "$cert_file"
((count++))
((total_count++))
check_filter "$valid_status" || continue
print_cert_line "$validity_color" "$cert_file" "$from" "$to" "$subject" "$issuer" "$valid_status"
fi
done < <(find "$BASE_DIR" -type f -name "*.pem" -not -path "/volume*" 2>/dev/null)
printf "$(get_line_format "\u2517" "\u2537" "\u251B")" | sed 's/ /━/g'
print_summary
}
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--no-color)
color=0
shift
;;
--valid-only)
mode=valid_only
shift
;;
--invalid-only)
mode=invalid_only
shift
;;
*)
echo "Usage: $0 [--valid-only | --invalid-only] [--no-color]"
exit 1
;;
esac
done
if [[ "$color" == "0" ]]; then
GREEN=
RED=
YELLOW=
BLUE=
NC=
BOLD=
HEAD_COL=
fi
}
get_line_format() {
local start=$1
local mid=$2
local end=$3
local color_line="${4:-}"
local color_status="${5:-}"
local line_format="__START__LINE_COLOR %-44s __MID__ STATUS_COLOR%-28s${NC}LINE_COLOR __MID__ \
STATUS_COLOR%-28s${NC}LINE_COLOR __MID__ %-20s __MID__ %-20s __MID__ STATUS_COLOR%-7s ${NC}__END__\n"
line_format="${line_format//__START__/${start}}"
line_format="${line_format//__MID__/${mid}}"
line_format="${line_format//__END__/${end}}"
line_format="${line_format//STATUS_COLOR/${color_status}}"
line_format="${line_format//LINE_COLOR/${color_line}}"
echo "$line_format"
}
# Skip files that are not valid certificates
check_valid_cert() {
local cert=$1
if [[ "$(head -n 1 "$cert")" != "-----BEGIN CERTIFICATE-----" ]]; then
return 1
fi
return 0
}
# Execute filtering based on mode
check_filter() {
local valid_status=$1
if [[ "$mode" == "valid_only" && "$valid_status" != "active" ]]; then
return 1
elif [[ "$mode" == "invalid_only" && "$valid_status" == "active" ]]; then
return 1
fi
return 0
}
print_cert_line() {
printf "$(get_line_format "\u2503" "\u2502" "\u2503" "" "$1")" "$2" "$3" "$4" "$5" "$6" "$7"
}
print_dir_warn() {
if [[ "${count:-0}" -ne 0 && "${valid_count:-0}" -eq 0 ]]; then
WARNINGS+=("$(echo -e "${RED}${BOLD}[WARN] No Valid Certs in: ${NC}${RED}$1/${NC}")")
((total_dirs_no_valid++))
fi
}
print_header() {
printf "$(get_line_format "\u250F" "\u252F" "\u2513")" | sed 's/ /\xE2\x94\x81/g'
printf "$(get_line_format "\u2503" "\u2502" "\u2503" "${HEAD_COL}")" "Filename" "Valid From" "Valid To" "Domain" "Issuer" "Status"
}
process_dir_change() {
local cert_dir="$1"
local cert_file="$2"
if [[ "$cert_dir" == "$PREV_DIR" ]]; then
return
fi
print_dir_warn "$PREV_DIR"
((total_dirs++))
valid_count=0
count=0
PREV_DIR="$cert_dir"
printf "$(get_line_format "\u2520" "\u2534" "\u2528")" | sed 's/ /─/g'
printf "\u2503 ${BLUE}%-162s${NC} \u2503\n" "$cert_dir"
printf "$(get_line_format "\u2520" "\u252C" "\u2528")" | sed 's/ /─/g'
}
print_summary() {
local line_format="${HEAD_COL}\u2503 ${BOLD}%-28s${NC}${HEAD_COL} \u2502 %-4s \u2503${NC}\n"
echo ""
printf "%s\n" "${WARNINGS[@]}"
echo ""
echo -e "${HEAD_COL}${BOLD}===== Summary =====${NC}"
printf "${HEAD_COL}\u250F%37s\u2513${NC}\n" | sed 's/ /\xE2\x94\x81/g'
printf "$line_format" "Total Directories" "$total_dirs"
printf "$line_format" "Total Certificates" "$total_count"
printf "$line_format" "Total Dirs w/ no valid cert" "$total_dirs_no_valid"
printf "$line_format" "Total Valid Certs" "$total_valid"
printf "$line_format" "Total InValid Certs" "$total_invalid"
printf "${HEAD_COL}\u2517%37s\u251B${NC}\n" | sed 's/ /\xE2\x94\x81/g'
}
# Parses info from a SSL cert, extracting domain, issuer and dates. Provides time validity and appropriate color for validity status.
parse_cert_info() {
local cert="$1"
local cert_info
# Parse details from cert_info
cert_info=$(openssl x509 -in "$cert" -noout -subject -issuer -startdate -enddate 2>/dev/null)
if [[ $? -ne 0 ]]; then
return 1
fi
subject=$(echo "$cert_info" | sed -n '/^subject=/s/.*[Cc][Nn][ ]*=[ ]*\([^,]*\).*/\1/p')
issuer=$(echo "$cert_info" | sed -n '/^issuer=/s/.*[Oo][ ]*=[ ]*\([^,]*\).*/\1/p')
from=$(echo "$cert_info" | sed -n 's/^notBefore=//p')
to=$(echo "$cert_info" | sed -n 's/^notAfter=//p')
# Convert dates to Unix timestamps for comparison
local from_ts=$(date -d "$from" +%s)
local to_ts=$(date -d "$to" +%s)
# Determine validity of certs
valid_status="invalid"
if (( from_ts > NOW_TS )); then
validity_color=$YELLOW # Not reached
valid_status="future"
((total_invalid++))
elif (( to_ts < NOW_TS )); then
validity_color=$RED # Expired
valid_status="expired"
((total_invalid++))
else
validity_color=$GREEN # Current
valid_status="active"
((valid_count++))
((total_valid++))
fi
return 0
}
### Run main program ###
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment