|
#!/bin/sh |
|
######################################################################################################################## |
|
# Check if a device has the correct ca bundle to support TLS communication |
|
# |
|
# The script checks if some urls are correctly verified, and will actively |
|
# download a new ca bundle if the urls don't pass the verification. After the |
|
# new ca bundle is downloaded and the urls are verified again, then the ca |
|
# bundle will be copied to the specified destination (default being /etc/ssl/certs/ca-certificates.crt) |
|
# If the file already exists, the new ca bundle will be appended to the existing file (without checking for duplicates) |
|
# |
|
######################################################################################################################## |
|
set -e |
|
|
|
sh_c='sh -c' |
|
|
|
usage() { |
|
cat <<EOF |
|
USAGE: |
|
$0 [<ca-bundle-file>] |
|
|
|
ARGUMENTS: |
|
<ca-bundle-file> CA bundle file where to write the file to if a new bundle is required |
|
Default: /etc/ssl/certs/ca-certificates.crt |
|
|
|
OPTIONS: |
|
--online-ca-bundle <url> Online ca bundle url to download from if the current bundle is |
|
not sufficient. Default: https://curl.se/ca/cacert.pem |
|
--dry-run Don't install anything, just let me know what it does |
|
--help Show this help |
|
|
|
Other options for online ca bundle sources are: |
|
* https://raw.githubusercontent.com/certifi/python-certifi/master/certifi/cacert.pem |
|
|
|
EOF |
|
} |
|
|
|
command_exists() { |
|
command -v "$@" > /dev/null 2>&1 |
|
} |
|
|
|
configure_shell() { |
|
# Check if has sudo rights or if it can be requested |
|
user="$(id -un 2>/dev/null || true)" |
|
sh_c='sh -c' |
|
if [ "$user" != 'root' ]; then |
|
if command_exists sudo; then |
|
sh_c='sudo -E sh -c' |
|
elif command_exists su; then |
|
sh_c='su -c' |
|
else |
|
cat >&2 <<-EOF |
|
Error: this installer needs the ability to run commands as root. |
|
We are unable to find either "sudo" or "su" available to make this happen. |
|
EOF |
|
exit 1 |
|
fi |
|
fi |
|
|
|
if is_dry_run; then |
|
sh_c="echo" |
|
fi |
|
} |
|
|
|
is_dry_run() { |
|
if [ -z "$DRY_RUN" ]; then |
|
return 1 |
|
else |
|
return 0 |
|
fi |
|
} |
|
|
|
|
|
if command_exists tedge; then |
|
C8Y_URL=$(tedge config get c8y.url 2>/dev/null) |
|
if [ -n "$C8Y_URL" ]; then |
|
CHECK_URLS="$CHECK_URLS https://$C8Y_URL" |
|
fi |
|
fi |
|
|
|
check_dependencies() { |
|
if command_exists curl; then |
|
return 0 |
|
elif command_exists wget && (wget --help 2>&1 | grep -q 'ca-certificate'); then |
|
return 0 |
|
else |
|
echo "Missing curl or wget. curl or wget is required for this script to work. Note wget must also support the --ca-certificate option!" >&2 |
|
exit 1 |
|
fi |
|
} |
|
|
|
test_url() { |
|
if command_exists curl; then |
|
for i_url in $CHECK_URLS; do |
|
if [ -n "$1" ]; then |
|
printf "Checking url=%s using ca bundle: %s ... " "$i_url" "$1" >&2 |
|
if ! SSL_CERT_FILE="$1" curl -sSL "$i_url" >/dev/null 2>&1; then |
|
echo "FAIL" >&2 |
|
return 1 |
|
fi |
|
echo "PASS" >&2 |
|
else |
|
printf "Checking url=%s with default ca bundle ... " "$i_url" >&2 |
|
if ! curl -sSL "$i_url" >/dev/null 2>&1; then |
|
echo "FAIL" >&2 |
|
return 1 |
|
fi |
|
echo "PASS" >&2 |
|
fi |
|
|
|
done |
|
return 0 |
|
elif command_exists wget; then |
|
for i_url in $CHECK_URLS; do |
|
if [ -n "$1" ]; then |
|
printf "Checking url=%s using ca bundle: %s ... " "$i_url" "$1" >&2 |
|
if ! wget --ca-certificate="$1" "$i_url" >/dev/null 2>&1; then |
|
echo "FAIL" >&2 |
|
return 1 |
|
fi |
|
echo "PASS" >&2 |
|
else |
|
printf "Checking url=%s with default ca bundle ... " "$i_url" >&2 |
|
if ! wget "$i_url" >/dev/null 2>&1; then |
|
echo "FAIL" >&2 |
|
return 1 |
|
fi |
|
echo "PASS" >&2 |
|
fi |
|
|
|
done |
|
return 0 |
|
else |
|
echo "Unable to check urls as curl or wget is not installed" >&2 |
|
exit 1 |
|
fi |
|
} |
|
|
|
scan_ca_bundle() { |
|
# Look for the first valid certificate |
|
VALID_CA_BUNDLE= |
|
for CA_PATH in $CA_PATHS; do |
|
if [ -z "$VALID_CA_BUNDLE" ]; then |
|
if [ -f "$CA_PATH" ]; then |
|
if test_url "$CA_PATH"; then |
|
VALID_CA_BUNDLE="$CA_PATH" |
|
fi |
|
fi |
|
fi |
|
done |
|
echo "$VALID_CA_BUNDLE" |
|
} |
|
|
|
download_bundle() { |
|
TMP_CA="${1:-/tmp/ca.bundle.crt}" |
|
rm -f "$TMP_CA" |
|
|
|
if command_exists curl; then |
|
# Download the ca bundle from a trusted source |
|
echo "No valid ca bundle found, downloading one from $ONLINE_CA_BUNDLE" >&2 |
|
curl --insecure -sSL "$ONLINE_CA_BUNDLE" > "$TMP_CA" |
|
elif command_exists wget; then |
|
echo "No valid ca bundle found, downloading one from $ONLINE_CA_BUNDLE" >&2 |
|
wget --no-check-certificate -O - "$ONLINE_CA_BUNDLE" > "$TMP_CA" 2>/dev/null |
|
else |
|
echo "curl or wget is required to be able to download the ca bundle" >&2 |
|
exit 1 |
|
fi |
|
|
|
if [ ! -f "$TMP_CA" ]; then |
|
echo "Temp ca file has not been created. It probably failed to download. file=$TMP_CA" >&2 |
|
exit 1 |
|
fi |
|
|
|
echo "$TMP_CA" |
|
} |
|
|
|
set_tedge_config() { |
|
bundle="$1" |
|
if command_exists tedge; then |
|
$sh_c "tedge config set c8y.root_cert_path '$bundle'" |
|
else |
|
echo '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' |
|
echo "" |
|
echo "Warning: tedge is not yet installed so the script can not update the tedge config" |
|
echo " to use the correct ca bundle." |
|
echo "" |
|
echo "After tedge is installed, run:" |
|
echo "" |
|
echo " sudo tedge config set c8y.root_cert_path '$bundle'" |
|
echo "" |
|
echo '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' |
|
fi |
|
} |
|
|
|
main() { |
|
DEST_CA_BUNDLE="$1" |
|
check_dependencies |
|
configure_shell |
|
|
|
if test_url ""; then |
|
echo "Native ca bundle looks ok (without doing anything)" >&2 |
|
|
|
SYMLINK_COUNT=$(find /etc/ssl/certs/ -type l | wc -l) |
|
if [ "$SYMLINK_COUNT" -lt 5 ] && [ -f /etc/ssl/certs/ca-certificates.crt ]; then |
|
set_tedge_config "/etc/ssl/certs/ca-certificates.crt" |
|
fi |
|
return |
|
fi |
|
|
|
CA_BUNDLE=$(scan_ca_bundle) |
|
if [ -z "$CA_BUNDLE" ]; then |
|
CA_BUNDLE=$(download_bundle "/tmp/ca.bundle.crt") |
|
fi |
|
|
|
if [ -z "$CA_BUNDLE" ]; then |
|
echo "Warning: Still no valid ca bundle found: $CA_BUNDLE" >&2 |
|
return 1 |
|
fi |
|
|
|
if ! test_url "$CA_BUNDLE"; then |
|
echo "Warning: Still no valid ca bundle found: $CA_BUNDLE" >&2 |
|
return 1 |
|
fi |
|
|
|
if [ -n "$DEST_CA_BUNDLE" ] && [ "$DEST_CA_BUNDLE" != "$CA_BUNDLE" ]; then |
|
if [ -f "$DEST_CA_BUNDLE" ]; then |
|
$sh_c "cp $DEST_CA_BUNDLE $DEST_CA_BUNDLE.bak" |
|
$sh_c "cat '$CA_BUNDLE' >> '$DEST_CA_BUNDLE'" |
|
else |
|
$sh_c "cat '$CA_BUNDLE' >> '$DEST_CA_BUNDLE'" |
|
$sh_c "chown root:root $DEST_CA_BUNDLE" |
|
$sh_c "chmod 644 $DEST_CA_BUNDLE" |
|
fi |
|
CA_BUNDLE="$DEST_CA_BUNDLE" |
|
fi |
|
|
|
echo "Found valid ca bundle: $CA_BUNDLE" >&2 |
|
set_tedge_config "$CA_BUNDLE" |
|
export SSL_CERT_FILE="$CA_BUNDLE" |
|
} |
|
|
|
# |
|
# Parsing |
|
# |
|
CA_PATHS="/etc/ssl/certs/ca-certificates.crt /var/etc/ssl/certs/ca-certificates.crt" |
|
CHECK_URLS="https://cloudsmith.io https://thin-edge.io" |
|
ONLINE_CA_BUNDLE="${ONLINE_CA_BUNDLE:-https://curl.se/ca/cacert.pem}" |
|
DEST_CA_BUNDLE="${DEST_CA_BUNDLE:-/etc/ssl/certs/ca-certificates.crt}" |
|
DRY_RUN= |
|
|
|
while [ $# -gt 0 ]; do |
|
case $1 in |
|
--dry-run) |
|
DRY_RUN=1 |
|
;; |
|
--online-ca-bundle) |
|
ONLINE_CA_BUNDLE="$2" |
|
shift |
|
;; |
|
--help|-h) |
|
usage |
|
exit 0 |
|
;; |
|
--*|-*) |
|
log "Unknown option: $1" |
|
usage |
|
exit 1 |
|
;; |
|
*) |
|
DEST_CA_BUNDLE="$1" |
|
;; |
|
esac |
|
# Use if statement as some devices |
|
# don't support math $((arith)) statements |
|
if [ $# -gt 0 ]; then |
|
shift 1 |
|
fi |
|
done |
|
|
|
main "$DEST_CA_BUNDLE" |