(2024.2) dod_crl.sh script
Create the dod-crl script, enter:
vi /usr/local/sbin/dod-crl.sh
Add the following contents:
#!/usr/bin/env bash # CRL Downloader script for manually downloading # CRL .zip bundle from CRL server to support # offline CRL validation. # # Last updated 04 FEB 2023 # # Declare Variables SCRIPT="$(readlink -f "$0")" LOCALREPO_DODLOCAL_CRL_URL="http://crl.dod.local/CertEnroll/ALLCRLZIP.zip" LOCALREPO_DODJITC_CRL_URL="http://crl.dod.local/CertEnroll/JITCALLCRLZIP.zip" DODJITC_CRL_URL="https://crl.nit.disa.mil/getcrlzip?ALL+CRL+ZIP" DODPROD_CRL_URL="https://crl.gds.disa.mil/getcrlzip?ALL+CRL+ZIP" ALLCRLURL="http://crl.dod.local/CertEnroll/ALLCRLZIP.zip" default_crldir="/etc/pki/tls/crl/" # Script Validation: Ensure this script is run as root if [[ "$(whoami)" != "root" ]]; then logger "$0 ERROR: script must be run as root." echo "$0 ERROR: script must be run as root." >&2 exit 77 fi validate_required_commands() { type -P c_rehash >/dev/null 2>&1 || { logger "$0 ERROR: Required dependency c_rehash is not installed."; echo "$0 ERROR: Required dependency c_rehash is not installed." >&2; exit 72; } type -P curl >/dev/null 2>&1 || { logger "$0 ERROR: Required dependency curl is not installed."; echo "$0 ERROR: Required dependency curl is not installed." >&2; exit 72; } type -P unzip >/dev/null 2>&1 || { logger "$0 ERROR: Required dependency unzip is not installed."; echo "$0 ERROR: Required dependency unzip is not installed." >&2; exit 72; } } # End validate_required_commands() override_dir_format_ck() { if [[ -n "${override_crldir}" ]];then if [[ "${override_crldir}" =~ ^(/)+([a-zA-Z0-9]*/)+$ ]];then effective_crldir="${override_crldir}" else logger "$0 ERROR: --override_crldir ${override_crldir} incorrect directory format." echo "$0 ERROR: --override_crldir ${override_crldir} incorrect directory format." >&2 exit 64 fi else effective_crldir="${default_crldir}" fi } # End override_dir_format_ck() dir_setup() { # Check input parameter for valid crldir or default if [[ ! -d "${effective_crldir}" ]];then mkdir -m 755 -p ${effective_crldir} || { logger "$0 ERROR: Could not create CRL directory at ${effective_crldir}."; echo "$0 ERROR: Could not create CRL directory at ${effective_crldir}." >&2; exit 74; } && { logger "$0 INFO: ${effective_crldir} did not exist and has been created."; echo "$0 INFO: ${effective_crldir} did not exist and has been created." >&2; } fi } # End dir_setup() staging_area() { # Set up a clean staging area to process CRL ZIP # Remove existing /tmp/.crl_staging so we start clean if [[ -d /tmp/.crl_staging ]];then rm -rf /tmp/.crl_staging || { logger "$0 ERROR: Could not remove existing staging area at /tmp/.crl_staging."; echo "$0 ERROR: Could not remove existing staging area at /tmp/.crl_staging." >&2; exit 74; } fi mkdir -m 755 -p /tmp/.crl_staging || { logger "$0 ERROR: Could not create staging area at /tmp/.crl_staging."; echo "$0 ERROR: Could not create staging area at /tmp/.crl_staging." >&2; exit 74; } } # End staging_area() fetch_crls_from_repo() { crlerrorqty=0 # Loop through each CRL source and perform fetch and extract process if source was requested for CRL_SOURCE in LOCALREPO_DODLOCAL_CRL LOCALREPO_DODJITC_CRL DODJITC_CRL DODPROD_CRL;do unset CRL_SOURCE_URL while [[ "${!CRL_SOURCE}" = true ]];do CRL_SOURCE_URL=${CRL_SOURCE}_URL curl "${!CRL_SOURCE_URL}" -o /tmp/.crl_staging/"${CRL_SOURCE}".zip || { logger "$0 ERROR: Could not download ${CRL_SOURCE} from ${!CRL_SOURCE_URL}."; echo "$0 ERROR: Could not download ${CRL_SOURCE} from ${!CRL_SOURCE_URL}." >&2; ((crlerrorqty++)) break } if [[ -f /tmp/.crl_staging/"${CRL_SOURCE}".zip ]];then unzip -o /tmp/.crl_staging/"${CRL_SOURCE}".zip -d /tmp/.crl_staging/ || { logger "$0 ERROR: Could not extract /tmp/.crl_staging/${CRL_SOURCE}.zip."; echo "$0 ERROR: Could not extract /tmp/.crl_staging/${CRL_SOURCE}.zip." >&2; ((crlerrorqty++)) break } else logger "$0 ERROR: Could not find /tmp/.crl_staging/${CRL_SOURCE}.zip for processing."; echo "$0 ERROR: Could not find /tmp/.crl_staging/${CRL_SOURCE}.zip for processing." >&2; ((crlerrorqty++)) break fi # Workaround for JITC CA naming issue if [[ "${CRL_SOURCE}" =~ ^(LOCALREPO_DODJITC_CRL|DODJITC_CRL)$ ]];then if [[ -f /tmp/.crl_staging/DODJITCSWCA_60.crl ]];then /bin/cp -rf /tmp/.crl_staging/DODJITCSWCA_60.crl /tmp/.crl_staging/DODJITCSWCA_60_SSL.crl || { logger "$0 ERROR: Could not copy /tmp/.crl_staging/DODJITCSWCA_60.crl to /tmp/.crl_staging/DODJITCSWCA_60_SSL.crl. CRL for this CA may not work."; echo "$0 ERROR: Could not copy /tmp/.crl_staging/DODJITCSWCA_60.crl to /tmp/.crl_staging/DODJITCSWCA_60_SSL.crl. CRL for this CA may not work." >&2; ((crlerrorqty++)) } fi fi break done done } # End fetch_crls_from_repo() convert_crl_to_pem_and_rehash() { converterror=0 crldirpermerror=0 # If there are .crl files in the staging directory, convert them from DER to PEM and place them in effective CRL directory # then run c_rehash on the effective CRL directory to create hashed symbolic links to the CRL files if [[ -n "$(find /tmp/.crl_staging/ -maxdepth 1 -name '*.crl' -print -quit)" ]];then for dercrl in $(find /tmp/.crl_staging/ -maxdepth 1 -name '*.crl' -printf "%f\n");do openssl crl -in "/tmp/.crl_staging/${dercrl}" -inform DER -outform PEM -out "${effective_crldir}"/"${dercrl}" || { logger "$0 ERROR: Could not convert /tmp/.crl_staging/${dercrl} to PEM."; echo "$0 ERROR: Could not convert /tmp/.crl_staging/${dercrl} to PEM." >&2; (converterror++) } done chown root:root "${effective_crldir}"/*.crl || { logger "$0 ERROR: Could not set ownership on ${effective_crldir}/*.crl."; echo "$0 ERROR: Could not set ownership on ${effective_crldir}/*.crl." >&2; ((crldirpermerror++)); } chmod 644 "${effective_crldir}"/*.crl || { logger "$0 ERROR: Could not set permissions on ${effective_crldir}/*.crl."; echo "$0 ERROR: Could not set permissions on ${effective_crldir}/*.crl." >&2; ((crldirpermerror++)); } c_rehash "${effective_crldir}" || { logger "$0 ERROR: Could not rehash CRLs in ${effective_crldir}. CRLs may be in an inconsistent state!"; echo "$0 ERROR: Could not rehash CRLs in ${effective_crldir}. CRLs may be in an inconsistent state!" >&2; staging_area_cleanup exit 74 } else logger "$0 ERROR: No .crl files to process under /tmp/.crl_staging/."; echo "$0 ERROR: No .crl files to process under /tmp/.crl_staging/." >&2; staging_area_cleanup exit 74 fi } # End convert_crl_to_pem_and_rehash() staging_area_cleanup() { # Clean up the staging area before exiting if [[ "${debug}" = true ]];then logger "$0 DEBUG: Skipping removal of existing staging area at /tmp/.crl_staging."; echo "$0 DEBUG: Skipping removal of existing staging area at /tmp/.crl_staging." >&2; else if [[ -d /tmp/.crl_staging ]];then rm -rf /tmp/.crl_staging || { logger "$0 ERROR: Could not remove existing staging area at /tmp/.crl_staging."; echo "$0 ERROR: Could not remove existing staging area at /tmp/.crl_staging." >&2; exit 74; } fi fi } # End staging_area_cleanup() crontab_update() { # Create or replace the crontab entry for this script # Remove previous entry for this script from crontab (crontab -l 2>/dev/null | sed -e "\,$SCRIPT,d")| crontab - # Append new entry for this script to crontab (crontab -l 2>/dev/null || true; echo "0 */4 * * * ${SCRIPT} ${SCRIPTARGS} >/dev/null 2>&1") | crontab - } exit_status() { # Evaluate success and abnormal but non-abort exit status counters and exit script if [[ "${crlerrorqty}" > 0 ]]|| [[ "${converterror}" > 0 ]]|| [[ "${crldirpermerror}" > 0 ]];then logger "$0 ERROR: Script completed, but failed to update all requested CRLs." echo "$0 ERROR: Script completed, but failed to update all requested CRLs." >&2 exit 75 else logger "$0 INFO: Script completed. All requested CRLs updated successfully." echo "$0 INFO: Script completed. All requested CRLs updated successfully." >&2 exit 0 fi } # End exit_status() print_usage() { echo "Usage for $0:" echo "Usage: $0 [OPTION] [REPOSITORY]" echo "Update local CRL cache from web repository, either manually or via crontab schedule." echo "Example 1, update CRLs to include internal and DoD JITC CRLs from local repository: $0 -uc -lri -lrdj" echo "Example 2, update cron schedule to include internal and DoD JITC CRLs from local repository: $0 -us -lri -lrdj" echo "" echo "Mode arguments, must specify only one mode." echo " -uc, --update-cache update local CRL cache from one or more repositories" echo " -us, --update-schedule update crontab schedule to run this script automatically for one or more repositories" echo "Repository arguments, must specify one or more repository arguments." echo " -lri, --local-repo-internal include the local repository for the internal CA CRLs" echo " -lrdj, --local-repo-dodjitc include the local repository for the DoD JITC CA CRLs" echo " -dj, --dodjitc include the DISA repository for the DoD JITC CA CRLs" echo " -dp, --dodprod include the DISA repository for the DoD production CA CRLs" echo "Optional arguments. May specify optional override path for CRLs. The default should be sufficient for most use cases." echo " -o, --output [/target/dir/] override the default /etc/pki/tls/crl/ path with an alternative path. must include trailing /" echo " -d, --debug retain /tmp/.crl_staging on exit to support troubleshooting" echo " -h, --help print this help menu" echo "" echo "Exit status:" echo " 0 if OK," echo " 64 if syntax error," echo " 72 if missing required dependencies," echo " 74 if error performing file/directory tasks," echo " 75 if error processing one or more requested CRL," echo " 77 if script not run as root." } # End print_usage() # Script execution options debug=false uc=false us=false LOCALREPO_DODLOCAL_CRL=false LOCALREPO_DODJITC_CRL=false DODJITC_CRL=false DODPROD_CRL=false SCRIPTARGS=$(echo $@ | sed -e 's/-us//g' -e 's/--update-schedule//g' -e 's/ $//g' -e 's/^ //g' -e 's/ -/ -/g' -e 's/^/-uc /g') while [[ $# -gt 0 ]]&& [[ ."$1" =~ .([-]){0,1}+([a-z]) ]]; do opt="$1"; shift; case "$opt" in "-"|"--" ) break 2 ;; "-uc"|"--update-cache" ) uc=true ;; "-us"|"--update-" ) us=true ;; "-lri"|"--local-repo-internal" ) LOCALREPO_DODLOCAL_CRL=true ;; "-lrdj"|"--local-repo-dodjitc" ) LOCALREPO_DODJITC_CRL=true ;; "-dj"|"--dodjitc" ) DODJITC_CRL=true ;; "-dp"|"--dodprod" ) DODPROD_CRL=true ;; "-o"|"--output" ) override_crldir="$1" shift ;; "-d"|"--debug" ) debug=true ;; "-h"|"--help" ) print_usage exit 0 ;; *) echo >&2 "Invalid option: $opt $1" print_usage exit 1 ;; esac done if [[ "${uc}" = false ]]&& [[ "${us}" = false ]];then logger "$0 ERROR: No modes specified, expected a single mode." echo "$0 ERROR: No modes specified, expected a single mode." >&2 print_usage exit 64 fi if [[ "${uc}" = true ]]&& [[ "${us}" = true ]];then logger "$0 ERROR: Multiple modes specified, expected a single mode." echo "$0 ERROR: Multiple modes specified, expected a single mode." >&2 print_usage exit 64 fi if [[ "${uc}" = true ]]&& [[ "${us}" = false ]];then mode="update-cache" fi if [[ "${us}" = true ]]&& [[ "${uc}" = false ]];then mode="update-schedule" fi if [[ "${LOCALREPO_DODLOCAL_CRL}" = false ]]&& [[ "${LOCALREPO_DODJITC_CRL}" = false ]]&& [[ "${DODJITC_CRL}" = false ]]&& [[ "${DODPROD_CRL}" = false ]];then logger "$0 ERROR: No repository specified, expected one or more repositories." echo "$0 ERROR: No repositories specified, expected one or more repositories." >&2 print_usage exit 64 fi validate_required_commands if [[ "${mode}" = "update-cache" ]];then override_dir_format_ck dir_setup staging_area fetch_crls_from_repo convert_crl_to_pem_and_rehash staging_area_cleanup exit_status fi if [[ "${mode}" = "update-schedule" ]];then override_dir_format_ck crontab_update fi
Save the file, enter: :wq!
Set the permissions on the dod-crl script, enter:
chmod 700 /usr/local/sbin/dod-crl.sh
Install the dod-crl script as a cron task, enter:
Note -dp is for DoD Production CRLs. The system must have internet access to https://crl.gds.disa.mil/getcrlzip?ALL+CRL+ZIP
Manually run the dod-crl script for the first time, enter: /usr/local/sbin/dod-crl.sh -uc -dp