diff --git a/.env.dist b/.env.dist index 0df0068..f915808 100644 --- a/.env.dist +++ b/.env.dist @@ -12,10 +12,14 @@ COMPOSE_PROJECT_NAME=slapd SLAPD_NAME=slapd SLAPD_IMAGE=corpusops/slapd SLAPD_IMAGE_VERSION=latest -SLAPD_SCHEMA_VERSION=1.9.0.1 +SLAPD_SCHEMA_VERSION=1.0.9.1 SLAPD_LISTEN_IP=0.0.0.0 SLAPD_LDAP_PORT=389 SLAPD_LDAPS_PORT=636 SLAPD_REBOOT_CRON=1 2 * * * -SLAPD_CERTS_DIR=/etc/ssl/certs +#SLAPD_CERTS_DIR=/etc/ssl/certs # vim: set ft=sh: +#SLAPD_LOGLEVEL=22000 +#SLAPD_VERSION=2.4.0 +#BASE=corpusops/ubuntu-bare:24.04 +COMPOSE_FILE="docker-compose.yml:docker-compose-build.yml" diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index a3b3f01..4cceb44 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -32,19 +32,19 @@ jobs: IMGV=${IMG_RELEASE}; BASE=${IMG_RELEASE//latest/$IMG_LATEST}; t=${IMG}:${IMGV}; - db() { docker build - --build-arg BASE="corpusops/ubuntu:$BASE" --build-arg BUILDKIT_INLINE_CACHE="1" -t ${3} -f ${1} ${2} "${@:4}"; }; - db ${DFILE} ${DPATH} ${t}-no-squash;docker tag ${t}-no-squash ${t}; - if [ "x${NO_SQUASH}" = "x" ];then db ${DFILE} ${DPATH} ${t} --squash;fi; - if [ "x${{steps.v.outputs.releasable}}" = "xtrue" ];then for i in "" -no-squash;do docker push ${t}${i};done;fi; + db() { docker build --build-arg BASE="corpusops/ubuntu-bare:$BASE" --build-arg BUILDKIT_INLINE_CACHE="1" -t ${3} -f ${1} ${2} "${@:4}"; }; + db ${DFILE} ${DPATH} ${t};docker tag ${t} ${IMG}:latest; + if [ "${IMG_RELEASE}" = "${IMG_LATEST}" ];then echo "Also release latest">&2; t="${t} ${IMG}:latest";fi; + if [ "x${{steps.v.outputs.releasable}}" = "xtrue" ];then echo "Releasing $t" >&2;for i in ${t};do docker push ${i};done;fi; strategy: max-parallel: 5 fail-fast: false matrix: IMG_RELEASE: - - latest - 18.04 - 20.04 + - 22.04 + - 24.04 IMG_LATEST: [20.04] IMG: ["corpusops/slapd"] DFILE: ["Dockerfile"] diff --git a/.gitignore b/.gitignore index f21d69f..ad821c0 100644 --- a/.gitignore +++ b/.gitignore @@ -71,3 +71,4 @@ yarn-error.log /.ansible/inventory\.* +/cert diff --git a/Dockerfile b/Dockerfile index fef10ef..9bdfe12 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,10 @@ -ARG BASE=corpusops/ubuntu:20.04 -FROM $BASE +ARG BASE=corpusops/ubuntu-bare:24.04 +ARG RSYNC=corpusops/rsync +FROM $BASE AS final ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get update -qq +ADD apt.txt ./ RUN set -x \ - && apt-get install -qq \ - ldap-utils \ - ca-certificates \ - slapd \ - monitoring-plugins libio-socket-ssl-perl \ - python3-ldap \ - libdate-manip-perl libgetopt-complete-perl libnet-ldap-perl \ + && DO_UPDATE="$DO_UPDATE" WANTED_EXTRA_PACKAGES="$(cat apt.txt)" cops_pkgmgr_install.sh\ && mkdir -p /etc/ldap /var/run/slapd/ /var/lib/ldap/ \ && chown openldap /etc/ldap /var/run/slapd/ /var/lib/ldap/ \ && apt-get clean && rm -rfv /var/lib/apt/lists/* /var/cache/apt/archives/* @@ -18,4 +13,14 @@ ENV LTB_VERSION=$LTB_VERSION RUN git clone https://github.com/ltb-project/nagios-plugins.git && cd nagios-plugins && git reset --hard $LTB_VERSION RUN find /etc/ldap/slapd.d -type f -delete ADD ./rootfs/ / + +# SQUASH CODE +FROM $RSYNC AS squashed-rsync +FROM $BASE AS squashed-ancestor +ARG ROOTFS="/BASE_ROOTFS_TO_COPY_THAT_WONT_COLLIDE_1234567890" +ARG PATH="${ROOTFS}_rsync/bin:$PATH" +SHELL ["busybox", "sh", "-c"] +RUN --mount=type=bind,from=final,target=$ROOTFS --mount=type=bind,from=squashed-rsync,target=${ROOTFS}_rsync \ +rsync -Aaz --delete ${ROOTFS}/ / --exclude=/proc --exclude=/sys --exclude=/etc/resolv.conf --exclude=/etc/hosts --exclude=$ROOTFS* --exclude=dev/shm --exclude=dev/pts --exclude=dev/mqueue +SHELL ["/bin/sh", "-c"] ENTRYPOINT ["/init.sh"] diff --git a/README.md b/README.md index be8c312..1d44093 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,10 @@ - Those variables need to be encoded to base64 without newlines (a one line (`\n` removed)): - `SLAPD_SYNCREPL`: syncrepl configuration lines to add (bare slapd.conf configuration lines) - `SLAPD_ACLS`: acl configuration lines to add (bare slapd.conf configuration lines) + +## test in dev +```bash +./create_ca_cert.sh +COMPOSE_FILE="docker-compose.yml:docker-compose-build.yml" docker-compose build +COMPOSE_FILE="docker-compose.yml:docker-compose-build.yml" docker-compose up +``` diff --git a/apt.txt b/apt.txt new file mode 100644 index 0000000..a33f4de --- /dev/null +++ b/apt.txt @@ -0,0 +1,82 @@ +accountsservice +acl +apparmor +apparmor-utils +apt-transport-https +apt-utils +autoconf +binutils +bsdmainutils +build-essential +busybox +ca-certificates +console-setup +cpio +cron +curl +debconf-utils +debian-archive-keyring +debian-keyring +dirmngr +dpkg-dev +ed +file +gettext +gettext-base +git +glib-networking +gnupg2 +gnupg-utils +gpg +gpg-agent +gpgconf +groff +iproute2 +iptables +iso-codes +jq +ldap-utils +less +libdate-manip-perl +libgetopt-complete-perl +libio-socket-ssl-perl +libnet-ldap-perl +locales +logrotate +lsb-release +make +man-db +monit +monitoring-plugins +netcat +netcat-openbsd +openssh-client +openssl +openssl-client +p7zip +p7zip-full +pass +patch +perl +pkg-config +python3-ldap +readline-common +rsync +rsyslog +runit +screen +slapd +socat +software-properties-common +sudo +swig +tree +tzdata +ucf +unzip +virtualenv +virt-what +wget +x11-common +xz-utils +zip diff --git a/conf/slapd.conf b/conf/slapd.conf index 043d055..fa7cd66 100644 --- a/conf/slapd.conf +++ b/conf/slapd.conf @@ -1,7 +1,10 @@ # modulepath /usr/lib/ldap -{{ $mods := .Env.SLAPD_MODULES|default "back_hdb|back_bdb|syncprov|memberof" }} -{{ range $mod := $mods | splitList "|"}} + +{{ $250andonwards := semverCompare ">= 2.5.0" ( .Env.SLAPD_VERSION | default "2.4.0" ) }} +{{ $dbt := ( .Env.SLAPD_DATABASE_TYPE | default ( $250andonwards | ternary "mdb" "bdb" ) ) }} +{{ $mods := list ( $250andonwards | ternary "syncprov|memberof" "back_hdb|syncprov|memberof" ) "|back_" $dbt | join "" }} +{{ range $mod := ( .Env.SLAPD_MODULES | default $mods ) | splitList "|"}} moduleload {{$mod}}.la {{ end }} # schemas @@ -43,7 +46,7 @@ TLSCACertificateFile {{ .Env.SLAPD_TLSCACERTIFICATEFILE | default "/cert/chain.p loglevel {{.Env.SLAPD_LOGLEVEL | default "256" }} ############################################################" # database conf -database bdb +database {{ $dbt }} suffix {{.Env.SLAPD_BASE_DN}} rootdn {{.Env.SLAPD_ROOT_DN}} rootpw {{ (print "{SHA}" (.Env.SLAPD_ROOT_PASSWORD|sha1sum|b64enc))}} diff --git a/create_ca_cert.sh b/create_ca_cert.sh new file mode 100755 index 0000000..2d1da39 --- /dev/null +++ b/create_ca_cert.sh @@ -0,0 +1,138 @@ +#! /bin/bash + +set -Eeuo pipefail + +declare -i DEBUG=0 +w=$(cd "$(dirname "$0")" && pwd) + +logInfo() { + echo "INFO: $@" +} + +ALLDOMAINS=${ALLDOMAINS:-slapd.local} +PROJ_NAME=DockerMirrorBox +logInfo "Will create certificate with names $ALLDOMAINS" + +CADATE=$(date "+%Y.%m.%d %H:%M") +CAID="$(hostname -f) ${CADATE}" + +CN_CA="${PROJ_NAME} CA Root ${CAID}" +CN_IA="${PROJ_NAME} Intermediate IA ${CAID}" +CN_WEB="${PROJ_NAME} Web Cert ${CAID}" + +CN_CA=${CN_CA:0:64} +CN_IA=${CN_IA:0:64} +CN_WEB=${CN_WEB:0:64} + +for i in $w/cert/certs $w/cert/ca;do + if [ ! -e $i ];then + mkdir -p $i + fi +done +cd $w/cert/ca + +CA_KEY_FILE=${CA_KEY_FILE:-$w/cert/ca/ca.key} +CA_CRT_FILE=${CA_CRT_FILE:-$w/cert/ca/ca.crt} +CA_SRL_FILE=${CA_SRL_FILE:-$w/cert/ca/ca.srl} + +if [ -f "$CA_CRT_FILE" ] ; then + logInfo "CA already exists. Good. We'll reuse it." + if [ ! -f "$CA_SRL_FILE" ] ; then + echo 01 > ${CA_SRL_FILE} + fi +else + logInfo "No CA was found. Generating one." + logInfo "*** Please *** make sure to mount /ca as a volume -- if not, everytime this container starts, it will regenerate the CA and nothing will work." + + openssl genrsa -des3 -passout pass:foobar -out ${CA_KEY_FILE} 4096 + + logInfo "generate CA cert with key and self sign it: ${CAID}" + openssl req -new -x509 -days 1300 -sha256 -key ${CA_KEY_FILE} -out ${CA_CRT_FILE} -passin pass:foobar -subj "/C=NL/ST=Noord Holland/L=Amsterdam/O=ME/OU=IT/CN=${CN_CA}" -extensions IA -config <( +cat <<-EOF +[req] +distinguished_name = dn +[dn] +[IA] +basicConstraints = critical,CA:TRUE +keyUsage = critical, digitalSignature, cRLSign, keyCertSign +subjectKeyIdentifier = hash +EOF +) + + [[ ${DEBUG} -gt 0 ]] && logInfo "show the CA cert details" + [[ ${DEBUG} -gt 0 ]] && openssl x509 -noout -text -in ${CA_CRT_FILE} + + echo 01 > ${CA_SRL_FILE} + +fi + +cd $w/cert/certs + +logInfo "Generate IA key" +openssl genrsa -des3 -passout pass:foobar -out ia.key 4096 &> /dev/null + +logInfo "Create a signing request for the IA: ${CAID}" +openssl req -new -key ia.key -out ia.csr -passin pass:foobar -subj "/C=NL/ST=Noord Holland/L=Amsterdam/O=ME/OU=IT/CN=${CN_IA}" -reqexts IA -config <( +cat <<-EOF +[req] +distinguished_name = dn +[dn] +[IA] +basicConstraints = critical,CA:TRUE,pathlen:0 +keyUsage = critical, digitalSignature, cRLSign, keyCertSign +subjectKeyIdentifier = hash +EOF +) + +[[ ${DEBUG} -gt 0 ]] && logInfo "Show the singing request, to make sure extensions are there" +[[ ${DEBUG} -gt 0 ]] && openssl req -in ia.csr -noout -text + +logInfo "Sign the IA request with the CA cert and key, producing the IA cert" +openssl x509 -req -days 730 -in ia.csr -CA ${CA_CRT_FILE} -CAkey ${CA_KEY_FILE} -CAserial ${CA_SRL_FILE} -out ia.crt -passin pass:foobar -extensions IA -extfile <( +cat <<-EOF +[req] +distinguished_name = dn +[dn] +[IA] +basicConstraints = critical,CA:TRUE,pathlen:0 +keyUsage = critical, digitalSignature, cRLSign, keyCertSign +subjectKeyIdentifier = hash +EOF +) &> /dev/null + + +[[ ${DEBUG} -gt 0 ]] && logInfo "show the IA cert details" +[[ ${DEBUG} -gt 0 ]] && openssl x509 -noout -text -in ia.crt + +logInfo "Initialize the serial number for signed certificates" +echo 01 > ia.srl + +logInfo "Create the key (w/o passphrase..)" +openssl genrsa -des3 -passout pass:foobar -out web.orig.key 2048 &> /dev/null +openssl rsa -passin pass:foobar -in web.orig.key -out web.key &> /dev/null + +SANREQ=DNS:$(python -c "print(',DNS:'.join('$ALLDOMAINS'.split(',')))") + +logInfo "Create the signing request, using extensions" +openssl req -new -key web.key -sha256 -out web.csr -passin pass:foobar -subj "/C=NL/ST=Noord Holland/L=Amsterdam/O=ME/OU=IT/CN=${CN_WEB}" -reqexts SAN -config <(cat <(printf "[req]\ndistinguished_name = dn\n[dn]\n[SAN]\nsubjectAltName=${SANREQ}")) + +[[ ${DEBUG} -gt 0 ]] && logInfo "Show the singing request, to make sure extensions are there" +[[ ${DEBUG} -gt 0 ]] && openssl req -in web.csr -noout -text + +logInfo "Sign the request, using the intermediate cert and key" +openssl x509 -req -days 365 -in web.csr -CA ia.crt -CAkey ia.key -out web.crt -passin pass:foobar -extensions SAN -extfile <(cat <(printf "[req]\ndistinguished_name = dn\n[dn]\n[SAN]\nsubjectAltName=${SANREQ}")) &> /dev/null + +[[ ${DEBUG} -gt 0 ]] && logInfo "Show the final cert details" +[[ ${DEBUG} -gt 0 ]] && openssl x509 -noout -text -in web.crt + +logInfo "Concatenating fullchain.pem..." +cat web.crt ia.crt ${CA_CRT_FILE} > fullchain.pem + +logInfo "Concatenating fullchain_with_key.pem" +cat fullchain.pem web.key > fullchain_with_key.pem + +cd $w +cp $w/cert/certs/fullchain.pem $w/cert/chain.pem +cp $w/cert/certs/web.key $w/cert/privkey.pem +cp $w/cert/certs/web.crt $w/cert/cert.pem +chmod 755 $w/cert/*.pem diff --git a/docker-compose-build.yml b/docker-compose-build.yml index 76fa875..abe1035 100644 --- a/docker-compose-build.yml +++ b/docker-compose-build.yml @@ -1,4 +1,7 @@ version: '3.7' services: slapd: - build: {context: "."} + build: + context: "." + args: + BASE: "${BASE-corpusops/ubuntu-bare:20.04}" diff --git a/docker-compose.yml b/docker-compose.yml index 2d7cb0f..f242d02 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ x-images: environment: {"COMPOSE_PROJECT_NAME": "$COMPOSE_PROJECT_NAME"} slapd: &slapd volumes: - - "${SLAPD_CERTS_DIR}:/cert:ro" + - "${SLAPD_CERTS_DIR:-./cert}:/cert:ro" - slapd-data:/var/lib/ldap - slapd-run:/var/run/slapd - ./conf:/slapdconf:ro diff --git a/rootfs/init.sh b/rootfs/init.sh index d2e0e4d..da42562 100755 --- a/rootfs/init.sh +++ b/rootfs/init.sh @@ -6,6 +6,10 @@ if [[ -n $SLAPD_SDEBUG ]];then set -x;fi join_by() { local IFS="$1"; shift; echo "$*"; } log() { echo "$@" >&2; } vv() { log "$@"; "$@"; } +version_lte() { [ "$1" = "$(printf "$1\n$2" | sort -V | head -n1)" ]; } +version_lt() { [ "$1" = "$2" ] && return 1 || version_lte $1 $2; } +version_gte() { [ "$2" = "$(printf "$1\n$2" | sort -V | head -n1)" ]; } +version_gt() { [ "$1" = "$2" ] && return 1 || version_gte $1 $2; } NO_LOG="${NO_LOG-}" SLAPD_EXTRA_ARGS="${SLAPD_EXTRA_ARGS-}" @@ -19,6 +23,8 @@ HAS_FILE_SLAPD_REPL=${HAS_FILE_SLAPD_REPL-} HAS_FILE_SLAPD_ACLS=${HAS_FILE_SLAPD_ACLS-} SUPERVISORD_CONFIGS="${SUPERVISORD_CONFIGS:-"/etc/supervisor.d/cron /etc/supervisor.d/rsyslog /slapdconf/supervisor"}" +export SLAPD_VERSION=${SLAPD_VERSION:-$(dpkg-query --showformat='${Version}' --show slapd|sed -re "s/\+.*//g")} + test_conf_files_presence() { local t="" k="" for t in slapd.conf slapd.acls slapd.repl;do @@ -43,7 +49,7 @@ init() { /etc/ldap/schema/ fi if [[ -z "$SLAPD_SCHEMAS" ]] ;then - schemas=$(find /etc/ldap/schema -type f 2>/dev/null | sort -V) + schemas=$(find /etc/ldap/schema -type f 2>/dev/null -name "*.schema" | grep -v README | sort -V) if [[ -n "$schemas" ]];then SLAPD_SCHEMAS="$(join_by "|" $schemas)" fi