mirror of
https://github.com/mudler/LocalAI.git
synced 2026-07-30 09:57:57 -04:00
fix(distributed): setup script (#10551)
Assisted-by: OpenCode:GPT-5.5 [Read] [Edit] Signed-off-by: Nicholas Ciechanowski <nicholas@ciech.anow.ski>
This commit is contained in:
committed by
GitHub
parent
e9056399a7
commit
8aa8e0fac0
@@ -1,20 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
# Generate NATS account + service user JWTs for LocalAI distributed mode.
|
||||
# Generate NATS JWT authentication material and server configuration
|
||||
# for LocalAI distributed mode.
|
||||
#
|
||||
# Requires: nsc (https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nsc)
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/nats-auth-setup.sh
|
||||
# Outputs:
|
||||
# ./nats-keys/localai-nats.env
|
||||
# ./nats-keys/localai-frontend.creds
|
||||
# ./nats-keys/nats-auth.conf
|
||||
# ./nats-keys/nats-server.conf
|
||||
#
|
||||
# Outputs operator/account seeds and a service user JWT suitable for:
|
||||
# LOCALAI_NATS_ACCOUNT_SEED
|
||||
# LOCALAI_NATS_SERVICE_JWT
|
||||
# Environment overrides:
|
||||
# NATS_OPERATOR_NAME
|
||||
# NATS_ACCOUNT_NAME
|
||||
# NATS_SERVICE_USER
|
||||
# NATS_KEYS_DIR
|
||||
|
||||
#
|
||||
# Per-node worker JWTs are minted automatically by the frontend at registration
|
||||
# when LOCALAI_NATS_ACCOUNT_SEED is set.
|
||||
# LocalAI workers receive their own JWT and user seed when registering
|
||||
# with the frontend.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Ensure newly created secret files are private by default.
|
||||
umask 077
|
||||
|
||||
if ! command -v nsc >/dev/null 2>&1; then
|
||||
echo "nsc is required. Install from https://github.com/nats-io/nsc/releases" >&2
|
||||
exit 1
|
||||
@@ -22,28 +32,189 @@ fi
|
||||
|
||||
OPERATOR="${NATS_OPERATOR_NAME:-localai-operator}"
|
||||
ACCOUNT="${NATS_ACCOUNT_NAME:-localai}"
|
||||
SYSTEM_ACCOUNT="${NATS_SYSTEM_ACCOUNT_NAME:-SYS}"
|
||||
SERVICE_USER="${NATS_SERVICE_USER:-localai-frontend}"
|
||||
OUTPUT_DIR="${NATS_KEYS_DIR:-./nats-keys}"
|
||||
|
||||
nsc add operator -n "$OPERATOR" --generate-signing-key
|
||||
nsc add account -n "$ACCOUNT"
|
||||
nsc add user -n "$SERVICE_USER" --account "$ACCOUNT"
|
||||
CREDS_FILE="$OUTPUT_DIR/${SERVICE_USER}.creds"
|
||||
ENV_FILE="$OUTPUT_DIR/localai-nats.env"
|
||||
AUTH_CONFIG_FILE="$OUTPUT_DIR/nats-auth.conf"
|
||||
SERVER_CONFIG_FILE="$OUTPUT_DIR/nats-server.conf"
|
||||
|
||||
# Broad publish for frontend control plane (tighten with custom claims in production).
|
||||
nsc edit user -n "$SERVICE_USER" --account "$ACCOUNT" \
|
||||
--allow-pub "nodes.>,gallery.>,agent.>,jobs.>,mcp.>,cache.>,prefixcache.>,finetune.>" \
|
||||
--allow-sub "nodes.>,gallery.>,agent.>,jobs.>,mcp.>,cache.>,prefixcache.>,_INBOX.>"
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
KEYS_DIR="${NATS_KEYS_DIR:-./nats-keys}"
|
||||
mkdir -p "$KEYS_DIR"
|
||||
nsc generate creds -a "$ACCOUNT" -n "$SERVICE_USER" -o "$KEYS_DIR"
|
||||
echo "Configuring NATS operator: $OPERATOR"
|
||||
|
||||
ACCOUNT_SEED=$(nsc describe account "$ACCOUNT" -o json | jq -r '.nats.private_key')
|
||||
SERVICE_JWT=$(cat "$KEYS_DIR/${ACCOUNT}/${SERVICE_USER}.jwt" 2>/dev/null || cat "$KEYS_DIR/${SERVICE_USER}.jwt")
|
||||
# Create the operator if it does not exist, otherwise select it.
|
||||
if nsc select operator "$OPERATOR" >/dev/null 2>&1; then
|
||||
echo "[ OK ] using existing operator '$OPERATOR'"
|
||||
else
|
||||
nsc add operator \
|
||||
-n "$OPERATOR" \
|
||||
--generate-signing-key
|
||||
|
||||
echo ""
|
||||
echo "=== LocalAI NATS auth material ==="
|
||||
echo "LOCALAI_NATS_ACCOUNT_SEED=${ACCOUNT_SEED}"
|
||||
echo "LOCALAI_NATS_SERVICE_JWT=${SERVICE_JWT}"
|
||||
echo ""
|
||||
echo "Configure the NATS server with the generated operator/account JWTs under $KEYS_DIR"
|
||||
echo "and set LOCALAI_NATS_REQUIRE_AUTH=true on frontends and workers in production."
|
||||
nsc select operator "$OPERATOR" >/dev/null
|
||||
fi
|
||||
|
||||
# Create and assign the NATS system account.
|
||||
if nsc describe account \
|
||||
-n "$SYSTEM_ACCOUNT" >/dev/null 2>&1; then
|
||||
echo "[ OK ] using existing system account '$SYSTEM_ACCOUNT'"
|
||||
else
|
||||
nsc add account -n "$SYSTEM_ACCOUNT"
|
||||
fi
|
||||
|
||||
nsc edit operator \
|
||||
--system-account "$SYSTEM_ACCOUNT"
|
||||
|
||||
# Create the LocalAI account if it does not exist.
|
||||
if nsc describe account -n "$ACCOUNT" >/dev/null 2>&1; then
|
||||
echo "[ OK ] using existing account '$ACCOUNT'"
|
||||
else
|
||||
nsc add account -n "$ACCOUNT"
|
||||
fi
|
||||
|
||||
nsc select account "$ACCOUNT" >/dev/null
|
||||
|
||||
# Create the frontend service user if it does not exist.
|
||||
if nsc describe user \
|
||||
-n "$SERVICE_USER" \
|
||||
--account "$ACCOUNT" >/dev/null 2>&1; then
|
||||
echo "[ OK ] using existing user '$SERVICE_USER'"
|
||||
else
|
||||
nsc add user \
|
||||
-n "$SERVICE_USER" \
|
||||
--account "$ACCOUNT"
|
||||
fi
|
||||
|
||||
# Frontend control-plane permissions.
|
||||
nsc edit user \
|
||||
-n "$SERVICE_USER" \
|
||||
--account "$ACCOUNT" \
|
||||
--allow-pub "nodes.>,gallery.>,agent.>,staging.>,state.>,jobs.>,mcp.>,cache.>,prefixcache.>,finetune.>" \
|
||||
--allow-sub "nodes.>,gallery.>,agent.>,staging.>,state.>,jobs.>,mcp.>,cache.>,prefixcache.>,_INBOX.>"
|
||||
|
||||
# Generate a credentials file containing the frontend user JWT and seed.
|
||||
rm -f "$CREDS_FILE"
|
||||
|
||||
nsc generate creds \
|
||||
-a "$ACCOUNT" \
|
||||
-n "$SERVICE_USER" \
|
||||
-o "$CREDS_FILE"
|
||||
|
||||
# Extract the frontend JWT from the credentials file.
|
||||
SERVICE_JWT="$(
|
||||
awk '
|
||||
/BEGIN NATS USER JWT/ {
|
||||
capture = 1
|
||||
next
|
||||
}
|
||||
/END NATS USER JWT/ {
|
||||
capture = 0
|
||||
}
|
||||
capture
|
||||
' "$CREDS_FILE" | tr -d '\r\n'
|
||||
)"
|
||||
|
||||
# Extract the frontend user seed from the credentials file.
|
||||
SERVICE_SEED="$(
|
||||
awk '
|
||||
/BEGIN USER NKEY SEED/ {
|
||||
capture = 1
|
||||
next
|
||||
}
|
||||
/END USER NKEY SEED/ {
|
||||
capture = 0
|
||||
}
|
||||
capture
|
||||
' "$CREDS_FILE" | tr -d '\r\n'
|
||||
)"
|
||||
|
||||
# Retrieve the seed belonging to this exact account rather than taking
|
||||
# the first account key found in the keystore.
|
||||
ACCOUNT_SEED="$(
|
||||
nsc list keys \
|
||||
--account "$ACCOUNT" \
|
||||
--accounts \
|
||||
--show-seeds |
|
||||
awk -F '|' -v expected="$ACCOUNT" '
|
||||
function trim(value) {
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
|
||||
return value
|
||||
}
|
||||
|
||||
NF >= 3 {
|
||||
entity = trim($2)
|
||||
seed = trim($3)
|
||||
|
||||
if (entity == expected && seed ~ /^SA[A-Z0-9]+$/) {
|
||||
print seed
|
||||
exit
|
||||
}
|
||||
}
|
||||
'
|
||||
)"
|
||||
|
||||
# Validate all extracted values before writing output files.
|
||||
if [[ ! "$ACCOUNT_SEED" =~ ^SA[A-Z0-9]+$ ]]; then
|
||||
echo "Unable to extract the account seed for '$ACCOUNT'." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! "$SERVICE_JWT" =~ ^eyJ ]]; then
|
||||
echo "Unable to extract the service JWT from '$CREDS_FILE'." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! "$SERVICE_SEED" =~ ^SU[A-Z0-9]+$ ]]; then
|
||||
echo "Unable to extract the service seed from '$CREDS_FILE'." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Generate the trusted operator and memory resolver configuration.
|
||||
# This contains public operator/account JWT claims, not the private seeds.
|
||||
nsc generate config \
|
||||
--mem-resolver \
|
||||
--config-file "$AUTH_CONFIG_FILE" \
|
||||
--force
|
||||
|
||||
# Generate the primary NATS server configuration.
|
||||
# The include path matches the Docker Compose mounts shown below.
|
||||
cat >"$SERVER_CONFIG_FILE" <<'NATS_CONFIG'
|
||||
server_name: localai-nats
|
||||
port: 4222
|
||||
http: 8222
|
||||
|
||||
jetstream {
|
||||
store_dir: /data/jetstream
|
||||
}
|
||||
|
||||
include nats-auth.conf
|
||||
NATS_CONFIG
|
||||
|
||||
# Generate the environment file consumed by the LocalAI frontend.
|
||||
cat >"$ENV_FILE" <<EOF
|
||||
LOCALAI_NATS_ACCOUNT_SEED=$ACCOUNT_SEED
|
||||
LOCALAI_NATS_SERVICE_JWT=$SERVICE_JWT
|
||||
LOCALAI_NATS_SERVICE_SEED=$SERVICE_SEED
|
||||
EOF
|
||||
|
||||
# The environment and credentials files contain private seeds.
|
||||
chmod 600 "$CREDS_FILE" "$ENV_FILE"
|
||||
|
||||
# These contain server configuration and public JWT claims.
|
||||
chmod 644 "$AUTH_CONFIG_FILE" "$SERVER_CONFIG_FILE"
|
||||
|
||||
echo
|
||||
echo "=== LocalAI NATS JWT setup complete ==="
|
||||
echo
|
||||
echo "LocalAI environment: $ENV_FILE"
|
||||
echo "Service credentials: $CREDS_FILE"
|
||||
echo "NATS server config: $SERVER_CONFIG_FILE"
|
||||
echo "NATS auth config: $AUTH_CONFIG_FILE"
|
||||
echo
|
||||
echo "Keep '$ENV_FILE' and '$CREDS_FILE' secret."
|
||||
echo "Do not commit them to source control."
|
||||
echo
|
||||
echo "=== LocalAI NATS environment ==="
|
||||
cat "$ENV_FILE"
|
||||
|
||||
Reference in New Issue
Block a user