Hello,

First of all, I wouldn't consider myself a Docker expert so it's very possible 
I'm doing something wrong.  That said, I'm trying to set up a docker container 
that will use system CA certs to enable LDAPS auth.  Since I wasn't able to 
find much info on this, I created my own hacky way to do it by creating a 
shared volume for /opt/java/openjdk/lib/security and then creating a service 
that injects my PKI root certs.  It works, but I can't say I'm completely happy 
with it.  After doing this, I noticed a file is created on the root of the 
drive called __cacert_entrypoint.sh.  This file says there should be an env 
variable called USE_SYSTEM_CA_CERTS, which when set to true should inject any 
.crt cert mounted in /certificates.  After finding this script, I found this 
post https://lists.apache.org/thread/4g8o2kb01srxv43nsk0f31csq556x4hq but he 
has very similar questions to mine .  In the latest tag, as of right now, 
(https://hub.docker.com/layers/guacamole/guacamole/latest/images/sha256-5cc63aea0b427fb5fa12ff79f8c1cfbbbbfeee6a823131fa3aeafa49d8ba4188)
 it appears the script is called on line 15, the thing is it doesn't seem to do 
anything.  In the docker logs I don't see anything logged from the script.  I 
can't seem to find anything about what this __cacert_entrypoint.sh script is, 
or even the environment variable USE_SYSTEM_CA_CERTS in any of the docs.  If I 
attach to the container and run the script manually, it appears to complete 
successfully, but the cert is not actually inserted for Guacamole/JVM to use.  
In the logs I still see the error "javax.net.ssl.SSLHandshakeException: PKIX 
path building failed: 
sun.security.provider.certpath.SunCertPathBuilderException: unable to find 
valid certification path to requested target".  My best guess is that because 
I'm not running the container as root it's not doing completing correctly, even 
though the comments at the end of the script suggest that running as a non-root 
user should not matter (except for anything other than JVM, which I don't care 
about at the moment).  Notice in my hacky solution that I'm adding the certs 
with the root user.  Here is the output from the script:

$ ./__cacert_entrypoint.sh
Using a temporary truststore at /tmp/tmp.VDvaEBw2fR
Picked up JAVA_TOOL_OPTIONS:  -Djavax.net.ssl.trustStore=/tmp/tmp.VDvaEBw2fR 
-Djavax.net.ssl.trustStorePassword=changeit
Importing keystore /tmp/tmp.W62oZqncN9 to /tmp/tmp.VDvaEBw2fR...
Entry for alias naverglobalrootcertificationauthority successfully imported.
...a whole bunch of standard CAs...
Entry for alias hellenicacademicandresearchinstitutionsrootca2015 successfully 
imported.
Import command completed:  146 entries successfully imported, 0 entries failed 
or cancelled
Adding certificate with alias PKIIssuingCA to the JVM truststore
Picked up JAVA_TOOL_OPTIONS:  -Djavax.net.ssl.trustStore=/tmp/tmp.VDvaEBw2fR 
-Djavax.net.ssl.trustStorePassword=changeit
Certificate was added to keystore
Adding certificate with alias PKIOfflineCA to the JVM truststore
Picked up JAVA_TOOL_OPTIONS:  -Djavax.net.ssl.trustStore=/tmp/tmp.VDvaEBw2fR 
-Djavax.net.ssl.trustStorePassword=changeit
Certificate was added to keystore



Contents of __cacert_entrypoint.sh:
#!/usr/bin/env bash
# ------------------------------------------------------------------------------
#             NOTE: THIS FILE IS GENERATED VIA "generate_dockerfiles.py"
#
#                       PLEASE DO NOT EDIT IT DIRECTLY.
# ------------------------------------------------------------------------------
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This script defines `sh` as the interpreter, which is available in all POSIX 
environments. However, it might get
# started with `bash` as the shell to support dotted.environment.variable.names 
which are not supported by POSIX, but
# are supported by `sh` in some Linux flavours.

set -e

TMPDIR=${TMPDIR:-/tmp}

# JDK truststore location
JRE_CACERTS_PATH=$JAVA_HOME/lib/security/cacerts

# Opt-in is only activated if the environment variable is set
if [ -n "$USE_SYSTEM_CA_CERTS" ]; then

    if [ ! -w "$TMPDIR" ]; then
        echo "Using additional CA certificates requires write permissions to 
$TMPDIR. Cannot create truststore."
        exit 1
    fi

    # Figure out whether we can write to the JVM truststore. If we can, we'll 
add the certificates there. If not,
    # we'll use a temporary truststore.
    if [ ! -w "$JRE_CACERTS_PATH" ]; then
        # We cannot write to the JVM truststore, so we create a temporary one
        JRE_CACERTS_PATH_NEW=$(mktemp)
        echo "Using a temporary truststore at $JRE_CACERTS_PATH_NEW"
        cp "$JRE_CACERTS_PATH" "$JRE_CACERTS_PATH_NEW"
        JRE_CACERTS_PATH=$JRE_CACERTS_PATH_NEW
        # If we use a custom truststore, we need to make sure that the JVM uses 
it
        export JAVA_TOOL_OPTIONS="${JAVA_TOOL_OPTIONS} 
-Djavax.net.ssl.trustStore=${JRE_CACERTS_PATH} 
-Djavax.net.ssl.trustStorePassword=changeit"
    fi

    tmp_store=$(mktemp)

    # Copy full system CA store to a temporary location
    trust extract --overwrite --format=java-cacerts --filter=ca-anchors 
--purpose=server-auth "$tmp_store" > /dev/null

    # Add the system CA certificates to the JVM truststore.
    keytool -importkeystore -destkeystore "$JRE_CACERTS_PATH" -srckeystore 
"$tmp_store" -srcstorepass changeit -deststorepass changeit -noprompt > 
/dev/null

    # Clean up the temporary truststore
    rm -f "$tmp_store"

    # Import the additional certificate into JVM truststore
    for i in /certificates/*crt; do
        if [ ! -f "$i" ]; then
            continue
        fi
        tmp_dir=$(mktemp -d)
        BASENAME=$(basename "$i" .crt)

        # We might have multiple certificates in the file. Split this file into 
single files. The reason is that
        # `keytool` does not accept multi-certificate files
        csplit -s -z -b %02d.crt -f "$tmp_dir/$BASENAME-" "$i" '/-----BEGIN 
CERTIFICATE-----/' '{*}'

        for crt in "$tmp_dir/$BASENAME"-*; do
            # Extract the Common Name (CN) and Serial Number from the 
certificate
            CN=$(openssl x509 -in "$crt" -noout -subject -nameopt -space_eq | 
sed -n 's/^.*CN=\([^,]*\).*$/\1/p')
            SERIAL=$(openssl x509 -in "$crt" -noout -serial | sed -n 
's/^serial=\(.*\)$/\1/p')

            # Check if an alias with the CN already exists in the keystore
            ALIAS=$CN
            if keytool -list -keystore "$JRE_CACERTS_PATH" -storepass changeit 
-alias "$ALIAS" >/dev/null 2>&1; then
                # If the CN already exists, append the serial number to the 
alias
                ALIAS="${CN}_${SERIAL}"
            fi

            echo "Adding certificate with alias $ALIAS to the JVM truststore"

            # Add the certificate to the JVM truststore
            keytool -import -noprompt -alias "$ALIAS" -file "$crt" -keystore 
"$JRE_CACERTS_PATH" -storepass changeit >/dev/null
        done
    done

    # Add additional certificates to the system CA store. This requires write 
permissions to several system
    # locations, which is not possible in a container with read-only filesystem 
and/or non-root container.
    if [ "$(id -u)" -eq 0 ]; then

        # Copy certificates from /certificates to the system truststore, but 
only if the directory exists and is not empty.
        # The reason why this is not part of the opt-in is because it leaves 
open the option to mount certificates at the
        # system location, for whatever reason.
        if [ -d /certificates ] && [ "$(ls -A /certificates 2>/dev/null)" ]; 
then
            cp -La /certificates/* /usr/local/share/ca-certificates/
        fi
        update-ca-certificates
    else
        # If we are not root, we cannot update the system truststore. That's 
bad news for tools like `curl` and `wget`,
        # but since the JVM is the primary focus here, we can live with that.
        true
    fi
fi

# Let's provide a variable with the correct path for tools that want or need to 
use it
export JRE_CACERTS_PATH

exec "$@"




Here's my docker-compose.yml (modified from 
https://github.com/boschkundendienst/guacamole-docker-compose) with the cert 
hack I mentioned above commented out.
#https://github.com/boschkundendienst/guacamole-docker-compose/blob/master/docker-compose.yml

# networks
# create a network 'guac' in mode 'bridged'
networks:
  guac:
    driver: bridge

# services
services:
  # guacd
  guacd:
    image: guacamole/guacd
    networks:
      - guac
    restart: always
    volumes:
      - ./drive:/drive:Z
      - ./record:/record:Z
  # postgres
  postgres:
    environment:
      PGDATA: /var/lib/postgresql/data/guacamole
      POSTGRES_DB: guacamole_db
      POSTGRES_PASSWORD: 'pass'
      POSTGRES_USER: guacamole_user
    image: postgres:15.2-alpine
    networks:
      - guac
    restart: always
    volumes:
      - ./init:/docker-entrypoint-initdb.d:Z
      - ./data:/var/lib/postgresql/data:Z
      - ./backups/postgres:/backups/postgres:Z

  # guacamole
  guacamole:
    group_add:
      - "1000"
    depends_on:
      - guacd
      - postgres
    environment:
      GUACD_HOSTNAME: guacd
      POSTGRESQL_DATABASE: guacamole_db
      POSTGRESQL_HOSTNAME: postgres
      POSTGRESQL_PASSWORD: 'pass'
      POSTGRESQL_USERNAME: guacamole_user
      RECORDING_SEARCH_PATH: /record
      LDAP_HOSTNAME: "guac"
      LDAP_PORT: 636
      LDAP_ENCRYPTION_METHOD: ssl
      LDAP_USER_BASE_DN: "OU=some-ou,DC=some-domain"
      LDAP_USER_SEARCH_FILTER: 
"(&(objectClass=user)(memberOf:=CN=some-group,OU=some-ou,DC=some-domain))"
      LDAP_USERNAME_ATTRIBUTE: sAMAccountName
      LDAP_SEARCH_BIND_DN: "CN=some-account,OU=some-ou,DC=some-domain"
      LDAP_SEARCH_BIND_PASSWORD: "pass"
      USE_SYSTEM_CA_CERTS: true
    image: guacamole/guacamole
    networks:
      - guac
    volumes:
      - ./record:/record:Z
      - ./backups/guacamole:/backups/guacamole:Z
      #- ca_certs:/opt/java/openjdk/lib/security:z
      - ./FullChain.pem:/certificates/FullChain.crt:Z
    ports:
    - 8080:8080/tcp # Guacamole is on :8080/guacamole, not /.
    restart: always

# guacamole_ca_certs
#  guacamole_ca_certs:
#    user: root
#    depends_on:
#      - guacamole
#    image: guacamole/guacamole
#    volumes:
#      - ./FullChain.pem:/etc/ssl/certs/FullChain.pem:Z
#      - ca_certs:/opt/java/openjdk/lib/security:z
#    entrypoint: [ "sh", "-c" ]
#    command: [ "keytool -importcert -alias domain -file 
/etc/ssl/certs/FullChain.pem -keystore /opt/java/openjdk/lib/security/cacerts 
-storepass changeit -noprompt" ]

#volumes:
#  ca_certs:

Note: The message and attachment(s) to this email are intended for the use of 
the individual or entity to which it is addressed, and may contain information 
that is privileged, confidential and exempt from disclosure under applicable 
law. If the reader of this message is not the intended recipient, you are 
hereby notified that any dissemination, distribution, printing or copying of 
this communication is strictly prohibited. If you received this in error, 
please contact the sender and delete the original message, any attachment(s) 
and copies. Thank you for your cooperation.

Reply via email to