feat(auth): Enhance authentication management with yq support for YAML parsing
Some checks failed
/ validate-docker-compose (push) Has been cancelled

This commit is contained in:
Jose Daniel G. Percy 2025-04-26 03:17:22 +08:00
parent a74707dc1f
commit f4409eb258

View File

@ -31,6 +31,16 @@ AUTHELIA_CONFIG_BACKUP="authelia/configuration.${TIMESTAMP}.bak"
COMPOSE_FILE="docker-compose.yml"
COMPOSE_BACKUP="docker-compose.${TIMESTAMP}.bak"
# Check if yq is installed
check_yq() {
if ! command -v yq &> /dev/null; then
echo -e "${YELLOW}Warning: 'yq' is not installed. While not required, it provides better YAML handling.${NC}"
echo -e "${YELLOW}Installation instructions: https://github.com/mikefarah/yq#install${NC}"
return 1
fi
return 0
}
# Print section header
print_header() {
echo -e "\n${CYAN}${BOLD}$1${NC}"
@ -368,14 +378,31 @@ generate_passphrase() {
# PART 4: Authentication Management
##################################################
# Get the current auth status for a service
get_auth_status() {
local service=$1
if grep -q "traefik.http.routers.$service.middlewares=.*authelia-auth" "$COMPOSE_FILE"; then
echo "enabled"
elif grep -q "traefik.http.routers.$service.middlewares=" "$COMPOSE_FILE"; then
echo "disabled"
# Use yq if available for more reliable YAML parsing
if command -v yq &> /dev/null; then
local middlewares=$(yq e ".services.$service.labels[] | select(contains(\"traefik.http.routers.$service.middlewares=\"))" "$COMPOSE_FILE" 2>/dev/null)
if [ -n "$middlewares" ]; then
if echo "$middlewares" | grep -q "authelia-auth"; then
echo "enabled"
else
echo "disabled"
fi
else
echo "unknown"
fi
else
echo "unknown"
# Fall back to grep if yq isn't available
if grep -q "traefik.http.routers.$service.middlewares=.*authelia-auth" "$COMPOSE_FILE"; then
echo "enabled"
elif grep -q "traefik.http.routers.$service.middlewares=" "$COMPOSE_FILE"; then
echo "disabled"
else
echo "unknown"
fi
fi
}
@ -383,6 +410,66 @@ enable_auth() {
local service=$1
echo -e "${BLUE}Enabling authentication for $service...${NC}"
# Check if service exists in the compose file
# Use yq if available for more reliable parsing
if command -v yq &> /dev/null; then
# Check if service exists
local service_exists=$(yq e ".services.$service" "$COMPOSE_FILE" 2>/dev/null)
if [ -z "$service_exists" ] || [ "$service_exists" == "null" ]; then
echo -e "${RED}Service $service not found in $COMPOSE_FILE${NC}"
return 1
fi
# Check if auth is already enabled
local middlewares=$(yq e ".services.$service.labels[] | select(contains(\"traefik.http.routers.$service.middlewares=\"))" "$COMPOSE_FILE" 2>/dev/null)
if [ -n "$middlewares" ] && echo "$middlewares" | grep -q "authelia-auth"; then
echo -e "${GREEN}Authentication already enabled for $service${NC}"
return 0
fi
# Create a temporary file for editing with yq
local temp_file="${COMPOSE_FILE}.tmp"
# Check if there's already a middlewares label
if [ -n "$middlewares" ]; then
# Modify the existing middlewares label to include authelia-auth
if echo "$middlewares" | grep -q "middlewares=$"; then
# Empty middlewares
yq e ".services.$service.labels |= map(. | select(contains(\"traefik.http.routers.$service.middlewares=\")) |= \"traefik.http.routers.$service.middlewares=authelia-auth@docker\" // .)" "$COMPOSE_FILE" > "$temp_file"
elif echo "$middlewares" | grep -q "middlewares=.*@docker"; then
# Has other middlewares with @docker
local current_value=$(echo "$middlewares" | sed 's/.*middlewares=\(.*\)@docker.*/\1/')
yq e ".services.$service.labels |= map(. | select(contains(\"traefik.http.routers.$service.middlewares=\")) |= \"traefik.http.routers.$service.middlewares=${current_value},authelia-auth@docker\" // .)" "$COMPOSE_FILE" > "$temp_file"
else
# Has middlewares without @docker
local current_value=$(echo "$middlewares" | sed 's/.*middlewares=\(.*\).*/\1/')
yq e ".services.$service.labels |= map(. | select(contains(\"traefik.http.routers.$service.middlewares=\")) |= \"traefik.http.routers.$service.middlewares=${current_value},authelia-auth@docker\" // .)" "$COMPOSE_FILE" > "$temp_file"
fi
else
# Need to add a new middlewares label
# Check if the service has any labels
local has_labels=$(yq e ".services.$service.labels" "$COMPOSE_FILE" 2>/dev/null)
if [ -z "$has_labels" ] || [ "$has_labels" == "null" ]; then
# Add a new labels array with the middlewares
yq e ".services.$service.labels = [\"traefik.http.routers.$service.middlewares=authelia-auth@docker\"]" "$COMPOSE_FILE" > "$temp_file"
else
# Add the middlewares to the existing labels
yq e ".services.$service.labels += [\"traefik.http.routers.$service.middlewares=authelia-auth@docker\"]" "$COMPOSE_FILE" > "$temp_file"
fi
fi
# Replace the original file if the temp file was created successfully
if [ $? -eq 0 ] && [ -f "$temp_file" ]; then
mv "$temp_file" "$COMPOSE_FILE"
echo -e "${GREEN}Authentication enabled for $service${NC}"
return 0
else
echo -e "${RED}Failed to update the compose file with yq. Falling back to sed.${NC}"
rm -f "$temp_file" # Clean up if the temp file exists
fi
fi
# Fall back to the existing implementation if yq is not available or failed
if ! grep -q "container_name: $service" "$COMPOSE_FILE"; then
echo -e "${RED}Service $service not found in $COMPOSE_FILE${NC}"
return 1
@ -443,6 +530,63 @@ disable_auth() {
local service=$1
echo -e "${BLUE}Disabling authentication for $service...${NC}"
# Use yq if available for more reliable parsing
if command -v yq &> /dev/null; then
# Check if service exists
local service_exists=$(yq e ".services.$service" "$COMPOSE_FILE" 2>/dev/null)
if [ -z "$service_exists" ] || [ "$service_exists" == "null" ]; then
echo -e "${RED}Service $service not found in $COMPOSE_FILE${NC}"
return 1
fi
# Check if auth is already disabled
local middlewares=$(yq e ".services.$service.labels[] | select(contains(\"traefik.http.routers.$service.middlewares=\"))" "$COMPOSE_FILE" 2>/dev/null)
if [ -n "$middlewares" ] && ! echo "$middlewares" | grep -q "authelia-auth"; then
echo -e "${GREEN}Authentication already disabled for $service${NC}"
return 0
fi
# Create a temporary file for editing with yq
local temp_file="${COMPOSE_FILE}.tmp"
# Extract current middlewares
if [ -n "$middlewares" ]; then
if echo "$middlewares" | grep -q "traefik.http.routers.$service.middlewares=authelia-auth@docker$"; then
# Only authelia-auth@docker, replace with empty
yq e ".services.$service.labels |= map(. | select(contains(\"traefik.http.routers.$service.middlewares=\")) |= \"traefik.http.routers.$service.middlewares=\" // .)" "$COMPOSE_FILE" > "$temp_file"
elif echo "$middlewares" | grep -q "traefik.http.routers.$service.middlewares=authelia-auth@docker,"; then
# authelia-auth@docker at start with comma
local remaining=$(echo "$middlewares" | sed 's/.*middlewares=authelia-auth@docker,\(.*\)/\1/')
yq e ".services.$service.labels |= map(. | select(contains(\"traefik.http.routers.$service.middlewares=\")) |= \"traefik.http.routers.$service.middlewares=${remaining}\" // .)" "$COMPOSE_FILE" > "$temp_file"
elif echo "$middlewares" | grep -q "traefik.http.routers.$service.middlewares=.*,authelia-auth@docker$"; then
# authelia-auth@docker at end with comma
local remaining=$(echo "$middlewares" | sed 's/\(.*\),authelia-auth@docker$/\1/')
yq e ".services.$service.labels |= map(. | select(contains(\"traefik.http.routers.$service.middlewares=\")) |= \"traefik.http.routers.$service.middlewares=${remaining}\" // .)" "$COMPOSE_FILE" > "$temp_file"
elif echo "$middlewares" | grep -q "traefik.http.routers.$service.middlewares=.*,authelia-auth@docker,.*"; then
# authelia-auth@docker in the middle with commas
local remaining=$(echo "$middlewares" | sed 's/\(.*\),authelia-auth@docker,\(.*\)/\1,\2/')
yq e ".services.$service.labels |= map(. | select(contains(\"traefik.http.routers.$service.middlewares=\")) |= \"traefik.http.routers.$service.middlewares=${remaining}\" // .)" "$COMPOSE_FILE" > "$temp_file"
else
echo -e "${RED}Could not determine how to remove authelia-auth from middlewares for $service${NC}"
return 1
fi
# Replace the original file if the temp file was created successfully
if [ $? -eq 0 ] && [ -f "$temp_file" ]; then
mv "$temp_file" "$COMPOSE_FILE"
echo -e "${GREEN}Authentication disabled for $service${NC}"
return 0
else
echo -e "${RED}Failed to update the compose file with yq. Falling back to sed.${NC}"
rm -f "$temp_file" # Clean up if the temp file exists
fi
else
echo -e "${GREEN}No middlewares found for $service, authentication is already disabled${NC}"
return 0
fi
fi
# Fall back to the existing implementation if yq is not available or failed
if ! grep -q "container_name: $service" "$COMPOSE_FILE"; then
echo -e "${RED}Service $service not found in $COMPOSE_FILE${NC}"
return 1
@ -473,39 +617,91 @@ disable_auth() {
list_services() {
print_header "Services Authentication Status"
# Check if file exists
if ! check_file "$COMPOSE_FILE"; then
echo -e "${RED}Error: $COMPOSE_FILE doesn't exist${NC}"
return 1
fi
# Show a warning if we're not creating a backup for this operation
echo -e "${BLUE}Checking services in $COMPOSE_FILE...${NC}"
echo -e "${CYAN}SERVICE\t\tAUTH STATUS${NC}"
echo -e "${CYAN}-------\t\t-----------${NC}"
local services=$(grep "container_name:" "$COMPOSE_FILE" | awk '{print $3}')
local service_count=0
for service in $services; do
if [[ "$service" == "redis" || "$service" == "authelia" || "$service" == "traefik" || "$service" == "tailscale" || "$service" == "watchtower" || "$service" == "autoheal" || "$service" == "middlewares" ]]; then
continue
fi
# Use yq if available for more reliable parsing
if command -v yq &> /dev/null; then
# Get all services from the docker-compose.yml file
local services=$(yq e '.services | keys | .[]' "$COMPOSE_FILE" 2>/dev/null)
local status=$(get_auth_status "$service")
for service in $services; do
# Skip infrastructure containers
if [[ "$service" == "redis" || "$service" == "authelia" || "$service" == "traefik" || "$service" == "tailscale" || "$service" == "watchtower" || "$service" == "autoheal" || "$service" == "middlewares" ]]; then
continue
fi
# Check if this service has Traefik router configured
local has_router=$(yq e ".services.$service.labels[] | select(contains(\"traefik.http.routers.$service\"))" "$COMPOSE_FILE" 2>/dev/null)
if [ -n "$has_router" ]; then
local status=$(get_auth_status "$service")
# Format the output with padding
printf "${BOLD}%-20s${NC}" "$service"
case "$status" in
"enabled")
echo -e "${GREEN}Enabled${NC}"
service_count=$((service_count + 1))
;;
"disabled")
echo -e "${YELLOW}Disabled${NC}"
service_count=$((service_count + 1))
;;
*)
echo -e "${RED}Unknown${NC}"
;;
esac
fi
done
else
# Fallback to using grep for parsing (less reliable)
# First identify all container names
local services=$(grep "container_name:" "$COMPOSE_FILE" | awk '{print $3}')
printf "${BOLD}%-20s${NC}" "$service"
case "$status" in
"enabled")
echo -e "${GREEN}Enabled${NC}"
service_count=$((service_count + 1))
;;
"disabled")
echo -e "${YELLOW}Disabled${NC}"
service_count=$((service_count + 1))
;;
*)
echo -e "${RED}Unknown${NC}"
;;
esac
done
for service in $services; do
# Skip infrastructure containers
if [[ "$service" == "redis" || "$service" == "authelia" || "$service" == "traefik" || "$service" == "tailscale" || "$service" == "watchtower" || "$service" == "autoheal" || "$service" == "middlewares" ]]; then
continue
fi
# Look specifically for router configuration for this service
if grep -q "traefik.http.routers.$service" "$COMPOSE_FILE"; then
local status=$(get_auth_status "$service")
printf "${BOLD}%-20s${NC}" "$service"
case "$status" in
"enabled")
echo -e "${GREEN}Enabled${NC}"
service_count=$((service_count + 1))
;;
"disabled")
echo -e "${YELLOW}Disabled${NC}"
service_count=$((service_count + 1))
;;
*)
echo -e "${RED}Unknown${NC}"
;;
esac
fi
done
fi
if [ $service_count -eq 0 ]; then
echo -e "${YELLOW}No services found with authentication status.${NC}"
echo -e "${YELLOW}This could indicate that no services are configured with Traefik routers,${NC}"
echo -e "${YELLOW}or that the compose file has an unexpected structure.${NC}"
fi
}