diff --git a/README.md b/README.md index ff18e7a..a4b489c 100644 --- a/README.md +++ b/README.md @@ -297,17 +297,33 @@ List all firewall rules related to container `httpd` ufw-docker list httpd -Expose the port `80` of the container `httpd` +Expose the port `80` of the container `httpd` to any source - ufw-docker allow httpd 80 + ufw-docker allow httpd any 80 + +Expose the port `80` of the container `httpd` only to specific IP address + + ufw-docker allow httpd 192.168.1.10 80 + +Expose the port `80` of the container `httpd` only to specific subnet + + ufw-docker allow httpd 192.168.1.0/24 80 Expose the `443` port of the container `httpd` and the protocol is `tcp` - ufw-docker allow httpd 443/tcp + ufw-docker allow httpd any 443/tcp + +Expose the `443` port of the container `httpd` only to specific IP, protocol is `tcp` + + ufw-docker allow httpd 192.168.1.10 443/tcp Expose the `443` port of the container `httpd` and the protocol is `tcp` and the network is `foobar-external-network` when the container `httpd` is attached to multiple networks - ufw-docker allow httpd 443/tcp foobar-external-network + ufw-docker allow httpd any 443/tcp foobar-external-network + +Expose the `443` port of the container `httpd` only to specific subnet, protocol is `tcp`, network is `foobar-external-network` + + ufw-docker allow httpd 192.168.1.0/24 443/tcp foobar-external-network Expose all published ports of the container `httpd` @@ -317,9 +333,13 @@ Remove all rules related to the container `httpd` ufw-docker delete allow httpd -Remove the rule which port is `443` and protocol is `tcp` for the container `httpd` +Remove all rules related to the container `httpd` for specific source + + ufw-docker delete allow httpd 192.168.1.10 + +Remove the rule for specific source, port `443` and protocol is `tcp` for the container `httpd` - ufw-docker delete allow httpd 443/tcp + ufw-docker delete allow httpd 192.168.1.10 443/tcp Expose the port `80` of the service `web` @@ -645,17 +665,33 @@ UFW 是 Ubuntu 上很流行的一个 iptables 前端,可以非常方便的管 ufw-docker list httpd -暴露容器 `httpd` 的 `80` 端口 +暴露容器 `httpd` 的 `80` 端口给任何来源 - ufw-docker allow httpd 80 + ufw-docker allow httpd any 80 + +暴露容器 `httpd` 的 `80` 端口仅给特定 IP 地址 + + ufw-docker allow httpd 192.168.1.10 80 + +暴露容器 `httpd` 的 `80` 端口仅给特定子网 + + ufw-docker allow httpd 192.168.1.0/24 80 暴露容器 `httpd` 的 `443` 端口,且协议为 `tcp` - ufw-docker allow httpd 443/tcp + ufw-docker allow httpd any 443/tcp + +暴露容器 `httpd` 的 `443` 端口仅给特定 IP,协议为 `tcp` + + ufw-docker allow httpd 192.168.1.10 443/tcp 如果容器 `httpd` 绑定到多个网络上,暴露其 `443` 端口,协议为 `tcp`,网络为 `foobar-external-network` - ufw-docker allow httpd 443/tcp foobar-external-network + ufw-docker allow httpd any 443/tcp foobar-external-network + +暴露容器 `httpd` 的 `443` 端口仅给特定子网,协议为 `tcp`,网络为 `foobar-external-network` + + ufw-docker allow httpd 192.168.1.0/24 443/tcp foobar-external-network 把容器 `httpd` 的所有映射端口都暴露出来 @@ -665,9 +701,13 @@ UFW 是 Ubuntu 上很流行的一个 iptables 前端,可以非常方便的管 ufw-docker delete allow httpd -删除容器 `httpd` 的 `tcp` 端口 `443` 的规则 +删除容器 `httpd` 针对特定来源的所有规则 + + ufw-docker delete allow httpd 192.168.1.10 + +删除容器 `httpd` 针对特定来源的 `tcp` 端口 `443` 规则 - ufw-docker delete allow httpd 443/tcp + ufw-docker delete allow httpd 192.168.1.10 443/tcp 暴露服务 `web` 的 `80` 端口 diff --git a/test/ufw-docker.test.sh b/test/ufw-docker.test.sh index 69913fe..cf60baa 100755 --- a/test/ufw-docker.test.sh +++ b/test/ufw-docker.test.sh @@ -211,7 +211,7 @@ test-list-command-for-instance() { ufw-docker list httpd } test-list-command-for-instance-assert() { - ufw-docker--list httpd-container-name "" tcp "" + ufw-docker--list httpd-container-name "" "" tcp "" } @@ -220,34 +220,70 @@ test-allow-command-for-instance() { ufw-docker allow httpd } test-allow-command-for-instance-assert() { - ufw-docker--allow httpd-container-name "" tcp "" + ufw-docker--allow httpd-container-name "" "" tcp "" } test-allow-command-for-instance-with-port() { @mock ufw-docker--instance-name httpd === @stdout httpd-container-name - ufw-docker allow httpd 80 + ufw-docker allow httpd any 80 } test-allow-command-for-instance-with-port-assert() { - ufw-docker--allow httpd-container-name 80 tcp "" + ufw-docker--allow httpd-container-name any 80 tcp "" } test-allow-command-for-instance-with-port-and-tcp-protocol() { @mock ufw-docker--instance-name httpd === @stdout httpd-container-name - ufw-docker allow httpd 80/tcp + ufw-docker allow httpd any 80/tcp } test-allow-command-for-instance-with-port-and-tcp-protocol-assert() { - ufw-docker--allow httpd-container-name 80 tcp "" + ufw-docker--allow httpd-container-name any 80 tcp "" } test-allow-command-for-instance-with-port-and-udp-protocol() { @mock ufw-docker--instance-name httpd === @stdout httpd-container-name - ufw-docker allow httpd 80/udp + ufw-docker allow httpd any 80/udp } test-allow-command-for-instance-with-port-and-udp-protocol-assert() { - ufw-docker--allow httpd-container-name 80 udp "" + ufw-docker--allow httpd-container-name any 80 udp "" +} + + +test-allow-command-for-instance-with-source-ip() { + @mock ufw-docker--instance-name httpd === @stdout httpd-container-name + ufw-docker allow httpd 192.168.1.10 80/tcp +} +test-allow-command-for-instance-with-source-ip-assert() { + ufw-docker--allow httpd-container-name 192.168.1.10 80 tcp "" +} + + +test-allow-command-for-instance-with-source-subnet() { + @mock ufw-docker--instance-name httpd === @stdout httpd-container-name + ufw-docker allow httpd 192.168.1.0/24 80/tcp +} +test-allow-command-for-instance-with-source-subnet-assert() { + ufw-docker--allow httpd-container-name 192.168.1.0/24 80 tcp "" +} + + +test-allow-command-for-instance-with-source-any() { + @mock ufw-docker--instance-name httpd === @stdout httpd-container-name + ufw-docker allow httpd any 80/tcp +} +test-allow-command-for-instance-with-source-any-assert() { + ufw-docker--allow httpd-container-name any 80 tcp "" +} + + +test-allow-command-for-instance-with-source-and-network() { + @mock ufw-docker--instance-name httpd === @stdout httpd-container-name + ufw-docker allow httpd 192.168.1.10 80/tcp default +} +test-allow-command-for-instance-with-source-and-network-assert() { + ufw-docker--allow httpd-container-name 192.168.1.10 80 tcp default } @@ -255,7 +291,15 @@ test-ASSERT-FAIL-allow-httpd-INVALID-port() { @mock ufw-docker--instance-name httpd === @stdout httpd-container-name @mock die 'invalid port syntax: "invalid".' === exit 1 - ufw-docker allow httpd invalid + ufw-docker allow httpd any invalid +} + + +test-ASSERT-FAIL-allow-httpd-INVALID-source() { + @mock ufw-docker--instance-name httpd === @stdout httpd-container-name + @mock die 'invalid source syntax: "invalid-source".' === exit 1 + + ufw-docker allow httpd invalid-source } @@ -264,7 +308,25 @@ test-delete-allow-command-for-instance() { ufw-docker delete allow httpd } test-delete-allow-command-for-instance-assert() { - ufw-docker--delete httpd-container-name "" tcp "" + ufw-docker--delete httpd-container-name "" "" tcp "" +} + + +test-delete-allow-command-for-instance-with-source() { + @mock ufw-docker--instance-name httpd === @stdout httpd-container-name + ufw-docker delete allow httpd 192.168.1.10 80/tcp +} +test-delete-allow-command-for-instance-with-source-assert() { + ufw-docker--delete httpd-container-name 192.168.1.10 80 tcp "" +} + + +test-delete-allow-command-for-instance-with-source-and-network() { + @mock ufw-docker--instance-name httpd === @stdout httpd-container-name + ufw-docker delete allow httpd 192.168.1.10 80/tcp default +} +test-delete-allow-command-for-instance-with-source-and-network-assert() { + ufw-docker--delete httpd-container-name 192.168.1.10 80 tcp default } @@ -316,7 +378,7 @@ test-allow-internal-fails-for-non-existent-instance() { @mockfalse docker inspect invalid-instance @mockfalse die "Docker instance \"invalid-instance\" doesn't exist." - ufw-docker--allow invalid-instance 80 tcp + ufw-docker--allow invalid-instance any 80 tcp } test-allow-internal-fails-for-non-existent-instance-assert() { @do-nothing @@ -327,7 +389,7 @@ test-allow-internal-fails-for-non-existent-instance-assert() { test-allow-internal-fails-when-port-does-not-match() { setup-ufw-docker--allow - ufw-docker--allow instance-name 80 tcp + ufw-docker--allow instance-name any 80 tcp } test-allow-internal-fails-when-port-does-not-match-assert() { @do-nothing @@ -338,7 +400,7 @@ test-allow-internal-fails-when-port-does-not-match-assert() { test-allow-internal-fails-when-protocol-does-not-match() { setup-ufw-docker--allow - ufw-docker--allow instance-name 5353 tcp + ufw-docker--allow instance-name any 5353 tcp } test-allow-internal-fails-when-protocol-does-not-match-assert() { @do-nothing @@ -349,202 +411,202 @@ test-allow-internal-fails-when-protocol-does-not-match-assert() { test-allow-internal-succeeds-when-port-matches() { setup-ufw-docker--allow - ufw-docker--allow instance-name 5000 tcp + ufw-docker--allow instance-name any 5000 tcp } test-allow-internal-succeeds-when-port-matches-assert() { - ufw-docker--add-rule instance-name 172.18.0.3 5000 tcp default + ufw-docker--add-rule instance-name 172.18.0.3 any 5000 tcp default } test-allow-internal-succeeds-for-all-published-ports() { setup-ufw-docker--allow - ufw-docker--allow instance-name "" "" + ufw-docker--allow instance-name any "" "" } test-allow-internal-succeeds-for-all-published-ports-assert() { - ufw-docker--add-rule instance-name 172.18.0.3 5000 tcp default - ufw-docker--add-rule instance-name 172.18.0.3 8080 tcp default - ufw-docker--add-rule instance-name 172.18.0.3 5353 udp default + ufw-docker--add-rule instance-name 172.18.0.3 any 5000 tcp default + ufw-docker--add-rule instance-name 172.18.0.3 any 8080 tcp default + ufw-docker--add-rule instance-name 172.18.0.3 any 5353 udp default } test-allow-internal-succeeds-for-all-published-tcp-ports() { setup-ufw-docker--allow - ufw-docker--allow instance-name "" tcp + ufw-docker--allow instance-name any "" tcp } test-allow-internal-succeeds-for-all-published-tcp-ports-assert() { - ufw-docker--add-rule instance-name 172.18.0.3 5000 tcp default - ufw-docker--add-rule instance-name 172.18.0.3 8080 tcp default - ufw-docker--add-rule instance-name 172.18.0.3 5353 udp default # FIXME + ufw-docker--add-rule instance-name 172.18.0.3 any 5000 tcp default + ufw-docker--add-rule instance-name 172.18.0.3 any 8080 tcp default + ufw-docker--add-rule instance-name 172.18.0.3 any 5353 udp default # FIXME } test-allow-internal-succeeds-for-all-published-ports-on-multinetwork() { setup-ufw-docker--allow--multinetwork - ufw-docker--allow instance-name "" "" + ufw-docker--allow instance-name any "" "" } test-allow-internal-succeeds-for-all-published-ports-on-multinetwork-assert() { - ufw-docker--add-rule instance-name 172.18.0.3 5000 tcp default - ufw-docker--add-rule instance-name 172.19.0.7 5000 tcp awesomenet - ufw-docker--add-rule instance-name 172.18.0.3 8080 tcp default - ufw-docker--add-rule instance-name 172.19.0.7 8080 tcp awesomenet - ufw-docker--add-rule instance-name 172.18.0.3 5353 udp default - ufw-docker--add-rule instance-name 172.19.0.7 5353 udp awesomenet + ufw-docker--add-rule instance-name 172.18.0.3 any 5000 tcp default + ufw-docker--add-rule instance-name 172.19.0.7 any 5000 tcp awesomenet + ufw-docker--add-rule instance-name 172.18.0.3 any 8080 tcp default + ufw-docker--add-rule instance-name 172.19.0.7 any 8080 tcp awesomenet + ufw-docker--add-rule instance-name 172.18.0.3 any 5353 udp default + ufw-docker--add-rule instance-name 172.19.0.7 any 5353 udp awesomenet } test-allow-internal-succeeds-for-all-published-ports-on-selected-multinetwork() { setup-ufw-docker--allow--multinetwork - ufw-docker--allow instance-name "" "" awesomenet + ufw-docker--allow instance-name any "" "" awesomenet } test-allow-internal-succeeds-for-all-published-ports-on-selected-multinetwork-assert() { - ufw-docker--add-rule instance-name 172.19.0.7 5000 tcp awesomenet - ufw-docker--add-rule instance-name 172.19.0.7 8080 tcp awesomenet - ufw-docker--add-rule instance-name 172.19.0.7 5353 udp awesomenet + ufw-docker--add-rule instance-name 172.19.0.7 any 5000 tcp awesomenet + ufw-docker--add-rule instance-name 172.19.0.7 any 8080 tcp awesomenet + ufw-docker--add-rule instance-name 172.19.0.7 any 5353 udp awesomenet } test-ipv6-allow-internal-succeeds-when-port-matches() { setup-IPv6-ufw-docker--allow - ufw-docker--allow instance-name 5000 tcp + ufw-docker--allow instance-name any 5000 tcp } test-ipv6-allow-internal-succeeds-when-port-matches-assert() { - ufw-docker--add-rule instance-name 172.18.0.3 5000 tcp default - ufw-docker--add-rule instance-name/v6 fd00:cf::42 5000 tcp default + ufw-docker--add-rule instance-name 172.18.0.3 any 5000 tcp default + ufw-docker--add-rule instance-name/v6 fd00:cf::42 any 5000 tcp default } test-ipv6-allow-internal-succeeds-for-all-published-ports() { setup-IPv6-ufw-docker--allow - ufw-docker--allow instance-name "" "" + ufw-docker--allow instance-name any "" "" } test-ipv6-allow-internal-succeeds-for-all-published-ports-assert() { - ufw-docker--add-rule instance-name 172.18.0.3 5000 tcp default - ufw-docker--add-rule instance-name/v6 fd00:cf::42 5000 tcp default - ufw-docker--add-rule instance-name 172.18.0.3 8080 tcp default - ufw-docker--add-rule instance-name/v6 fd00:cf::42 8080 tcp default - ufw-docker--add-rule instance-name 172.18.0.3 5353 udp default - ufw-docker--add-rule instance-name/v6 fd00:cf::42 5353 udp default + ufw-docker--add-rule instance-name 172.18.0.3 any 5000 tcp default + ufw-docker--add-rule instance-name/v6 fd00:cf::42 any 5000 tcp default + ufw-docker--add-rule instance-name 172.18.0.3 any 8080 tcp default + ufw-docker--add-rule instance-name/v6 fd00:cf::42 any 8080 tcp default + ufw-docker--add-rule instance-name 172.18.0.3 any 5353 udp default + ufw-docker--add-rule instance-name/v6 fd00:cf::42 any 5353 udp default } test-ipv6-allow-internal-succeeds-for-all-published-tcp-ports() { setup-IPv6-ufw-docker--allow - ufw-docker--allow instance-name "" tcp + ufw-docker--allow instance-name any "" tcp } test-ipv6-allow-internal-succeeds-for-all-published-tcp-ports-assert() { - ufw-docker--add-rule instance-name 172.18.0.3 5000 tcp default - ufw-docker--add-rule instance-name/v6 fd00:cf::42 5000 tcp default - ufw-docker--add-rule instance-name 172.18.0.3 8080 tcp default - ufw-docker--add-rule instance-name/v6 fd00:cf::42 8080 tcp default - ufw-docker--add-rule instance-name 172.18.0.3 5353 udp default # FIXME - ufw-docker--add-rule instance-name/v6 fd00:cf::42 5353 udp default # FIXME + ufw-docker--add-rule instance-name 172.18.0.3 any 5000 tcp default + ufw-docker--add-rule instance-name/v6 fd00:cf::42 any 5000 tcp default + ufw-docker--add-rule instance-name 172.18.0.3 any 8080 tcp default + ufw-docker--add-rule instance-name/v6 fd00:cf::42 any 8080 tcp default + ufw-docker--add-rule instance-name 172.18.0.3 any 5353 udp default # FIXME + ufw-docker--add-rule instance-name/v6 fd00:cf::42 any 5353 udp default # FIXME } test-ipv6-allow-internal-succeeds-for-all-published-ports-on-multinetwork() { setup-IPv6-ufw-docker--allow--multinetwork - ufw-docker--allow instance-name "" "" + ufw-docker--allow instance-name any "" "" } test-ipv6-allow-internal-succeeds-for-all-published-ports-on-multinetwork-assert() { - ufw-docker--add-rule instance-name 172.18.0.3 5000 tcp default - ufw-docker--add-rule instance-name/v6 fd00:cf::42 5000 tcp default - ufw-docker--add-rule instance-name 172.19.0.7 5000 tcp awesomenet - ufw-docker--add-rule instance-name/v6 fd00:cf::207 5000 tcp awesomenet - ufw-docker--add-rule instance-name 172.18.0.3 8080 tcp default - ufw-docker--add-rule instance-name/v6 fd00:cf::42 8080 tcp default - ufw-docker--add-rule instance-name 172.19.0.7 8080 tcp awesomenet - ufw-docker--add-rule instance-name/v6 fd00:cf::207 8080 tcp awesomenet - ufw-docker--add-rule instance-name 172.18.0.3 5353 udp default - ufw-docker--add-rule instance-name/v6 fd00:cf::42 5353 udp default - ufw-docker--add-rule instance-name 172.19.0.7 5353 udp awesomenet - ufw-docker--add-rule instance-name/v6 fd00:cf::207 5353 udp awesomenet + ufw-docker--add-rule instance-name 172.18.0.3 any 5000 tcp default + ufw-docker--add-rule instance-name/v6 fd00:cf::42 any 5000 tcp default + ufw-docker--add-rule instance-name 172.19.0.7 any 5000 tcp awesomenet + ufw-docker--add-rule instance-name/v6 fd00:cf::207 any 5000 tcp awesomenet + ufw-docker--add-rule instance-name 172.18.0.3 any 8080 tcp default + ufw-docker--add-rule instance-name/v6 fd00:cf::42 any 8080 tcp default + ufw-docker--add-rule instance-name 172.19.0.7 any 8080 tcp awesomenet + ufw-docker--add-rule instance-name/v6 fd00:cf::207 any 8080 tcp awesomenet + ufw-docker--add-rule instance-name 172.18.0.3 any 5353 udp default + ufw-docker--add-rule instance-name/v6 fd00:cf::42 any 5353 udp default + ufw-docker--add-rule instance-name 172.19.0.7 any 5353 udp awesomenet + ufw-docker--add-rule instance-name/v6 fd00:cf::207 any 5353 udp awesomenet } test-ipv6-allow-internal-succeeds-for-all-published-ports-on-selected-multinetwork() { setup-IPv6-ufw-docker--allow--multinetwork - ufw-docker--allow instance-name "" "" awesomenet + ufw-docker--allow instance-name any "" "" awesomenet } test-ipv6-allow-internal-succeeds-for-all-published-ports-on-selected-multinetwork-assert() { - ufw-docker--add-rule instance-name 172.19.0.7 5000 tcp awesomenet - ufw-docker--add-rule instance-name/v6 fd00:cf::207 5000 tcp awesomenet - ufw-docker--add-rule instance-name 172.19.0.7 8080 tcp awesomenet - ufw-docker--add-rule instance-name/v6 fd00:cf::207 8080 tcp awesomenet - ufw-docker--add-rule instance-name 172.19.0.7 5353 udp awesomenet - ufw-docker--add-rule instance-name/v6 fd00:cf::207 5353 udp awesomenet + ufw-docker--add-rule instance-name 172.19.0.7 any 5000 tcp awesomenet + ufw-docker--add-rule instance-name/v6 fd00:cf::207 any 5000 tcp awesomenet + ufw-docker--add-rule instance-name 172.19.0.7 any 8080 tcp awesomenet + ufw-docker--add-rule instance-name/v6 fd00:cf::207 any 8080 tcp awesomenet + ufw-docker--add-rule instance-name 172.19.0.7 any 5353 udp awesomenet + ufw-docker--add-rule instance-name/v6 fd00:cf::207 any 5353 udp awesomenet } test-add-rule-for-non-existing-rule() { - @mockfalse ufw-docker--list webapp 5000 tcp "" + @mockfalse ufw-docker--list webapp any 5000 tcp "" @ignore echo load-ufw-docker-function ufw-docker--add-rule - ufw-docker--add-rule webapp 172.18.0.4 5000 tcp + ufw-docker--add-rule webapp 172.18.0.4 any 5000 tcp } test-add-rule-for-non-existing-rule-assert() { - ufw route allow proto tcp from any to 172.18.0.4 port 5000 comment "allow webapp 5000/tcp" + ufw route allow proto tcp from any to 172.18.0.4 port 5000 comment "allow webapp any 5000/tcp" } test-add-rule-for-non-existing-rule-with-network() { - @mockfalse ufw-docker--list webapp 5000 tcp default + @mockfalse ufw-docker--list webapp any 5000 tcp default @ignore echo load-ufw-docker-function ufw-docker--add-rule - ufw-docker--add-rule webapp 172.18.0.4 5000 tcp default + ufw-docker--add-rule webapp 172.18.0.4 any 5000 tcp default } test-add-rule-for-non-existing-rule-with-network-assert() { - ufw route allow proto tcp from any to 172.18.0.4 port 5000 comment "allow webapp 5000/tcp default" + ufw route allow proto tcp from any to 172.18.0.4 port 5000 comment "allow webapp any 5000/tcp default" } test-add-rule-modifies-existing-rule() { - @mocktrue ufw-docker--list webapp 5000 tcp default - @mock ufw --dry-run route allow proto tcp from any to 172.18.0.4 port 5000 comment "allow webapp 5000/tcp default" === @echo + @mocktrue ufw-docker--list webapp any 5000 tcp default + @mock ufw --dry-run route allow proto tcp from any to 172.18.0.4 port 5000 comment "allow webapp any 5000/tcp default" === @echo @mockfalse grep "^Skipping" @ignore echo load-ufw-docker-function ufw-docker--add-rule - ufw-docker--add-rule webapp 172.18.0.4 5000 tcp default + ufw-docker--add-rule webapp 172.18.0.4 any 5000 tcp default } test-add-rule-modifies-existing-rule-assert() { - ufw-docker--delete webapp 5000 tcp default + ufw-docker--delete webapp any 5000 tcp default - ufw route allow proto tcp from any to 172.18.0.4 port 5000 comment "allow webapp 5000/tcp default" + ufw route allow proto tcp from any to 172.18.0.4 port 5000 comment "allow webapp any 5000/tcp default" } test-ipv6-add-rule-modifies-existing-rule() { - @mocktrue ufw-docker--list webapp/v6 5000 tcp default - @mock ufw --dry-run route allow proto tcp from any to fd00:cf::42 port 5000 comment "allow webapp/v6 5000/tcp default" === @echo + @mocktrue ufw-docker--list webapp/v6 any 5000 tcp default + @mock ufw --dry-run route allow proto tcp from any to fd00:cf::42 port 5000 comment "allow webapp/v6 any 5000/tcp default" === @echo @mockfalse grep "^Skipping" @ignore echo load-ufw-docker-function ufw-docker--add-rule - ufw-docker--add-rule webapp/v6 fd00:cf::42 5000 tcp default + ufw-docker--add-rule webapp/v6 fd00:cf::42 any 5000 tcp default } test-ipv6-add-rule-modifies-existing-rule-assert() { - ufw-docker--delete webapp/v6 5000 tcp default + ufw-docker--delete webapp/v6 any 5000 tcp default - ufw route allow proto tcp from any to fd00:cf::42 port 5000 comment "allow webapp/v6 5000/tcp default" + ufw route allow proto tcp from any to fd00:cf::42 port 5000 comment "allow webapp/v6 any 5000/tcp default" } test-add-rule-skips-existing-rule() { - @mocktrue ufw-docker--list webapp 5000 tcp "" - @mocktrue ufw --dry-run route allow proto tcp from any to 172.18.0.4 port 5000 comment "allow webapp 5000/tcp" + @mocktrue ufw-docker--list webapp any 5000 tcp "" + @mocktrue ufw --dry-run route allow proto tcp from any to 172.18.0.4 port 5000 comment "allow webapp any 5000/tcp" @mocktrue grep "^Skipping" @ignore echo load-ufw-docker-function ufw-docker--add-rule - ufw-docker--add-rule webapp 172.18.0.4 5000 tcp "" + ufw-docker--add-rule webapp 172.18.0.4 any 5000 tcp "" } test-add-rule-skips-existing-rule-assert() { @do-nothing @@ -552,19 +614,67 @@ test-add-rule-skips-existing-rule-assert() { test-add-rule-modifies-existing-rule-without-port() { - @mocktrue ufw-docker--list webapp "" tcp "" - @mock ufw --dry-run route allow proto tcp from any to 172.18.0.4 comment "allow webapp" === @echo + @mocktrue ufw-docker--list webapp any "" tcp "" + @mock ufw --dry-run route allow proto tcp from any to 172.18.0.4 comment "allow webapp any" === @echo @mockfalse grep "^Skipping" @ignore echo load-ufw-docker-function ufw-docker--add-rule - ufw-docker--add-rule webapp 172.18.0.4 "" tcp "" + ufw-docker--add-rule webapp 172.18.0.4 any "" tcp "" } test-add-rule-modifies-existing-rule-without-port-assert() { - ufw-docker--delete webapp "" tcp "" + ufw-docker--delete webapp any "" tcp "" + + ufw route allow proto tcp from any to 172.18.0.4 comment "allow webapp any" +} + + +test-add-rule-for-source-ip() { + @mockfalse ufw-docker--list webapp 192.168.1.10 5000 tcp "" + @ignore echo + + load-ufw-docker-function ufw-docker--add-rule + ufw-docker--add-rule webapp 172.18.0.4 192.168.1.10 5000 tcp +} +test-add-rule-for-source-ip-assert() { + ufw route allow proto tcp from 192.168.1.10 to 172.18.0.4 port 5000 comment "allow webapp 192.168.1.10 5000/tcp" +} + + +test-add-rule-for-source-subnet() { + @mockfalse ufw-docker--list webapp 192.168.1.0/24 5000 tcp "" + @ignore echo + + load-ufw-docker-function ufw-docker--add-rule + ufw-docker--add-rule webapp 172.18.0.4 192.168.1.0/24 5000 tcp +} +test-add-rule-for-source-subnet-assert() { + ufw route allow proto tcp from 192.168.1.0/24 to 172.18.0.4 port 5000 comment "allow webapp 192.168.1.0/24 5000/tcp" +} - ufw route allow proto tcp from any to 172.18.0.4 comment "allow webapp" + +test-ipv6-add-rule-for-source-ip() { + @mockfalse ufw-docker--list webapp fd00:cf::42 5000 tcp "" + @ignore echo + + load-ufw-docker-function ufw-docker--add-rule + ufw-docker--add-rule webapp 172.18.0.4 fd00:cf::42 5000 tcp +} +test-ipv6-add-rule-for-source-ip-assert() { + ufw route allow proto tcp from fd00:cf::42 to 172.18.0.4 port 5000 comment "allow webapp fd00:cf::42 5000/tcp" +} + + +test-ipv6-add-rule-for-source-subnet() { + @mockfalse ufw-docker--list webapp fd00:cf::/8 5000 tcp "" + @ignore echo + + load-ufw-docker-function ufw-docker--add-rule + ufw-docker--add-rule webapp 172.18.0.4 fd00:cf::/8 5000 tcp +} +test-ipv6-add-rule-for-source-subnet-assert() { + ufw route allow proto tcp from fd00:cf::/8 to 172.18.0.4 port 5000 comment "allow webapp fd00:cf::/8 5000/tcp" } @@ -605,26 +715,33 @@ function mock-ufw-status-numbered-foo() { -- ------ ---- [ 1] OpenSSH ALLOW IN Anywhere [ 2] Anywhere ALLOW IN 192.168.56.128/28 -[ 3] 172.17.0.3 80/tcp ALLOW FWD Anywhere # allow foo 80/tcp bridge -[ 4] 172.20.0.3 80/tcp ALLOW FWD Anywhere # allow bar 80/tcp bar-external -[ 5] 172.17.0.3 53/udp ALLOW FWD Anywhere # allow foo 53/udp foo-internal -[ 6] 172.17.0.3 53/tcp ALLOW FWD Anywhere # allow foo 53/tcp -[ 7] 172.18.0.2 29090/tcp ALLOW FWD Anywhere # allow id111111 29090/tcp -[ 8] 172.18.0.2 48080/tcp ALLOW FWD Anywhere # allow id222222 48080/tcp -[ 9] 172.18.0.2 40080/tcp ALLOW FWD Anywhere # allow id333333 40080/tcp -[10] OpenSSH (v6) ALLOW IN Anywhere (v6) -[11] Anywhere (v6) ALLOW IN fd00:a:b:0:cafe::/80 -[12] fd00:a:b:deaf::3 80/tcp ALLOW FWD Anywhere (v6) # allow foo/v6 80/tcp bridge -[13] fd05:8f23:c937:2::3 80/tcp ALLOW FWD Anywhere (v6) # allow bar/v6 80/tcp bar-external -[14] fd00:a:b:deaf::3 53/udp ALLOW FWD Anywhere (v6) # allow foo/v6 53/udp foo-internal -[15] fd00:a:b:deaf::3 53/tcp ALLOW FWD Anywhere (v6) # allow foo/v6 53/tcp +[ 3] 172.17.0.3 80/tcp ALLOW FWD Anywhere # allow foo any 80/tcp bridge +[ 4] 172.20.0.3 80/tcp ALLOW FWD Anywhere # allow bar any 80/tcp bar-external +[ 5] 172.17.0.3 53/udp ALLOW FWD Anywhere # allow foo any 53/udp foo-internal +[ 6] 172.17.0.3 53/tcp ALLOW FWD Anywhere # allow foo any 53/tcp +[ 7] 172.18.0.2 29090/tcp ALLOW FWD Anywhere # allow id111111 any 29090/tcp +[ 8] 172.18.0.2 48080/tcp ALLOW FWD Anywhere # allow id222222 any 48080/tcp +[ 9] 172.18.0.2 40080/tcp ALLOW FWD Anywhere # allow id333333 any 40080/tcp +[10] 172.18.0.4 8080/tcp ALLOW FWD 192.168.1.10 # allow baz 192.168.1.10 8080/tcp +[11] 172.18.0.4 8082/udp ALLOW FWD 192.168.1.10 # allow baz 192.168.1.10 8082/udp +[12] 172.18.0.4 8080/tcp ALLOW FWD 192.168.2.10/24 # allow baz 192.168.2.10/24 8080/tcp +[13] 172.18.0.4 8081/tcp ALLOW FWD 192.168.2.10/24 # allow baz 192.168.2.10/24 8081/tcp bridge +[14] OpenSSH (v6) ALLOW IN Anywhere (v6) +[15] Anywhere (v6) ALLOW IN fd00:a:b:0:cafe::/80 +[16] fd00:a:b:deaf::3 80/tcp ALLOW FWD Anywhere (v6) # allow foo/v6 any 80/tcp bridge +[17] fd05:8f23:c937:2::3 80/tcp ALLOW FWD Anywhere (v6) # allow bar/v6 any 80/tcp bar-external +[18] fd00:a:b:deaf::3 53/udp ALLOW FWD Anywhere (v6) # allow foo/v6 any 53/udp foo-internal +[19] fd00:a:b:deaf::3 53/tcp ALLOW FWD Anywhere (v6) # allow foo/v6 any 53/tcp +[20] fd00:a:b:deaf::4 8080/tcp ALLOW FWD fd00:a:b:0:cafe::1 # allow baz/v6 fd00:a:b:0:cafe::1 8080/tcp +[21] fd00:a:b:deaf::4 8080/tcp ALLOW FWD fd00:a:b:0:cafe::/80 # allow baz/v6 fd00:a:b:0:cafe::/80 8080/tcp +[22] fd00:a:b:deaf::4 8081/tcp ALLOW FWD fd00:a:b:0:cafe::/80 # allow baz/v6 fd00:a:b:0:cafe::/80 8081/tcp bridge " } test-status-internal() { mock-ufw-status-numbered-foo - @allow-real grep '# allow [-_.[:alnum:]]\+\(/v6\)\?\( [[:digit:]]\+/\(tcp\|udp\)\( [-_.[:alnum:]]\+\)\?\)\?$' + @allow-real grep '# allow [-_.[:alnum:]]\+\(/v6\)\? \([.:/[:xdigit:]]\+\|any\) [[:digit:]]\+/\(tcp\|udp\)\( [-_.[:alnum:]]\+\)\?$' load-ufw-docker-function ufw-docker--list load-ufw-docker-function ufw-docker--status @@ -636,110 +753,179 @@ test-status-internal-assert() { test-list-internal-all-rules() { mock-ufw-status-numbered-foo - @allow-real grep '# allow [-_.[:alnum:]]\+\(/v6\)\?\( [[:digit:]]\+/\(tcp\|udp\)\( [-_.[:alnum:]]\+\)\?\)\?$' + @allow-real grep '# allow [-_.[:alnum:]]\+\(/v6\)\? \([.:/[:xdigit:]]\+\|any\) [[:digit:]]\+/\(tcp\|udp\)\( [-_.[:alnum:]]\+\)\?$' load-ufw-docker-function ufw-docker--list ufw-docker--list } test-list-internal-all-rules-assert() { - @stdout "[ 3] 172.17.0.3 80/tcp ALLOW FWD Anywhere # allow foo 80/tcp bridge" - @stdout "[ 4] 172.20.0.3 80/tcp ALLOW FWD Anywhere # allow bar 80/tcp bar-external" - @stdout "[ 5] 172.17.0.3 53/udp ALLOW FWD Anywhere # allow foo 53/udp foo-internal" - @stdout "[ 6] 172.17.0.3 53/tcp ALLOW FWD Anywhere # allow foo 53/tcp" - @stdout "[ 7] 172.18.0.2 29090/tcp ALLOW FWD Anywhere # allow id111111 29090/tcp" - @stdout "[ 8] 172.18.0.2 48080/tcp ALLOW FWD Anywhere # allow id222222 48080/tcp" - @stdout "[ 9] 172.18.0.2 40080/tcp ALLOW FWD Anywhere # allow id333333 40080/tcp" - @stdout "[12] fd00:a:b:deaf::3 80/tcp ALLOW FWD Anywhere (v6) # allow foo/v6 80/tcp bridge" - @stdout "[13] fd05:8f23:c937:2::3 80/tcp ALLOW FWD Anywhere (v6) # allow bar/v6 80/tcp bar-external" - @stdout "[14] fd00:a:b:deaf::3 53/udp ALLOW FWD Anywhere (v6) # allow foo/v6 53/udp foo-internal" - @stdout "[15] fd00:a:b:deaf::3 53/tcp ALLOW FWD Anywhere (v6) # allow foo/v6 53/tcp" + @stdout "[ 3] 172.17.0.3 80/tcp ALLOW FWD Anywhere # allow foo any 80/tcp bridge" + @stdout "[ 4] 172.20.0.3 80/tcp ALLOW FWD Anywhere # allow bar any 80/tcp bar-external" + @stdout "[ 5] 172.17.0.3 53/udp ALLOW FWD Anywhere # allow foo any 53/udp foo-internal" + @stdout "[ 6] 172.17.0.3 53/tcp ALLOW FWD Anywhere # allow foo any 53/tcp" + @stdout "[ 7] 172.18.0.2 29090/tcp ALLOW FWD Anywhere # allow id111111 any 29090/tcp" + @stdout "[ 8] 172.18.0.2 48080/tcp ALLOW FWD Anywhere # allow id222222 any 48080/tcp" + @stdout "[ 9] 172.18.0.2 40080/tcp ALLOW FWD Anywhere # allow id333333 any 40080/tcp" + @stdout "[10] 172.18.0.4 8080/tcp ALLOW FWD 192.168.1.10 # allow baz 192.168.1.10 8080/tcp" + @stdout "[11] 172.18.0.4 8082/udp ALLOW FWD 192.168.1.10 # allow baz 192.168.1.10 8082/udp" + @stdout "[12] 172.18.0.4 8080/tcp ALLOW FWD 192.168.2.10/24 # allow baz 192.168.2.10/24 8080/tcp" + @stdout "[13] 172.18.0.4 8081/tcp ALLOW FWD 192.168.2.10/24 # allow baz 192.168.2.10/24 8081/tcp bridge" + @stdout "[16] fd00:a:b:deaf::3 80/tcp ALLOW FWD Anywhere (v6) # allow foo/v6 any 80/tcp bridge" + @stdout "[17] fd05:8f23:c937:2::3 80/tcp ALLOW FWD Anywhere (v6) # allow bar/v6 any 80/tcp bar-external" + @stdout "[18] fd00:a:b:deaf::3 53/udp ALLOW FWD Anywhere (v6) # allow foo/v6 any 53/udp foo-internal" + @stdout "[19] fd00:a:b:deaf::3 53/tcp ALLOW FWD Anywhere (v6) # allow foo/v6 any 53/tcp" + @stdout "[20] fd00:a:b:deaf::4 8080/tcp ALLOW FWD fd00:a:b:0:cafe::1 # allow baz/v6 fd00:a:b:0:cafe::1 8080/tcp" + @stdout "[21] fd00:a:b:deaf::4 8080/tcp ALLOW FWD fd00:a:b:0:cafe::/80 # allow baz/v6 fd00:a:b:0:cafe::/80 8080/tcp" + @stdout "[22] fd00:a:b:deaf::4 8081/tcp ALLOW FWD fd00:a:b:0:cafe::/80 # allow baz/v6 fd00:a:b:0:cafe::/80 8081/tcp bridge" } test-list-internal-rules-by-name() { mock-ufw-status-numbered-foo - @allow-real grep '# allow foo\(/v6\)\?\( [[:digit:]]\+/\(tcp\|udp\)\( [-_.[:alnum:]]\+\)\?\)\?$' + @allow-real grep '# allow foo\(/v6\)\? \([.:/[:xdigit:]]\+\|any\) [[:digit:]]\+/\(tcp\|udp\)\( [-_.[:alnum:]]\+\)\?$' load-ufw-docker-function ufw-docker--list ufw-docker--list foo } test-list-internal-rules-by-name-assert() { - @stdout "[ 3] 172.17.0.3 80/tcp ALLOW FWD Anywhere # allow foo 80/tcp bridge" - @stdout "[ 5] 172.17.0.3 53/udp ALLOW FWD Anywhere # allow foo 53/udp foo-internal" - @stdout "[ 6] 172.17.0.3 53/tcp ALLOW FWD Anywhere # allow foo 53/tcp" - @stdout "[12] fd00:a:b:deaf::3 80/tcp ALLOW FWD Anywhere (v6) # allow foo/v6 80/tcp bridge" - @stdout "[14] fd00:a:b:deaf::3 53/udp ALLOW FWD Anywhere (v6) # allow foo/v6 53/udp foo-internal" - @stdout "[15] fd00:a:b:deaf::3 53/tcp ALLOW FWD Anywhere (v6) # allow foo/v6 53/tcp" + @stdout "[ 3] 172.17.0.3 80/tcp ALLOW FWD Anywhere # allow foo any 80/tcp bridge" + @stdout "[ 5] 172.17.0.3 53/udp ALLOW FWD Anywhere # allow foo any 53/udp foo-internal" + @stdout "[ 6] 172.17.0.3 53/tcp ALLOW FWD Anywhere # allow foo any 53/tcp" + @stdout "[16] fd00:a:b:deaf::3 80/tcp ALLOW FWD Anywhere (v6) # allow foo/v6 any 80/tcp bridge" + @stdout "[18] fd00:a:b:deaf::3 53/udp ALLOW FWD Anywhere (v6) # allow foo/v6 any 53/udp foo-internal" + @stdout "[19] fd00:a:b:deaf::3 53/tcp ALLOW FWD Anywhere (v6) # allow foo/v6 any 53/tcp" } test-list-internal-rules-by-name-and-udp-protocol() { mock-ufw-status-numbered-foo - @allow-real grep '# allow foo\(/v6\)\? [[:digit:]]\+/udp\( [-_.[:alnum:]]\+\)\?$' + @allow-real grep '# allow foo\(/v6\)\? \([.:/[:xdigit:]]\+\|any\) [[:digit:]]\+/udp\( [-_.[:alnum:]]\+\)\?$' load-ufw-docker-function ufw-docker--list - ufw-docker--list foo "" udp + ufw-docker--list foo "" "" udp } test-list-internal-rules-by-name-and-udp-protocol-assert() { - @stdout "[ 5] 172.17.0.3 53/udp ALLOW FWD Anywhere # allow foo 53/udp foo-internal" - @stdout "[14] fd00:a:b:deaf::3 53/udp ALLOW FWD Anywhere (v6) # allow foo/v6 53/udp foo-internal" + @stdout "[ 5] 172.17.0.3 53/udp ALLOW FWD Anywhere # allow foo any 53/udp foo-internal" + @stdout "[18] fd00:a:b:deaf::3 53/udp ALLOW FWD Anywhere (v6) # allow foo/v6 any 53/udp foo-internal" } test-list-internal-rules-by-name-port-and-bridge-network() { mock-ufw-status-numbered-foo - @allow-real grep '# allow foo\(/v6\)\? 80/tcp bridge$' + @allow-real grep '# allow foo\(/v6\)\? \([.:/[:xdigit:]]\+\|any\) 80/tcp bridge$' load-ufw-docker-function ufw-docker--list - ufw-docker--list foo 80 "" bridge + ufw-docker--list foo "" 80 "" bridge } test-list-internal-rules-by-name-port-and-bridge-network-assert() { - @stdout "[ 3] 172.17.0.3 80/tcp ALLOW FWD Anywhere # allow foo 80/tcp bridge" - @stdout "[12] fd00:a:b:deaf::3 80/tcp ALLOW FWD Anywhere (v6) # allow foo/v6 80/tcp bridge" + @stdout "[ 3] 172.17.0.3 80/tcp ALLOW FWD Anywhere # allow foo any 80/tcp bridge" + @stdout "[16] fd00:a:b:deaf::3 80/tcp ALLOW FWD Anywhere (v6) # allow foo/v6 any 80/tcp bridge" } test-list-internal-rules-by-name-port-and-udp-protocol() { mock-ufw-status-numbered-foo - @allow-real grep '# allow foo\(/v6\)\? 53/udp\( [-_.[:alnum:]]\+\)\?$' + @allow-real grep '# allow foo\(/v6\)\? \([.:/[:xdigit:]]\+\|any\) 53/udp\( [-_.[:alnum:]]\+\)\?$' load-ufw-docker-function ufw-docker--list - ufw-docker--list foo 53 udp + ufw-docker--list foo "" 53 udp } test-list-internal-rules-by-name-port-and-udp-protocol-assert() { - @stdout "[ 5] 172.17.0.3 53/udp ALLOW FWD Anywhere # allow foo 53/udp foo-internal" - @stdout "[14] fd00:a:b:deaf::3 53/udp ALLOW FWD Anywhere (v6) # allow foo/v6 53/udp foo-internal" + @stdout "[ 5] 172.17.0.3 53/udp ALLOW FWD Anywhere # allow foo any 53/udp foo-internal" + @stdout "[18] fd00:a:b:deaf::3 53/udp ALLOW FWD Anywhere (v6) # allow foo/v6 any 53/udp foo-internal" } test-list-internal-fails-with-incorrect-network() { mock-ufw-status-numbered-foo - @allow-real grep '# allow foo\(/v6\)\? 53/udp incorrect-network$' + @allow-real grep '# allow foo\(/v6\)\? any 53/udp incorrect-network$' load-ufw-docker-function ufw-docker--list - ufw-docker--list foo 53 udp incorrect-network + ufw-docker--list foo any 53 udp incorrect-network } test-list-internal-fails-with-incorrect-network-assert() { @fail } +test-list-internal-rules-by-name-and-source-ip() { + mock-ufw-status-numbered-foo + @allow-real grep '# allow baz\(/v6\)\? 192.168.1.10 [[:digit:]]\+/\(tcp\|udp\)\( [-_.[:alnum:]]\+\)\?$' + + load-ufw-docker-function ufw-docker--list + ufw-docker--list baz 192.168.1.10 +} +test-list-internal-rules-by-name-and-source-ip-assert() { + @stdout "[10] 172.18.0.4 8080/tcp ALLOW FWD 192.168.1.10 # allow baz 192.168.1.10 8080/tcp" + @stdout "[11] 172.18.0.4 8082/udp ALLOW FWD 192.168.1.10 # allow baz 192.168.1.10 8082/udp" +} + + +test-list-internal-rules-by-name-and-source-subnet() { + mock-ufw-status-numbered-foo + @allow-real grep '# allow baz\(/v6\)\? 192.168.2.10/24 [[:digit:]]\+/\(tcp\|udp\)\( [-_.[:alnum:]]\+\)\?$' + + load-ufw-docker-function ufw-docker--list + ufw-docker--list baz 192.168.2.10/24 +} +test-list-internal-rules-by-name-and-source-subnet-assert() { + @stdout "[12] 172.18.0.4 8080/tcp ALLOW FWD 192.168.2.10/24 # allow baz 192.168.2.10/24 8080/tcp" + @stdout "[13] 172.18.0.4 8081/tcp ALLOW FWD 192.168.2.10/24 # allow baz 192.168.2.10/24 8081/tcp bridge" +} + + +test-list-internal-rules-by-name-source-and-port() { + mock-ufw-status-numbered-foo + @allow-real grep '# allow baz\(/v6\)\? 192.168.2.10/24 8081/tcp\( [-_.[:alnum:]]\+\)\?$' + + load-ufw-docker-function ufw-docker--list + ufw-docker--list baz 192.168.2.10/24 8081 +} +test-list-internal-rules-by-name-source-and-port-assert() { + @stdout "[13] 172.18.0.4 8081/tcp ALLOW FWD 192.168.2.10/24 # allow baz 192.168.2.10/24 8081/tcp bridge" +} + + +test-list-internal-rules-by-name-source-port-and-udp-protocol() { + mock-ufw-status-numbered-foo + @allow-real grep '# allow baz\(/v6\)\? 192.168.1.10 8082/udp\( [-_.[:alnum:]]\+\)\?$' + + load-ufw-docker-function ufw-docker--list + ufw-docker--list baz 192.168.1.10 8082 udp +} +test-list-internal-rules-by-name-source-port-and-udp-protocol-assert() { + @stdout "[11] 172.18.0.4 8082/udp ALLOW FWD 192.168.1.10 # allow baz 192.168.1.10 8082/udp" +} + + +test-list-internal-rules-by-name-source-port-protocol-and-network() { + mock-ufw-status-numbered-foo + @allow-real grep '# allow baz\(/v6\)\? 192.168.2.10/24 8081/tcp bridge$' + + load-ufw-docker-function ufw-docker--list + ufw-docker--list baz 192.168.2.10/24 8081 tcp bridge +} +test-list-internal-rules-by-name-source-port-protocol-and-network-assert() { + @stdout "[13] 172.18.0.4 8081/tcp ALLOW FWD 192.168.2.10/24 # allow baz 192.168.2.10/24 8081/tcp bridge" +} + + test-list-internal-rules-by-name-and-port() { mock-ufw-status-numbered-foo - @allow-real grep '# allow foo\(/v6\)\? 80/tcp\( [-_.[:alnum:]]\+\)\?$' + @allow-real grep '# allow foo\(/v6\)\? \([.:/[:xdigit:]]\+\|any\) 80/tcp\( [-_.[:alnum:]]\+\)\?$' load-ufw-docker-function ufw-docker--list - ufw-docker--list foo 80 + ufw-docker--list foo "" 80 } test-list-internal-rules-by-name-and-port-assert() { - @stdout "[ 3] 172.17.0.3 80/tcp ALLOW FWD Anywhere # allow foo 80/tcp bridge" - @stdout "[12] fd00:a:b:deaf::3 80/tcp ALLOW FWD Anywhere (v6) # allow foo/v6 80/tcp bridge" + @stdout "[ 3] 172.17.0.3 80/tcp ALLOW FWD Anywhere # allow foo any 80/tcp bridge" + @stdout "[16] fd00:a:b:deaf::3 80/tcp ALLOW FWD Anywhere (v6) # allow foo/v6 any 80/tcp bridge" } test-list-number-internal() { - @mocktrue ufw-docker--list foo 53 udp + @mocktrue ufw-docker--list foo any 53 udp load-ufw-docker-function ufw-docker--list-number - ufw-docker--list-number foo 53 udp + ufw-docker--list-number foo any 53 udp } test-list-number-internal-assert() { sed -e 's/^\[[[:blank:]]*\([[:digit:]]\+\)\].*/\1/' @@ -747,11 +933,11 @@ test-list-number-internal-assert() { test-delete-internal-does-nothing-for-empty-result() { - @mock ufw-docker--list-number webapp 80 tcp === @stdout "" + @mock ufw-docker--list-number webapp any 80 tcp === @stdout "" @mockpipe sort -rn load-ufw-docker-function ufw-docker--delete - ufw-docker--delete webapp 80 tcp + ufw-docker--delete webapp any 80 tcp } test-delete-internal-does-nothing-for-empty-result-assert() { @do-nothing @@ -759,12 +945,12 @@ test-delete-internal-does-nothing-for-empty-result-assert() { test-delete-internal-all-rules() { - @mock ufw-docker--list-number webapp 80 tcp === @stdout 5 8 9 + @mock ufw-docker--list-number webapp any 80 tcp === @stdout 5 8 9 @mockpipe sort -rn @ignore echo load-ufw-docker-function ufw-docker--delete - ufw-docker--delete webapp 80 tcp + ufw-docker--delete webapp any 80 tcp } test-delete-internal-all-rules-assert() { ufw delete 5 diff --git a/ufw-docker b/ufw-docker index 2ae620f..00632bc 100755 --- a/ufw-docker +++ b/ufw-docker @@ -7,6 +7,8 @@ LC_ALL=C PATH="/bin:/usr/bin:/sbin:/usr/sbin:/snap/bin/" GREP_REGEXP_NAME="[-_.[:alnum:]]\\+" +GREP_REGEXP_SOURCE="\\([.:/[:xdigit:]]\\+\\|any\\)" +DEFAULT_SOURCE=any DEFAULT_PROTO=tcp ENV_VALUE_SPLITTER=',' @@ -33,32 +35,34 @@ function ufw-docker--status() { function ufw-docker--list() { local INSTANCE_NAME="${1:-}" - local INSTANCE_PORT="${2:-}" - local PROTO="${3:-}" - local NETWORK="${4:-}" + local SOURCE="${2:-}" + local PORT="${3:-}" + local PROTO="${4:-}" + local NETWORK="${5:-}" 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}\\)\\?\\)\\?" + [[ -n "${SOURCE:-}" ]] || SOURCE="$GREP_REGEXP_SOURCE" + if [[ -z "${PORT:-}" && -z "${PROTO:-}" && -z "${NETWORK:-}" ]]; then + PORT="[[:digit:]]\\+" + PROTO="\\(tcp\\|udp\\)" + _grep_regexp=" ${PORT}/${PROTO}\\( ${GREP_REGEXP_NAME}\\)\\?" else - [[ -n "${INSTANCE_PORT:-}" ]] || INSTANCE_PORT="[[:digit:]]\\+" + [[ -n "${PORT:-}" ]] || 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}" + _grep_regexp=" ${PORT}/${PROTO}${NETWORK}" fi local ufw_output ufw_output="$(ufw status numbered)" - grep "# allow ${INSTANCE_NAME}\\(/v6\\)\\?${_grep_regexp}\$" <<< "$ufw_output" + grep "# allow ${INSTANCE_NAME}\\(/v6\\)\\? ${SOURCE}${_grep_regexp}\$" <<< "$ufw_output" } function ufw-docker--list-number() { @@ -74,10 +78,11 @@ function ufw-docker--delete() { 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 + local SOURCE="${2:-${DEFAULT_SOURCE}}" + local PORT="$3" + local PROTO="$4" + local NETWORK="${5:-}" + local NETWORK_ADDRESSES PORT_PROTO_LIST PORT_PROTO IP SUFFIX docker inspect "$INSTANCE_NAME" &>/dev/null || die "Docker instance \"$INSTANCE_NAME\" doesn't exist." @@ -95,7 +100,7 @@ function ufw-docker--allow() { local count=0 for PORT_PROTO in "${PORT_PROTO_LIST[@]}"; do - if [[ -z "$INSTANCE_PORT" || "$PORT_PROTO" = "${INSTANCE_PORT}/${PROTO}" ]]; then + if [[ -z "$PORT" || "$PORT_PROTO" = "${PORT}/${PROTO}" ]]; then for item in "${NETWORK_ADDRESSES[@]}"; do INSTANCE_NETWORK="${item% *}" IP="${item#* }" @@ -103,13 +108,13 @@ function ufw-docker--allow() { continue fi if [[ "$IP" = *:* ]]; then SUFFIX="/v6"; else SUFFIX=""; fi - ufw-docker--add-rule "${INSTANCE_NAME}${SUFFIX}" "$IP" "${PORT_PROTO%/*}" "${PORT_PROTO#*/}" "${INSTANCE_NETWORK}" + ufw-docker--add-rule "${INSTANCE_NAME}${SUFFIX}" "$IP" "${SOURCE}" "${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)." + err "Fail to add rule(s), cannot find the published port ${PORT}/${PROTO} of instance \"${INSTANCE_NAME}\" or cannot update outdated rule(s)." return 1 fi return 0 @@ -125,23 +130,24 @@ function ufw-docker--add-service-rule() { [[ -z "$target_ip_port" ]] && die "Could not find VIP of service ${service_id}." - ufw-docker--add-rule "$service_id" "${target_ip_port%:*}" "$port" "$proto" + ufw-docker--add-rule "$service_id" "${target_ip_port%:*}" "$DEFAULT_SOURCE" "$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:-}" + local SOURCE="$3" + local PORT="$4" + local PROTO="$5" + local NETWORK="${6:-}" declare comment - echo "allow ${INSTANCE_NAME} ${PORT}/${PROTO} ${NETWORK}" + echo "allow ${INSTANCE_NAME} ${SOURCE} ${PORT}/${PROTO} ${NETWORK}" typeset -a UFW_OPTS UFW_OPTS=(route allow proto "${PROTO}" - from any to "$INSTANCE_IP_ADDRESS") - comment="allow ${INSTANCE_NAME}" + from "$SOURCE" to "$INSTANCE_IP_ADDRESS") + comment="allow ${INSTANCE_NAME} ${SOURCE}" [[ -n "$PORT" ]] && { UFW_OPTS+=(port "${PORT}") comment="$comment ${PORT}/${PROTO}" @@ -151,10 +157,10 @@ function ufw-docker--add-rule() { } UFW_OPTS+=(comment "$comment") - if ufw-docker--list "$INSTANCE_NAME" "$PORT" "$PROTO" "$NETWORK" &>/dev/null; then + if ufw-docker--list "$INSTANCE_NAME" "$SOURCE" "$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" + ufw-docker--delete "$INSTANCE_NAME" "$SOURCE" "$PORT" "$PROTO" "$NETWORK" fi echo ufw "${UFW_OPTS[@]}" ufw "${UFW_OPTS[@]}" @@ -572,48 +578,52 @@ HELP } function ufw-docker--help() { - cat <<-EOF >&2 - Usage: - ufw-docker [docker-instance-id-or-name [port[/tcp|/udp]] [network]] - ufw-docker delete allow [docker-instance-id-or-name [port[/tcp|/udp]] [network]] + cat <&2 +Usage: + ufw-docker [docker-instance-id-or-name [source] [port[/tcp|/udp]] [network]] + ufw-docker delete allow [docker-instance-id-or-name [source] [port[/tcp|/udp]] [network]] - ufw-docker service allow >> - ufw-docker service delete allow + ufw-docker service allow >> + ufw-docker service delete allow - ufw-docker [--docker-subnets [SUBNET0 SUBNET1 ...]] + ufw-docker [--docker-subnets [SUBNET0 SUBNET1 ...]] - ufw-docker + ufw-docker - Examples: - ufw-docker help - ufw-docker check --help - ufw-docker install --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 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 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 status - ufw-docker list httpd + 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 allow httpd + ufw-docker allow httpd any + ufw-docker allow httpd 192.168.1.10 + ufw-docker allow httpd any 80 + ufw-docker allow httpd any 80/tcp + ufw-docker allow httpd any 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 delete allow httpd + ufw-docker delete allow httpd any + ufw-docker delete allow httpd 192.168.1.10 + ufw-docker delete allow httpd any 80/tcp + ufw-docker delete allow httpd any 80/tcp default - ufw-docker service allow httpd 80/tcp + ufw-docker service allow httpd 80/tcp - ufw-docker service delete allow httpd - EOF + ufw-docker service delete allow httpd +EOF } function remove_blank_lines() { @@ -655,26 +665,29 @@ case "$ufw_action" in 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\"." + SOURCE="${1:-}" + if [[ -n "$SOURCE" && ! "$SOURCE" =~ ^[.:[:xdigit:]]+|any$ ]]; then + die "invalid source syntax: \"$SOURCE\"." + fi + shift || true + + PORT="${1:-}" + if [[ -n "$PORT" && ! "$PORT" =~ [0-9]+(/(tcp|udp))? ]]; then + die "invalid port syntax: \"$PORT\"." fi PROTO="$DEFAULT_PROTO" - if [[ "$INSTANCE_PORT" = */udp ]]; then + if [[ "$PORT" = */udp ]]; then PROTO=udp fi shift || true NETWORK="${1:-}" - INSTANCE_PORT="${INSTANCE_PORT%/*}" + PORT="${PORT%/*}" ;;& - delete|list) - "ufw-docker--$ufw_action" "$INSTANCE_NAME" "$INSTANCE_PORT" "$PROTO" "$NETWORK" - ;; - allow) - "ufw-docker--$ufw_action" "$INSTANCE_NAME" "$INSTANCE_PORT" "$PROTO" "$NETWORK" + delete|list|allow) + "ufw-docker--$ufw_action" "$INSTANCE_NAME" "$SOURCE" "$PORT" "$PROTO" "$NETWORK" ;; service|raw-command|add-service-rule) shift || true @@ -691,7 +704,7 @@ case "$ufw_action" in status) ufw-docker--"$ufw_action" ;; - *) + help|*) ufw-docker--help ;; esac