Skip to content

Commit 5d2e6e5

Browse files
committed
fix(docker): refactor volume mounts and path resolution for server deployment
- Simplified Docker volume architecture from multiple mounts to single mount point * Changed from /opt/cryptic to /opt/cryptic/server_data to avoid overwriting Erlang release * Updated docker-compose.yml to use single volume: ./server_data:/opt/cryptic/server_data:rw - Fixed certificate and database path resolution * Removed hardcoded ENV vars from Dockerfile (CRYPTIC_SERVER_CERT, etc.) * Added cryptic_lib:get_server_file/2 for unified path resolution * Paths from sys.config now relative, prepended with CRYPTIC_SERVER_DIR * Fixed empty string handling in environment variables ("" treated as false) - Enhanced docker-entrypoint.sh * Auto-generates CA certificates if missing using generate-mtls-certs.sh * Sets default CRYPTIC_SERVER_DIR=/opt/cryptic/server_data * Improved debug logging and directory structure validation - Updated GPG bootstrap configuration * Added bootstrap_dir to sys.config and cryptic.app.src * Updated cryptic_ca_bootstrap to use get_server_file/2 for bootstrap directory * Simplified cryptic_ca_store certificate/key loading to use helper functions - Improved documentation (DOCKER.md) * Simplified deployment instructions with single volume mount * Removed redundant mkdir commands (script handles internally) * Updated all examples to use CRYPTIC_SERVER_DIR environment variable This refactoring improves deployment reliability and simplifies the Docker setup process by eliminating mount conflicts and standardizing path resolution.
1 parent 2a29a8f commit 5d2e6e5

File tree

12 files changed

+154
-169
lines changed

12 files changed

+154
-169
lines changed

Dockerfile

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,10 @@ RUN mkdir -p /opt/cryptic/certs /opt/cryptic/logs /opt/cryptic/data/ca /opt/cryp
7373
EXPOSE 8443
7474

7575
# Set environment variables with defaults
76+
# Note: Certificate paths are configured in sys.config as relative paths
77+
# CRYPTIC_SERVER_DIR will be prepended by cryptic_lib:get_server_file/2
7678
ENV CRYPTIC_SERVER_HOST=0.0.0.0 \
7779
CRYPTIC_SERVER_PORT=8443 \
78-
CRYPTIC_SERVER_CERT=/opt/cryptic/certs/server.crt \
79-
CRYPTIC_SERVER_KEY=/opt/cryptic/certs/server.key \
80-
CRYPTIC_CA_CERT=/opt/cryptic/certs/ca.crt \
81-
CRYPTIC_CA_DB_FILE=/opt/cryptic/data/ca/cryptic_ca.db \
8280
CRYPTIC_EVENT_HANDLERS=cryptic_file_logger
8381

8482
# Health check

config/sys.config

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
{cert_renewal_check_interval, 3600}, % Check every hour (3600 seconds)
88
{cert_renewal_max_retries, 5}, % Max retry attempts before alerting user
99
{cert_renewal_retry_interval, 3600}, % Base retry interval: 1 hour (seconds)
10-
10+
1111
%% CA Certificate Files (generated by myca)
1212
{ca_cert_file, "priv/ssl/ca.crt"},
1313
{ca_key_file, "priv/ssl/ca.key"},
@@ -16,6 +16,9 @@
1616
{server_cert_file, "priv/ssl/server.crt"},
1717
{server_key_file, "priv/ssl/server.key"},
1818

19+
%% Directory holding GPG bootstrap keys
20+
{bootstrap_dir, "priv/ca/bootstrap"},
21+
1922
%% Database Configuration
2023
{ca_db_file, "data/ca/cryptic_ca.db"},
2124
{storage_backend, esqlite},

docker-compose.yml

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,38 +17,23 @@ services:
1717
environment:
1818
- CRYPTIC_SERVER_HOST=0.0.0.0
1919
- CRYPTIC_SERVER_PORT=8443
20-
- CRYPTIC_SERVER_CERT=/opt/cryptic/certs/server.crt
21-
- CRYPTIC_SERVER_KEY=/opt/cryptic/certs/server.key
22-
- CRYPTIC_CA_CERT=/opt/cryptic/certs/ca.crt
23-
- CRYPTIC_CA_DB_FILE=/opt/cryptic/data/ca/cryptic_ca.db
20+
# Set server directory - all server data stored here
21+
# Paths in sys.config (e.g., "priv/ssl/ca.crt") are relative to this directory
22+
- CRYPTIC_SERVER_DIR=/opt/cryptic/server_data
2423
- CRYPTIC_EVENT_HANDLERS=cryptic_file_logger
2524
# Optional: Enable debug logging (set to "true" for verbose output)
2625
- CRYPTIC_DEBUG=true
2726

2827
# Volume mounts for certificates and persistent data
2928
volumes:
30-
# Mount certificates from priv/ssl (generated by scripts/generate-mtls-certs.sh)
31-
- ./priv/ssl/server.crt:/opt/cryptic/certs/server.crt:ro
32-
- ./priv/ssl/server.key:/opt/cryptic/certs/server.key:ro
33-
- ./priv/ssl/ca.crt:/opt/cryptic/certs/ca.crt:ro
34-
35-
# Mount CA cert and key to release priv directory for certificate issuance
36-
- ./priv/ssl/ca.crt:/opt/cryptic/lib/cryptic-1.0.0/priv/ssl/ca.crt:ro
37-
- ./priv/ssl/ca.key:/opt/cryptic/lib/cryptic-1.0.0/priv/ssl/ca.key:ro
38-
39-
# Bootstrap GPG fingerprints for pre-verified users
40-
# Read-write so bootstrap script can export GPG keys from inside container
41-
- ./priv/ca/bootstrap:/opt/cryptic/lib/cryptic-1.0.0/priv/ca/bootstrap:rw
29+
# Single mount point for all server data (priv/, logs/, data/)
30+
# Note: /opt/cryptic contains Erlang release, so mount to subdirectory
31+
- ./server_data:/opt/cryptic/server_data:rw
4232

4333
# Shared GPG keyring - allows GPG key generation inside container
4434
# Keys generated in container are available on host and vice versa
4535
- ${HOME}/.gnupg:/home/cryptic/.gnupg:rw
4636

47-
# Persistent data volumes (logs and CA database)
48-
- cryptic-logs:/opt/cryptic/logs
49-
- cryptic-ca-data:/opt/cryptic/data/ca
50-
- cryptic-data:/opt/cryptic/data
51-
5237
# Restart policy
5338
restart: unless-stopped
5439

docs/DOCKER.md

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,42 +63,37 @@ docker run -it --rm --name cryptic-client \
6363
# Get the latest Server docker image
6464
docker pull ghcr.io/etnt/cryptic:latest
6565

66-
# Create a directory for storing the Cryptic server data
66+
# Create a directory for storing all Cryptic server data
6767
mkdir ~/.cryptic_server
6868
cd ~/.cryptic_server
6969

7070
# Setup the server certificates (one-time step)
7171
# Creates ca.crt, ca.key, server.crt, server.key in ./priv/ssl/
72-
mkdir -p priv/ssl priv/ca/bootstrap
7372
docker run -it --rm \
74-
-v $(pwd)/priv/ssl:/opt/cryptic/certs \
75-
ghcr.io/etnt/cryptic:latest sh -c 'DIR=/opt/cryptic/certs generate-mtls-certs.sh'
73+
-v $(pwd):/opt/cryptic/server_data \
74+
-e CRYPTIC_SERVER_DIR=/opt/cryptic/server_data \
75+
ghcr.io/etnt/cryptic:latest \
76+
sh -c 'DIR=${CRYPTIC_SERVER_DIR}/priv/ssl generate-mtls-certs.sh'
7677

7778
# Bootstrap the first admin user BEFORE starting the server
7879
# Step 1: Generate a GPG key on your host (if you don't have one)
7980
# No passphrase needed - it's only used for signing CSRs
8081
gpg --quick-generate-key 'alice <alice@cryptic.local>' rsa4096
8182

8283
# Step 2: Export your GPG public key to the bootstrap directory
84+
mkdir -p priv/ca/bootstrap
8385
gpg --armor --export alice@cryptic.local > priv/ca/bootstrap/alice.gpg
8486

85-
# Run the server (requires certificates in ./priv/ssl/)
86-
# Here we show how to expose port 9898 instead of the default (8443)
87+
# Run the server with single directory mount
88+
# All server data (priv/, logs/, data/) will be in ~/.cryptic_server/
89+
# Note: Mount to /opt/cryptic/server_data (not /opt/cryptic which contains the Erlang release)
8790
# For debug log output, add: -e CRYPTIC_DEBUG=true
88-
mkdir -p logs data/ca/bootstrap
8991
docker run -d \
9092
--name cryptic-server \
91-
-p 9898:8443 \
92-
-v $(pwd)/priv/ssl:/opt/cryptic/lib/cryptic-1.0.0/priv/ssl:ro \
93-
-v $(pwd)/priv/ca:/opt/cryptic/lib/cryptic-1.0.0/priv/ca:ro \
94-
-v $(pwd)/logs:/opt/cryptic/logs \
95-
-v $(pwd)/data:/opt/cryptic/data \
93+
-p 8443:8443 \
94+
-v $(pwd):/opt/cryptic/server_data:rw \
9695
-e CRYPTIC_SERVER_HOST=0.0.0.0 \
97-
-e CRYPTIC_SERVER_CERT=/opt/cryptic/lib/cryptic-1.0.0/priv/ssl/server.crt \
98-
-e CRYPTIC_SERVER_KEY=/opt/cryptic/lib/cryptic-1.0.0/priv/ssl/server.key \
99-
-e CRYPTIC_CA_CERT=/opt/cryptic/lib/cryptic-1.0.0/priv/ssl/ca.crt \
100-
-e CRYPTIC_CA_CERT_FILE=/opt/cryptic/lib/cryptic-1.0.0/priv/ssl/ca.crt \
101-
-e CRYPTIC_CA_KEY_FILE=/opt/cryptic/lib/cryptic-1.0.0/priv/ssl/ca.key \
96+
-e CRYPTIC_SERVER_DIR=/opt/cryptic/server_data \
10297
ghcr.io/etnt/cryptic:latest
10398

10499
# Check server logs

scripts/docker-entrypoint.sh

Lines changed: 48 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,66 @@
11
#!/bin/sh
22
set -e
33

4-
# Ensure required directories exist (in case volumes are mounted empty)
5-
mkdir -p /opt/cryptic/data/ca
6-
mkdir -p /opt/cryptic/logs
7-
mkdir -p /opt/cryptic/certs
8-
9-
# Fix ownership of mounted volumes (they may be created as root)
10-
chown -R cryptic:cryptic /opt/cryptic/data
11-
chown -R cryptic:cryptic /opt/cryptic/logs
12-
13-
# Note: Certificate files at /opt/cryptic/certs/ are bind-mounted as read-only
14-
# Ensure they have correct permissions on the HOST before mounting:
15-
# chmod 644 priv/ssl/*.{crt,key}
4+
# Set default CRYPTIC_SERVER_DIR to /opt/cryptic/server_data if not already set
5+
# This will contain all server data: priv/, logs/, and data/
6+
# Note: /opt/cryptic contains the Erlang release (bin/, lib/, etc.)
7+
if [ -z "${CRYPTIC_SERVER_DIR}" ]; then
8+
CRYPTIC_SERVER_DIR="/opt/cryptic/server_data"
9+
export CRYPTIC_SERVER_DIR
10+
fi
1611

17-
# Ensure priv/ssl directory exists for CA cert/key mounts
18-
# The release path includes version, but we'll create under all lib versions
19-
for libdir in /opt/cryptic/lib/cryptic-*/; do
20-
if [ -d "$libdir" ]; then
21-
mkdir -p "${libdir}priv/ssl"
22-
# Only chown the directory, not the read-only mounted files inside
23-
chown cryptic:cryptic "${libdir}priv/ssl" 2>/dev/null || true
24-
chown cryptic:cryptic "${libdir}priv" 2>/dev/null || true
25-
fi
26-
done
12+
# Ensure all required directories exist under CRYPTIC_SERVER_DIR
13+
mkdir -p "${CRYPTIC_SERVER_DIR}/data/ca"
14+
mkdir -p "${CRYPTIC_SERVER_DIR}/logs"
15+
mkdir -p "${CRYPTIC_SERVER_DIR}/priv/ssl"
16+
mkdir -p "${CRYPTIC_SERVER_DIR}/priv/ca/bootstrap"
2717

28-
# Also create priv/ssl in the working directory since config uses relative paths
29-
mkdir -p /opt/cryptic/priv/ssl
18+
echo "INFO: CRYPTIC_SERVER_DIR is set to ${CRYPTIC_SERVER_DIR}"
19+
echo "INFO: Expected structure:"
20+
echo " ${CRYPTIC_SERVER_DIR}/priv/ssl/ca.crt - CA certificate"
21+
echo " ${CRYPTIC_SERVER_DIR}/priv/ssl/ca.key - CA private key"
22+
echo " ${CRYPTIC_SERVER_DIR}/priv/ssl/server.crt - Server certificate"
23+
echo " ${CRYPTIC_SERVER_DIR}/priv/ssl/server.key - Server private key"
24+
echo " ${CRYPTIC_SERVER_DIR}/priv/ca/bootstrap/*.gpg - Bootstrap GPG keys"
25+
echo " ${CRYPTIC_SERVER_DIR}/data/ - Database files"
26+
echo " ${CRYPTIC_SERVER_DIR}/logs/ - Log files"
3027

31-
# Copy CA cert/key files from mounted certs directory to priv/ssl
32-
# The application config uses relative path "priv/ssl/ca.crt"
33-
if [ -f "/opt/cryptic/certs/ca.crt" ] && [ -f "/opt/cryptic/certs/ca.key" ]; then
34-
echo "DEBUG: Copying CA files from /opt/cryptic/certs/ to priv/ssl/"
35-
cp /opt/cryptic/certs/ca.crt /opt/cryptic/priv/ssl/
36-
cp /opt/cryptic/certs/ca.key /opt/cryptic/priv/ssl/
37-
chown cryptic:cryptic /opt/cryptic/priv/ssl/*
28+
# Check if CA certificates exist, generate if missing
29+
if [ ! -f "${CRYPTIC_SERVER_DIR}/priv/ssl/ca.crt" ] || [ ! -f "${CRYPTIC_SERVER_DIR}/priv/ssl/ca.key" ]; then
30+
echo "INFO: CA certificates not found, generating..."
31+
DIR="${CRYPTIC_SERVER_DIR}/priv/ssl" /usr/local/bin/generate-mtls-certs.sh
3832
else
39-
echo "WARNING: CA certificate files not found in /opt/cryptic/certs/"
40-
echo " Expected: /opt/cryptic/certs/ca.crt and /opt/cryptic/certs/ca.key"
41-
echo " Run certificate generation first or mount the files."
33+
echo "INFO: CA certificates found"
4234
fi
4335

44-
# Debug: Show directory permissions
45-
echo "DEBUG: /opt/cryptic/data permissions:"
46-
ls -ld /opt/cryptic/data
47-
echo "DEBUG: /opt/cryptic/data/ca permissions:"
48-
ls -ld /opt/cryptic/data/ca
49-
echo "DEBUG: /opt/cryptic/data/ca contents:"
50-
ls -la /opt/cryptic/data/ca/ || echo "Directory empty or not readable"
51-
echo "DEBUG: Environment CRYPTIC_CA_DB_FILE=${CRYPTIC_CA_DB_FILE}"
52-
echo "DEBUG: Environment CRYPTIC_DEBUG=${CRYPTIC_DEBUG}"
53-
echo "DEBUG: Test file creation:"
54-
su-exec cryptic touch /opt/cryptic/data/ca/test.txt && echo "SUCCESS" || echo "FAILED"
36+
# Fix ownership of mounted volumes (they may be created as root)
37+
chown -R cryptic:cryptic "${CRYPTIC_SERVER_DIR}/data" 2>/dev/null || true
38+
chown -R cryptic:cryptic "${CRYPTIC_SERVER_DIR}/logs" 2>/dev/null || true
39+
40+
# Debug: Show directory structure and permissions
41+
if [ "${CRYPTIC_DEBUG}" = "true" ]; then
42+
echo "DEBUG: ${CRYPTIC_SERVER_DIR}/data permissions:"
43+
ls -ld "${CRYPTIC_SERVER_DIR}/data"
44+
echo "DEBUG: ${CRYPTIC_SERVER_DIR}/data/ca permissions:"
45+
ls -ld "${CRYPTIC_SERVER_DIR}/data/ca"
46+
echo "DEBUG: ${CRYPTIC_SERVER_DIR}/data/ca contents:"
47+
ls -la "${CRYPTIC_SERVER_DIR}/data/ca/" || echo "Directory empty or not readable"
48+
49+
echo "DEBUG: ${CRYPTIC_SERVER_DIR}/priv contents:"
50+
ls -laR "${CRYPTIC_SERVER_DIR}/priv" 2>/dev/null || echo "Directory not readable"
51+
52+
echo "DEBUG: Environment variables:"
53+
echo " CRYPTIC_SERVER_DIR=${CRYPTIC_SERVER_DIR}"
54+
echo " CRYPTIC_CA_DB_FILE=${CRYPTIC_CA_DB_FILE}"
55+
echo " CRYPTIC_DEBUG=${CRYPTIC_DEBUG}"
56+
fi
5557

5658
# Switch to cryptic user and execute the main command
5759
# Explicitly preserve environment variables that the application needs
5860
exec su-exec cryptic env \
5961
CRYPTIC_SERVER_HOST="${CRYPTIC_SERVER_HOST}" \
6062
CRYPTIC_SERVER_PORT="${CRYPTIC_SERVER_PORT}" \
61-
CRYPTIC_SERVER_CERT="${CRYPTIC_SERVER_CERT}" \
62-
CRYPTIC_SERVER_KEY="${CRYPTIC_SERVER_KEY}" \
63-
CRYPTIC_CA_CERT="${CRYPTIC_CA_CERT}" \
63+
CRYPTIC_SERVER_DIR="${CRYPTIC_SERVER_DIR}" \
6464
CRYPTIC_CA_DB_FILE="${CRYPTIC_CA_DB_FILE}" \
6565
CRYPTIC_EVENT_HANDLERS="${CRYPTIC_EVENT_HANDLERS}" \
6666
CRYPTIC_DEBUG="${CRYPTIC_DEBUG}" \

src/cryptic.app.src

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@
2727
{websocket_mtls_port, 8443},
2828

2929
% Poll interval (seconds)
30-
31-
% FIXME: adjust as needed
3230
{poll_interval, 2},
3331

3432
% Enable debug output
@@ -42,6 +40,9 @@
4240
{server_cert_file, "priv/ssl/server.crt"},
4341
{server_key_file, "priv/ssl/server.key"},
4442

43+
%% Directory holding GPG bootstrap keys
44+
{bootstrap_dir, "priv/ca/bootstrap"},
45+
4546
%% Database Configuration
4647
{ca_db_file, "data/ca/cryptic_ca.db"},
4748
{storage_backend, esqlite},
@@ -51,10 +52,6 @@
5152
{cert_max_lifetime_days, 30},
5253
{cert_renewal_threshold, 0.5},
5354

54-
%% Invite Policy
55-
{invite_default_ttl_hours, 24},
56-
{invite_max_ttl_hours, 72},
57-
5855
%% Audit Configuration
5956
{audit_enabled, true},
6057
{audit_retention_days, 90}

src/cryptic_ca_app.erl

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ init_ca() ->
9696
%% Get database file path from config
9797
DbFile = get_ca_db_file(),
9898

99-
?info("Initializing CA database: ~s", [DbFile]),
99+
?info("Initializing CA database at: ~p", [DbFile]),
100100

101101
%% Ensure directory exists
102102
DbDir = filename:dirname(DbFile),
@@ -142,18 +142,27 @@ get_ca_db() ->
142142
%% @doc Get CA database file path from configuration.
143143
-spec get_ca_db_file() -> string().
144144
get_ca_db_file() ->
145-
%% Try environment variable first
146-
case os:getenv("CRYPTIC_CA_DB_FILE") of
147-
false ->
148-
%% Try application config
149-
case application:get_env(cryptic, ca_db_file) of
150-
{ok, Path} ->
151-
Path;
152-
undefined ->
153-
%% Use default path
154-
PrivDir = code:priv_dir(cryptic),
155-
filename:join([PrivDir, "ca", "cryptic_ca.db"])
145+
%% Debug: Check application environment
146+
AppEnvResult = application:get_env(cryptic, ca_db_file),
147+
?info("DEBUG: application:get_env(cryptic, ca_db_file) = ~p", [AppEnvResult]),
148+
149+
EnvVar = os:getenv("CRYPTIC_CA_DB_FILE"),
150+
?info("DEBUG: os:getenv(CRYPTIC_CA_DB_FILE) = ~p", [EnvVar]),
151+
152+
ServerDir = os:getenv("CRYPTIC_SERVER_DIR"),
153+
?info("DEBUG: os:getenv(CRYPTIC_SERVER_DIR) = ~p", [ServerDir]),
154+
155+
case cryptic_lib:get_server_file("CRYPTIC_CA_DB_FILE", ca_db_file) of
156+
undefined ->
157+
?info("DEBUG: get_server_file returned undefined, using fallback", []),
158+
%% Fallback to default path under CRYPTIC_SERVER_DIR
159+
case ServerDir of
160+
false ->
161+
"data/ca/cryptic_ca.db";
162+
_ ->
163+
filename:join([ServerDir, "data/ca/cryptic_ca.db"])
156164
end;
157-
EnvPath ->
158-
EnvPath
165+
DbFile ->
166+
?info("DEBUG: get_server_file returned: ~p", [DbFile]),
167+
DbFile
159168
end.

src/cryptic_ca_bootstrap.erl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@
2525
%% @doc Get the bootstrap directory path
2626
-spec get_bootstrap_dir() -> file:filename().
2727
get_bootstrap_dir() ->
28-
PrivDir = code:priv_dir(cryptic),
29-
filename:join([PrivDir, "ca", "bootstrap"]).
28+
cryptic_lib:get_server_file(
29+
"CRYPTIC_BOOTSTRAP_DIR",
30+
bootstrap_dir
31+
).
3032

3133
%% @doc Load all GPG bootstrap registrations from filesystem
3234
%%

src/cryptic_ca_init.erl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,18 @@ get_db_ref() ->
4242

4343
%% @doc Initialize the CA database.
4444
init([]) ->
45+
LogDir =
46+
case os:getenv("CRYPTIC_SERVER_DIR") of
47+
false ->
48+
"logs";
49+
ServerDir ->
50+
filename:join([ServerDir, "logs"])
51+
end,
52+
4553
%% Set up event handlers for logging (event manager is already started by supervisor)
4654
cryptic_event_manager:setup_event_handlers(#{
4755
log_type => server,
48-
log_dir => "logs"
56+
log_dir => LogDir
4957
}),
5058

5159
?info("CA initializer starting...", []),

0 commit comments

Comments
 (0)