377 lines
9.6 KiB
Bash
Executable File
377 lines
9.6 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
PLUGIN_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
PLUGIN_SETTINGS_FILE="${MYSQL_HOST_SYNC_PLUGIN_SETTINGS:-$PLUGIN_DIR/plugin-settings.conf}"
|
|
|
|
# shellcheck source=./mysql_common.sh
|
|
source "$SCRIPT_DIR/mysql_common.sh"
|
|
|
|
load_plugin_settings() {
|
|
local file="$1"
|
|
local raw_line key value
|
|
|
|
[ -r "$file" ] || return 0
|
|
|
|
while IFS= read -r raw_line || [ -n "$raw_line" ]; do
|
|
raw_line="$(trim_value "$raw_line")"
|
|
[ -n "$raw_line" ] || continue
|
|
case "$raw_line" in
|
|
\#*) continue ;;
|
|
esac
|
|
[[ "$raw_line" == *=* ]] || continue
|
|
|
|
key="$(trim_value "${raw_line%%=*}")"
|
|
value="$(unquote_value "$(strip_inline_comment "${raw_line#*=}")")"
|
|
|
|
case "$key" in
|
|
MYSQL_PLUGIN_CREDENTIALS_FILE) MYSQL_PLUGIN_CREDENTIALS_FILE="$value" ;;
|
|
MYSQL_PLUGIN_METADATA_FILE) MYSQL_PLUGIN_METADATA_FILE="$value" ;;
|
|
MYSQL_PLUGIN_ALT_MY_CNF) MYSQL_PLUGIN_ALT_MY_CNF="$value" ;;
|
|
MYSQL_PLUGIN_CLIENT_BIN_DIR) MYSQL_PLUGIN_CLIENT_BIN_DIR="$value" ;;
|
|
MYSQL_PLUGIN_REMOTE_BIND_ADDRESS) MYSQL_PLUGIN_REMOTE_BIND_ADDRESS="$value" ;;
|
|
MYSQL_PLUGIN_CSF_ALLOW_FILE) MYSQL_HOST_SYNC_CSF_ALLOW="$value" ;;
|
|
MYSQL_PLUGIN_CSF_CONF_FILE) MYSQL_HOST_SYNC_CSF_CONF="$value" ;;
|
|
MYSQL_PLUGIN_CSF_MANAGED_ALLOW_FILE) MYSQL_HOST_SYNC_MANAGED_ALLOW="$value" ;;
|
|
MYSQL_PLUGIN_ALT_MARIADB_CNF) MYSQL_HOST_SYNC_INSTANCE_CNF="$value" ;;
|
|
MYSQL_PLUGIN_ALT_MARIADB_SERVICE) MYSQL_HOST_SYNC_SERVICE="$value" ;;
|
|
esac
|
|
done < "$file"
|
|
}
|
|
|
|
require_root_or_sudo() {
|
|
if [ "${MYSQL_HOST_SYNC_ASSUME_ROOT:-0}" = "1" ]; then
|
|
return 0
|
|
fi
|
|
|
|
if [ "${EUID:-$(id -u)}" -eq 0 ]; then
|
|
return 0
|
|
fi
|
|
|
|
if command -v sudo >/dev/null 2>&1; then
|
|
exec sudo -n "$0" "$@"
|
|
fi
|
|
|
|
echo "mysql: synchronizacja hostów wymaga uprawnień root." >&2
|
|
exit 1
|
|
}
|
|
|
|
is_ipv4() {
|
|
local ip="${1:-}"
|
|
local IFS=.
|
|
local -a octets
|
|
[[ "$ip" =~ ^[0-9]+(\.[0-9]+){3}$ ]] || return 1
|
|
read -r -a octets <<< "$ip"
|
|
[ "${#octets[@]}" -eq 4 ] || return 1
|
|
local octet
|
|
for octet in "${octets[@]}"; do
|
|
[[ "$octet" =~ ^[0-9]+$ ]] || return 1
|
|
[ "$octet" -ge 0 ] && [ "$octet" -le 255 ] || return 1
|
|
done
|
|
}
|
|
|
|
is_local_host() {
|
|
case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in
|
|
""|localhost|127.0.0.1|::1) return 0 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
collect_remote_hosts() {
|
|
local sql output host
|
|
sql="SELECT DISTINCT host_pattern FROM da_plugin_access_hosts WHERE host_pattern <> '' ORDER BY host_pattern"
|
|
|
|
if ! output="$(mysql_exec mysql --batch --skip-column-names --raw -e "$sql")"; then
|
|
echo "mysql: nie można odczytać listy hostów zdalnych z da_plugin_access_hosts." >&2
|
|
return 1
|
|
fi
|
|
|
|
while IFS= read -r host || [ -n "$host" ]; do
|
|
host="$(trim_value "$host")"
|
|
[ -n "$host" ] || continue
|
|
is_local_host "$host" && continue
|
|
is_ipv4 "$host" || continue
|
|
printf '%s\n' "$host"
|
|
done <<< "$output" | sort -u
|
|
}
|
|
|
|
write_managed_allow_file() {
|
|
local target="$1"
|
|
local port="$2"
|
|
local tmp
|
|
shift 2
|
|
local -a hosts=("$@")
|
|
|
|
install -d -m 0755 "$(dirname "$target")"
|
|
tmp="$(mktemp)"
|
|
|
|
{
|
|
echo "# Managed by DirectAdmin MySQL plugin. Do not edit manually."
|
|
echo "# Remote MariaDB access for port $port."
|
|
echo "# Source: da_plugin_access_hosts in the plugin metadata schema."
|
|
local host
|
|
for host in "${hosts[@]}"; do
|
|
echo "tcp|in|d=$port|s=$host"
|
|
done
|
|
} > "$tmp"
|
|
|
|
if [ ! -f "$target" ] || ! cmp -s "$tmp" "$target"; then
|
|
if [ -f "$target" ]; then
|
|
cat "$tmp" > "$target"
|
|
chmod 0644 "$target"
|
|
else
|
|
install -m 0644 "$tmp" "$target"
|
|
fi
|
|
changed_csf=1
|
|
fi
|
|
|
|
rm -f "$tmp"
|
|
}
|
|
|
|
ensure_csf_include() {
|
|
local csf_allow="$1"
|
|
local managed_allow="$2"
|
|
local include_line="Include $managed_allow"
|
|
|
|
[ -f "$csf_allow" ] || {
|
|
echo "mysql: brak pliku CSF allow: $csf_allow" >&2
|
|
return 1
|
|
}
|
|
|
|
if grep -Fxq "$include_line" "$csf_allow"; then
|
|
return 0
|
|
fi
|
|
|
|
printf '\n%s\n' "$include_line" >> "$csf_allow"
|
|
changed_csf=1
|
|
}
|
|
|
|
remove_port_from_csv() {
|
|
local csv="$1"
|
|
local port="$2"
|
|
local old_ifs="$IFS"
|
|
local item out="" start end left_start left_end right_start right_end
|
|
IFS=','
|
|
for item in $csv; do
|
|
item="$(trim_value "$item")"
|
|
[ -n "$item" ] || continue
|
|
|
|
if [ "$item" = "$port" ]; then
|
|
continue
|
|
fi
|
|
|
|
if [[ "$item" =~ ^([0-9]+):([0-9]+)$ ]]; then
|
|
start="${BASH_REMATCH[1]}"
|
|
end="${BASH_REMATCH[2]}"
|
|
if [ "$start" -le "$port" ] && [ "$port" -le "$end" ]; then
|
|
left_start="$start"
|
|
left_end=$((port - 1))
|
|
right_start=$((port + 1))
|
|
right_end="$end"
|
|
|
|
if [ "$left_start" -le "$left_end" ]; then
|
|
if [ -z "$out" ]; then
|
|
out="$left_start"
|
|
else
|
|
out="$out,$left_start"
|
|
fi
|
|
if [ "$left_start" -ne "$left_end" ]; then
|
|
out="$out:$left_end"
|
|
fi
|
|
fi
|
|
|
|
if [ "$right_start" -le "$right_end" ]; then
|
|
if [ -z "$out" ]; then
|
|
out="$right_start"
|
|
else
|
|
out="$out,$right_start"
|
|
fi
|
|
if [ "$right_start" -ne "$right_end" ]; then
|
|
out="$out:$right_end"
|
|
fi
|
|
fi
|
|
continue
|
|
fi
|
|
fi
|
|
|
|
if [ -z "$out" ]; then
|
|
out="$item"
|
|
else
|
|
out="$out,$item"
|
|
fi
|
|
done
|
|
IFS="$old_ifs"
|
|
printf '%s' "$out"
|
|
}
|
|
|
|
remove_port_from_global_csf_lists() {
|
|
local csf_conf="$1"
|
|
local port="$2"
|
|
local tmp changed=0 line new_list new_line
|
|
|
|
[ -f "$csf_conf" ] || {
|
|
echo "mysql: brak pliku CSF config: $csf_conf" >&2
|
|
return 1
|
|
}
|
|
|
|
tmp="$(mktemp)"
|
|
while IFS= read -r line || [ -n "$line" ]; do
|
|
if [[ "$line" =~ ^([[:space:]]*)(TCP6?_IN)([[:space:]]*=[[:space:]]*)\"([^\"]*)\"(.*)$ ]]; then
|
|
new_list="$(remove_port_from_csv "${BASH_REMATCH[4]}" "$port")"
|
|
new_line="${BASH_REMATCH[1]}${BASH_REMATCH[2]}${BASH_REMATCH[3]}\"$new_list\"${BASH_REMATCH[5]}"
|
|
if [ "$new_line" != "$line" ]; then
|
|
changed=1
|
|
fi
|
|
printf '%s\n' "$new_line" >> "$tmp"
|
|
else
|
|
printf '%s\n' "$line" >> "$tmp"
|
|
fi
|
|
done < "$csf_conf"
|
|
|
|
if [ "$changed" -eq 1 ]; then
|
|
cat "$tmp" > "$csf_conf"
|
|
chmod 0600 "$csf_conf"
|
|
changed_csf=1
|
|
fi
|
|
rm -f "$tmp"
|
|
}
|
|
|
|
ensure_remote_bind_address() {
|
|
local cnf="$1"
|
|
local desired="$2"
|
|
local tmp changed=0 inserted=0 in_mariadb=0 line new_line
|
|
|
|
[ -f "$cnf" ] || {
|
|
echo "mysql: brak pliku konfiguracji MariaDB: $cnf" >&2
|
|
return 1
|
|
}
|
|
is_ipv4 "$desired" || {
|
|
echo "mysql: nieprawidłowy bind-address dla zdalnego MySQL: $desired" >&2
|
|
return 1
|
|
}
|
|
|
|
tmp="$(mktemp)"
|
|
while IFS= read -r line || [ -n "$line" ]; do
|
|
if [[ "$line" =~ ^[[:space:]]*\[mariadb\][[:space:]]*$ ]]; then
|
|
in_mariadb=1
|
|
printf '%s\n' "$line" >> "$tmp"
|
|
continue
|
|
fi
|
|
|
|
if [ "$in_mariadb" -eq 1 ] && [[ "$line" =~ ^[[:space:]]*\[.*\][[:space:]]*$ ]]; then
|
|
if [ "$inserted" -eq 0 ]; then
|
|
printf 'bind-address=%s\n' "$desired" >> "$tmp"
|
|
inserted=1
|
|
changed=1
|
|
fi
|
|
in_mariadb=0
|
|
fi
|
|
|
|
if [ "$in_mariadb" -eq 1 ] && [[ "$line" =~ ^[[:space:]]*bind-address[[:space:]]*= ]]; then
|
|
new_line="bind-address=$desired"
|
|
if [ "$line" != "$new_line" ]; then
|
|
changed=1
|
|
fi
|
|
printf '%s\n' "$new_line" >> "$tmp"
|
|
inserted=1
|
|
continue
|
|
fi
|
|
|
|
printf '%s\n' "$line" >> "$tmp"
|
|
done < "$cnf"
|
|
|
|
if [ "$inserted" -eq 0 ]; then
|
|
printf '\n[mariadb]\nbind-address=%s\n' "$desired" >> "$tmp"
|
|
changed=1
|
|
fi
|
|
|
|
if [ "$changed" -eq 1 ]; then
|
|
cat "$tmp" > "$cnf"
|
|
chmod 0640 "$cnf"
|
|
changed_bind=1
|
|
fi
|
|
rm -f "$tmp"
|
|
}
|
|
|
|
reload_csf() {
|
|
if [ "${MYSQL_HOST_SYNC_SKIP_CSF_RELOAD:-0}" = "1" ]; then
|
|
return 0
|
|
fi
|
|
|
|
if command -v csf >/dev/null 2>&1; then
|
|
csf -r
|
|
return 0
|
|
fi
|
|
|
|
echo "mysql: nie znaleziono polecenia csf, nie można przeładować firewalla." >&2
|
|
return 1
|
|
}
|
|
|
|
restart_mariadb_service() {
|
|
local service="$1"
|
|
|
|
if [ "${MYSQL_HOST_SYNC_SKIP_SERVICE_RESTART:-0}" = "1" ]; then
|
|
return 0
|
|
fi
|
|
|
|
if command -v systemctl >/dev/null 2>&1; then
|
|
systemctl restart "$service"
|
|
return 0
|
|
fi
|
|
|
|
if command -v service >/dev/null 2>&1; then
|
|
service "$service" restart
|
|
return 0
|
|
fi
|
|
|
|
echo "mysql: nie znaleziono systemctl/service, nie można zrestartować $service." >&2
|
|
return 1
|
|
}
|
|
|
|
main() {
|
|
require_root_or_sudo "$@"
|
|
load_plugin_settings "$PLUGIN_SETTINGS_FILE"
|
|
mysql_load_alt_credentials
|
|
mysql_ensure_metadata_schema
|
|
|
|
local csf_dir="${MYSQL_HOST_SYNC_CSF_DIR:-/etc/csf}"
|
|
local csf_allow="${MYSQL_HOST_SYNC_CSF_ALLOW:-$csf_dir/csf.allow}"
|
|
local csf_conf="${MYSQL_HOST_SYNC_CSF_CONF:-$csf_dir/csf.conf}"
|
|
local managed_allow="${MYSQL_HOST_SYNC_MANAGED_ALLOW:-$csf_dir/alt-mysql-plugin.allow}"
|
|
local instance_cnf="${MYSQL_HOST_SYNC_INSTANCE_CNF:-$MYSQL_ALT_INSTANCE_CNF}"
|
|
local bind_address="${MYSQL_REMOTE_BIND_ADDRESS:-${MYSQL_PLUGIN_REMOTE_BIND_ADDRESS:-0.0.0.0}}"
|
|
local service="${MYSQL_HOST_SYNC_SERVICE:-$MYSQL_ALT_SERVICE_NAME}"
|
|
local changed_csf=0 changed_bind=0 host_output
|
|
local -a hosts=()
|
|
|
|
host_output="$(collect_remote_hosts)"
|
|
while IFS= read -r host || [ -n "$host" ]; do
|
|
[ -n "$host" ] || continue
|
|
hosts+=("$host")
|
|
done <<< "$host_output"
|
|
|
|
write_managed_allow_file "$managed_allow" "$MYSQL_PORT" "${hosts[@]}"
|
|
ensure_csf_include "$csf_allow" "$managed_allow"
|
|
remove_port_from_global_csf_lists "$csf_conf" "$MYSQL_PORT"
|
|
ensure_remote_bind_address "$instance_cnf" "$bind_address"
|
|
|
|
if [ "$changed_bind" -eq 1 ]; then
|
|
restart_mariadb_service "$service"
|
|
fi
|
|
|
|
if [ "$changed_csf" -eq 1 ]; then
|
|
reload_csf
|
|
fi
|
|
|
|
cat <<EOF
|
|
mysql: zsynchronizowano zdalne hosty MariaDB.
|
|
Port: $MYSQL_PORT
|
|
Bind address: $bind_address
|
|
Plik CSF include: $managed_allow
|
|
Liczba hostów: ${#hosts[@]}
|
|
EOF
|
|
}
|
|
|
|
main "$@"
|