2024-12-16 13:47:16 +01:00
#!/usr/bin/env bash
2025-01-01 13:37:29 +01:00
# Copyright (c) 2021-2025 community-scripts ORG
2024-12-16 13:47:16 +01:00
# Author: MickLesk (Canbiz)
# License: MIT
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/gitsang/lxc-iptag
function header_info {
2024-12-17 15:30:51 +01:00
clear
cat <<"EOF"
2024-12-16 13:47:16 +01:00
__ _ ________ ________ ______
/ / | | / / ____/ / _/ __ \ /_ __/___ _____ _
/ / | / / / // /_/ /_____/ / / __ ` / __ ` /
/ /___/ / /___ _/ // ____/_____/ / / /_/ / /_/ /
/_____/_/| _\_ ___/ /___/_/ /_/ \_ _,_/\_ _, /
/____/
EOF
}
clear
header_info
APP = "LXC IP-Tag"
hostname = $( hostname)
# Farbvariablen
YW = $( echo "\033[33m" )
GN = $( echo "\033[1;92m" )
RD = $( echo "\033[01;31m" )
CL = $( echo "\033[m" )
BFR = "\\r\\033[K"
HOLD = " "
CM = " ✔️ ${ CL } "
CROSS = " ✖️ ${ CL } "
# This function enables error handling in the script by setting options and defining a trap for the ERR signal.
catch_errors( ) {
set -Eeuo pipefail
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
}
# This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message.
error_handler( ) {
2024-12-17 15:30:51 +01:00
if [ -n " $SPINNER_PID " ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi
2024-12-16 13:47:16 +01:00
printf "\e[?25h"
local exit_code = " $? "
local line_number = " $1 "
local command = " $2 "
local error_message = " ${ RD } [ERROR] ${ CL } in line ${ RD } $line_number ${ CL } : exit code ${ RD } $exit_code ${ CL } : while executing command ${ YW } $command ${ CL } "
echo -e " \n $error_message \n "
}
2024-12-17 15:30:51 +01:00
# This function displays a spinner.
2024-12-16 13:47:16 +01:00
spinner( ) {
2024-12-17 15:30:51 +01:00
local frames = ( '⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏' )
local spin_i = 0
local interval = 0.1
printf "\e[?25l"
local color = " ${ YWB } "
while true; do
printf " \r ${ color } %s ${ CL } " " ${ frames [spin_i] } "
spin_i = $(( ( spin_i + 1 ) % ${# frames [@] } ))
sleep " $interval "
done
2024-12-16 13:47:16 +01:00
}
# This function displays an informational message with a yellow color.
msg_info( ) {
2024-12-17 15:30:51 +01:00
local msg = " $1 "
echo -ne " ${ TAB } ${ YW } ${ HOLD } ${ msg } ${ HOLD } "
spinner &
SPINNER_PID = $!
2024-12-16 13:47:16 +01:00
}
# This function displays a success message with a green color.
msg_ok( ) {
2024-12-17 15:30:51 +01:00
if [ -n " $SPINNER_PID " ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi
2024-12-16 13:47:16 +01:00
printf "\e[?25h"
local msg = " $1 "
2024-12-17 15:30:51 +01:00
echo -e " ${ BFR } ${ CM } ${ GN } ${ msg } ${ CL } "
2024-12-16 13:47:16 +01:00
}
# This function displays a error message with a red color.
msg_error( ) {
2024-12-17 15:30:51 +01:00
if [ -n " $SPINNER_PID " ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi
2024-12-16 13:47:16 +01:00
printf "\e[?25h"
local msg = " $1 "
2024-12-17 15:30:51 +01:00
echo -e " ${ BFR } ${ CROSS } ${ RD } ${ msg } ${ CL } "
2024-12-16 13:47:16 +01:00
}
while true; do
2024-12-17 15:30:51 +01:00
read -p " This will install ${ APP } on ${ hostname } . Proceed? (y/n): " yn
case $yn in
[ Yy] *) break ; ;
[ Nn] *)
msg_error "Installation cancelled."
exit
; ;
*) msg_error "Please answer yes or no." ; ;
esac
2024-12-16 13:47:16 +01:00
done
2025-01-30 11:26:30 +01:00
if ! pveversion | grep -Eq "pve-manager/8\.[0-3](\.[0-9]+)*" ; then
2024-12-16 13:47:16 +01:00
msg_error "This version of Proxmox Virtual Environment is not supported"
msg_error "⚠️ Requires Proxmox Virtual Environment Version 8.0 or later."
msg_error "Exiting..."
sleep 2
exit
fi
FILE_PATH = "/usr/local/bin/iptag"
if [ [ -f " $FILE_PATH " ] ] ; then
msg_info " The file already exists: ' $FILE_PATH '. Skipping installation. "
exit 0
fi
msg_info "Installing Dependencies"
apt-get update & >/dev/null
apt-get install -y ipcalc net-tools & >/dev/null
msg_ok "Installed Dependencies"
msg_info "Setting up IP-Tag Scripts"
mkdir -p /opt/lxc-iptag
2024-12-17 15:30:51 +01:00
msg_ok "Setup IP-Tag Scripts"
2024-12-16 13:47:16 +01:00
msg_info "Setup Default Config"
if [ [ ! -f /opt/lxc-iptag/iptag.conf ] ] ; then
2024-12-17 15:30:51 +01:00
cat <<EOF >/opt/lxc-iptag/iptag.conf
2024-12-16 13:47:16 +01:00
# Configuration file for LXC IP tagging
# List of allowed CIDRs
CIDR_LIST=(
2024-12-17 15:30:51 +01:00
192.168.0.0/16
2024-12-26 02:36:52 -07:00
172.16.0.0/12
2024-12-17 15:30:51 +01:00
10.0.0.0/8
2024-12-26 02:36:52 -07:00
100.64.0.0/10
2024-12-16 13:47:16 +01:00
)
# Interval settings (in seconds)
LOOP_INTERVAL=60
FW_NET_INTERFACE_CHECK_INTERVAL=60
LXC_STATUS_CHECK_INTERVAL=-1
FORCE_UPDATE_INTERVAL=1800
EOF
2024-12-17 15:30:51 +01:00
msg_ok "Setup default config"
2024-12-16 13:47:16 +01:00
else
2024-12-17 15:30:51 +01:00
msg_ok "Default config already exists"
2024-12-16 13:47:16 +01:00
fi
msg_info "Setup Main Function"
if [ [ ! -f /opt/lxc-iptag/iptag ] ] ; then
2024-12-17 15:30:51 +01:00
cat <<'EOF' >/opt/lxc-iptag/iptag
2024-12-16 13:47:16 +01:00
#!/bin/bash
# =============== CONFIGURATION =============== #
CONFIG_FILE="/opt/lxc-iptag/iptag.conf"
# Load the configuration file if it exists
if [ -f "$CONFIG_FILE" ]; then
2024-12-17 15:30:51 +01:00
# shellcheck source=./lxc-iptag.conf
source "$CONFIG_FILE"
2024-12-16 13:47:16 +01:00
fi
# Convert IP to integer for comparison
ip_to_int() {
2024-12-17 15:30:51 +01:00
local ip="${1}"
local a b c d
2024-12-16 13:47:16 +01:00
2024-12-17 15:30:51 +01:00
IFS=. read -r a b c d <<< "${ip}"
echo "$((a << 24 | b << 16 | c << 8 | d))"
2024-12-16 13:47:16 +01:00
}
# Check if IP is in CIDR
ip_in_cidr() {
2024-12-17 15:30:51 +01:00
local ip="${1}"
local cidr="${2}"
2024-12-16 13:47:16 +01:00
2024-12-17 15:30:51 +01:00
ip_int=$(ip_to_int "${ip}")
netmask_int=$(ip_to_int "$(ipcalc -b "${cidr}" | grep Broadcast | awk '{print $2}')")
masked_ip_int=$(( "${ip_int}" & "${netmask_int}" ))
[[ ${ip_int} -eq ${masked_ip_int} ]] && return 0 || return 1
2024-12-16 13:47:16 +01:00
}
# Check if IP is in any CIDRs
ip_in_cidrs() {
2024-12-17 15:30:51 +01:00
local ip="${1}"
local cidrs=()
2024-12-16 13:47:16 +01:00
2024-12-17 15:30:51 +01:00
mapfile -t cidrs < <(echo "${2}" | tr ' ' '\n')
for cidr in "${cidrs[@]}"; do
ip_in_cidr "${ip}" "${cidr}" && return 0
done
2024-12-16 13:47:16 +01:00
2024-12-17 15:30:51 +01:00
return 1
2024-12-16 13:47:16 +01:00
}
# Check if IP is valid
is_valid_ipv4() {
2024-12-17 15:30:51 +01:00
local ip=$1
local regex="^([0-9]{1,3}\.){3}[0-9]{1,3}$"
if [[ $ip =~ $regex ]]; then
IFS='.' read -r -a parts <<< "$ip"
for part in "${parts[@]}"; do
if ! [[ $part =~ ^[0-9]+$ ]] || ((part < 0 || part > 255)); then
2024-12-16 13:47:16 +01:00
return 1
2024-12-17 15:30:51 +01:00
fi
done
return 0
else
return 1
fi
2024-12-16 13:47:16 +01:00
}
lxc_status_changed() {
2024-12-17 15:30:51 +01:00
current_lxc_status=$(pct list 2>/dev/null)
if [ "${last_lxc_status}" == "${current_lxc_status}" ]; then
return 1
else
last_lxc_status="${current_lxc_status}"
return 0
fi
2024-12-16 13:47:16 +01:00
}
fw_net_interface_changed() {
2024-12-17 15:30:51 +01:00
current_net_interface=$(ifconfig | grep "^fw")
if [ "${last_net_interface}" == "${current_net_interface}" ]; then
return 1
else
last_net_interface="${current_net_interface}"
return 0
fi
2024-12-16 13:47:16 +01:00
}
# =============== MAIN =============== #
update_lxc_iptags() {
2024-12-17 15:30:51 +01:00
vmid_list=$(pct list 2>/dev/null | grep -v VMID | awk '{print $1}')
for vmid in ${vmid_list}; do
last_tagged_ips=()
current_valid_ips=()
next_tags=()
# Parse current tags
mapfile -t current_tags < <(pct config "${vmid}" | grep tags | awk '{print $2}' | sed 's/;/\n/g')
for current_tag in "${current_tags[@]}"; do
if is_valid_ipv4 "${current_tag}"; then
last_tagged_ips+=("${current_tag}")
continue
fi
next_tags+=("${current_tag}")
2024-12-16 13:47:16 +01:00
done
2024-12-17 15:30:51 +01:00
# Get current IPs
current_ips_full=$(lxc-info -n "${vmid}" -i | awk '{print $2}')
for ip in ${current_ips_full}; do
if is_valid_ipv4 "${ip}" && ip_in_cidrs "${ip}" "${CIDR_LIST[*]}"; then
current_valid_ips+=("${ip}")
next_tags+=("${ip}")
fi
done
2024-12-16 13:47:16 +01:00
2024-12-17 15:30:51 +01:00
# Skip if no ip change
if [[ "$(echo "${last_tagged_ips[@]}" | tr ' ' '\n' | sort -u)" == "$(echo "${current_valid_ips[@]}" | tr ' ' '\n' | sort -u)" ]]; then
echo "Skipping ${vmid} cause ip no changes"
continue
2024-12-16 13:47:16 +01:00
fi
2024-12-17 15:30:51 +01:00
# Set tags
echo "Setting ${vmid} tags from ${current_tags[*]} to ${next_tags[*]}"
pct set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")"
done
}
check() {
current_time=$(date +%s)
time_since_last_lxc_status_check=$((current_time - last_lxc_status_check_time))
if [[ "${LXC_STATUS_CHECK_INTERVAL}" -gt 0 ]] \
&& [[ "${time_since_last_lxc_status_check}" -ge "${STATUS_CHECK_INTERVAL}" ]]; then
echo "Checking lxc status..."
last_lxc_status_check_time=${current_time}
if lxc_status_changed; then
update_lxc_iptags
last_update_time=${current_time}
return
2024-12-16 13:47:16 +01:00
fi
2024-12-17 15:30:51 +01:00
fi
time_since_last_fw_net_interface_check=$((current_time - last_fw_net_interface_check_time))
if [[ "${FW_NET_INTERFACE_CHECK_INTERVAL}" -gt 0 ]] \
&& [[ "${time_since_last_fw_net_interface_check}" -ge "${FW_NET_INTERFACE_CHECK_INTERVAL}" ]]; then
echo "Checking fw net interface..."
last_fw_net_interface_check_time=${current_time}
if fw_net_interface_changed; then
update_lxc_iptags
last_update_time=${current_time}
return
fi
fi
time_since_last_update=$((current_time - last_update_time))
if [ ${time_since_last_update} -ge ${FORCE_UPDATE_INTERVAL} ]; then
echo "Force updating lxc iptags..."
update_lxc_iptags
last_update_time=${current_time}
return
fi
2024-12-16 13:47:16 +01:00
}
# main: Set the IP tags for all LXC containers
main() {
2024-12-17 15:30:51 +01:00
while true; do
check
sleep "${LOOP_INTERVAL}"
done
2024-12-16 13:47:16 +01:00
}
main
EOF
2024-12-17 15:30:51 +01:00
msg_ok "Setup Main Function"
2024-12-16 13:47:16 +01:00
else
2024-12-17 15:30:51 +01:00
msg_ok "Main Function already exists"
2024-12-16 13:47:16 +01:00
fi
2024-12-17 15:30:51 +01:00
chmod +x /opt/lxc-iptag/iptag
2024-12-16 13:47:16 +01:00
msg_info "Creating Service"
if [ [ ! -f /lib/systemd/system/iptag.service ] ] ; then
2024-12-17 15:30:51 +01:00
cat <<EOF >/lib/systemd/system/iptag.service
2024-12-16 13:47:16 +01:00
[Unit]
Description=LXC IP-Tag service
After=network.target
[Service]
Type=simple
ExecStart=/opt/lxc-iptag/iptag
Restart=always
[Install]
WantedBy=multi-user.target
EOF
2024-12-17 15:30:51 +01:00
msg_ok "Created Service"
2024-12-16 13:47:16 +01:00
else
2024-12-17 15:30:51 +01:00
msg_ok "Service already exists."
2024-12-16 13:47:16 +01:00
fi
msg_ok "Setup IP-Tag Scripts"
msg_info "Starting Service"
systemctl daemon-reload & >/dev/null
systemctl enable -q --now iptag.service & >/dev/null
msg_ok "Started Service"
2024-12-17 15:30:51 +01:00
SPINNER_PID = ""
2024-12-16 13:47:16 +01:00
echo -e " \n ${ APP } installation completed successfully! ${ CL } \n "