k0s-cluster/selinux-script.sh
2025-06-19 07:55:22 -04:00

245 lines
7.2 KiB
Bash

#!/bin/bash
set -euo pipefail
# Configuration
readonly DATA_DIR="/var/lib/k0s"
readonly SCRIPT_NAME="$(basename "$0")"
readonly COMPLETION_FLAG="$HOME/.k0s-selinuxsetup-complete"
# Logging function
log() {
printf "[%s] %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$*" >&2
}
# Error handling function
error_exit() {
log "ERROR: $1"
exit 1
}
# Check if script has already been run successfully
check_completion_flag() {
if [[ -f "$COMPLETION_FLAG" ]]; then
log "SKIP: SELinux setup has already been completed successfully"
log "Completion flag found at: $COMPLETION_FLAG"
log "If you need to re-run this setup, remove the flag file and run again:"
log " rm '$COMPLETION_FLAG'"
exit 0
fi
}
# Check if running with sufficient privileges
check_privileges() {
if ! sudo -n true 2>/dev/null; then
error_exit "This script requires passwordless sudo access"
fi
}
# Check if SELinux is available and enabled
check_selinux() {
if ! command -v getenforce >/dev/null 2>&1; then
error_exit "getenforce command not found - SELinux may not be installed"
fi
local selinux_status
if ! selinux_status=$(getenforce 2>/dev/null); then
error_exit "Failed to get SELinux status"
fi
case "$selinux_status" in
"Enforcing"|"Permissive")
log "SELinux status: $selinux_status"
;;
*)
error_exit "SELinux is not enabled (status: $selinux_status)"
;;
esac
}
# Check if required SELinux management tools are available
check_selinux_tools() {
local missing_tools=()
for tool in semanage restorecon; do
if ! command -v "$tool" >/dev/null 2>&1; then
missing_tools+=("$tool")
fi
done
if [[ ${#missing_tools[@]} -gt 0 ]]; then
error_exit "Missing required SELinux tools: ${missing_tools[*]}"
fi
}
# Check if target directory exists
check_target_directory() {
if [[ ! -d "$DATA_DIR" ]]; then
error_exit "Target directory $DATA_DIR does not exist"
fi
log "Target directory $DATA_DIR exists"
}
# Check if fcontext rule already exists (safer grep with fixed strings)
fcontext_exists() {
local pattern="$1"
local context_type="$2"
# Use semanage to list and grep for exact matches
if sudo semanage fcontext -l 2>/dev/null | grep -F "$pattern" | grep -q "$context_type"; then
return 0
else
return 1
fi
}
# Add SELinux file context rule (idempotent)
add_fcontext_rule() {
local pattern="$1"
local context_type="$2"
local description="$3"
if fcontext_exists "$pattern" "$context_type"; then
log "SKIP: $description - fcontext rule already exists"
return 0
fi
log "ADDING: $description ($pattern -> $context_type)"
if sudo semanage fcontext -a -t "$context_type" "$pattern" 2>/dev/null; then
log "SUCCESS: Added fcontext rule for $pattern"
return 0
else
log "WARNING: Failed to add fcontext rule for $pattern"
return 1
fi
}
# Restore SELinux contexts (non-fatal)
restore_contexts() {
local target_path="$1"
local description="$2"
if [[ ! -e "$target_path" ]]; then
log "SKIP: $description - path $target_path does not exist"
return 0
fi
log "RESTORING: $description"
# Run restorecon and capture output, but don't fail the script
if sudo restorecon -R -v "$target_path" 2>&1 | while IFS= read -r line; do
[[ -n "$line" ]] && log " restorecon: $line"
done; then
log "SUCCESS: Context restoration completed for $target_path"
return 0
else
log "WARNING: restorecon reported issues for $target_path (continuing anyway)"
return 1
fi
}
# Show what contexts are actually present (informational only)
show_contexts() {
local target_path="$1"
local description="$2"
if [[ ! -e "$target_path" ]]; then
log "SKIP INFO: $description - path $target_path does not exist"
return 0
fi
log "CONTEXTS: $description"
# Show context summary without making assumptions about what's "correct"
local context_info
if context_info=$(find "$target_path" -exec ls -Z {} \; 2>/dev/null | awk '{print $1}' | sort | uniq -c 2>/dev/null); then
if [[ -n "$context_info" ]]; then
echo "$context_info" | while IFS= read -r line; do
log " $line"
done
else
log " (no context information available)"
fi
else
log " (unable to retrieve context information)"
fi
}
# Create completion flag file
create_completion_flag() {
# Use a here-document with proper quoting
if cat > "$COMPLETION_FLAG" << EOF
# k0s SELinux Setup Completion Flag
#
# This file indicates that the k0s SELinux configuration script has been
# run successfully. It prevents the script from running multiple times.
#
# The script configures SELinux file contexts for:
# - ${DATA_DIR}/bin/containerd.* (container_runtime_exec_t)
# - ${DATA_DIR}/bin/runc (container_runtime_exec_t)
# - ${DATA_DIR}/containerd directory tree (container_var_lib_t)
# - ${DATA_DIR}/containerd snapshots (container_ro_file_t)
#
# If you remove this file, the SELinux script will run again on the next
# k0sapply execution.
#
# Created: $(date)
# Script: $0
EOF
then
log "SUCCESS: Created completion flag at $COMPLETION_FLAG"
else
log "WARNING: Failed to create completion flag at $COMPLETION_FLAG"
return 1
fi
}
# Main execution function
main() {
log "Starting $SCRIPT_NAME"
# Early exit if already completed
check_completion_flag
# System checks
check_privileges
check_selinux
check_selinux_tools
check_target_directory
# Track any failures
local rule_failures=0
local restore_failures=0
# Add SELinux file context rules
add_fcontext_rule "${DATA_DIR}/bin/containerd.*" "container_runtime_exec_t" "containerd executables" || ((rule_failures++))
add_fcontext_rule "${DATA_DIR}/bin/runc" "container_runtime_exec_t" "runc executable" || ((rule_failures++))
add_fcontext_rule "${DATA_DIR}/containerd(/.*)?" "container_var_lib_t" "containerd directory" || ((rule_failures++))
add_fcontext_rule "${DATA_DIR}/containerd/io.containerd.snapshotter.*/snapshots(/.*)?" "container_ro_file_t" "containerd snapshots" || ((rule_failures++))
# Apply contexts to existing files
restore_contexts "${DATA_DIR}/bin" "k0s binaries" || ((restore_failures++))
restore_contexts "${DATA_DIR}/containerd" "containerd directory" || ((restore_failures++))
# Show what contexts are actually present (informational)
show_contexts "${DATA_DIR}/bin" "k0s binaries"
show_contexts "${DATA_DIR}/containerd" "containerd directory"
# Report any issues but don't fail
if [[ $rule_failures -gt 0 ]]; then
log "WARNING: $rule_failures fcontext rule(s) failed to add"
fi
if [[ $restore_failures -gt 0 ]]; then
log "WARNING: $restore_failures context restoration(s) had issues"
fi
# Create completion flag regardless of warnings
create_completion_flag
log "Completed $SCRIPT_NAME"
}
# Execute main function
main "$@"