mirror of
				https://github.com/community-scripts/ProxmoxVE.git
				synced 2025-11-04 10:22:50 +00:00 
			
		
		
		
	New Script: LXC IP-Tag (#536)
* New Script: LXC IP-Tag * add comma in json * Update misc/add-lxc-iptag.sh Co-authored-by: Håvard Gjøby Thom <34199185+havardthom@users.noreply.github.com> * Update json/add-lxc-iptag.json Co-authored-by: Håvard Gjøby Thom <34199185+havardthom@users.noreply.github.com> * Update json/add-lxc-iptag.json Co-authored-by: Håvard Gjøby Thom <34199185+havardthom@users.noreply.github.com> * remove files * Full-Update to Single-File * Finalo * Update add-lxc-iptag.json --------- Co-authored-by: Håvard Gjøby Thom <34199185+havardthom@users.noreply.github.com>
This commit is contained in:
		
							
								
								
									
										43
									
								
								json/add-lxc-iptag.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								json/add-lxc-iptag.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "Proxmox VE LXC IP-Tag",
 | 
			
		||||
    "slug": "add-lxc-iptag",
 | 
			
		||||
    "categories": [
 | 
			
		||||
        1
 | 
			
		||||
    ],
 | 
			
		||||
    "date_created": "2024-11-27",
 | 
			
		||||
    "type": "misc",
 | 
			
		||||
    "updateable": false,
 | 
			
		||||
    "privileged": false,
 | 
			
		||||
    "interface_port": null,
 | 
			
		||||
    "documentation": null,
 | 
			
		||||
    "website": null,
 | 
			
		||||
    "logo": "https://raw.githubusercontent.com/home-assistant/brands/master/core_integrations/proxmoxve/icon.png",
 | 
			
		||||
    "description": "This script automatically adds IP address as tags to LXC containers using a Systemd service. The service also updates the tags if a LXC IP address is changed.",
 | 
			
		||||
    "install_methods": [
 | 
			
		||||
        {
 | 
			
		||||
            "type": "default",
 | 
			
		||||
            "script": "misc/add-lxc-iptag.sh",
 | 
			
		||||
            "resources": {
 | 
			
		||||
                "cpu": null,
 | 
			
		||||
                "ram": null,
 | 
			
		||||
                "hdd": null,
 | 
			
		||||
                "os": null,
 | 
			
		||||
                "version": null
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "default_credentials": {
 | 
			
		||||
        "username": null,
 | 
			
		||||
        "password": null
 | 
			
		||||
    },
 | 
			
		||||
    "notes": [
 | 
			
		||||
        {
 | 
			
		||||
            "text": "Execute within the Proxmox shell",
 | 
			
		||||
            "type": "Info"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "text": "Configuration: `nano /opt/lxc-iptag/iptag.conf`. iptag.service must be restarted after change.",
 | 
			
		||||
            "type": "Info"
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										351
									
								
								misc/add-lxc-iptag.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										351
									
								
								misc/add-lxc-iptag.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,351 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
# Copyright (c) 2021-2024 community-scripts ORG
 | 
			
		||||
# Author: MickLesk (Canbiz)
 | 
			
		||||
# License: MIT
 | 
			
		||||
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
			
		||||
# Source: https://github.com/gitsang/lxc-iptag
 | 
			
		||||
 | 
			
		||||
function header_info {
 | 
			
		||||
  clear
 | 
			
		||||
  cat <<"EOF"
 | 
			
		||||
    __   _  ________   ________      ______           
 | 
			
		||||
   / /  | |/ / ____/  /  _/ __ \    /_  __/___ _____ _
 | 
			
		||||
  / /   |   / /       / // /_/ /_____/ / / __ `/ __ `/
 | 
			
		||||
 / /___/   / /___   _/ // ____/_____/ / / /_/ / /_/ / 
 | 
			
		||||
/_____/_/|_\____/  /___/_/         /_/  \__,_/\__, /  
 | 
			
		||||
                                             /____/   
 | 
			
		||||
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() {
 | 
			
		||||
  if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID > /dev/null; then kill $SPINNER_PID > /dev/null; fi
 | 
			
		||||
  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"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
spinner() {
 | 
			
		||||
    local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
 | 
			
		||||
    local spin_i=0
 | 
			
		||||
    local interval=0.1 
 | 
			
		||||
    printf "\e[?25l"  
 | 
			
		||||
    local orange="\e[38;5;214m"
 | 
			
		||||
 | 
			
		||||
    while true; do
 | 
			
		||||
        printf "\r ${orange}%s\e[0m " "${frames[spin_i]}"
 | 
			
		||||
        spin_i=$(( (spin_i + 1) % ${#frames[@]} ))
 | 
			
		||||
        sleep "$interval"
 | 
			
		||||
    done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# This function displays an informational message with a yellow color.
 | 
			
		||||
msg_info() {
 | 
			
		||||
    local msg="$1"
 | 
			
		||||
    echo -ne " ${HOLD} ${YW}${msg}   "
 | 
			
		||||
    spinner & 
 | 
			
		||||
    SPINNER_PID=$!  
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# This function displays a success message with a green color.
 | 
			
		||||
msg_ok() {
 | 
			
		||||
  if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID > /dev/null; then kill $SPINNER_PID > /dev/null; fi
 | 
			
		||||
  printf "\e[?25h"
 | 
			
		||||
  local msg="$1"
 | 
			
		||||
  echo -e "${BFR}${CM} ${GN}${msg}${CL}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# This function displays a error message with a red color.
 | 
			
		||||
msg_error() {
 | 
			
		||||
  if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID > /dev/null; then kill $SPINNER_PID > /dev/null; fi
 | 
			
		||||
  printf "\e[?25h"
 | 
			
		||||
  local msg="$1"
 | 
			
		||||
  echo -e "${BFR}${CROSS} ${RD}${msg}${CL}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
while true; do
 | 
			
		||||
    read -p "This will install ${APP} on ${hostname}. Proceed? (y/n): " yn
 | 
			
		||||
    case $yn in
 | 
			
		||||
    [Yy]*) break ;;
 | 
			
		||||
    [Nn]*) msg_info "Installation cancelled."; exit ;;
 | 
			
		||||
    *) msg_info "Please answer yes or no." ;;
 | 
			
		||||
    esac
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
if ! pveversion | grep -Eq "pve-manager/8.[0-3]"; then
 | 
			
		||||
  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
 | 
			
		||||
 | 
			
		||||
msg_info "Setup Default Config"
 | 
			
		||||
if [[ ! -f /opt/lxc-iptag/iptag.conf ]]; then
 | 
			
		||||
    cat <<EOF > /opt/lxc-iptag/iptag.conf
 | 
			
		||||
# Configuration file for LXC IP tagging
 | 
			
		||||
 | 
			
		||||
# List of allowed CIDRs
 | 
			
		||||
CIDR_LIST=(
 | 
			
		||||
    192.168.0.0/16
 | 
			
		||||
    100.64.0.0/10
 | 
			
		||||
    10.0.0.0/8
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# Interval settings (in seconds)
 | 
			
		||||
LOOP_INTERVAL=60
 | 
			
		||||
FW_NET_INTERFACE_CHECK_INTERVAL=60
 | 
			
		||||
LXC_STATUS_CHECK_INTERVAL=-1
 | 
			
		||||
FORCE_UPDATE_INTERVAL=1800
 | 
			
		||||
EOF
 | 
			
		||||
    msg_ok "Setup default config"
 | 
			
		||||
else
 | 
			
		||||
    msg_ok "Default config already exists"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
msg_info "Setup Main Function"
 | 
			
		||||
if [[ ! -f /opt/lxc-iptag/iptag ]]; then
 | 
			
		||||
    cat <<'EOF' > /opt/lxc-iptag/iptag
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
# =============== CONFIGURATION =============== #
 | 
			
		||||
 | 
			
		||||
CONFIG_FILE="/opt/lxc-iptag/iptag.conf"
 | 
			
		||||
 | 
			
		||||
# Load the configuration file if it exists
 | 
			
		||||
if [ -f "$CONFIG_FILE" ]; then
 | 
			
		||||
    # shellcheck source=./lxc-iptag.conf
 | 
			
		||||
    source "$CONFIG_FILE"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Convert IP to integer for comparison
 | 
			
		||||
ip_to_int() {
 | 
			
		||||
    local ip="${1}"
 | 
			
		||||
    local a b c d
 | 
			
		||||
 | 
			
		||||
    IFS=. read -r a b c d <<< "${ip}"
 | 
			
		||||
    echo "$((a << 24 | b << 16 | c << 8 | d))"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Check if IP is in CIDR
 | 
			
		||||
ip_in_cidr() {
 | 
			
		||||
    local ip="${1}"
 | 
			
		||||
    local cidr="${2}"
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Check if IP is in any CIDRs
 | 
			
		||||
ip_in_cidrs() {
 | 
			
		||||
    local ip="${1}"
 | 
			
		||||
    local cidrs=()
 | 
			
		||||
 | 
			
		||||
    mapfile -t cidrs < <(echo "${2}" | tr ' ' '\n')
 | 
			
		||||
    for cidr in "${cidrs[@]}"; do
 | 
			
		||||
        ip_in_cidr "${ip}" "${cidr}" && return 0
 | 
			
		||||
    done
 | 
			
		||||
 | 
			
		||||
    return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Check if IP is valid
 | 
			
		||||
is_valid_ipv4() {
 | 
			
		||||
    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
 | 
			
		||||
                return 1
 | 
			
		||||
            fi
 | 
			
		||||
        done
 | 
			
		||||
        return 0
 | 
			
		||||
    else
 | 
			
		||||
        return 1
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lxc_status_changed() {
 | 
			
		||||
    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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fw_net_interface_changed() {
 | 
			
		||||
    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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# =============== MAIN =============== #
 | 
			
		||||
 | 
			
		||||
update_lxc_iptags() {
 | 
			
		||||
    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}")
 | 
			
		||||
        done
 | 
			
		||||
 | 
			
		||||
        # 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
 | 
			
		||||
 | 
			
		||||
        # 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
 | 
			
		||||
        fi
 | 
			
		||||
 | 
			
		||||
        # 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
 | 
			
		||||
        fi
 | 
			
		||||
    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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# main: Set the IP tags for all LXC containers
 | 
			
		||||
main() {
 | 
			
		||||
    while true; do
 | 
			
		||||
        check
 | 
			
		||||
        sleep "${LOOP_INTERVAL}"
 | 
			
		||||
    done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main
 | 
			
		||||
EOF
 | 
			
		||||
    msg_ok "Setup Main Function"
 | 
			
		||||
else
 | 
			
		||||
    msg_ok "Main Function already exists"
 | 
			
		||||
fi
 | 
			
		||||
chmod +x /opt/lxc-iptag/iptag 
 | 
			
		||||
 | 
			
		||||
msg_info "Creating Service"
 | 
			
		||||
if [[ ! -f /lib/systemd/system/iptag.service ]]; then
 | 
			
		||||
    echo "Systemd service file not found. Creating it now..."
 | 
			
		||||
    cat <<EOF > /lib/systemd/system/iptag.service
 | 
			
		||||
[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
 | 
			
		||||
    msg_ok "Created Service"
 | 
			
		||||
else
 | 
			
		||||
    msg_ok "Service already exists."
 | 
			
		||||
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"
 | 
			
		||||
 | 
			
		||||
echo -e "\n${APP} installation completed successfully! ${CL}\n"
 | 
			
		||||
		Reference in New Issue
	
	Block a user