Compare commits

...

No commits in common. "1cf3e830d3400458686986dcff9f74039da69e7b" and "afa0616c37eb5160a9c3d02818d05d3bd05900c7" have entirely different histories.

10 changed files with 195 additions and 246 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Sang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

71
README.md Normal file
View File

@ -0,0 +1,71 @@
# lxc-iptag
lxc-iptag is a simple script to add ip tags to LXC containers.
![](./img/pve-lxc-iptag.png)
This script is only a transitional solution for the official UI to support displaying IPs of LXC containers. See [Displaying IP of container in GUI?](https://forum.proxmox.com/threads/displaying-ip-of-container-in-gui.120841/#post-685752)
> My original intention was to provide a script that is simple enough and easy to review, avoiding introducing too much complexity as much as possible. After all, this script may be replaced by official support in the near future, and shell scripts and tags are certainly not the best solution. Therefore, this script may not consider introducing too much complex logic or requirements.
>
> Simple scripts allow users to easily modify the script according to their needs with just a little knowledge of Shell scripting. So feel free to fork it. And to save your time, https://github.com/MorsStefan/proxmox-ip2tag is a cool project that supports more features, especially adding iptag support for VMs. You can first check if this project meets your needs.
## 1. Installation
```sh
curl -sL https://github.com/gitsang/lxc-iptag/raw/main/install.sh | bash
```
This script will:
- Install script prerequisites
- Install the `lxc-iptag` script to `/usr/local/bin/lxc-iptag`
- Copy config file to `/usr/local/etc/lxc-iptag.conf`
- Add a systemd unit to start the service
### 1.1 Update
```sh
curl -sSL https://raw.githubusercontent.com/gitsang/lxc-iptag/main/lxc-iptag -o /usr/local/bin/lxc-iptag && chmod +x /usr/local/bin/lxc-iptag
systemctl restart lxc-iptag.service
```
This script will only update the `lxc-iptag` executable script
## 2. Configure
Open `/usr/local/etc/lxc-iptag.conf` and change the config
| Option | Example | Description |
| ------------------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| CIDR_LIST | `(192.168.0.0/16 172.16.0.0/12 10.0.0.0/8)` | IP filter list in CIDR format |
| LOOP_INTERVAL | `60` | Main loop interval(seconds) |
| FW_NET_INTERFACE_CHECK_INTERVAL | `60` | The interval(seconds) for using `ip link` to check lxc status changed (Set -1 to disable this feature) |
| LXC_STATUS_CHECK_INTERVAL | `-1` | The interval(seconds) for using `pct list` to check lxc status changed (Set -1 to disable this feature) |
| FORCE_UPDATE_INTERVAL | `1800` | The interval(seconds) for force check and update lxc tags |
| EXCLUSION_LIST | see [lxc-iptag.conf](./lxc-iptag.conf) | List of container id or ip to exclude from tagging |
## 3. Uninstall
```sh
# stop lxc-iptag
systemctl stop lxc-iptag.service
systemctl disable lxc-iptag.service
# remove lxc-iptag
rm -f /lib/systemd/system/lxc-iptag.service
rm -f /usr/local/etc/lxc-iptag.conf
rm -f /usr/local/bin/lxc-iptag
```
If you want to remove all lxc-iptag related datas (includes all ip tags), run the following command:
This script will:
- Stop and disable `lxc-iptag` systemd service
- Remove pve all lxc ip tags
- Delete all lxc-iptag related systemd unit, config file and script
```sh
curl -sL https://github.com/gitsang/lxc-iptag/raw/main/uninstall.sh | bash
```

BIN
img/pve-lxc-iptag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

18
install.sh Normal file
View File

@ -0,0 +1,18 @@
#!/bin/bash
set -xe
# install prerequisites
apt install -y ipcalc
# install lxc-iptag
curl -sSL https://raw.githubusercontent.com/gitsang/lxc-iptag/main/lxc-iptag -o /usr/local/bin/lxc-iptag && chmod +x /usr/local/bin/lxc-iptag
curl -sSL https://raw.githubusercontent.com/gitsang/lxc-iptag/main/lxc-iptag.conf -o /usr/local/etc/lxc-iptag.conf
# configure lxc-iptag systemd
curl -sSL https://raw.githubusercontent.com/gitsang/lxc-iptag/main/lxc-iptag.service -o /etc/systemd/system/lxc-iptag.service
# start lxc-iptag
systemctl daemon-reload
systemctl enable lxc-iptag.service
systemctl start lxc-iptag.service

View File

@ -1,15 +0,0 @@
# Configuration file for LXC IP tagging
# List of allowed CIDRs
CIDR_LIST=(
192.168.0.0/16
172.16.0.0/12
10.0.0.0/8
100.64.0.0/10
)
# Interval settings (in seconds)
LOOP_INTERVAL=60
FW_NET_INTERFACE_CHECK_INTERVAL=60
LXC_STATUS_CHECK_INTERVAL=-1
FORCE_UPDATE_INTERVAL=1800

168
iptag.ori
View File

@ -1,168 +0,0 @@
#!/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

View File

@ -2,16 +2,20 @@
# =============== CONFIGURATION =============== #
# The first three are RFC1918 addresses which should be used inside your local network.
# The last one is the RFC6598 address pool (CGNAT) used for ISPs to allocate to networks
# without allocating all the routers public IP addresses. It has also been used by a lot
# of VPN services for their internal addressing (e.g. Tailscale, Netbird).
CIDR_LIST=(
192.168.0.0/16
100.64.0.0/10
172.16.0.0/12
10.0.0.0/8
100.64.0.0/10
)
LOOP_INTERVAL=60
FW_NET_INTERFACE_CHECK_INTERVAL=60
LXC_STATUS_CHECK_INTERVAL=-1
FORCE_UPDATE_INTERVAL=1800
VM_STATUS_CHECK_INTERVAL=60
if [ -f "/usr/local/etc/lxc-iptag.conf" ]; then
# shellcheck source=./lxc-iptag.conf
@ -94,56 +98,6 @@ fw_net_interface_changed() {
# =============== MAIN =============== #
update_vm_iptags() {
vmid_list=$(qm list 2>/dev/null | grep -v VMID | awk '{print $1}')
for vmid in ${vmid_list}; do
# Check if the VM ID is in the exclusion list
if [[ " ${EXCLUSION_LIST[*]} " == *" ${vmid} "* ]]; then
echo "Skipping ${vmid} as it is in the exclusion list"
continue
fi
last_tagged_ips=()
current_valid_ips=()
next_tags=()
# Parse current tags
mapfile -t current_tags < <(qm 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=$(qm guest cmd "${vmid}" network-get-interfaces | jq -r '.[] | .["ip-addresses"][]? | ."ip-address"')
for ip in ${current_ips_full}; do
# Check if the IP is in the exclusion list
if [[ " ${EXCLUSION_LIST[*]} " == *" ${ip} "* ]]; then
echo "Skipping IP ${ip} for VM ${vmid} as it is in the exclusion list"
continue
fi
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[*]}"
qm set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")"
done
}
update_lxc_iptags() {
vmid_list=$(pct list 2>/dev/null | grep -v VMID | awk '{print $1}')
for vmid in ${vmid_list}; do
@ -198,7 +152,6 @@ init() {
current_time=$(date +%s)
last_lxc_status_check_time=${current_time}
last_fw_net_interface_check_time=${current_time}
last_vm_status_check_time=${current_time}
fw_net_interface_changed
last_update_time=0
}
@ -230,16 +183,6 @@ check() {
fi
fi
time_since_last_vm_status_check=$((current_time - last_vm_status_check_time))
if [[ "${VM_STATUS_CHECK_INTERVAL}" -gt 0 ]] \
&& [[ "${time_since_last_vm_status_check}" -ge "${VM_STATUS_CHECK_INTERVAL}" ]]; then
echo "Checking VM status..."
last_vm_status_check_time=${current_time}
update_vm_iptags
last_update_time=${current_time}
return
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..."

22
lxc-iptag.conf Normal file
View File

@ -0,0 +1,22 @@
# The first three are RFC1918 addresses which should be used inside your local network.
# The last one is the RFC6598 address pool (CGNAT) used for ISPs to allocate to networks
# without allocating all the routers public IP addresses. It has also been used by a lot
# of VPN services for their internal addressing (e.g. Tailscale, Netbird).
CIDR_LIST=(
192.168.0.0/16
172.16.0.0/12
10.0.0.0/8
100.64.0.0/10
)
LOOP_INTERVAL=60
FW_NET_INTERFACE_CHECK_INTERVAL=60
LXC_STATUS_CHECK_INTERVAL=-1
FORCE_UPDATE_INTERVAL=1800
# Exclusion list for IPs or container IDs
EXCLUSION_LIST=(
# Add IPs or container IDs here
# Example:
# 192.168.1.100
# container_id_1
)

11
lxc-iptag.service Normal file
View File

@ -0,0 +1,11 @@
[Unit]
Description=Start lxc-iptag service
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/lxc-iptag
Restart=always
[Install]
WantedBy=multi-user.target

46
uninstall.sh Normal file
View File

@ -0,0 +1,46 @@
#!/bin/bash
set -xe
# stop lxc-iptag
systemctl stop lxc-iptag.service
systemctl disable lxc-iptag.service
# clean ip tags
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
}
vmid_list=$(pct list 2>/dev/null | grep -v VMID | awk '{print $1}')
for vmid in ${vmid_list}; do
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
next_tags+=("${current_tag}")
fi
done
# Set tags
echo "Setting ${vmid} tags from ${current_tags[*]} to ${next_tags[*]}"
pct set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")"
done
# remove lxc-iptag
rm -f /lib/systemd/system/lxc-iptag.service
rm -f /usr/local/etc/lxc-iptag.conf
rm -f /usr/local/bin/lxc-iptag