lathspell
lathspell

Reputation: 3310

sdkman hook to link /etc/ssl/java/cacerts to the currently used Java SDK

Debian has a great mechanism to compile all commonly used CA certificates from Thawte, Let's Encrypt etc. as well as locally installed ones from /usr/local/share/ssl into one /etc/ssl/certs/java/cacerts JKS file. That is usually symlinked to $JAVA_HOME/lib/security/cacerts.

With sdkman I switch between different non-Debian Java versions in ~/.sdkman/candidates/java/current/ which use the cacerts provided by the SDK creator.

Is there some kind of post-inst hook mechanism where I could automatically create a symlink to the Debian cacerts file whenever I switch sdkman Java versions?

Upvotes: 21

Views: 6929

Answers (3)

Lucas
Lucas

Reputation: 14979

You could use an extension script. If you create a file in the ${SDKMAN_DIR}/ext/ folder (ie: ${SDKMAN_DIR}/ext/sdk-install-then-symlink-cacerts.sh) with the following code:

#!/bin/bash

source <(sed 's/__sdk_install/__sdk_builtin_install/g' "${SDKMAN_DIR}/src/sdkman-install.sh")

function __sdk_install() {
  __sdk_builtin_install "$@"
  local exit_code=$?

  if ((exit_code==0)); then
    local java_home
    java_home="$(__sdk_home "${1}" "${2}")"
    rm "${java_home}/lib/security/cacerts"
    ln -s "/etc/ssl/certs/java/cacerts" "${java_home}/lib/security/cacerts"
  fi

  return "${exit_code}"
}

It would replace the pre-packaged cacerts with a symlink to the /etc/ssl/certs/java/cacerts for each new install.

Upvotes: 0

Danyel
Danyel

Reputation: 2250

SDKMAN has predefined post installation hooks but there doesn't seem any way to customize them or add your own. I didn't want to create a new wrapper tool like @PeterMue because it'd cause cognitive load and also the footgun of users forgetting to run that, so my solution was to hook into the shell's prompt mechanism. I installed starship (https://starship.rs) and put the eval "$(starship init <shell>)" below SDKMan, and in ~/.config/starship.toml I defined a section for a custom command which will be run during prompt generation:

[custom.java-cacerts]
when = true
shell = "sh"
command = """
set -xe
[ -f /etc/ssl/certs/java/cacerts ]
ALL_CACERTS=$(find -P "$SDKMAN_DIR/candidates/java" -name cacerts -type f | grep .)
echo "[java: symlinking cacerts]"
for cacerts in $ALL_CACERTS; do
  mv "$cacerts" "$cacerts.original"
  ln -s /etc/ssl/certs/java/cacerts "$cacerts"
done 2>&1
"""

Explanation:

  • set -xe sets the e flag (exit when any command errors) and the x flag (log commands to stderr)
  • Since e flag is set, we can skip the if clause and write the condition at the top. If it fails, the script will short circuit
  • find -P doesn't follow symlinks, so the current symlink is ignored. -type f to only find cacerts which are regular files (no symlinks)
  • find always returns success even when no match was found. Piping that into grep . matches anything and causes a non-zero status when there is no input so the script will stop
  • set -x writes everything to stderr, but starship only captures stdout and ignores stderr, so I use 2>&1 on the for loop to print those 2 commands to inform the user.

This runs very fast and on my machine the prompt delay is almost not noticeable. Also using find rather than a predefined path on $JDK/lib/security/cacerts is more backward compatible as for Java 8 the cacerts is under $JDK/jre/lib/security/cacerts.

Upvotes: 0

PeterMue
PeterMue

Reputation: 13

I solves this with a custom zsh function so I just have to run sdkman cacerts ~/.cacerts to symlink all cacerts for all sdkman installed jdks.

# sdkman
sdkman() {
    case $1 in
        --help)
            cat <<EOF
Alias for sdk with additional custom functions.

Usage: $0 <subcommand> [candidate] [version]

Additional Commands

    $0 --help               Print this help message
    $0 cacerts [cacerts]    Symlink all java cacerts to a custom location (default: ~/.cacerts)

EOF
            ;;
        cacerts)
            cacerts="${2:-$HOME/.cacerts}"
            if [[ -f "$cacerts" ]]; then
                for jdk in $(sdk list java | grep installed | sed 's/^.*\| \(.*\)$/\1/g'); do
                    jdkHome=$(sdk home java $jdk)
                    pushd "$jdkHome/lib/security"
                    mv cacerts cacerts.orginal
                    ln -s "$cacerts" "cacerts"
                    popd
                done
            else
                echo "Abort: file $cacerts not found."
            fi
            ;;
        *)
            sdk "$@"
    esac
}

Upvotes: 1

Related Questions