mirror of
https://github.com/andsyrovatko/s4k-admin-toolbox.git
synced 2026-04-21 21:58:54 +02:00
feat(docker): Implemented a robust Bash utility for managing Docker bridge networks.
Key improvements and features:
- Automated IPAM: Scans for available /24 subnets within a defined BASE_NET range.
- Native OS Integration: Forces static bridge names using 'com.docker.network.bridge.name' for easier netfilter/iptables rules.
- Infrastructure Persistence: Tracks managed networks via a flat-file database (NET_FILE).
- Safety Mechanisms:
- Enforced 15-char limit for Linux interface compatibility.
- ShellCheck-validated code with 'set -euo pipefail' (Strict Mode).
- Interactive confirmation for bulk decommissioning.
- Comprehensive Dashboard: Provides 'info' command for real-time network status and IP range overview.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
# 🐳 Docker Network Manager
|
||||
|
||||
A lightweight, production-ready Bash utility to manage external Docker bridge networks with automatic IPAM (IP Address Management). It ensures your network configurations are persistent, documented, and free from subnet overlaps.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Key Features
|
||||
* **Smart Subnet Allocation:** Automatically finds the next available /24 block within your specified range (e.g., `172.30.x.x`).
|
||||
* **Infrastructure as Code (Lite):** Keep your network names in a simple text file; the script handles the rest.
|
||||
* **Safe Operations:**
|
||||
* Interactive confirmation for bulk deletions.
|
||||
* ShellCheck-validated code (Strict Mode: `set -euo pipefail`).
|
||||
* Non-interactive mode support (via `FORCE=true`).
|
||||
* **Status Dashboard:** Instant overview of which tracked networks are `ONLINE` or `OFFLINE`.
|
||||
|
||||
## 🛠 Installation & Setup
|
||||
1. **Clone the repo** (or add the code to your admin toolbox).
|
||||
2. Create the config file:
|
||||
```bash
|
||||
cp docker-network-manager.conf.example docker-network-manager.conf
|
||||
#OR
|
||||
cat <<EOF > docker-network-manager.conf
|
||||
NET_FILE="./dnm-networks.txt"
|
||||
LOG_FILE="./dnm-networks.log"
|
||||
BASE_NET="172.30"
|
||||
START_OCTET=0
|
||||
END_OCTET=255
|
||||
EOF
|
||||
```
|
||||
3. **Make script executable:**
|
||||
```bash
|
||||
chmod +x docker-network-manager.sh
|
||||
```
|
||||
|
||||
## 📖 Usage Examples
|
||||
1. **Check current status**
|
||||
```bash
|
||||
./docker-network-manager.sh info
|
||||
```
|
||||
Example:\
|
||||

|
||||
2. **Provision networks**
|
||||
* **From file:** Add network names to dnm-networks.txt and run:
|
||||
```bash
|
||||
./docker-network-manager.sh create
|
||||
```
|
||||
* **Single network:**
|
||||
```bash
|
||||
./docker-network-manager.sh create br-project-alpha
|
||||
```
|
||||
3. **Decommission networks**
|
||||
* **Remove and cleanup:**
|
||||
```bash
|
||||
./docker-network-manager.sh delete br-project-alpha
|
||||
```
|
||||
* **Purge all (from file):***
|
||||
```bash
|
||||
./docker-network-manager.sh delete
|
||||
```
|
||||
|
||||
### ⚠️ IMPORTANT
|
||||
> **Interface Name Limit**: Linux has a **15-character** limit for network interface names. Ensure your Docker network names stay within this limit to maintain consistent bridge naming.
|
||||
|
||||
## 🧩 Native OS Integration
|
||||
Unlike standard Docker networks that create cryptic interface names (e.g., br-837d9f...), this manager assigns the actual network name to the Linux bridge interface.
|
||||
|
||||
This allows you to:
|
||||
* Monitor traffic per-network using standard tools (tcpdump -i br-test1).
|
||||
* Create persistent firewall rules (IPTables/NFTables) targeting specific bridges.
|
||||
* Easily identify networks in ip addr or ifconfig output.\
|
||||
Example:\
|
||||

|
||||
|
||||
## 📊 Summary Table of Commands
|
||||
| Command | Short | Argument | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `create` | `c` | [name] | Provisions network(s) and updates config file. |
|
||||
| `delete` | `d` | [name] | Removes network(s) from Docker and config file. |
|
||||
| `info` | `i` | - | Displays dashboard with IP ranges and statuses. |
|
||||
|
||||
## ⚙️ Configuration Variables
|
||||
| Variable | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `BASE_NET` | `172.30` | The first two octets of your managed pool. |
|
||||
| `START_OCTET` | `0` | Starting range for the 3rd octet. |
|
||||
| `END_OCTET` | `255` | Ending range for the 3rd octet. |
|
||||
| `NET_FILE` | `./dnm-networks.txt` | File where network names are stored. |
|
||||
---
|
||||
|
||||
### ⚖️ License
|
||||
MIT [LICENSE](https://github.com/andsyrovatko/s4k-admin-toolbox/blob/main/LICENSE). Free to use and modify.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
@@ -0,0 +1,5 @@
|
||||
# br-test1
|
||||
#br-test2
|
||||
br-test3
|
||||
br-longname_not_allowed-test4
|
||||
br-test5
|
||||
@@ -0,0 +1,6 @@
|
||||
# MAIN VARIABLES
|
||||
NET_FILE="./dnm-networks.txt"
|
||||
LOG_FILE="./dnm-networks.log"
|
||||
BASE_NET="172.30"
|
||||
START_OCTET=0
|
||||
END_OCTET=255
|
||||
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# Script Name : docker-network-manager.sh
|
||||
# Description : Manage Docker external networks with automatic subnet allocation and tracking.
|
||||
# Usage: : ./docker-network-manager.sh <COMMAND> [ARGUMENTS]
|
||||
# Commands:
|
||||
# create [NAME] - Create a specific network by name or all networks from file if NO NAME provided.
|
||||
# delete [NAME] - Delete a specific network by name or all networks from file if NO NAME provided.
|
||||
# info - Show current status and configuration.
|
||||
# For details - see README.md
|
||||
# Author : syr4ok (Andrii Syrovatko)
|
||||
# Version : 1.1.0
|
||||
# =============================================================================
|
||||
|
||||
# --- STRICT MODE ---
|
||||
set -euo pipefail
|
||||
|
||||
# Configuration Loader
|
||||
CONFIG_FILE="$(dirname "$0")/docker-network-manager.conf"
|
||||
if [[ -f "$CONFIG_FILE" ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
source "$CONFIG_FILE"
|
||||
else
|
||||
echo "[Error]: Configuration file not found. Create ip_manager.conf from example."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
touch "$NET_FILE"
|
||||
|
||||
log() {
|
||||
local level="$1"; shift
|
||||
printf '%s [%s] %s\n' "$(date -Is)" "$level" "$*" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
confirm_action() {
|
||||
[[ "${FORCE:-false}" == "true" ]] && return 0
|
||||
|
||||
read -rp "Are you sure you want to delete ALL networks from $NET_FILE? (y/N): " response
|
||||
case "$response" in
|
||||
[yY][eE][sS]|[yY]) return 0 ;;
|
||||
*) log INFO "Operation cancelled by user"; exit 0 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Network check
|
||||
network_exists() {
|
||||
docker network ls --format '{{.Name}}' | grep -Fxq "$1"
|
||||
}
|
||||
|
||||
used_subnets() {
|
||||
local escaped_base="${BASE_NET//./\\.}"
|
||||
|
||||
# Get all subnets from existing Docker networks and filter those that match our base pattern
|
||||
docker network ls -q | xargs -r docker network inspect 2>/dev/null \
|
||||
| grep -oP '"Subnet":\s*"\K'"${escaped_base}"'\.[0-9]+\.0/24' || echo ""
|
||||
}
|
||||
|
||||
next_free_subnet() {
|
||||
local used="$1"
|
||||
for i in $(seq "$START_OCTET" "$END_OCTET"); do
|
||||
local subnet="${BASE_NET}.${i}.0/24"
|
||||
if ! grep -q "$subnet" <<<"$used"; then
|
||||
echo "$subnet"
|
||||
return
|
||||
fi
|
||||
done
|
||||
log ERROR "no free /24 subnet found in ${BASE_NET}.x.x"
|
||||
exit 1
|
||||
}
|
||||
|
||||
name_length_check() {
|
||||
local name="$1"
|
||||
if [[ ${#name} -gt 15 ]]; then
|
||||
log WARN "Network name '$name' is too long for a Linux bridge (max 15 chars). It might be truncated."
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
provision_single() {
|
||||
local name="$1"
|
||||
if ! name_length_check "$name"; then
|
||||
return 1
|
||||
fi
|
||||
if network_exists "$name"; then
|
||||
log INFO "network already exists name=$name"
|
||||
return
|
||||
fi
|
||||
|
||||
local used
|
||||
local subnet
|
||||
used="$(used_subnets)"
|
||||
subnet="$(next_free_subnet "$used")"
|
||||
local gw="${subnet%0/24}1"
|
||||
|
||||
log INFO "creating network name=$name subnet=$subnet gateway=$gw"
|
||||
docker network create --driver bridge --subnet "$subnet" --gateway "$gw" \
|
||||
--opt com.docker.network.bridge.name="$name" "$name" >/dev/null
|
||||
|
||||
if ! grep -Fxq "$name" "$NET_FILE"; then
|
||||
echo "$name" >> "$NET_FILE"
|
||||
log INFO "network name=$name added to $NET_FILE"
|
||||
sed -i '/^$/d' "$NET_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
provision_from_file() {
|
||||
[[ ! -s "$NET_FILE" ]] && { log WARN "NET_FILE is empty"; return; }
|
||||
log INFO "starting mass provisioning from $NET_FILE"
|
||||
while read -u 3 -r line; do
|
||||
[[ -z "$line" || "$line" =~ ^# ]] && continue
|
||||
provision_single "$line" || true
|
||||
done 3< "$NET_FILE"
|
||||
}
|
||||
|
||||
delete_network() {
|
||||
local name="$1"
|
||||
if network_exists "$name"; then
|
||||
log INFO "deleting network name=$name"
|
||||
docker network rm "$name" >/dev/null
|
||||
log INFO "network name=$name deleted from docker"
|
||||
else
|
||||
log WARN "network not found in docker name=$name — cleanup config anyway"
|
||||
fi
|
||||
|
||||
# Remove from NET_FILE if exists
|
||||
if grep -Fxq "$name" "$NET_FILE"; then
|
||||
sed -i "/^$name$/d" "$NET_FILE"
|
||||
log INFO "network name=$name removed from $NET_FILE"
|
||||
else
|
||||
log INFO "network name=$name not found in $NET_FILE, no cleanup needed"
|
||||
fi
|
||||
}
|
||||
|
||||
delete_all_from_file() {
|
||||
log INFO "deleting all networks listed in $NET_FILE"
|
||||
while read -u 3 -r line; do
|
||||
[[ -z "$line" || "$line" =~ ^# ]] && continue
|
||||
delete_network "$line"
|
||||
done 3< "$NET_FILE"
|
||||
}
|
||||
|
||||
show_info() {
|
||||
local tracked_count
|
||||
tracked_count=$(grep -cE '^\s*[^#]' "$NET_FILE" || true)
|
||||
|
||||
echo "--- Docker Network Manager v1.1.0 ---"
|
||||
echo "Configured Base : ${BASE_NET}.0.0/16 (Range: .${START_OCTET}.x to .${END_OCTET}.x)"
|
||||
echo "Config File : $(basename "$NET_FILE")"
|
||||
echo "Log File : $(basename "$LOG_FILE")"
|
||||
echo "Tracked Networks: $tracked_count"
|
||||
|
||||
echo -e "\n[Current Status]"
|
||||
if [[ "$tracked_count" -gt 0 ]]; then
|
||||
printf "%-20s %-15s %-10s\n" "NETWORK NAME" "IP RANGE" "STATUS"
|
||||
echo "--------------------------------------------------------"
|
||||
while read -u 3 -r line; do
|
||||
[[ -z "$line" || "$line" =~ ^# ]] && continue
|
||||
|
||||
local status="OFFLINE"
|
||||
local subnet="N/A"
|
||||
|
||||
if network_exists "$line"; then
|
||||
status="ONLINE"
|
||||
subnet=$(docker network inspect "$line" --format '{{(index .IPAM.Config 0).Subnet}}' 2>/dev/null || echo "error")
|
||||
fi
|
||||
printf "%-20s %-15s %-10s\n" "$line" "$subnet" "$status"
|
||||
done 3< "$NET_FILE"
|
||||
else
|
||||
echo "No networks tracked in $NET_FILE"
|
||||
fi
|
||||
|
||||
echo -e "\n[Usage]"
|
||||
echo " $0 create (c) [name] - Create single network and add to file"
|
||||
echo " $0 create (c) - Create all networks from file"
|
||||
echo " $0 delete (d) [name] - Remove network and clean file"
|
||||
echo " $0 delete (d) - Remove all networks from file"
|
||||
}
|
||||
|
||||
case "${1:-info}" in
|
||||
[Cc][Rr][Ee][Aa][Tt][Ee]|[Cc])
|
||||
if [[ ${2:-} ]]; then
|
||||
provision_single "$2"
|
||||
else
|
||||
provision_from_file
|
||||
fi
|
||||
;;
|
||||
[Dd][Ee][Ll][Ee][Tt][Ee]|[Dd])
|
||||
if [[ ${2:-} ]]; then
|
||||
delete_network "$2"
|
||||
else
|
||||
count=$( (grep -vE '^\s*(#|$)' "$NET_FILE" || true) | wc -l | xargs)
|
||||
|
||||
if (( count == 0 )); then
|
||||
log INFO "Nothing to delete. $NET_FILE is empty or contains only comments."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
confirm_action
|
||||
delete_all_from_file
|
||||
fi
|
||||
;;
|
||||
[Ii][Nn][Ff][Oo]|[Ii])
|
||||
show_info
|
||||
;;
|
||||
*)
|
||||
log ERROR "Unknown command: $1"
|
||||
show_info
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user