Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion custom-domain/dstack-ingress/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ configs:
- `CERTBOT_EMAIL`: Your email address used in Let's Encrypt certificate requests
- `TARGET_ENDPOINT`: The plain HTTP endpoint of your dstack application (for single domain mode)
- `SET_CAA`: Set to `true` to enable CAA record setup
- `CLIENT_MAX_BODY_SIZE`: Optional value for nginx `client_max_body_size` (e.g. `50m`) in single-domain mode
- `CLIENT_MAX_BODY_SIZE`: Optional value for nginx `client_max_body_size` (numeric with optional `k|m|g` suffix, e.g. `50m`) in single-domain mode

**Backward Compatibility:**

Expand Down
19 changes: 19 additions & 0 deletions custom-domain/dstack-ingress/scripts/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,27 @@

set -e

source "/scripts/functions.sh"

PORT=${PORT:-443}
TXT_PREFIX=${TXT_PREFIX:-"_dstack-app-address"}

if ! PORT=$(sanitize_port "$PORT"); then
exit 1
fi
if ! DOMAIN=$(sanitize_domain "$DOMAIN"); then
exit 1
fi
if ! TARGET_ENDPOINT=$(sanitize_target_endpoint "$TARGET_ENDPOINT"); then
exit 1
fi
if ! CLIENT_MAX_BODY_SIZE=$(sanitize_client_max_body_size "$CLIENT_MAX_BODY_SIZE"); then
exit 1
fi
if ! TXT_PREFIX=$(sanitize_dns_label "$TXT_PREFIX"); then
exit 1
fi

PROXY_CMD="proxy"
if [[ "${TARGET_ENDPOINT}" == grpc://* ]]; then
PROXY_CMD="grpc"
Expand Down
70 changes: 70 additions & 0 deletions custom-domain/dstack-ingress/scripts/functions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/bin/bash

# Sanitizer helpers shared across scripts. Each function echoes the sanitized
# value on success; on failure it writes an error to stderr and returns non-zero.

sanitize_port() {
local candidate="$1"
if [[ "$candidate" =~ ^[0-9]+$ ]] && (( candidate >= 1 && candidate <= 65535 )); then
echo "$candidate"
else
echo "Error: Invalid PORT value: $candidate" >&2
return 1
fi
}

sanitize_domain() {
local candidate="$1"
if [ -z "$candidate" ]; then
echo ""
return 0
fi
if [[ "$candidate" =~ ^(\*\.)?[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$ ]]; then
echo "$candidate"
else
echo "Error: Invalid DOMAIN value: $candidate" >&2
return 1
fi
}

sanitize_target_endpoint() {
local candidate="$1"
if [ -z "$candidate" ]; then
echo ""
return 0
fi
if [[ "$candidate" =~ ^(grpc|https?)://[A-Za-z0-9._-]+(:[0-9]{1,5})?(/[A-Za-z0-9._~:/?&=%-]*)?$ ]]; then
echo "$candidate"
else
echo "Error: Invalid TARGET_ENDPOINT value: $candidate" >&2
return 1
fi
}

sanitize_client_max_body_size() {
local candidate="$1"
if [ -z "$candidate" ]; then
echo ""
return 0
fi
if [[ "$candidate" =~ ^[0-9]+[kKmMgG]?$ ]]; then
echo "$candidate"
else
echo "Warning: Ignoring invalid CLIENT_MAX_BODY_SIZE value: $candidate" >&2
echo ""
fi
}

sanitize_dns_label() {
local candidate="$1"
if [ -z "$candidate" ]; then
echo "Error: TXT_PREFIX cannot be empty" >&2
return 1
fi
if [[ "$candidate" =~ ^[A-Za-z0-9_-]+$ ]]; then
echo "$candidate"
else
echo "Error: Invalid TXT_PREFIX value: $candidate" >&2
return 1
fi
}
70 changes: 70 additions & 0 deletions custom-domain/dstack-ingress/scripts/tests/test_sanitizers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/bin/bash

set -euo pipefail

THIS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCRIPTS_DIR="$(cd "${THIS_DIR}/.." && pwd)"

# shellcheck source=../functions.sh
source "${SCRIPTS_DIR}/functions.sh"

failures=0

assert_equal() {
local actual="$1"
local expected="$2"
local msg="$3"
if [[ "$actual" != "$expected" ]]; then
echo "FAIL: ${msg} (expected '${expected}', got '${actual}')" >&2
failures=$((failures + 1))
else
echo "PASS: ${msg}"
fi
}

assert_fails() {
local msg="$1"
shift
local output_file
output_file="$(mktemp)"
if "$@" >"$output_file" 2>&1; then
echo "FAIL: ${msg} (expected failure)" >&2
cat "$output_file" >&2
failures=$((failures + 1))
else
cat "$output_file"
echo "PASS: ${msg}"
fi
rm -f "$output_file"
}

# Successful cases
assert_equal "$(sanitize_port 8080)" "8080" "sanitize_port accepts numeric port"
assert_equal "$(sanitize_domain example.com)" "example.com" "sanitize_domain accepts fqdn"
assert_equal "$(sanitize_domain '*.example.com')" "*.example.com" "sanitize_domain accepts wildcard"
assert_equal "$(sanitize_target_endpoint http://service:80/path)" "http://service:80/path" "sanitize_target_endpoint accepts http"
assert_equal "$(sanitize_target_endpoint grpc://svc:50051)" "grpc://svc:50051" "sanitize_target_endpoint accepts grpc"
assert_equal "$(sanitize_client_max_body_size 50m)" "50m" "sanitize_client_max_body_size accepts suffix"
assert_equal "$(sanitize_dns_label test_label)" "test_label" "sanitize_dns_label accepts lowercase"
assert_equal "$(sanitize_dns_label test-label)" "test-label" "sanitize_dns_label accepts hyphen"

# Failing cases
assert_fails "sanitize_port rejects non-numeric" sanitize_port abc
assert_fails "sanitize_domain rejects invalid domain" sanitize_domain bad_domain
assert_fails "sanitize_target_endpoint rejects malformed URL" sanitize_target_endpoint "http:///broken"
warning_output="$(sanitize_client_max_body_size "50mb" 2>&1 || true)"
if [[ "$warning_output" == "Warning: Ignoring invalid CLIENT_MAX_BODY_SIZE value: 50mb" ]]; then
echo "PASS: sanitize_client_max_body_size warns and returns empty"
else
echo "FAIL: sanitize_client_max_body_size warning unexpected"
printf '%s\n' "$warning_output"
failures=$((failures + 1))
fi
assert_fails "sanitize_dns_label rejects invalid characters" sanitize_dns_label "bad*label"

if [[ $failures -eq 0 ]]; then
echo "All sanitizer tests passed"
else
echo "$failures sanitizer tests failed" >&2
exit 1
fi
Loading