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

2

21

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?

Spire answered 6/6, 2019 at 14:2 Comment(1)
If you create symlink manually, would the installation or update process override it?Radiobroadcast
C
1

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
}
Cutcheon answered 29/11, 2023 at 13:2 Comment(0)
N
0

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.

Napoli answered 10/1 at 15:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.