-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathip-mapper.sh
More file actions
133 lines (116 loc) · 4.72 KB
/
ip-mapper.sh
File metadata and controls
133 lines (116 loc) · 4.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/bin/sh
# Solution to this problem: https://github.com/pterodactyl/panel/issues/459
# Reference to iptables NAT tutorial: https://www.karlrupp.net/en/computer/nat_tutorial
#H#
#H# ip-mapper-nft.sh — Maps container IPs to public IPs set in their environment from the panel.
#H#
#H# Examples:
#H# sh ip-mapper-nft.sh
#H# sh ip-mapper-nft.sh --list <all | uuid>
#H# sh ip-mapper-nft.sh --remove <all | uuid>
#H#
#H# Options:
#H# --list <all | UUID> Lists active rules added by the script.
#H# --remove <all | UUID> Removes rules added by the script.
#H# --help Shows this message.
help() {
sed -rn 's/^#H# ?//;T;p' "$0"
}
removeRule() {
echo "Removing rules from nat IP-MAPPER-POSTROUTING: $1"
rules=$(/usr/sbin/nft -a list chain nat IP-MAPPER-POSTROUTING | grep "$1" | cut -f 1 -d ' ' --complement)
if [ -z "$rules" ]; then
echo "Couldn't find any references."
else
echo "$rules" | while IFS= read -r rule; do
handleNumber=$(echo "$rule" | awk '{print $NF}')
# using eval to expand $rule before the command
eval /usr/sbin/nft delete rule nat IP-MAPPER-POSTROUTING handle "$handleNumber"
echo "Removed: $rule"
done
fi
}
listRule() {
rules=$(/usr/sbin/nft list chain nat IP-MAPPER-POSTROUTING | grep "$1" | cut -f 1 -d ' ' --complement)
if [ -z "$rules" ]; then
echo "Couldn't find any references."
else
echo "$rules" | while IFS= read -r rule; do
formattedRule=$(echo "$rule" | awk '{ $12 = substr($12, 12, 36); print $12" :: "$2" -> "$10; }')
echo "$formattedRule"
done
fi
}
checkDependencies() {
mainShellPID="$$"
printf "docker\ngrep\nawk\nnft\ncut" | while IFS= read -r program; do
if ! [ -x "$(command -v "$program")" ]; then
echo "Error: $program is not installed." >&2
kill -9 "$mainShellPID"
fi
done
}
checkDependencies
#setting up the table and chain
nft add table ip nat
# priority needs to be lower than POSTROUTING chain of docker which is 100
nft add chain nat IP-MAPPER-POSTROUTING \{type nat hook postrouting priority 50\; policy accept\;\}
if [ "$1" = "--remove" ]; then
# quick validation
[ -z "$2" ] && echo 'Specify server id or "all"' && exit
[ "$2" = "all" ] && removeRule ip-mapper || removeRule "$2"
exit
elif [ "$1" = "--list" ]; then
# quick validation
[ -z "$2" ] && echo 'Specify server id or "all"' && exit
[ "$2" = "all" ] && listRule ip-mapper || listRule "$2"
exit
elif [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
help
exit 0
fi
echo "Listening for docker events..."
docker events --filter type=container --format '{{.Status}} {{.Actor.Attributes.name}}' | while read -r event
do
status=$(echo "$event" | awk '{print $1}')
if [ "$status" = 'start' ]; then
server_id=$(echo "$event" | awk '{print $2}')
local_ip=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$server_id")
public_ip=$(docker exec "$server_id" printenv SERVER_IP)
echo "========================================="
date
echo "Status=$status"
echo "Server_ID=$server_id"
echo "Local_IP=$local_ip"
echo "Public_IP=$public_ip"
echo "Action: Adding to NAT"
echo "Trying to remove old rules just in case..."
removeRule "$server_id"
echo "Adding the new rule..."
if [ -z "$public_ip" ]; then
echo "Missing environmental variable: SERVER_IP"
echo "Cannot be added to nftables!"
else
# add new rule
#eval /sbin/iptables -t nat -I POSTROUTING -s "$local_ip" -j SNAT --to "$public_ip" -m comment --comment ip-mapper-"$server_id" -w
eval nft insert rule nat IP-MAPPER-POSTROUTING ip saddr "$local_ip" counter snat to "$public_ip" comment \"ip-mapper-"$server_id"\"
echo "Finished."
fi
echo "========================================="
elif [ "$status" = 'die' ]; then
server_id=$(echo "$event" | awk '{print $2}')
echo "========================================="
date
echo "Status=$status"
echo "Server_ID=$server_id"
echo "Action: Removing from NAT by Server ID"
removeRule "$server_id"
echo "Finished."
echo "========================================="
fi
# You can configure all events from here: https://docs.docker.com/engine/reference/commandline/events/
done
# Why I chose to use the die event
# kill - no public ip on 2nd kick after stop (kicks 2 times on stop? wtf)
# die - no local,public ip (Kicks only once always)
# stop - no local,public ip (Doesn't kick on kill)