ufw-docker/ufw-docker
2025-07-08 16:21:31 +08:00

697 lines
23 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
set -euo pipefail
[[ -n "${DEBUG:-}" ]] && set -x
# UFW-DOCKER GLOBAL VARIABLES START #
LC_ALL=C
PATH="/bin:/usr/bin:/sbin:/usr/sbin:/snap/bin/"
GREP_REGEXP_NAME="[-_.[:alnum:]]\\+"
DEFAULT_PROTO=tcp
ENV_VALUE_SPLITTER=','
ufw_docker_agent=ufw-docker-agent
ufw_docker_agent_image="${UFW_DOCKER_AGENT_IMAGE:-chaifeng/${ufw_docker_agent}:250708-nf_tables}"
after_rules="/etc/ufw/after.rules"
after6_rules="/etc/ufw/after6.rules"
# UFW-DOCKER GLOBAL VARIABLES END #
if [[ "${ufw_docker_agent_image}" = *-@(legacy|nf_tables) ]]; then
if iptables --version | grep -F '(legacy)' &>/dev/null; then
ufw_docker_agent_image="${ufw_docker_agent_image%-*}-legacy"
else
ufw_docker_agent_image="${ufw_docker_agent_image%-*}-nf_tables"
fi
fi
test -n "$ufw_docker_agent_image"
function ufw-docker--status() {
ufw-docker--list
}
function ufw-docker--list() {
local INSTANCE_NAME="${1:-}"
local INSTANCE_PORT="${2:-}"
local PROTO="${3:-}"
local NETWORK="${4:-}"
local params_count="$#"
local _grep_regexp
[[ -n "${INSTANCE_NAME:-}" ]] || INSTANCE_NAME="$GREP_REGEXP_NAME"
if [[ -z "${INSTANCE_PORT:-}" && -z "${PROTO:-}" && -z "${NETWORK-}" ]]; then
INSTANCE_PORT="[[:digit:]]\\+"
[[ -n "${PROTO:-}" ]] || PROTO="\\(tcp\\|udp\\)"
_grep_regexp="\\( ${INSTANCE_PORT}/${PROTO}\\( ${GREP_REGEXP_NAME}\\)\\?\\)\\?"
else
[[ -n "${INSTANCE_PORT:-}" ]] || INSTANCE_PORT="[[:digit:]]\\+"
[[ -n "${PROTO:-}" ]] || PROTO="${DEFAULT_PROTO}"
if [[ -n "${NETWORK:-}" ]]; then
NETWORK=" ${NETWORK}"
else
NETWORK="\\( ${GREP_REGEXP_NAME}\\)\\?"
fi
_grep_regexp=" ${INSTANCE_PORT}/${PROTO}${NETWORK}"
fi
local ufw_output
ufw_output="$(ufw status numbered)"
grep "# allow ${INSTANCE_NAME}\\(/v6\\)\\?${_grep_regexp}\$" <<< "$ufw_output"
}
function ufw-docker--list-number() {
ufw-docker--list "$@" | sed -e 's/^\[[[:blank:]]*\([[:digit:]]\+\)\].*/\1/'
}
function ufw-docker--delete() {
for UFW_NUMBER in $(ufw-docker--list-number "$@" | sort -rn); do
echo "delete \"$UFW_NUMBER\""
echo y | ufw delete "$UFW_NUMBER" || true
done
}
function ufw-docker--allow() {
local INSTANCE_NAME="$1"
local INSTANCE_PORT="$2"
local PROTO="$3"
local NETWORK="${4:-}"
local NETWORK_ADDRESSES PORT_PROTO_LIST PROT_PROTO IP SUFFIX
docker inspect "$INSTANCE_NAME" &>/dev/null ||
die "Docker instance \"$INSTANCE_NAME\" doesn't exist."
mapfile -t NETWORK_ADDRESSES < <(docker inspect --format '{{range $name, $net := .NetworkSettings.Networks}}{{if $net.IPAddress}}{{$name}} {{$net.IPAddress}}{{"\n"}}{{end}}{{if $net.GlobalIPv6Address}}{{$name}} {{$net.GlobalIPv6Address}}{{"\n"}}{{end}}{{end}}' "$INSTANCE_NAME" 2>/dev/null | remove_blank_lines)
[[ -z "${NETWORK_ADDRESSES:-}" ]] && die "Could not find a running instance \"$INSTANCE_NAME\"."
mapfile -t PORT_PROTO_LIST < <(docker inspect --format='{{range $p, $conf := .NetworkSettings.Ports}}{{with $conf}}{{$p}}{{"\n"}}{{end}}{{end}}' "$INSTANCE_NAME" | remove_blank_lines)
if [[ -z "${PORT_PROTO_LIST:-}" ]]; then
err "\"$INSTANCE_NAME\" doesn't have any published ports."
return 1
fi
local count=0
for PORT_PROTO in "${PORT_PROTO_LIST[@]}"; do
if [[ -z "$INSTANCE_PORT" || "$PORT_PROTO" = "${INSTANCE_PORT}/${PROTO}" ]]; then
for item in "${NETWORK_ADDRESSES[@]}"; do
INSTANCE_NETWORK="${item% *}"
IP="${item#* }"
if [[ -n "$NETWORK" ]] && [[ "$NETWORK" != "$INSTANCE_NETWORK" ]]; then
continue
fi
if [[ "$IP" = *:* ]]; then SUFFIX="/v6"; else SUFFIX=""; fi
ufw-docker--add-rule "${INSTANCE_NAME}${SUFFIX}" "$IP" "${PORT_PROTO%/*}" "${PORT_PROTO#*/}" "${INSTANCE_NETWORK}"
(( ++count ))
done
fi
done
if [[ "$count" -eq 0 ]]; then
err "Fail to add rule(s), cannot find the published port ${INSTANCE_PORT}/${PROTO} of instance \"${INSTANCE_NAME}\" or cannot update outdated rule(s)."
return 1
fi
return 0
}
function ufw-docker--add-service-rule() {
declare service_id="$1"
declare port="${2%/*}"
declare proto="${2#*/}"
declare target_ip_port
target_ip_port="$(iptables -t nat -L DOCKER-INGRESS | grep -E "^DNAT\\s+${proto}\\s+.+\\sto:[.0-9]+:${port}\$" | grep -Eo "[.0-9]+:${port}\$")"
[[ -z "$target_ip_port" ]] && die "Could not find VIP of service ${service_id}."
ufw-docker--add-rule "$service_id" "${target_ip_port%:*}" "$port" "$proto"
}
function ufw-docker--add-rule() {
local INSTANCE_NAME="$1"
local INSTANCE_IP_ADDRESS="$2"
local PORT="$3"
local PROTO="$4"
local NETWORK="${5:-}"
declare comment
echo "allow ${INSTANCE_NAME} ${PORT}/${PROTO} ${NETWORK}"
typeset -a UFW_OPTS
UFW_OPTS=(route allow proto "${PROTO}"
from any to "$INSTANCE_IP_ADDRESS")
comment="allow ${INSTANCE_NAME}"
[[ -n "$PORT" ]] && {
UFW_OPTS+=(port "${PORT}")
comment="$comment ${PORT}/${PROTO}"
}
[[ -n "$NETWORK" ]] && {
comment="$comment ${NETWORK}"
}
UFW_OPTS+=(comment "$comment")
if ufw-docker--list "$INSTANCE_NAME" "$PORT" "$PROTO" "$NETWORK" &>/dev/null; then
ufw --dry-run "${UFW_OPTS[@]}" | grep "^Skipping" && return 0
err "Remove outdated rule."
ufw-docker--delete "$INSTANCE_NAME" "$PORT" "$PROTO" "$NETWORK"
fi
echo ufw "${UFW_OPTS[@]}"
ufw "${UFW_OPTS[@]}"
}
function ufw-docker--instance-name() {
local INSTANCE_ID="$1"
{
{
docker inspect --format='{{.Name}}' "$INSTANCE_ID" 2>/dev/null | sed -e 's,^/,,' |
grep "^${GREP_REGEXP_NAME}\$" 2>/dev/null
} || echo -n "$INSTANCE_ID";
} | remove_blank_lines
}
function ufw-docker--service() {
declare service_action="${1:-help}"
case "$service_action" in
delete)
shift || true
if [[ "${1:?Invalid 'delete' command syntax.}" != "allow" ]]; then
die "\"delete\" command only support removing allowed rules"
fi
shift || true
declare service_id_or_name="${1:?Missing swarm service name or service ID}"
"ufw-docker--service-${service_action}" "$@"
;;
allow)
shift || true
declare service_id_or_name="${1:?Missing swarm service name or service ID}"
declare service_port="${2:?Missing the port number, such as '80/tcp'.}"
"ufw-docker--service-${service_action}" "${service_id_or_name}" "${service_port}"
;;
*)
ufw-docker--help
;;
esac
}
function ufw-docker--get-service-id() {
declare service_name="$1"
docker service inspect "${service_name}" --format "{{.ID}}"
}
function ufw-docker--get-service-name() {
declare service_name="$1"
docker service inspect "${service_name}" --format "{{.Spec.Name}}"
}
function ufw-docker--list-service-ports() {
declare service_name="$1"
docker service inspect "$service_name" \
--format '{{range .Endpoint.Spec.Ports}}{{.PublishedPort}} {{.TargetPort}}/{{.Protocol}}{{"\n"}}{{end}}'
}
function ufw-docker--service-allow() {
declare service_name="$1"
declare service_port="$2"
declare service_proto=tcp
if [[ -n "$service_port" ]] &&
! grep -E '^[0-9]+(/(tcp|udp))?$' <<< "$service_port" &>/dev/null; then
die "Invalid port syntax: $service_port"
return 1
fi
if [[ "$service_port" = */* ]]; then
service_proto="${service_port#*/}"
service_port="${service_port%/*}"
fi
declare service_id
service_id="$(ufw-docker--get-service-id "${service_name}")"
[[ -z "${service_id:-}" ]] && die "Could not find service \"$service_name\""
service_name="$(ufw-docker--get-service-name "${service_name}")"
declare env_value= published_port= target_port=
exec 9< <(ufw-docker--list-service-ports "$service_name")
while read -u 9 -r published_port target_port; do
if [[ "$target_port" = "${service_port}/${service_proto}" ]]; then
env_value="${service_name}/${published_port}/${service_proto}"
break;
fi
done
exec 9<&-
[[ -z "${env_value:-}" ]] && die "Service $service_name does not publish port $service_port."
declare service_env
if ! docker service inspect "$ufw_docker_agent" &>/dev/null; then
err "Not found ufw-docker-agent service, creating ..."
service_env="ufw_public_${service_id}=${env_value}"
docker service create --name "$ufw_docker_agent" --mode global \
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
--mount type=bind,source=/etc/ufw,target=/etc/ufw,readonly=true \
--env ufw_docker_agent_image="${ufw_docker_agent_image}" \
--env DEBUG="${DEBUG:-}" \
--env "${service_env}" \
"${ufw_docker_agent_image}"
else
declare -a value_list=() service_env_list=()
exec 8< <(ufw-docker--get-env-list)
while read -u 8 -r id value; do
[[ "$id" != "$service_id" && "$value" = "${service_name}"/* ]] && service_env_list+=(--env-rm "ufw_public_${id}")
[[ "$id" = "$service_id" ]] || continue
[[ "${value}" = "${env_value}" || "${value}" = "${env_value}"/* ]] ||
value_list+=("${value}")
done
exec 8<&-
value_list=("${env_value}" "${value_list[@]}")
service_env="ufw_public_${service_id}=$(IFS="${ENV_VALUE_SPLITTER}"; printf '%s' "${value_list[*]}")"
service_env_list+=(--env-add "${service_env}")
docker service update --update-parallelism=0 \
--env-add ufw_docker_agent_image="${ufw_docker_agent_image}" \
--env-add DEBUG="${DEBUG:-}" \
"${service_env_list[@]}" \
--image "${ufw_docker_agent_image}" \
"${ufw_docker_agent}"
fi
}
function ufw-docker--get-env-list() {
docker service inspect "${ufw_docker_agent}" \
--format '{{range $k,$v := .Spec.TaskTemplate.ContainerSpec.Env}}{{ $v }}{{"\n"}}{{end}}' |
sed -e '/^ufw_public_/!d' \
-e 's/^ufw_public_//' \
-e 's/=/ /' |
while read -r id value; do
tr ',' '\n' <<< "$value" | sed "s/^/${id} /g"
done
}
function ufw-docker--service-delete() {
declare service_name="$1"
declare service_port="${2:-}"
declare port proto
if [[ "${service_port:-}" = */* ]]; then
port="${service_port%/*}"
proto="${service_port#*/}"
elif [[ -n "${service_port:-}" ]]; then
port="$service_port"
proto="$DEFAULT_PROTO"
fi
declare service_id
service_id="$(ufw-docker--get-service-id "${service_name}")"
[[ -z "${service_id:-}" ]] && die "Could not find service \"$service_name\""
service_name="$(ufw-docker--get-service-name "${service_name}")"
declare env_value= published_port= target_port=
if [[ -n "${port:-}" ]]; then
exec 9< <(ufw-docker--list-service-ports "$service_name")
while read -u 9 -r published_port target_port; do
if [[ "$target_port" = "${port}/${proto}" ]]; then
env_value="${service_name}/${published_port}/${proto}"
break;
fi
done
exec 9<&-
[[ -n "${env_value:-}" ]] || die "Service $service_name does not publish port $service_port."
else
declare env_value="${service_name}"
fi
declare id value
declare -a value_list=()
exec 8< <(ufw-docker--get-env-list)
while read -u 8 -r id value; do
[[ "$id" = "$service_id" ]] || continue
[[ "${value}" = "${env_value}" || "${value}" = "${env_value}"/* ]] ||
value_list+=("${value}")
done
exec 8<&-
value_list=("${env_value}/deny" "${value_list[@]}")
declare service_env
service_env="ufw_public_${service_id}=$(IFS="${ENV_VALUE_SPLITTER}"; printf '%s' "${value_list[*]}")"
docker service update --update-parallelism=0 \
--env-add ufw_docker_agent_image="${ufw_docker_agent_image}" \
--env-add "${service_env}" \
--env-add DEBUG="${DEBUG-}" \
--image "${ufw_docker_agent_image}" \
"${ufw_docker_agent}"
}
function ufw-docker--raw-command() {
ufw "$@"
}
function ufw-docker--check() {
err "\\n########## iptables -n -L DOCKER-USER ##########"
iptables -n -L DOCKER-USER
err "\\n\\n########## diff $after_rules ##########"
ufw-docker--check-install "$@" && err "\\nCheck IPv4 firewall rules done."
if command -v ip6tables >/dev/null 2>&1; then
err "\\n########## ip6tables -n -L DOCKER-USER ##########"
ip6tables -n -L DOCKER-USER
err "\\n\\n########## diff $after6_rules ##########"
ufw-docker--check-install_ipv6 "$@" && err "\\nCheck IPv6 firewall rules done."
fi
}
declare -a files_to_be_deleted
function rm-on-exit() {
[[ $# -gt 0 ]] && files_to_be_deleted+=("$@")
}
function on-exit() {
for file in "${files_to_be_deleted[@]:-}"; do
[[ -f "$file" ]] && rm -r "$file"
done
files_to_be_deleted=()
}
trap on-exit EXIT INT TERM QUIT ABRT ERR
function ufw-docker--list-docker-subnets() {
local ipversion="$1"
shift || true
if [[ -z "${1-}" ]]; then
docker network ls --format '{{.ID}}' |
while read -r net; do
docker network inspect "$net" --format '{{range .IPAM.Config}}{{.Subnet}}{{"\n"}}{{end}}'
done
else
printf "%s\n" "$@"
fi |
while read -r cidr; do
if [[ "${ipversion}" = "IPv4" && "$cidr" = *.* ]] || [[ "${ipversion}" = "IPv6" && "$cidr" = *:* ]]
then echo "$cidr"
fi
done |
sort
}
function ufw-docker--check-install() {
declare -a cidr_list
declare cidr
if [[ -z "${1-}" ]]; then
cidr_list=(10.0.0.0/8 172.16.0.0/12 192.168.0.0/16)
elif [[ "${1-}" = '--docker-subnets' ]]; then
shift || true
mapfile -t cidr_list < <(ufw-docker--list-docker-subnets IPv4 "$@")
fi
if [[ -z "${cidr_list:-}" ]]; then
err "ERROR: Could not find any IPv4 subnets used by docker engine\n"
exit 1
fi
after_rules_tmp="${after_rules_tmp:-$(mktemp)}"
rm-on-exit "$after_rules_tmp"
sed "/^# BEGIN UFW AND DOCKER/,/^# END UFW AND DOCKER/d" "$after_rules" | tee "$after_rules_tmp" >/dev/null
{
cat <<-\EOF
# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:ufw-docker-logging-deny - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j ufw-user-forward
EOF
for cidr in "${cidr_list[@]}"; do
echo "-A DOCKER-USER -j RETURN -s ${cidr}"
done
cat <<-\EOF
-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
EOF
for cidr in "${cidr_list[@]}"; do
echo "-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d ${cidr}"
done
for cidr in "${cidr_list[@]}"; do
echo "-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d ${cidr}"
done
cat <<-\EOF
-A DOCKER-USER -j RETURN
-A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
-A ufw-docker-logging-deny -j DROP
COMMIT
# END UFW AND DOCKER
EOF
} | tee -a "${after_rules_tmp}" >/dev/null
diff -u --color=auto "$after_rules" "$after_rules_tmp"
}
function ufw-docker--check-install_ipv6() {
declare -a cidr6_list
declare cidr
if [[ -z "${1-}" ]]; then
cidr6_list=(fd00::/8)
elif [[ "${1-}" = '--docker-subnets' ]]; then
shift || true
mapfile -t cidr6_list < <(ufw-docker--list-docker-subnets IPv6 "$@")
fi
if [[ -z "${cidr6_list:-}" ]]; then
err "INFO: Could not find any IPv6 subnets used by docker engine, will disable IPv6 support.\n"
return 0
fi
after6_rules_tmp="${after6_rules_tmp:-$(mktemp)}"
rm-on-exit "$after6_rules_tmp"
sed "/^# BEGIN UFW AND DOCKER/,/^# END UFW AND DOCKER/d" "$after6_rules" | tee "$after6_rules_tmp" >/dev/null
{
cat <<-\EOF
# BEGIN UFW AND DOCKER
*filter
:ufw6-user-forward - [0:0]
:ufw6-docker-logging-deny - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j ufw6-user-forward
EOF
for cidr in "${cidr6_list[@]}"; do
echo "-A DOCKER-USER -j RETURN -s ${cidr}"
done
cat <<-\EOF
-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
EOF
for cidr in "${cidr6_list[@]}"; do
echo "-A DOCKER-USER -j ufw6-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d ${cidr}"
echo "-A DOCKER-USER -j ufw6-docker-logging-deny -p udp -m udp --dport 0:32767 -d ${cidr}"
done
cat <<-\EOF
-A DOCKER-USER -j RETURN
-A ufw6-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
-A ufw6-docker-logging-deny -j DROP
COMMIT
# END UFW AND DOCKER
EOF
} | tee -a "${after6_rules_tmp}"
diff -u --color=auto "$after6_rules" "$after6_rules_tmp"
}
function ufw-docker--install() {
local changed=false
if ! ufw-docker--check-install "$@"; then
changed=true
local after_rules_bak
after_rules_bak="${after_rules}-ufw-docker~$(date '+%Y-%m-%d-%H%M%S')~"
err "\\nBacking up $after_rules to $after_rules_bak"
cp "$after_rules" "$after_rules_bak"
cat "$after_rules_tmp" > "$after_rules"
fi
if ! ufw-docker--check-install_ipv6 "$@"; then
changed=true
local after6_rules_bak
after6_rules_bak="${after6_rules}-ufw-docker~$(date '+%Y-%m-%d-%H%M%S')~"
err "\\nBacking up $after6_rules to $after6_rules_bak"
cp "$after6_rules" "$after6_rules_bak"
cat "$after6_rules_tmp" > "$after6_rules"
fi
if "$changed"; then
err "Please restart UFW service manually by using the following command:"
if type systemctl &>/dev/null; then
err " sudo systemctl restart ufw"
else
err " sudo service ufw restart"
fi
fi
}
function ufw-docker--install--help() {
cat <<HELP
ufw-docker $1 --docker-subnets [SUBNET1 SUBNET2 …]
Specify which subnets should be used when configuring firewall rules for Docker containers and any allowed networks that communicate with containers.
- If this option is not provided, only standard private LAN subnets are used (RFC1918 for IPv4 and fd00::/8 for IPv6).
- If --docker-subnets is given without any arguments, all Docker network subnets will be automatically detected and used.
- If one or more subnets are specified, these subnets will be used for firewall rules, and they can include any networks that need to communicate with containers—not just the subnets configured by the Docker engine.
You can specify multiple subnets separated by spaces (each in CIDR format).
Examples:
- ufw-docker $1
Use only standard private subnets (default behavior).
- IPv4 subnets: 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
- IPv6 subnet: fd00::/8
- ufw-docker $1 --docker-subnets
Auto-detect and use all Docker network subnets.
- ufw-docker $1 --docker-subnets 10.207.0.0/16 192.168.207.0/24 fd00:cf::/64
Use only the specified subnets, including those outside of Dockers own configuration, for all networks that should be allowed to communicate with containers.
HELP
}
function ufw-docker--help() {
cat <<-EOF >&2
Usage:
ufw-docker <list|allow> [docker-instance-id-or-name [port[/tcp|/udp]] [network]]
ufw-docker delete allow [docker-instance-id-or-name [port[/tcp|/udp]] [network]]
ufw-docker service allow <swarm-service-id-or-name <port</tcp|/udp>>>
ufw-docker service delete allow <swarm-service-id-or-name>
ufw-docker <install|check> [--docker-subnets [SUBNET0 SUBNET1 ...]]
ufw-docker <status|install|check|help>
Examples:
ufw-docker help
ufw-docker check --help
ufw-docker install --help
ufw-docker check # Check the installation of firewall rules
ufw-docker check --docker-subnets # Auto-detect and use all Docker network subnets
ufw-docker check --docker-subnets 192.168.207.0/24 10.207.0.0/16 fd00:cf::/64
ufw-docker install # Install firewall rules
ufw-docker install --docker-subnets # Auto-detect and use all Docker network subnets
ufw-docker install --docker-subnets 192.168.207.0/24 10.207.0.0/16 fd00:cf::/64
ufw-docker status
ufw-docker list httpd
ufw-docker allow httpd
ufw-docker allow httpd 80
ufw-docker allow httpd 80/tcp
ufw-docker allow httpd 80/tcp default
ufw-docker delete allow httpd
ufw-docker delete allow httpd 80/tcp
ufw-docker delete allow httpd 80/tcp default
ufw-docker service allow httpd 80/tcp
ufw-docker service delete allow httpd
EOF
}
function remove_blank_lines() {
sed '/^[[:blank:]]*$/d'
}
function err() {
echo -e "$@" >&2
}
function die() {
err "ERROR:" "$@"
exit 1
}
# __main__
if ! ufw status 2>/dev/null | grep -Fq "Status: active" ; then
die "UFW is disabled or you are not root user, or mismatched iptables legacy/nf_tables, current $(iptables --version)"
fi
if ! docker -v &> /dev/null; then
die "Docker executable not found."
fi
ufw_action="${1:-help}"
case "$ufw_action" in
delete)
shift || true
if [[ "${1:?Invalid 'delete' command syntax.}" != "allow" ]]; then
die "\"delete\" command only support removing allowed rules"
fi
;&
list|allow)
shift || true
INSTANCE_ID="${1:?Docker instance name/ID cannot be empty.}"
INSTANCE_NAME="$(ufw-docker--instance-name "$INSTANCE_ID")"
shift || true
INSTANCE_PORT="${1:-}"
if [[ -n "$INSTANCE_PORT" && ! "$INSTANCE_PORT" =~ [0-9]+(/(tcp|udp))? ]]; then
die "invalid port syntax: \"$INSTANCE_PORT\"."
fi
PROTO="$DEFAULT_PROTO"
if [[ "$INSTANCE_PORT" = */udp ]]; then
PROTO=udp
fi
shift || true
NETWORK="${1:-}"
INSTANCE_PORT="${INSTANCE_PORT%/*}"
;;&
delete|list)
"ufw-docker--$ufw_action" "$INSTANCE_NAME" "$INSTANCE_PORT" "$PROTO" "$NETWORK"
;;
allow)
"ufw-docker--$ufw_action" "$INSTANCE_NAME" "$INSTANCE_PORT" "$PROTO" "$NETWORK"
;;
service|raw-command|add-service-rule)
shift || true
"ufw-docker--$ufw_action" "$@"
;;
install|check)
shift || true
if [[ "${1-}" = @(help|-h|--help) ]]; then
ufw-docker--install--help "$ufw_action"
exit
fi
"ufw-docker--$ufw_action" "$@"
;;
status)
ufw-docker--"$ufw_action"
;;
*)
ufw-docker--help
;;
esac