240 lines
6.5 KiB
Bash
Executable File
240 lines
6.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ENV_FILE=".env"
|
|
SERVICE_ACCOUNT_NAME="FileShare_ServiceAccount"
|
|
|
|
BOOTSTRAP_ENV_FILE=""
|
|
ORIGINAL_ENV_BACKUP=""
|
|
BOOTSTRAP_ENV_WRITTEN=0
|
|
FINAL_ENV_WRITTEN=0
|
|
ENV_PREEXISTED=0
|
|
|
|
cleanup() {
|
|
if [[ -n "$BOOTSTRAP_ENV_FILE" && -f "$BOOTSTRAP_ENV_FILE" ]]; then
|
|
rm -f "$BOOTSTRAP_ENV_FILE"
|
|
fi
|
|
|
|
if [[ "$BOOTSTRAP_ENV_WRITTEN" -eq 1 && "$FINAL_ENV_WRITTEN" -eq 0 ]]; then
|
|
if [[ "$ENV_PREEXISTED" -eq 1 && -n "$ORIGINAL_ENV_BACKUP" && -f "$ORIGINAL_ENV_BACKUP" ]]; then
|
|
cp "$ORIGINAL_ENV_BACKUP" "$ENV_FILE"
|
|
chmod 600 "$ENV_FILE"
|
|
printf "Restored original %s after setup failure.\n" "$ENV_FILE" >&2
|
|
else
|
|
rm -f "$ENV_FILE"
|
|
printf "Removed temporary %s after setup failure.\n" "$ENV_FILE" >&2
|
|
fi
|
|
fi
|
|
|
|
if [[ -n "$ORIGINAL_ENV_BACKUP" && -f "$ORIGINAL_ENV_BACKUP" ]]; then
|
|
rm -f "$ORIGINAL_ENV_BACKUP"
|
|
fi
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
sanitize_netbios_name() {
|
|
local raw_name="$1"
|
|
local upper_name="${raw_name^^}"
|
|
local cleaned_name
|
|
cleaned_name="$(printf '%s' "$upper_name" | tr -cd 'A-Z0-9')"
|
|
if [[ -z "$cleaned_name" ]]; then
|
|
cleaned_name="ADSAMBAFSRV"
|
|
fi
|
|
printf '%s' "${cleaned_name:0:15}"
|
|
}
|
|
|
|
sanitize_sam_account_name() {
|
|
local raw_name="$1"
|
|
local cleaned_name
|
|
cleaned_name="$(printf '%s' "$raw_name" | tr -cd 'A-Za-z0-9._-')"
|
|
if [[ "$cleaned_name" == "FileShare_ServiceAccount" ]]; then
|
|
printf '%s' "FileShare_ServiceAcc"
|
|
return
|
|
fi
|
|
if [[ -z "$cleaned_name" ]]; then
|
|
cleaned_name="FileShareSvc"
|
|
fi
|
|
printf '%s' "${cleaned_name:0:20}"
|
|
}
|
|
|
|
prompt_value() {
|
|
local var_name="$1"
|
|
local prompt_text="$2"
|
|
local is_secret="${3:-false}"
|
|
local value=""
|
|
|
|
while [[ -z "$value" ]]; do
|
|
if [[ "$is_secret" == "true" ]]; then
|
|
read -r -s -p "$prompt_text: " value
|
|
printf "\n"
|
|
else
|
|
read -r -p "$prompt_text: " value
|
|
fi
|
|
done
|
|
|
|
printf -v "$var_name" '%s' "$value"
|
|
}
|
|
|
|
write_env_file() {
|
|
local realm=""
|
|
local workgroup=""
|
|
local domain=""
|
|
local admin_user=""
|
|
local admin_password=""
|
|
local domain_users_sid=""
|
|
local domain_admins_sid=""
|
|
local public_group_sid=""
|
|
local samba_hostname="adsambafsrv"
|
|
local netbios_name="ADSAMBAFSRV"
|
|
local service_password=""
|
|
local service_account_sam=""
|
|
local public_group_prompt=""
|
|
local samba_hostname_input=""
|
|
local netbios_name_input=""
|
|
local sanitized_netbios_name=""
|
|
|
|
prompt_value realm "REALM (e.g. EXAMPLE.COM)"
|
|
prompt_value workgroup "WORKGROUP (NetBIOS, e.g. EXAMPLE)"
|
|
prompt_value domain "DOMAIN (AD DNS name or reachable DC FQDN)"
|
|
prompt_value admin_user "Initial admin user (for provisioning service account)"
|
|
prompt_value admin_password "Initial admin password" true
|
|
prompt_value domain_users_sid "DOMAIN_USERS_SID (e.g. ...-513)"
|
|
prompt_value domain_admins_sid "DOMAIN_ADMINS_SID (e.g. ...-512)"
|
|
|
|
public_group_prompt="PUBLIC_GROUP_SID (press Enter to reuse DOMAIN_USERS_SID)"
|
|
read -r -p "${public_group_prompt}: " public_group_sid
|
|
if [[ -z "$public_group_sid" ]]; then
|
|
public_group_sid="$domain_users_sid"
|
|
fi
|
|
|
|
read -r -p "SAMBA_HOSTNAME [adsambafsrv]: " samba_hostname_input
|
|
if [[ -n "${samba_hostname_input:-}" ]]; then
|
|
samba_hostname="$samba_hostname_input"
|
|
fi
|
|
|
|
read -r -p "NETBIOS_NAME [ADSAMBAFSRV]: " netbios_name_input
|
|
if [[ -n "${netbios_name_input:-}" ]]; then
|
|
netbios_name="$netbios_name_input"
|
|
fi
|
|
sanitized_netbios_name="$(sanitize_netbios_name "$netbios_name")"
|
|
if [[ "$sanitized_netbios_name" != "$netbios_name" ]]; then
|
|
printf "Using sanitized NETBIOS_NAME: %s\n" "$sanitized_netbios_name"
|
|
fi
|
|
netbios_name="$sanitized_netbios_name"
|
|
|
|
service_account_sam="$(sanitize_sam_account_name "$SERVICE_ACCOUNT_NAME")"
|
|
if [[ "$service_account_sam" != "$SERVICE_ACCOUNT_NAME" ]]; then
|
|
printf "Using sAMAccountName '%s' (AD limit is 20 chars; requested '%s').\n" "$service_account_sam" "$SERVICE_ACCOUNT_NAME"
|
|
fi
|
|
|
|
service_password="$(python3 - <<'PY'
|
|
import secrets
|
|
import string
|
|
|
|
alphabet = string.ascii_letters + string.digits + '@#%+=:_-'
|
|
print(''.join(secrets.choice(alphabet) for _ in range(48)))
|
|
PY
|
|
)"
|
|
|
|
if [[ -f "$ENV_FILE" ]]; then
|
|
ENV_PREEXISTED=1
|
|
ORIGINAL_ENV_BACKUP="$(mktemp)"
|
|
cp "$ENV_FILE" "$ORIGINAL_ENV_BACKUP"
|
|
chmod 600 "$ORIGINAL_ENV_BACKUP"
|
|
fi
|
|
|
|
BOOTSTRAP_ENV_FILE="$(mktemp)"
|
|
chmod 600 "$BOOTSTRAP_ENV_FILE"
|
|
|
|
cat > "$BOOTSTRAP_ENV_FILE" <<EOF
|
|
REALM=${realm}
|
|
WORKGROUP=${workgroup}
|
|
DOMAIN=${domain}
|
|
JOIN_USER=${admin_user}
|
|
JOIN_PASSWORD=${admin_password}
|
|
SERVICE_ACCOUNT_NAME=${SERVICE_ACCOUNT_NAME}
|
|
SERVICE_ACCOUNT_SAM=${service_account_sam}
|
|
SERVICE_ACCOUNT_PASSWORD=${service_password}
|
|
DOMAIN_USERS_SID=${domain_users_sid}
|
|
DOMAIN_ADMINS_SID=${domain_admins_sid}
|
|
PUBLIC_GROUP_SID=${public_group_sid}
|
|
SAMBA_HOSTNAME=${samba_hostname}
|
|
NETBIOS_NAME=${netbios_name}
|
|
EOF
|
|
|
|
cp "$BOOTSTRAP_ENV_FILE" "$ENV_FILE"
|
|
chmod 600 "$ENV_FILE"
|
|
BOOTSTRAP_ENV_WRITTEN=1
|
|
|
|
printf "Building image...\n"
|
|
docker compose build samba
|
|
|
|
printf "Provisioning service account %s...\n" "$SERVICE_ACCOUNT_NAME"
|
|
docker compose run --rm --entrypoint /bin/bash samba -lc '
|
|
set -euo pipefail
|
|
|
|
cat > /tmp/bootstrap-smb.conf <<EOF
|
|
[global]
|
|
security = ADS
|
|
kerberos method = secrets and keytab
|
|
realm = ${REALM}
|
|
workgroup = ${WORKGROUP}
|
|
netbios name = ${NETBIOS_NAME}
|
|
EOF
|
|
|
|
run_net() {
|
|
net -s /tmp/bootstrap-smb.conf -A /tmp/bootstrap.auth -S "$DOMAIN" "$@"
|
|
}
|
|
|
|
cat > /tmp/bootstrap.auth <<EOF
|
|
username = ${JOIN_USER}
|
|
password = ${JOIN_PASSWORD}
|
|
domain = ${WORKGROUP}
|
|
EOF
|
|
chmod 600 /tmp/bootstrap.auth
|
|
|
|
if run_net ads search "(&(objectClass=user)(sAMAccountName=${SERVICE_ACCOUNT_SAM}))" sAMAccountName | grep -q "^sAMAccountName: ${SERVICE_ACCOUNT_SAM}$"; then
|
|
run_net ads password "${SERVICE_ACCOUNT_SAM}" "${SERVICE_ACCOUNT_PASSWORD}"
|
|
else
|
|
run_net ads user add "${SERVICE_ACCOUNT_SAM}" "${SERVICE_ACCOUNT_PASSWORD}"
|
|
fi
|
|
'
|
|
|
|
cat > "$ENV_FILE" <<EOF
|
|
REALM=${realm}
|
|
WORKGROUP=${workgroup}
|
|
DOMAIN=${domain}
|
|
JOIN_USER=${service_account_sam}
|
|
JOIN_PASSWORD=${service_password}
|
|
DOMAIN_USERS_SID=${domain_users_sid}
|
|
DOMAIN_ADMINS_SID=${domain_admins_sid}
|
|
PUBLIC_GROUP_SID=${public_group_sid}
|
|
SAMBA_HOSTNAME=${samba_hostname}
|
|
NETBIOS_NAME=${netbios_name}
|
|
# Optional overrides:
|
|
# LDAP_URI=ldaps://${domain}
|
|
# LDAP_BASE_DN=DC=example,DC=com
|
|
EOF
|
|
|
|
chmod 600 "$ENV_FILE"
|
|
FINAL_ENV_WRITTEN=1
|
|
printf "Created %s\n" "$ENV_FILE"
|
|
}
|
|
|
|
if [[ -f "$ENV_FILE" ]]; then
|
|
read -r -p ".env already exists. Overwrite? [y/N]: " overwrite
|
|
case "$overwrite" in
|
|
y|Y|yes|YES)
|
|
write_env_file
|
|
;;
|
|
*)
|
|
printf "Keeping existing .env\n"
|
|
;;
|
|
esac
|
|
else
|
|
write_env_file
|
|
fi
|
|
|
|
docker compose up -d
|
|
printf "Samba service started.\n"
|