From 23b2a6ac63267b14de4675a57a12c51a19c0b404 Mon Sep 17 00:00:00 2001 From: Slawomir Koszewski Date: Fri, 27 Feb 2026 11:18:35 +0100 Subject: [PATCH] update: create entrypoint.sh that configures and launches the service. --- .gitignore | 1 + Caddyfile.example | 14 ++-- Dockerfile | 3 +- entrypoint.sh | 167 ++++++++++++++++++++++++++++++++++------------ run-server.sh | 3 +- 5 files changed, 135 insertions(+), 53 deletions(-) diff --git a/.gitignore b/.gitignore index de0aafe..15c10aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ storage test Caddyfile +*.env diff --git a/Caddyfile.example b/Caddyfile.example index 27c8416..311c374 100644 --- a/Caddyfile.example +++ b/Caddyfile.example @@ -1,17 +1,17 @@ # Caddyfile for Azure Storage Emulator # It uses /etc/hosts entries to route requests to the emulator -# Replace "devstoreaccount1" with a desired storage account name if needed -devstoreaccount1.blob.core.windows.net { - tls storage/server_cert.pem storage/server_key.pem +# Replace "__ACCOUNT_NAME__" with a desired storage account name if needed +__ACCOUNT_NAME__.blob.core.windows.net { + tls __AZURITE_DIR__/__ACCOUNT_NAME___cert.pem __AZURITE_DIR__/__ACCOUNT_NAME___key.pem reverse_proxy localhost:10000 } -devstoreaccount1.queue.core.windows.net { - tls storage/server_cert.pem storage/server_key.pem +__ACCOUNT_NAME__.queue.core.windows.net { + tls __AZURITE_DIR__/__ACCOUNT_NAME___cert.pem __AZURITE_DIR__/__ACCOUNT_NAME___key.pem reverse_proxy localhost:10001 } -devstoreaccount1.table.core.windows.net { - tls storage/server_cert.pem storage/server_key.pem +__ACCOUNT_NAME__.table.core.windows.net { + tls __AZURITE_DIR__/__ACCOUNT_NAME___cert.pem __AZURITE_DIR__/__ACCOUNT_NAME___key.pem reverse_proxy localhost:10002 } diff --git a/Dockerfile b/Dockerfile index 44bf0f1..1c327da 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,6 +16,7 @@ FROM caddy:2-alpine AS caddy # Build final image on top of Node alpine FROM node:20-alpine WORKDIR /app +RUN apk add --no-cache openssl bash COPY --from=caddy /usr/bin/caddy /usr/bin/caddy COPY --from=caddy /etc/caddy /etc/caddy COPY --from=build /app/azurite/package*.json ./azurite/ @@ -26,6 +27,7 @@ RUN npm ci --omit=dev --unsafe-perm WORKDIR /app COPY ./entrypoint.sh . +COPY ./Caddyfile.example . RUN chmod +x entrypoint.sh EXPOSE 443 @@ -34,4 +36,3 @@ EXPOSE 10001 EXPOSE 10002 ENTRYPOINT [ "/app/entrypoint.sh" ] -CMD [ "ash" ] diff --git a/entrypoint.sh b/entrypoint.sh index 16c031f..fcce738 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,26 +1,90 @@ -#!/bin/ash +#!/bin/bash set -e -function check_ssl_files() { - if [[ ! -f "$AZURITE_DIR/server_key.pem" || ! -f "$AZURITE_DIR/server_cert.pem" ]]; then - echo "SSL enabled but server_key.pem or server_cert.pem not found in $AZURITE_DIR. Please provide these files for SSL support." - exit 1 - fi +function make_ca() { + # Use the provided directory argument or default to AZURITE_DIR if not provided + local CERT_DIR="${1:-$AZURITE_DIR}" + + if [[ ! -d "$CERT_DIR" ]]; then + echo "ERROR: Certificate directory $CERT_DIR does not exist." + return 1 + fi + + # Generate CA certificate and key if they don't exist + if [[ ! -f "$CERT_DIR/ca_cert.pem" || ! -f "$CERT_DIR/ca_key.pem" ]]; then + echo "Generating CA certificate and key..." + if ! openssl req \ + -x509 \ + -newkey rsa:4096 \ + -keyout "$CERT_DIR/ca_key.pem" \ + -out "$CERT_DIR/ca_cert.pem" \ + -days 3650 \ + -nodes \ + -subj "/CN=Azurite CA" \ + -text \ + -addext "basicConstraints=critical,CA:TRUE,pathlen:0"; then + echo "Error: Failed to generate CA certificate and key." >&2 + return 1 + fi + fi } -# We are running as root inside a container. -AZURITE_DIR="/storage" -AZURITE_ACCOUNTS_FILE="$AZURITE_DIR/accounts.env" +function make_server_cert() { + local ACCOUNT_NAME="${1:-devstoreaccount1}" + local CERT_DIR="${2:-$AZURITE_DIR}" -if [[ ! -f "$AZURITE_ACCOUNTS_FILE" ]]; then - echo "No accounts found. Provide $AZURITE_ACCOUNTS_FILE." - exit 1 + if [[ ! -d "$CERT_DIR" ]]; then + echo "ERROR: Certificate directory $CERT_DIR does not exist." + return 1 + fi + + # Generate server certificate and key if they don't exist + if [[ ! -f "$CERT_DIR/${ACCOUNT_NAME}_cert.pem" || ! -f "$CERT_DIR/${ACCOUNT_NAME}_key.pem" ]]; then + echo "Generating server certificate and key..." + if ! openssl req \ + -newkey rsa:4096 \ + -keyout "$CERT_DIR/${ACCOUNT_NAME}_key.pem" \ + -nodes \ + -subj "/CN=${ACCOUNT_NAME}.blob.core.windows.net" \ + -addext "basicConstraints=critical,CA:FALSE" \ + -addext "keyUsage=digitalSignature,keyEncipherment" \ + -addext "extendedKeyUsage=serverAuth,clientAuth" \ + -addext "subjectAltName=DNS:${ACCOUNT_NAME}.blob.core.windows.net,DNS:${ACCOUNT_NAME}.queue.core.windows.net,DNS:${ACCOUNT_NAME}.table.core.windows.net,DNS:localhost,IP:127.0.0.1" \ + | openssl x509 \ + -req \ + -CA "$CERT_DIR/ca_cert.pem" \ + -CAkey "$CERT_DIR/ca_key.pem" \ + -set_serial "0x$(openssl rand -hex 16)" \ + -copy_extensions copyall \ + -days 365 \ + -text \ + -out "$CERT_DIR/${ACCOUNT_NAME}_cert.pem"; then + echo "Error: Failed to generate server certificate and key." >&2 + exit 1 + fi + fi +} + +# Setup default storage location, account name and accounts file path. +AZURITE_DIR="/storage" + +# Check, if the AZURITE_ACCOUNTS variable is set +if [[ -z "$AZURITE_ACCOUNTS" ]]; then + # Generate a default account + export AZURITE_ACCOUNTS="devstoreaccount1:$(openssl rand -base64 32)" fi -set -a -. $AZURITE_ACCOUNTS_FILE -set +a +# Look up the account name from the AZURITE_ACCOUNTS variable, which is in the format "accountName:accountKey1:accountKey2;accountName2:accountKey1:accountKey2" +ACCOUNT_NAME=$(echo "$AZURITE_ACCOUNTS" | cut -f 1 -d ';' | cut -f 1 -d ':') + +# Ensure /etc/hosts contains an entry the Azure endpoint names, +# so Caddy can route requests to the correct service based on the hostname. +if ! grep -qE "${ACCOUNT_NAME}\.blob\.core\.windows\.net" /etc/hosts || + ! grep -qE "${ACCOUNT_NAME}\.queue\.core\.windows\.net" /etc/hosts || + ! grep -qE "${ACCOUNT_NAME}\.table\.core\.windows\.net" /etc/hosts; then + echo -e "\n127.0.0.1\t${ACCOUNT_NAME}.blob.core.windows.net ${ACCOUNT_NAME}.queue.core.windows.net ${ACCOUNT_NAME}.table.core.windows.net" >> /etc/hosts +fi CADDY=true AZURITE_SSL="" @@ -29,48 +93,63 @@ BLOB_ARGS=() OAUTH_ARGS=() while [[ $# -gt 0 ]]; do - case "$1" in - --oauth) - OAUTH_ARGS=("--oauth" "basic") - CADDY="" # Ensure OAuth is disabled when using Caddy, as Azurite does not support OAuth in reverse proxy mode. - shift - ;; - --ssl) - check_ssl_files - AZURITE_SSL=true - shift - ;; - --no-caddy) - CADDY="" - shift - ;; - *) - echo "Unknown argument: $1" - exit 1 - ;; - esac + case "$1" in + --oauth) + OAUTH_ARGS=("--oauth" "basic") + # Ensure Caddy is disabled when using OAuth, as Azurite does not support OAuth behind a reverse proxy. + CADDY="" + shift + ;; + --ssl) + AZURITE_SSL=true + # Ensure Caddy is disabled when using Azurite's built-in SSL support. + CADDY="" + shift + ;; + --no-caddy) + # Disable Caddy on request. + CADDY="" + shift + ;; + *) + echo "Unknown argument: $1" + exit 1 + ;; + esac done +# Ensure certificates are generated before starting Azurite or Caddy. +if [[ -n "$AZURITE_SSL" || -n "$CADDY" ]]; then + if ! make_ca "$AZURITE_DIR"; then + echo "Error: Failed to create CA certificate and key." >&2 + exit 1 + fi + + if ! make_server_cert "$ACCOUNT_NAME" "$AZURITE_DIR"; then + echo "Error: Failed to create server certificate and key." >&2 + exit 1 + fi +fi + if [[ -n "$CADDY" ]]; then - # If using Caddy, it will reverse proxy blob, queue, and table endpoints to different ports on localhost, - # allowing simultaneous access to all services on a single HTTPS port. - check_ssl_files # Ensure SSL files are present when using Caddy, as Caddy will handle SSL termination. - echo "Starting Caddy server..." - caddy start --config Caddyfile # Use start not run, start does not block the shell process. + # If using Caddy, it will reverse proxy blob, queue, and table endpoints to different ports on localhost, + # allowing simultaneous access to all services on a single HTTPS port. + # Generate a Caddyfile configuration based on the account name and storage directory. + sed -E "s/__ACCOUNT_NAME__/${ACCOUNT_NAME}/g; s|__AZURITE_DIR__|${AZURITE_DIR}|g" /app/Caddyfile.example > "$AZURITE_DIR/Caddyfile" + echo "Starting Caddy server..." + caddy start --config "$AZURITE_DIR/Caddyfile" # Use start not run, start does not block the shell process. else # If not using Caddy, configure Azurite to listen on all interfaces and use the generated self-signed certificate. # This mode does not require Caddy, but it will not allow simultaneous access to the table and queue services on the same HTTPS port. if [[ -n "$AZURITE_SSL" ]]; then BLOB_ARGS=("--blobHost" "0.0.0.0" "--blobPort" "443") - CERT_ARGS=("--key" "$AZURITE_DIR/server_key.pem" "--cert" "$AZURITE_DIR/server_cert.pem") + CERT_ARGS=("--key" "$AZURITE_DIR/${ACCOUNT_NAME}_key.pem" "--cert" "$AZURITE_DIR/${ACCOUNT_NAME}_cert.pem") fi fi -azurite \ +exec node /app/azurite/src/azurite.js \ --disableTelemetry \ --location "$AZURITE_DIR" \ "${CERT_ARGS[@]}" \ "${BLOB_ARGS[@]}" \ "${OAUTH_ARGS[@]}" - -exec "$@" diff --git a/run-server.sh b/run-server.sh index f359576..1643042 100755 --- a/run-server.sh +++ b/run-server.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash -AZURITE_DIR="storage" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +AZURITE_DIR="$SCRIPT_DIR/storage" if [[ ! -d "$AZURITE_DIR" ]]; then echo "No accounts found"