feat: add automatic SSH key cleanup when nodes are removed
- Create cleanup script that removes Pulse SSH keys from nodes - Add systemd path unit to watch for cleanup requests - Add systemd service to execute cleanup script - Update install-sensor-proxy.sh to install cleanup system - Handles both cluster nodes (pulse-managed-key) and standalone nodes (pulse-proxy-key) - Cleanup is triggered automatically when nodes are deleted from Pulse - All cleanup actions are logged via syslog for auditability
This commit is contained in:
parent
de3bb47930
commit
7a7158d9bd
4 changed files with 360 additions and 0 deletions
|
|
@ -305,6 +305,200 @@ fi
|
||||||
|
|
||||||
print_info "Socket ready at $SOCKET_PATH"
|
print_info "Socket ready at $SOCKET_PATH"
|
||||||
|
|
||||||
|
# Install cleanup system for automatic SSH key removal when nodes are deleted
|
||||||
|
print_info "Installing cleanup system..."
|
||||||
|
|
||||||
|
# Install cleanup script
|
||||||
|
CLEANUP_SCRIPT_PATH="/usr/local/bin/pulse-sensor-cleanup.sh"
|
||||||
|
cat > "$CLEANUP_SCRIPT_PATH" << 'CLEANUP_EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# pulse-sensor-cleanup.sh - Removes Pulse SSH keys from Proxmox nodes when they're removed from Pulse
|
||||||
|
# This script is triggered by systemd path unit when cleanup-request.json is created
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
WORK_DIR="/var/lib/pulse-sensor-proxy"
|
||||||
|
CLEANUP_REQUEST="${WORK_DIR}/cleanup-request.json"
|
||||||
|
LOG_TAG="pulse-sensor-cleanup"
|
||||||
|
|
||||||
|
# Logging functions
|
||||||
|
log_info() {
|
||||||
|
logger -t "$LOG_TAG" -p user.info "$1"
|
||||||
|
echo "[INFO] $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warn() {
|
||||||
|
logger -t "$LOG_TAG" -p user.warning "$1"
|
||||||
|
echo "[WARN] $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
logger -t "$LOG_TAG" -p user.err "$1"
|
||||||
|
echo "[ERROR] $1" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if cleanup request file exists
|
||||||
|
if [[ ! -f "$CLEANUP_REQUEST" ]]; then
|
||||||
|
log_info "No cleanup request found at $CLEANUP_REQUEST"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Processing cleanup request from $CLEANUP_REQUEST"
|
||||||
|
|
||||||
|
# Read and parse the cleanup request
|
||||||
|
CLEANUP_DATA=$(cat "$CLEANUP_REQUEST")
|
||||||
|
HOST=$(echo "$CLEANUP_DATA" | grep -o '"host":"[^"]*"' | cut -d'"' -f4 || echo "")
|
||||||
|
REQUESTED_AT=$(echo "$CLEANUP_DATA" | grep -o '"requestedAt":"[^"]*"' | cut -d'"' -f4 || echo "")
|
||||||
|
|
||||||
|
log_info "Cleanup requested at: ${REQUESTED_AT:-unknown}"
|
||||||
|
|
||||||
|
# Remove the cleanup request file immediately to prevent re-processing
|
||||||
|
rm -f "$CLEANUP_REQUEST"
|
||||||
|
|
||||||
|
# If no specific host was provided, clean up all known nodes
|
||||||
|
if [[ -z "$HOST" ]]; then
|
||||||
|
log_info "No specific host provided - cleaning up all cluster nodes"
|
||||||
|
|
||||||
|
# Discover cluster nodes
|
||||||
|
if command -v pvecm >/dev/null 2>&1; then
|
||||||
|
CLUSTER_NODES=$(pvecm status 2>/dev/null | awk '/0x[0-9a-f]+.*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ {print $3}')
|
||||||
|
|
||||||
|
if [[ -n "$CLUSTER_NODES" ]]; then
|
||||||
|
for node_ip in $CLUSTER_NODES; do
|
||||||
|
log_info "Cleaning up SSH keys on node $node_ip"
|
||||||
|
|
||||||
|
# Remove both pulse-managed-key and pulse-proxy-key entries
|
||||||
|
ssh -o StrictHostKeyChecking=no -o BatchMode=yes -o ConnectTimeout=5 root@"$node_ip" \
|
||||||
|
"sed -i -e '/# pulse-managed-key\$/d' -e '/# pulse-proxy-key\$/d' /root/.ssh/authorized_keys" 2>&1 | \
|
||||||
|
logger -t "$LOG_TAG" -p user.info || \
|
||||||
|
log_warn "Failed to clean up SSH keys on $node_ip"
|
||||||
|
done
|
||||||
|
log_info "Cluster cleanup completed"
|
||||||
|
else
|
||||||
|
# Standalone node - clean up localhost
|
||||||
|
log_info "Standalone node detected - cleaning up localhost"
|
||||||
|
sed -i -e '/# pulse-managed-key$/d' -e '/# pulse-proxy-key$/d' /root/.ssh/authorized_keys 2>&1 | \
|
||||||
|
logger -t "$LOG_TAG" -p user.info || \
|
||||||
|
log_warn "Failed to clean up SSH keys on localhost"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_warn "pvecm command not available - cleaning up localhost only"
|
||||||
|
sed -i -e '/# pulse-managed-key$/d' -e '/# pulse-proxy-key$/d' /root/.ssh/authorized_keys 2>&1 | \
|
||||||
|
logger -t "$LOG_TAG" -p user.info || \
|
||||||
|
log_warn "Failed to clean up SSH keys on localhost"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_info "Cleaning up specific host: $HOST"
|
||||||
|
|
||||||
|
# Extract IP from host URL
|
||||||
|
HOST_CLEAN=$(echo "$HOST" | sed -e 's|^https\?://||' -e 's|:.*$||')
|
||||||
|
|
||||||
|
# Check if this is localhost
|
||||||
|
LOCAL_IPS=$(hostname -I 2>/dev/null || echo "")
|
||||||
|
IS_LOCAL=false
|
||||||
|
|
||||||
|
for local_ip in $LOCAL_IPS; do
|
||||||
|
if [[ "$HOST_CLEAN" == "$local_ip" ]]; then
|
||||||
|
IS_LOCAL=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$HOST_CLEAN" == "127.0.0.1" || "$HOST_CLEAN" == "localhost" ]]; then
|
||||||
|
IS_LOCAL=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$IS_LOCAL" == true ]]; then
|
||||||
|
log_info "Cleaning up localhost SSH keys"
|
||||||
|
sed -i -e '/# pulse-managed-key$/d' -e '/# pulse-proxy-key$/d' /root/.ssh/authorized_keys 2>&1 | \
|
||||||
|
logger -t "$LOG_TAG" -p user.info || \
|
||||||
|
log_warn "Failed to clean up SSH keys on localhost"
|
||||||
|
else
|
||||||
|
log_info "Cleaning up remote host: $HOST_CLEAN"
|
||||||
|
|
||||||
|
# Remove both pulse-managed-key and pulse-proxy-key entries from remote host
|
||||||
|
ssh -o StrictHostKeyChecking=no -o BatchMode=yes -o ConnectTimeout=5 root@"$HOST_CLEAN" \
|
||||||
|
"sed -i -e '/# pulse-managed-key\$/d' -e '/# pulse-proxy-key\$/d' /root/.ssh/authorized_keys" 2>&1 | \
|
||||||
|
logger -t "$LOG_TAG" -p user.info
|
||||||
|
|
||||||
|
if [[ $? -eq 0 ]]; then
|
||||||
|
log_info "Successfully cleaned up SSH keys on $HOST_CLEAN"
|
||||||
|
else
|
||||||
|
log_error "Failed to clean up SSH keys on $HOST_CLEAN"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Cleanup completed successfully"
|
||||||
|
exit 0
|
||||||
|
CLEANUP_EOF
|
||||||
|
|
||||||
|
chmod +x "$CLEANUP_SCRIPT_PATH"
|
||||||
|
print_info "Cleanup script installed"
|
||||||
|
|
||||||
|
# Install systemd path unit
|
||||||
|
CLEANUP_PATH_UNIT="/etc/systemd/system/pulse-sensor-cleanup.path"
|
||||||
|
cat > "$CLEANUP_PATH_UNIT" << 'PATH_EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=Watch for Pulse sensor cleanup requests
|
||||||
|
Documentation=https://github.com/rcourtman/Pulse
|
||||||
|
|
||||||
|
[Path]
|
||||||
|
# Watch for the cleanup request file
|
||||||
|
PathChanged=/var/lib/pulse-sensor-proxy/cleanup-request.json
|
||||||
|
# Also watch for modifications
|
||||||
|
PathModified=/var/lib/pulse-sensor-proxy/cleanup-request.json
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
PATH_EOF
|
||||||
|
|
||||||
|
# Install systemd service unit
|
||||||
|
CLEANUP_SERVICE_UNIT="/etc/systemd/system/pulse-sensor-cleanup.service"
|
||||||
|
cat > "$CLEANUP_SERVICE_UNIT" << 'SERVICE_EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=Pulse Sensor Cleanup Service
|
||||||
|
Documentation=https://github.com/rcourtman/Pulse
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/local/bin/pulse-sensor-cleanup.sh
|
||||||
|
User=root
|
||||||
|
Group=root
|
||||||
|
WorkingDirectory=/var/lib/pulse-sensor-proxy
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
SyslogIdentifier=pulse-sensor-cleanup
|
||||||
|
|
||||||
|
# Security hardening (less restrictive than the proxy since we need SSH access)
|
||||||
|
NoNewPrivileges=true
|
||||||
|
ProtectSystem=strict
|
||||||
|
ReadWritePaths=/var/lib/pulse-sensor-proxy /root/.ssh
|
||||||
|
ProtectKernelTunables=true
|
||||||
|
ProtectKernelModules=true
|
||||||
|
ProtectControlGroups=true
|
||||||
|
PrivateTmp=true
|
||||||
|
RestrictSUIDSGID=true
|
||||||
|
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||||
|
LimitNOFILE=1024
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
# This service is triggered by the .path unit, no need to enable it directly
|
||||||
|
SERVICE_EOF
|
||||||
|
|
||||||
|
# Enable and start the path unit
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable pulse-sensor-cleanup.path
|
||||||
|
systemctl start pulse-sensor-cleanup.path
|
||||||
|
|
||||||
|
print_info "Cleanup system installed and enabled"
|
||||||
|
|
||||||
# Configure SSH keys for cluster temperature monitoring
|
# Configure SSH keys for cluster temperature monitoring
|
||||||
print_info "Configuring proxy SSH access to cluster nodes..."
|
print_info "Configuring proxy SSH access to cluster nodes..."
|
||||||
|
|
||||||
|
|
|
||||||
123
scripts/pulse-sensor-cleanup.sh
Normal file
123
scripts/pulse-sensor-cleanup.sh
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# pulse-sensor-cleanup.sh - Removes Pulse SSH keys from Proxmox nodes when they're removed from Pulse
|
||||||
|
# This script is triggered by systemd path unit when cleanup-request.json is created
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
WORK_DIR="/var/lib/pulse-sensor-proxy"
|
||||||
|
CLEANUP_REQUEST="${WORK_DIR}/cleanup-request.json"
|
||||||
|
LOG_TAG="pulse-sensor-cleanup"
|
||||||
|
|
||||||
|
# Logging functions
|
||||||
|
log_info() {
|
||||||
|
logger -t "$LOG_TAG" -p user.info "$1"
|
||||||
|
echo "[INFO] $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warn() {
|
||||||
|
logger -t "$LOG_TAG" -p user.warning "$1"
|
||||||
|
echo "[WARN] $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
logger -t "$LOG_TAG" -p user.err "$1"
|
||||||
|
echo "[ERROR] $1" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if cleanup request file exists
|
||||||
|
if [[ ! -f "$CLEANUP_REQUEST" ]]; then
|
||||||
|
log_info "No cleanup request found at $CLEANUP_REQUEST"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Processing cleanup request from $CLEANUP_REQUEST"
|
||||||
|
|
||||||
|
# Read and parse the cleanup request
|
||||||
|
CLEANUP_DATA=$(cat "$CLEANUP_REQUEST")
|
||||||
|
HOST=$(echo "$CLEANUP_DATA" | grep -o '"host":"[^"]*"' | cut -d'"' -f4 || echo "")
|
||||||
|
REQUESTED_AT=$(echo "$CLEANUP_DATA" | grep -o '"requestedAt":"[^"]*"' | cut -d'"' -f4 || echo "")
|
||||||
|
|
||||||
|
log_info "Cleanup requested at: ${REQUESTED_AT:-unknown}"
|
||||||
|
|
||||||
|
# Remove the cleanup request file immediately to prevent re-processing
|
||||||
|
rm -f "$CLEANUP_REQUEST"
|
||||||
|
|
||||||
|
# If no specific host was provided, clean up all known nodes
|
||||||
|
if [[ -z "$HOST" ]]; then
|
||||||
|
log_info "No specific host provided - cleaning up all cluster nodes"
|
||||||
|
|
||||||
|
# Discover cluster nodes
|
||||||
|
if command -v pvecm >/dev/null 2>&1; then
|
||||||
|
CLUSTER_NODES=$(pvecm status 2>/dev/null | awk '/0x[0-9a-f]+.*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ {print $3}')
|
||||||
|
|
||||||
|
if [[ -n "$CLUSTER_NODES" ]]; then
|
||||||
|
for node_ip in $CLUSTER_NODES; do
|
||||||
|
log_info "Cleaning up SSH keys on node $node_ip"
|
||||||
|
|
||||||
|
# Remove both pulse-managed-key and pulse-proxy-key entries
|
||||||
|
ssh -o StrictHostKeyChecking=no -o BatchMode=yes -o ConnectTimeout=5 root@"$node_ip" \
|
||||||
|
"sed -i -e '/# pulse-managed-key\$/d' -e '/# pulse-proxy-key\$/d' /root/.ssh/authorized_keys" 2>&1 | \
|
||||||
|
logger -t "$LOG_TAG" -p user.info || \
|
||||||
|
log_warn "Failed to clean up SSH keys on $node_ip"
|
||||||
|
done
|
||||||
|
log_info "Cluster cleanup completed"
|
||||||
|
else
|
||||||
|
# Standalone node - clean up localhost
|
||||||
|
log_info "Standalone node detected - cleaning up localhost"
|
||||||
|
sed -i -e '/# pulse-managed-key$/d' -e '/# pulse-proxy-key$/d' /root/.ssh/authorized_keys 2>&1 | \
|
||||||
|
logger -t "$LOG_TAG" -p user.info || \
|
||||||
|
log_warn "Failed to clean up SSH keys on localhost"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_warn "pvecm command not available - cleaning up localhost only"
|
||||||
|
sed -i -e '/# pulse-managed-key$/d' -e '/# pulse-proxy-key$/d' /root/.ssh/authorized_keys 2>&1 | \
|
||||||
|
logger -t "$LOG_TAG" -p user.info || \
|
||||||
|
log_warn "Failed to clean up SSH keys on localhost"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_info "Cleaning up specific host: $HOST"
|
||||||
|
|
||||||
|
# Extract IP from host URL
|
||||||
|
HOST_CLEAN=$(echo "$HOST" | sed -e 's|^https\?://||' -e 's|:.*$||')
|
||||||
|
|
||||||
|
# Check if this is localhost
|
||||||
|
LOCAL_IPS=$(hostname -I 2>/dev/null || echo "")
|
||||||
|
IS_LOCAL=false
|
||||||
|
|
||||||
|
for local_ip in $LOCAL_IPS; do
|
||||||
|
if [[ "$HOST_CLEAN" == "$local_ip" ]]; then
|
||||||
|
IS_LOCAL=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$HOST_CLEAN" == "127.0.0.1" || "$HOST_CLEAN" == "localhost" ]]; then
|
||||||
|
IS_LOCAL=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$IS_LOCAL" == true ]]; then
|
||||||
|
log_info "Cleaning up localhost SSH keys"
|
||||||
|
sed -i -e '/# pulse-managed-key$/d' -e '/# pulse-proxy-key$/d' /root/.ssh/authorized_keys 2>&1 | \
|
||||||
|
logger -t "$LOG_TAG" -p user.info || \
|
||||||
|
log_warn "Failed to clean up SSH keys on localhost"
|
||||||
|
else
|
||||||
|
log_info "Cleaning up remote host: $HOST_CLEAN"
|
||||||
|
|
||||||
|
# Remove both pulse-managed-key and pulse-proxy-key entries from remote host
|
||||||
|
ssh -o StrictHostKeyChecking=no -o BatchMode=yes -o ConnectTimeout=5 root@"$HOST_CLEAN" \
|
||||||
|
"sed -i -e '/# pulse-managed-key\$/d' -e '/# pulse-proxy-key\$/d' /root/.ssh/authorized_keys" 2>&1 | \
|
||||||
|
logger -t "$LOG_TAG" -p user.info
|
||||||
|
|
||||||
|
if [[ $? -eq 0 ]]; then
|
||||||
|
log_info "Successfully cleaned up SSH keys on $HOST_CLEAN"
|
||||||
|
else
|
||||||
|
log_error "Failed to clean up SSH keys on $HOST_CLEAN"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Cleanup completed successfully"
|
||||||
|
exit 0
|
||||||
12
scripts/systemd/pulse-sensor-cleanup.path
Normal file
12
scripts/systemd/pulse-sensor-cleanup.path
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Watch for Pulse sensor cleanup requests
|
||||||
|
Documentation=https://github.com/rcourtman/Pulse
|
||||||
|
|
||||||
|
[Path]
|
||||||
|
# Watch for the cleanup request file
|
||||||
|
PathChanged=/var/lib/pulse-sensor-proxy/cleanup-request.json
|
||||||
|
# Also watch for modifications
|
||||||
|
PathModified=/var/lib/pulse-sensor-proxy/cleanup-request.json
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
31
scripts/systemd/pulse-sensor-cleanup.service
Normal file
31
scripts/systemd/pulse-sensor-cleanup.service
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Pulse Sensor Cleanup Service
|
||||||
|
Documentation=https://github.com/rcourtman/Pulse
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/local/bin/pulse-sensor-cleanup.sh
|
||||||
|
User=root
|
||||||
|
Group=root
|
||||||
|
WorkingDirectory=/var/lib/pulse-sensor-proxy
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
SyslogIdentifier=pulse-sensor-cleanup
|
||||||
|
|
||||||
|
# Security hardening (less restrictive than the proxy since we need SSH access)
|
||||||
|
NoNewPrivileges=true
|
||||||
|
ProtectSystem=strict
|
||||||
|
ReadWritePaths=/var/lib/pulse-sensor-proxy /root/.ssh
|
||||||
|
ProtectKernelTunables=true
|
||||||
|
ProtectKernelModules=true
|
||||||
|
ProtectControlGroups=true
|
||||||
|
PrivateTmp=true
|
||||||
|
RestrictSUIDSGID=true
|
||||||
|
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||||
|
LimitNOFILE=1024
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
# This service is triggered by the .path unit, no need to enable it directly
|
||||||
Loading…
Reference in a new issue