Skip to content

Commit

Permalink
Overhaul container build (#18)
Browse files Browse the repository at this point in the history
* Use stock Alpine base image
* Download nginx using HTTPS
* Use a venv
* Split complex logic into separate scripts
* Fix spnego module build process
* General cleanup and optimization
  • Loading branch information
mattclay committed May 18, 2024
1 parent b77f113 commit 4733cf8
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 117 deletions.
143 changes: 43 additions & 100 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,117 +1,60 @@
ARG CONTAINER_VERSION=1.19.2-alpine
FROM quay.io/bedrock/alpine:3.19.1 AS base

# builder used to create a dynamic spnego auth module
# https://gist.github.com/hermanbanken/96f0ff298c162a522ddbba44cad31081
FROM nginx:${CONTAINER_VERSION} AS builder
RUN apk add \
nginx \
python3 \
--no-cache

ENV SPNEGO_AUTH_COMMIT_ID=72c8ee04c81f929ec84d5a6d126f789b77781a8c
FROM base AS builder

RUN set -x && \
NGINX_VERSION="$( nginx -v 2>&1 | awk -F/ '{print $2}' )" && \
NGINX_CONFIG="$( nginx -V 2>&1 | sed -n -e 's/^.*arguments: //p' )" && \
wget "http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz" -O nginx.tar.gz && \
wget https://github.com/stnoonan/spnego-http-auth-nginx-module/archive/${SPNEGO_AUTH_COMMIT_ID}.tar.gz -O spnego-http-auth.tar.gz && \
apk add --no-cache --virtual .build-deps \
RUN apk add \
gcc \
gd-dev \
geoip-dev \
krb5-dev \
libc-dev \
libffi-dev \
libmaxminddb-dev \
libxslt-dev \
linux-headers \
make \
openssl \
openssl-dev \
pcre-dev \
perl-dev \
python3-dev \
zlib-dev \
krb5-dev \
&& \
mkdir /usr/src && \
tar -xzC /usr/src -f nginx.tar.gz && \
tar -xzvf spnego-http-auth.tar.gz && \
SPNEGO_AUTH_DIR="$( pwd )/spnego-http-auth-nginx-module-${SPNEGO_AUTH_COMMIT_ID}" && \
cd "/usr/src/nginx-${NGINX_VERSION}" && \
./configure --with-compat "${NGINX_CONFIG}" --add-dynamic-module="${SPNEGO_AUTH_DIR}" && \
make modules && \
cp objs/ngx_*_module.so /usr/lib
--no-cache

# Create the actual httptester container
FROM nginx:${CONTAINER_VERSION}
COPY extract_nginx_options.py /usr/src/extract_nginx_options.py
COPY build_spnego_module.sh /usr/src/build_spnego_module.sh

ADD constraints.txt /root/constraints.txt
ADD krb5.conf /root/krb5.conf
COPY --from=builder /usr/lib/ngx_*_module.so /usr/lib/nginx/modules/
RUN /usr/src/build_spnego_module.sh

ENV PYTHONDONTWRITEBYTECODE=1
ADD requirements.txt /usr/src/requirements.txt
ADD constraints.txt /usr/src/constraints.txt

# The following packages are required to get httpbin/brotlipy/cffi installed
# openssl-dev python3-dev libffi-dev gcc libstdc++ make musl-dev
# Symlinking /usr/lib/libstdc++.so.6 to /usr/lib/libstdc++.so is specifically required for brotlipy
RUN set -x && \
apk add --no-cache \
ca-certificates \
gcc \
krb5-libs \
krb5-server \
libffi-dev \
libstdc++ \
make \
musl-dev \
openssl \
openssl-dev \
py3-pip \
py3-setuptools \
py3-wheel \
python3-dev \
&& \
update-ca-certificates && \
ln -s /usr/lib/libstdc++.so.6 /usr/lib/libstdc++.so && \
mkdir -p /root/ca/certs /root/ca/private /root/ca/newcerts && \
mkdir -p /root/ca2/certs /root/ca2/private /root/ca2/newcerts && \
echo 1000 > /root/ca/serial && \
echo 1000 > /root/ca2/serial && \
touch /root/ca/index.txt && \
touch /root/ca2/index.txt && \
cp /etc/ssl/openssl.cnf /etc/ssl/openssl_ca2.cnf && \
sed -i 's/\.\/demoCA/\/root\/ca/g' /etc/ssl/openssl.cnf && \
sed -i 's/\.\/demoCA/\/root\/ca2/g' /etc/ssl/openssl_ca2.cnf && \
openssl req -new -x509 -days 3650 -nodes -extensions v3_ca -keyout /root/ca/private/cakey.pem -out /root/ca/cacert.pem \
-subj "/C=US/ST=North Carolina/L=Durham/O=Ansible/CN=ansible.http.tests" && \
openssl req -new -x509 -days 3650 -nodes -extensions v3_ca -keyout /root/ca2/private/cakey.pem -out /root/ca2/cacert.pem \
-subj "/C=US/ST=North Carolina/L=Durham/O=Ansible/CN=ca2.ansible.http.tests" && \
openssl req -new -nodes -out /root/ca/ansible.http.tests-req.pem -keyout /root/ca/private/ansible.http.tests-key.pem \
-subj "/C=US/ST=North Carolina/L=Durham/O=Ansible/CN=ansible.http.tests" && \
yes | openssl ca -config /etc/ssl/openssl.cnf -days 3650 -out /root/ca/ansible.http.tests-cert.pem -infiles /root/ca/ansible.http.tests-req.pem && \
openssl req -new -nodes -out /root/ca/sni1.ansible.http.tests-req.pem -keyout /root/ca/private/sni1.ansible.http.tests-key.pem -config /etc/ssl/openssl.cnf \
-subj "/C=US/ST=North Carolina/L=Durham/O=Ansible/CN=sni1.ansible.http.tests" && \
yes | openssl ca -config /etc/ssl/openssl.cnf -days 3650 -out /root/ca/sni1.ansible.http.tests-cert.pem -infiles /root/ca/sni1.ansible.http.tests-req.pem && \
openssl req -new -nodes -out /root/ca/sni2.ansible.http.tests-req.pem -keyout /root/ca/private/sni2.ansible.http.tests-key.pem -config /etc/ssl/openssl.cnf \
-subj "/C=US/ST=North Carolina/L=Durham/O=Ansible/CN=sni2.ansible.http.tests" && \
yes | openssl ca -config /etc/ssl/openssl.cnf -days 3650 -out /root/ca/sni2.ansible.http.tests-cert.pem -infiles /root/ca/sni2.ansible.http.tests-req.pem && \
openssl req -new -nodes -out /root/ca/client.ansible.http.tests-req.pem -keyout /root/ca/private/client.ansible.http.tests-key.pem -config /etc/ssl/openssl.cnf \
-subj "/C=US/ST=North Carolina/L=Durham/O=Ansible/CN=client.ansible.http.tests" && \
yes | openssl ca -config /etc/ssl/openssl.cnf -days 3650 -out /root/ca/client.ansible.http.tests-cert.pem -infiles /root/ca/client.ansible.http.tests-req.pem && \
openssl req -new -nodes -out /root/ca2/self-signed.ansible.http.tests-req.pem -keyout /root/ca2/private/self-signed.ansible.http.tests-key.pem -config /etc/ssl/openssl_ca2.cnf \
-subj "/C=US/ST=North Carolina/L=Durham/O=Ansible/CN=self-signed.ansible.http.tests" && \
yes | openssl ca -config /etc/ssl/openssl_ca2.cnf -days 3650 -out /root/ca2/self-signed.ansible.http.tests-cert.pem -infiles /root/ca2/self-signed.ansible.http.tests-req.pem && \
cp /root/ca/cacert.pem /usr/share/nginx/html/cacert.pem && \
cp /root/ca2/cacert.pem /usr/share/nginx/html/ca2cert.pem && \
cp /root/ca/client.ansible.http.tests-cert.pem /usr/share/nginx/html/client.pem && \
cp /root/ca/private/client.ansible.http.tests-key.pem /usr/share/nginx/html/client.key && \
chmod 644 /usr/share/nginx/html/* && \
pip3 install --no-cache-dir --no-compile -c /root/constraints.txt gunicorn httpbin && \
apk del openssl-dev py3-pip py3-wheel python3-dev libffi-dev gcc libstdc++ make musl-dev && \
rm -rf /root/.cache/pip && \
find /usr/lib/python3.8 -type f -regex ".*\.py[co]" -delete && \
find /usr/lib/python3.8 -type d -name "__pycache__" -delete && \
echo "Microsoft Rulz" > /usr/share/nginx/html/gssapi && \
echo -e "load_module /usr/lib/nginx/modules/ngx_http_auth_spnego_module.so;\n$( cat /etc/nginx/nginx.conf )" > /etc/nginx/nginx.conf && \
cp /root/krb5.conf /etc/krb5.conf && \
PASSWORD="$( < /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c30 )" && \
echo -e "${PASSWORD}\n${PASSWORD}" | /usr/sbin/kdb5_util create -r HTTP.TESTS && \
echo -e "*/admin@HTTP.TESTS\t*" > /var/lib/krb5kdc/kadm5.acl && \
kadmin.local -q "addprinc -randkey HTTP/ansible@HTTP.TESTS" && \
kadmin.local -q "addprinc -randkey HTTP/ansible.http.tests@HTTP.TESTS" && \
kadmin.local -q "ktadd -k /etc/nginx.keytab HTTP/ansible@HTTP.TESTS" && \
kadmin.local -q "ktadd -k /etc/nginx.keytab HTTP/ansible.http.tests@HTTP.TESTS" && \
chmod 660 /etc/nginx.keytab && \
chown root:nginx /etc/nginx.keytab
RUN python3 -m venv /usr/share/nginx/venv/
RUN /usr/share/nginx/venv/bin/pip install --no-cache-dir --no-compile -r /usr/src/requirements.txt -c /usr/src/constraints.txt
RUN cd /usr/share/nginx/venv/lib/python*/site-packages/ && rm -rf pip pip-* setuptools setuptools-*

ADD create_certs.sh /usr/src/create_certs.sh
RUN /usr/src/create_certs.sh

FROM base AS output

COPY --from=builder /usr/lib/nginx/modules/ /usr/lib/nginx/modules/
COPY --from=builder /usr/share/nginx/venv/ /usr/share/nginx/venv/
COPY --from=builder /usr/src/nginx.conf /etc/nginx/nginx.conf
COPY --from=builder /root/ca/ /root/ca/
COPY --from=builder /root/ca2/ /root/ca2/

ADD krb5.conf /usr/src/krb5.conf
ADD configure_nginx.sh /usr/src/configure_nginx.sh
RUN /usr/src/configure_nginx.sh

ADD services.sh /services.sh
ADD nginx.sites.conf /etc/nginx/conf.d/default.conf
ADD nginx.sites.conf /etc/nginx/http.d/default.conf

EXPOSE 80 88 443 749

Expand Down
32 changes: 32 additions & 0 deletions build_spnego_module.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env sh

set -eux

SPNEGO_AUTH_COMMIT_ID="72c8ee04c81f929ec84d5a6d126f789b77781a8c"
NGINX_VERSION="$( nginx -v 2>&1 | awk -F/ '{print $2}' )"
NGINX_CONFIG="$( nginx -V 2>&1 | python3 /usr/src/extract_nginx_options.py )"
NGINX_TAR="nginx.tar.gz"
NGINX_SRC="/usr/src/nginx-${NGINX_VERSION}"
SPNEGO_TAR="spnego-http-auth.tar.gz"
SPNEGO_SRC="/usr/src/spnego-http-auth-nginx-module-${SPNEGO_AUTH_COMMIT_ID}"
MODULE_DIR="/usr/lib/nginx/modules/"
MODULE_NAME="ngx_http_auth_spnego_module.so"

wget "https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz" -O "${NGINX_TAR}"
wget "https://github.com/stnoonan/spnego-http-auth-nginx-module/archive/${SPNEGO_AUTH_COMMIT_ID}.tar.gz" -O "${SPNEGO_TAR}"

tar -xzC /usr/src -f "${NGINX_TAR}"
tar -xzC /usr/src -f "${SPNEGO_TAR}"

cd "${NGINX_SRC}"

# shellcheck disable=SC2086
./configure ${NGINX_CONFIG} --add-dynamic-module="${SPNEGO_SRC}"

make modules

cp "objs/${MODULE_NAME}" "${MODULE_DIR}"

echo "load_module ${MODULE_DIR}/${MODULE_NAME};" > /usr/src/nginx.conf

cat /etc/nginx/nginx.conf >> /usr/src/nginx.conf
35 changes: 35 additions & 0 deletions configure_nginx.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env sh

set -eux

apk add \
ca-certificates \
krb5-libs \
krb5-server \
--no-cache

update-ca-certificates

mkdir /usr/share/nginx/html/

cp /usr/src/krb5.conf /etc/krb5.conf

cp /root/ca/cacert.pem /usr/share/nginx/html/cacert.pem
cp /root/ca2/cacert.pem /usr/share/nginx/html/ca2cert.pem
cp /root/ca/client.ansible.http.tests-cert.pem /usr/share/nginx/html/client.pem
cp /root/ca/private/client.ansible.http.tests-key.pem /usr/share/nginx/html/client.key

chmod 644 /usr/share/nginx/html/*

echo "Microsoft Rulz" > /usr/share/nginx/html/gssapi

python3 -c "import secrets; password = secrets.token_hex(30); print(password); print(password);" | /usr/sbin/kdb5_util create -r HTTP.TESTS
python3 -c "print('*/admin@HTTP.TESTS\t*')" > /var/lib/krb5kdc/kadm5.acl

kadmin.local -q "addprinc -randkey HTTP/ansible@HTTP.TESTS"
kadmin.local -q "addprinc -randkey HTTP/ansible.http.tests@HTTP.TESTS"
kadmin.local -q "ktadd -k /etc/nginx.keytab HTTP/ansible@HTTP.TESTS"
kadmin.local -q "ktadd -k /etc/nginx.keytab HTTP/ansible.http.tests@HTTP.TESTS"

chmod 660 /etc/nginx.keytab
chown root:nginx /etc/nginx.keytab
38 changes: 24 additions & 14 deletions constraints.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
Flask==1.1.2
Jinja2==2.11.2
MarkupSafe==1.1.1
blinker==1.4
brotlipy==0.7.0
cffi==1.14.3
click==7.1.2
decorator==4.4.2
gunicorn==20.0.4
httpbin==0.7.0
itsdangerous==1.1.0
pycparser==2.20
raven==6.10.0
werkzeug==1.0.1
attrs==23.2.0
blinker==1.8.2
brotlicffi==1.1.0.0
cffi==1.16.0
click==8.1.7
decorator==5.1.1
flasgger==0.9.7.1
Flask==3.0.3
greenlet==2.0.2
gunicorn==22.0.0
httpbin==0.10.2
itsdangerous==2.2.0
Jinja2==3.1.4
jsonschema==4.22.0
jsonschema-specifications==2023.12.1
MarkupSafe==2.1.5
mistune==3.0.2
packaging==24.0
pycparser==2.22
PyYAML==6.0.1
referencing==0.35.1
rpds-py==0.18.1
six==1.16.0
Werkzeug==3.0.3
38 changes: 38 additions & 0 deletions create_certs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env sh

set -eux

subj="/C=US/ST=North Carolina/L=Durham/O=Ansible"
days=3650

ca1="/root/ca"
ca2="/root/ca2"

create_ca() {
ca="$1"
name="$2"

mkdir -p "${ca}/certs" "${ca}/private" "${ca}/newcerts"
echo 1000 > "${ca}/serial"
touch "${ca}/index.txt"
sed "s|\./demoCA|${ca}|g" /etc/ssl/openssl.cnf > "${ca}/openssl.cnf"
openssl req -new -x509 -nodes -extensions v3_ca \
-config "${ca}/openssl.cnf" -days "${days}" -subj "${subj}/CN=${name}" -out "${ca}/cacert.pem" -keyout "${ca}/private/cakey.pem"
}

create_cert() {
ca="$1"
name="$2"

openssl req -new -nodes -config "${ca}/openssl.cnf" -subj "${subj}/CN=${name}" -out "${ca}/${name}-req.pem" -keyout "${ca}/private/${name}-key.pem"
yes | openssl ca -config "${ca}/openssl.cnf" -days "${days}" -in "${ca}/${name}-req.pem" -out "${ca}/${name}-cert.pem"
}

create_ca "${ca1}" "ansible.http.tests"
create_ca "${ca2}" "ca2.ansible.http.tests"

create_cert "${ca1}" "ansible.http.tests"
create_cert "${ca1}" "sni1.ansible.http.tests"
create_cert "${ca1}" "sni2.ansible.http.tests"
create_cert "${ca1}" "client.ansible.http.tests"
create_cert "${ca2}" "self-signed.ansible.http.tests"
14 changes: 14 additions & 0 deletions extract_nginx_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import sys

prefix = 'configure arguments: '

for line in sys.stdin:
if line.startswith(prefix):
line = line.removeprefix(prefix)
options = line.split()
options = [option for option in options if not option.startswith('--add-dynamic-module=')]
line = ' '.join(options)
print(line)
break
else:
raise Exception()
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
gunicorn
httpbin
8 changes: 5 additions & 3 deletions services.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#!/bin/sh
#!/usr/bin/env sh

if [ -z ${KRB5_PASSWORD} ]; then
if [ -z "${KRB5_PASSWORD}" ]; then
echo "No KRB5_PASSWORD provided for the admin account."
exit 1
fi

kadmin.local -q "addprinc -pw ${KRB5_PASSWORD} admin"

/usr/sbin/krb5kdc
gunicorn -D httpbin:app
/usr/share/nginx/venv/bin/gunicorn -D httpbin:app

nginx -g "daemon off;"

0 comments on commit 4733cf8

Please sign in to comment.