21 Commits

Author SHA1 Message Date
Chai Feng 5033bf815c Auto select the correct agent image for different version of iptables 2022-09-20 21:38:10 +08:00
Chai Feng d110fc00ff Testing on ubuntu 22.04 2022-09-01 19:53:22 +08:00
Radosław Kłos 9df291d39e Bump Ubuntu version 2022-08-22 18:59:22 +08:00
Chai Feng c95d51c975 Run integration tests on Apple Silicon with Parallels 2022-07-28 14:53:50 +08:00
Egor Panfilov 8aecb89d4e Update ufw-docker 2021-11-09 22:11:30 +08:00
Egor Panfilov 1333dcd298 Update ufw-docker 2021-11-09 22:11:30 +08:00
Egor Panfilov e40bfd517c Fix tabs in ufw-docker 2021-11-09 22:11:30 +08:00
Chai Feng 97543811ea Re-indenting ufw-docker--allow 2021-10-23 21:27:37 +08:00
Chai Feng afd62aa96b Version 210925 released 2021-09-25 20:43:12 +08:00
Chai Feng 6986267d30 Revert "Release the new version of ufw-docker-agent image" since the DockerHub
cannot build the image automatically

This reverts commit fc7840efef.
2021-09-25 15:41:17 +08:00
Chai Feng 4833b190ff Update readme 2021-09-25 15:26:02 +08:00
Chai Feng fc7840efef Release the new version of ufw-docker-agent image 2021-09-25 15:25:29 +08:00
Patrice Brend'amour 22f04125d6 added missing test 2021-09-22 08:24:24 +08:00
Patrice Brend'amour 4335d6fb82 applied requested changes 2021-09-22 08:24:24 +08:00
Patrice Brend'amour cd783f91d7 fixed tests 2021-09-22 08:24:24 +08:00
Patrice Brend'amour 80a691f084 Added support for multiple networks
Now networks are added as well, so if a container has multiple networks (e.g. when using docker-compose) it still works
2021-09-22 08:24:24 +08:00
Chai Feng 0150af87dc Add Github Actions to run unit tests 2021-09-13 13:46:10 +08:00
Chai Feng cc58088bc5 Update test/bach to the latest version 2021-09-12 08:52:29 +08:00
Chai Feng 2b4a44ff7a Update the vagrant box 2021-04-02 10:42:01 +08:00
Further fa5cec9dc5 Update README.md 2021-04-02 10:41:12 +08:00
Chai Feng a444fb9457 Update README.md, add a link to the docker image 2020-08-19 17:50:20 +08:00
7 changed files with 270 additions and 93 deletions
+16
View File
@@ -0,0 +1,16 @@
name: Unit Testing ufw-docker
on: [push, pull_request]
jobs:
test:
name: Unit Testing
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Checkout submodules
shell: bash
run: |
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
git submodule sync --recursive
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
- name: Test ufw-docker
run: ./test.sh
+7 -6
View File
@@ -1,14 +1,15 @@
FROM ubuntu:20.04 FROM ubuntu:20.04
ARG docker_version="19.03.12" ARG docker_version="20.10.17"
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y --no-install-recommends apt-transport-https \ && apt-get install -y ca-certificates curl gnupg lsb-release \
ca-certificates curl software-properties-common gnupg dirmngr \ && mkdir -p /etc/apt/keyrings \
&& apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 9DC858229FC7DD38854AE2D88D81803C0EBFCD88 \ && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg \
&& add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg]" \
$(lsb_release -cs) stable" \ "https://download.docker.com/linux/ubuntu" "$(lsb_release -cs) stable" \
| tee /etc/apt/sources.list.d/docker.list > /dev/null \
&& apt-get update \ && apt-get update \
&& apt-get install -y --no-install-recommends locales ufw \ && apt-get install -y --no-install-recommends locales ufw \
&& ( apt-get install -y --no-install-recommends "docker-ce=5:${docker_version}~*" || \ && ( apt-get install -y --no-install-recommends "docker-ce=5:${docker_version}~*" || \
+10 -2
View File
@@ -2,7 +2,7 @@ To Fix The Docker and UFW Security Flaw Without Disabling Iptables
================== ==================
[![Build Status](https://travis-ci.org/chaifeng/ufw-docker.svg)](https://travis-ci.org/chaifeng/ufw-docker) [![Build Status](https://travis-ci.org/chaifeng/ufw-docker.svg)](https://travis-ci.org/chaifeng/ufw-docker)
![chaifeng/ufw-docker-agent](https://img.shields.io/docker/pulls/chaifeng/ufw-docker-agent) [![chaifeng/ufw-docker-agent](https://img.shields.io/docker/pulls/chaifeng/ufw-docker-agent)](https://hub.docker.com/r/chaifeng/ufw-docker-agent)
- [English](#tldr) - [English](#tldr)
- [中文](#太长不想读) - [中文](#太长不想读)
@@ -210,7 +210,7 @@ Download `ufw-docker` script
sudo wget -O /usr/local/bin/ufw-docker \ sudo wget -O /usr/local/bin/ufw-docker \
https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker
chmod +x /usr/local/bin/ufw-docker sudo chmod +x /usr/local/bin/ufw-docker
Then using the following command to modify the `after.rules` file of `ufw` Then using the following command to modify the `after.rules` file of `ufw`
@@ -259,6 +259,10 @@ Expose the `443` port of the container `httpd` and the protocol is `tcp`
ufw-docker allow httpd 443/tcp ufw-docker allow httpd 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
Expose all published ports of the container `httpd` Expose all published ports of the container `httpd`
ufw-docker allow httpd ufw-docker allow httpd
@@ -563,6 +567,10 @@ UFW 是 Ubuntu 上很流行的一个 iptables 前端,可以非常方便的管
ufw-docker allow httpd 443/tcp ufw-docker allow httpd 443/tcp
如果容器 `httpd` 绑定到多个网络上,暴露其 `443` 端口,协议为 `tcp`,网络为 `foobar-external-network`
ufw-docker allow httpd 443/tcp foobar-external-network
把容器 `httpd` 的所有映射端口都暴露出来 把容器 `httpd` 的所有映射端口都暴露出来
ufw-docker allow httpd ufw-docker allow httpd
Vendored
+50 -16
View File
@@ -3,16 +3,23 @@
# -*- mode: ruby -*- # -*- mode: ruby -*-
# vi: set ft=ruby : # vi: set ft=ruby :
ENV['VAGRANT_NO_PARALLEL']="true"
Vagrant.configure('2') do |config| Vagrant.configure('2') do |config|
config.vm.box = "chaifeng/ubuntu-20.04-docker-19.03.11" #config.vm.box = "chaifeng/ubuntu-22.04-docker-#{(`uname -m`.strip == "arm64")?"20.10.17-arm64":"19.03.13"}"
#config.vm.box = "chaifeng/ubuntu-16.04-docker-18.03" config.vm.box = "chaifeng/ubuntu-20.04-docker-#{(`uname -m`.strip == "arm64")?"19.03.13-arm64":"19.03.13"}"
config.vm.provider 'virtualbox' do |vb| config.vm.provider 'virtualbox' do |vb|
vb.memory = '1024' vb.memory = '1024'
vb.default_nic_type = "virtio" vb.default_nic_type = "virtio"
end end
config.vm.provider 'parallels' do |prl|
prl.memory = '1024'
prl.check_guest_tools = false
end
ip_prefix="192.168.56" ip_prefix="192.168.56"
config.vm.provision 'docker-daemon-config', type: 'shell', inline: <<-SHELL config.vm.provision 'docker-daemon-config', type: 'shell', inline: <<-SHELL
@@ -57,15 +64,16 @@ Vagrant.configure('2') do |config|
private_registry="#{ip_prefix}.130:5000" private_registry="#{ip_prefix}.130:5000"
config.vm.define "master" do |master| config.vm.define "master" do |master|
master_ip_address = "#{ip_prefix}.130"
master.vm.hostname = "master" master.vm.hostname = "master"
master.vm.network "private_network", ip: "#{ip_prefix}.130" master.vm.network "private_network", ip: "#{master_ip_address}"
master.vm.provision "unit-testing", type: 'shell', inline: <<-SHELL master.vm.provision "unit-testing", preserve_order: true, type: 'shell', inline: <<-SHELL
set -euo pipefail set -euo pipefail
/vagrant/test.sh /vagrant/test.sh
SHELL SHELL
master.vm.provision "docker-registry", type: 'docker' do |d| master.vm.provision "docker-registry", preserve_order: true, type: 'docker' do |d|
d.run "registry", d.run "registry",
image: "registry:2", image: "registry:2",
args: "-p 5000:5000", args: "-p 5000:5000",
@@ -75,27 +83,27 @@ Vagrant.configure('2') do |config|
ufw_docker_agent_image = "#{private_registry}/chaifeng/ufw-docker-agent:test" ufw_docker_agent_image = "#{private_registry}/chaifeng/ufw-docker-agent:test"
master.vm.provision "docker-build-ufw-docker-agent", type: 'shell', inline: <<-SHELL master.vm.provision "docker-build-ufw-docker-agent", preserve_order: true, type: 'shell', inline: <<-SHELL
set -euo pipefail set -euo pipefail
docker build -t #{ufw_docker_agent_image} /vagrant docker build -t #{ufw_docker_agent_image}-legacy /vagrant
docker push #{ufw_docker_agent_image} docker push #{ufw_docker_agent_image}-legacy
echo "export UFW_DOCKER_AGENT_IMAGE=#{ufw_docker_agent_image}" > /etc/profile.d/ufw-docker.sh echo "export UFW_DOCKER_AGENT_IMAGE=#{ufw_docker_agent_image}-nf_tables" > /etc/profile.d/ufw-docker.sh
echo "export DEBUG=true" >> /etc/profile.d/ufw-docker.sh echo "export DEBUG=true" >> /etc/profile.d/ufw-docker.sh
echo "Defaults env_keep += UFW_DOCKER_AGENT_IMAGE" > /etc/sudoers.d/98_ufw-docker echo "Defaults env_keep += UFW_DOCKER_AGENT_IMAGE" > /etc/sudoers.d/98_ufw-docker
echo "Defaults env_keep += DEBUG" >> /etc/sudoers.d/98_ufw-docker echo "Defaults env_keep += DEBUG" >> /etc/sudoers.d/98_ufw-docker
SHELL SHELL
master.vm.provision "swarm-init", type: 'shell', inline: <<-SHELL master.vm.provision "swarm-init", preserve_order: true, type: 'shell', inline: <<-SHELL
set -euo pipefail set -euo pipefail
docker info | fgrep 'Swarm: active' && exit 0 docker info | fgrep 'Swarm: active' && exit 0
docker swarm init --advertise-addr eth1 docker swarm init --advertise-addr "#{master_ip_address}"
docker swarm join-token worker --quiet > /vagrant/.vagrant/docker-join-token docker swarm join-token worker --quiet > /vagrant/.vagrant/docker-join-token
SHELL SHELL
master.vm.provision "build-webapp", type: 'shell', inline: <<-SHELL master.vm.provision "build-webapp", preserve_order: true, type: 'shell', inline: <<-SHELL
set -euo pipefail set -euo pipefail
docker build -t #{private_registry}/chaifeng/hostname-webapp - <<\\DOCKERFILE docker build -t #{private_registry}/chaifeng/hostname-webapp - <<\\DOCKERFILE
FROM httpd:alpine FROM httpd:alpine
@@ -111,7 +119,7 @@ DOCKERFILE
docker push #{private_registry}/chaifeng/hostname-webapp docker push #{private_registry}/chaifeng/hostname-webapp
SHELL SHELL
master.vm.provision "local-webapp", type: 'shell', inline: <<-SHELL master.vm.provision "local-webapp", preserve_order: true, type: 'shell', inline: <<-SHELL
set -euo pipefail set -euo pipefail
for name in public:18080 local:8000; do for name in public:18080 local:8000; do
webapp="${name%:*}_webapp" webapp="${name%:*}_webapp"
@@ -125,7 +133,30 @@ DOCKERFILE
ufw-docker allow public_webapp ufw-docker allow public_webapp
SHELL SHELL
master.vm.provision "swarm-webapp", type: 'shell', inline: <<-SHELL master.vm.provision "multiple-network", preserve_order: true, type: 'shell', inline: <<-SHELL
set -euo pipefail
if ! docker network ls | grep -F foo-internal; then
docker network create --internal foo-internal
fi
if ! docker network ls | grep -F bar-external; then
docker network create bar-external
fi
for app in internal-multinet-app:7000 public-multinet-app:17070; do
if ! docker inspect "${app%:*}" &>/dev/null; then
docker run -d --restart unless-stopped --name "${app%:*}" \
-p "${app#*:}":80 --env name="${app}" \
--network foo-internal \
192.168.56.130:5000/chaifeng/hostname-webapp
docker network connect bar-external "${app%:*}"
fi
done
ufw-docker allow public-multinet-app 80 bar-external
ufw-docker allow internal-multinet-app 80 foo-internal
SHELL
master.vm.provision "swarm-webapp", preserve_order: true, type: 'shell', inline: <<-SHELL
set -euo pipefail set -euo pipefail
for name in public:29090 local:9000; do for name in public:29090 local:9000; do
webapp="${name%:*}_service" webapp="${name%:*}_service"
@@ -144,7 +175,7 @@ DOCKERFILE
node.vm.hostname = "node#{ip}" node.vm.hostname = "node#{ip}"
node.vm.network "private_network", ip: "#{ip_prefix}.#{ 130 + ip }" node.vm.network "private_network", ip: "#{ip_prefix}.#{ 130 + ip }"
node.vm.provision "swarm-join", type: 'shell', inline: <<-SHELL node.vm.provision "swarm-join", preserve_order: true, type: 'shell', inline: <<-SHELL
set -euo pipefail set -euo pipefail
docker info | fgrep 'Swarm: active' && exit 0 docker info | fgrep 'Swarm: active' && exit 0
@@ -158,7 +189,7 @@ DOCKERFILE
external.vm.hostname = "external" external.vm.hostname = "external"
external.vm.network "private_network", ip: "#{ip_prefix}.127" external.vm.network "private_network", ip: "#{ip_prefix}.127"
external.vm.provision "testing", type: 'shell', inline: <<-SHELL external.vm.provision "testing", preserve_order: true, type: 'shell', inline: <<-SHELL
set -euo pipefail set -euo pipefail
set -x set -x
server="http://#{ip_prefix}.130" server="http://#{ip_prefix}.130"
@@ -166,6 +197,9 @@ DOCKERFILE
test-webapp "$server:18080" test-webapp "$server:18080"
! test-webapp "$server:8000" ! test-webapp "$server:8000"
test-webapp "$server:17070" # multiple networks app
! test-webapp "$server:7000" # internal multiple networks app
test-webapp "$server:29090" test-webapp "$server:29090"
! test-webapp "$server:9000" ! test-webapp "$server:9000"
+116 -30
View File
@@ -12,12 +12,17 @@ source "$working_dir"/bach/bach.sh
@mocktrue ufw status @mocktrue ufw status
@mocktrue grep -Fq "Status: active" @mocktrue grep -Fq "Status: active"
@mock iptables --version
@mocktrue grep -F '(legacy)'
@ignore remove_blank_lines @ignore remove_blank_lines
@ignore echo @ignore echo
@ignore err @ignore err
DEFAULT_PROTO=tcp DEFAULT_PROTO=tcp
GREP_REGEXP_INSTANCE_NAME="[-_.[:alnum:]]\\+" GREP_REGEXP_INSTANCE_NAME="[-_.[:alnum:]]\\+"
UFW_DOCKER_AGENT_IMAGE=chaifeng/ufw-docker-agent:090502-legacy
} }
function ufw-docker() { function ufw-docker() {
@@ -30,6 +35,41 @@ function load-ufw-docker-function() {
@load_function "$working_dir/../ufw-docker" "$1" @load_function "$working_dir/../ufw-docker" "$1"
} }
test-ufw-docker-init-legacy() {
@mocktrue grep -F '(legacy)'
@source <(@sed '/PATH=/d' "$working_dir/../ufw-docker") help
}
test-ufw-docker-init-legacy-assert() {
iptables --version
test -n chaifeng/ufw-docker-agent:090502-legacy
trap on-exit EXIT INT TERM QUIT ABRT ERR
@dryrun cat
}
test-ufw-docker-init-nf_tables() {
@mockfalse grep -F '(legacy)'
@source <(@sed '/PATH=/d' "$working_dir/../ufw-docker") help
}
test-ufw-docker-init-nf_tables-assert() {
iptables --version
test -n chaifeng/ufw-docker-agent:090502-nf_tables
trap on-exit EXIT INT TERM QUIT ABRT ERR
@dryrun cat
}
test-ufw-docker-init() {
UFW_DOCKER_AGENT_IMAGE=chaifeng/ufw-docker-agent:100917
@source <(@sed '/PATH=/d' "$working_dir/../ufw-docker") help
}
test-ufw-docker-init-assert() {
test -n chaifeng/ufw-docker-agent:100917
trap on-exit EXIT INT TERM QUIT ABRT ERR
@dryrun cat
}
test-ufw-docker-help() { test-ufw-docker-help() {
ufw-docker help ufw-docker help
} }
@@ -48,11 +88,12 @@ test-ufw-docker-without-parameters-assert() {
test-ufw-is-disabled() { test-ufw-is-disabled() {
@mockfalse grep -Fq "Status: active" @mockfalse grep -Fq "Status: active"
@mock iptables --version === @stdout 'iptables v1.8.4 (legacy)'
ufw-docker ufw-docker
} }
test-ufw-is-disabled-assert() { test-ufw-is-disabled-assert() {
die "UFW is disabled or you are not root user." die "UFW is disabled or you are not root user, or mismatched iptables legacy/nf_tables, current iptables v1.8.4 (legacy)"
ufw-docker--help ufw-docker--help
} }
@@ -143,7 +184,7 @@ test-ufw-docker-list-httpd() {
ufw-docker list httpd ufw-docker list httpd
} }
test-ufw-docker-list-httpd-assert() { test-ufw-docker-list-httpd-assert() {
ufw-docker--list httpd-container-name "" tcp ufw-docker--list httpd-container-name "" tcp ""
} }
@@ -152,7 +193,7 @@ test-ufw-docker-allow-httpd() {
ufw-docker allow httpd ufw-docker allow httpd
} }
test-ufw-docker-allow-httpd-assert() { test-ufw-docker-allow-httpd-assert() {
ufw-docker--allow httpd-container-name "" tcp ufw-docker--allow httpd-container-name "" tcp ""
} }
@@ -161,7 +202,7 @@ test-ufw-docker-allow-httpd-80() {
ufw-docker allow httpd 80 ufw-docker allow httpd 80
} }
test-ufw-docker-allow-httpd-80-assert() { test-ufw-docker-allow-httpd-80-assert() {
ufw-docker--allow httpd-container-name 80 tcp ufw-docker--allow httpd-container-name 80 tcp ""
} }
@@ -170,7 +211,7 @@ test-ufw-docker-allow-httpd-80tcp() {
ufw-docker allow httpd 80/tcp ufw-docker allow httpd 80/tcp
} }
test-ufw-docker-allow-httpd-80tcp-assert() { test-ufw-docker-allow-httpd-80tcp-assert() {
ufw-docker--allow httpd-container-name 80 tcp ufw-docker--allow httpd-container-name 80 tcp ""
} }
@@ -179,7 +220,7 @@ test-ufw-docker-allow-httpd-80udp() {
ufw-docker allow httpd 80/udp ufw-docker allow httpd 80/udp
} }
test-ufw-docker-allow-httpd-80udp-assert() { test-ufw-docker-allow-httpd-80udp-assert() {
ufw-docker--allow httpd-container-name 80 udp ufw-docker--allow httpd-container-name 80 udp ""
} }
@@ -196,7 +237,7 @@ test-ufw-docker-list-httpd() {
ufw-docker list httpd ufw-docker list httpd
} }
test-ufw-docker-list-httpd-assert() { test-ufw-docker-list-httpd-assert() {
ufw-docker--list httpd-container-name "" tcp ufw-docker--list httpd-container-name "" tcp ""
} }
@@ -205,7 +246,7 @@ test-ufw-docker-delete-allow-httpd() {
ufw-docker delete allow httpd ufw-docker delete allow httpd
} }
test-ufw-docker-delete-allow-httpd-assert() { test-ufw-docker-delete-allow-httpd-assert() {
ufw-docker--delete httpd-container-name "" tcp ufw-docker--delete httpd-container-name "" tcp ""
} }
@@ -223,6 +264,16 @@ function setup-ufw-docker--allow() {
@mocktrue docker inspect instance-name @mocktrue docker inspect instance-name
@mock docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{"\n"}}{{end}}' instance-name === @stdout 172.18.0.3 @mock docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{"\n"}}{{end}}' instance-name === @stdout 172.18.0.3
@mock docker inspect --format='{{range $k, $v := .NetworkSettings.Networks}}{{printf "%s\n" $k}}{{end}}' instance-name === @stdout default
@mock docker inspect --format='{{range $p, $conf := .NetworkSettings.Ports}}{{with $conf}}{{$p}}{{"\n"}}{{end}}{{end}}' instance-name === @stdout 5000/tcp 8080/tcp 5353/udp
}
function setup-ufw-docker--allow--multinetwork() {
load-ufw-docker-function ufw-docker--allow
@mocktrue docker inspect instance-name
@mock docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{"\n"}}{{end}}' instance-name === @stdout 172.18.0.3 172.19.0.7
@mock docker inspect --format='{{range $k, $v := .NetworkSettings.Networks}}{{printf "%s\n" $k}}{{end}}' instance-name === @stdout default awesomenet
@mock docker inspect --format='{{range $p, $conf := .NetworkSettings.Ports}}{{with $conf}}{{$p}}{{"\n"}}{{end}}{{end}}' instance-name === @stdout 5000/tcp 8080/tcp 5353/udp @mock docker inspect --format='{{range $p, $conf := .NetworkSettings.Ports}}{{with $conf}}{{$p}}{{"\n"}}{{end}}{{end}}' instance-name === @stdout 5000/tcp 8080/tcp 5353/udp
} }
@@ -269,7 +320,7 @@ test-ufw-docker--allow-instance-and-match-the-port() {
ufw-docker--allow instance-name 5000 tcp ufw-docker--allow instance-name 5000 tcp
} }
test-ufw-docker--allow-instance-and-match-the-port-assert() { test-ufw-docker--allow-instance-and-match-the-port-assert() {
ufw-docker--add-rule instance-name 172.18.0.3 5000 tcp ufw-docker--add-rule instance-name 172.18.0.3 5000 tcp default
} }
@@ -279,9 +330,9 @@ test-ufw-docker--allow-instance-all-published-port() {
ufw-docker--allow instance-name "" "" ufw-docker--allow instance-name "" ""
} }
test-ufw-docker--allow-instance-all-published-port-assert() { test-ufw-docker--allow-instance-all-published-port-assert() {
ufw-docker--add-rule instance-name 172.18.0.3 5000 tcp 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 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 ufw-docker--add-rule instance-name 172.18.0.3 5353 udp default
} }
@@ -291,14 +342,39 @@ test-ufw-docker--allow-instance-all-published-tcp-port() {
ufw-docker--allow instance-name "" tcp ufw-docker--allow instance-name "" tcp
} }
test-ufw-docker--allow-instance-all-published-tcp-port-assert() { test-ufw-docker--allow-instance-all-published-tcp-port-assert() {
ufw-docker--add-rule instance-name 172.18.0.3 5000 tcp 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 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 # FIXME ufw-docker--add-rule instance-name 172.18.0.3 5353 udp default # FIXME
} }
test-ufw-docker--allow-instance-all-published-port-multinetwork() {
setup-ufw-docker--allow--multinetwork
ufw-docker--allow instance-name "" ""
}
test-ufw-docker--allow-instance-all-published-port-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
}
test-ufw-docker--allow-instance-all-published-port-multinetwork-select-network() {
setup-ufw-docker--allow--multinetwork
ufw-docker--allow instance-name "" "" awesomenet
}
test-ufw-docker--allow-instance-all-published-port-multinetwork-select-network-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
}
test-ufw-docker--add-rule-a-non-existing-rule() { test-ufw-docker--add-rule-a-non-existing-rule() {
@mockfalse ufw-docker--list webapp 5000 tcp @mockfalse ufw-docker--list webapp 5000 tcp ""
load-ufw-docker-function ufw-docker--add-rule 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 5000 tcp
@@ -307,29 +383,39 @@ test-ufw-docker--add-rule-a-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 5000/tcp"
} }
test-ufw-docker--add-rule-a-non-existing-rule-with-network() {
@mockfalse ufw-docker--list webapp 5000 tcp default
load-ufw-docker-function ufw-docker--add-rule
ufw-docker--add-rule webapp 172.18.0.4 5000 tcp default
}
test-ufw-docker--add-rule-a-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"
}
test-ufw-docker--add-rule-modify-an-existing-rule() { test-ufw-docker--add-rule-modify-an-existing-rule() {
@mocktrue ufw-docker--list webapp 5000 tcp @mocktrue ufw-docker--list webapp 5000 tcp default
@mocktrue ufw --dry-run route allow proto tcp from any to 172.18.0.4 port 5000 comment "allow 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 default"
@mockfalse grep "^Skipping" @mockfalse grep "^Skipping"
load-ufw-docker-function ufw-docker--add-rule 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 5000 tcp default
} }
test-ufw-docker--add-rule-modify-an-existing-rule-assert() { test-ufw-docker--add-rule-modify-an-existing-rule-assert() {
ufw-docker--delete webapp 5000 tcp ufw-docker--delete webapp 5000 tcp default
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 5000/tcp default"
} }
test-ufw-docker--add-rule-skip-an-existing-rule() { test-ufw-docker--add-rule-skip-an-existing-rule() {
@mocktrue ufw-docker--list webapp 5000 tcp @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 --dry-run route allow proto tcp from any to 172.18.0.4 port 5000 comment "allow webapp 5000/tcp"
@mocktrue grep "^Skipping" @mocktrue grep "^Skipping"
load-ufw-docker-function ufw-docker--add-rule 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 5000 tcp ""
} }
test-ufw-docker--add-rule-skip-an-existing-rule-assert() { test-ufw-docker--add-rule-skip-an-existing-rule-assert() {
@do-nothing @do-nothing
@@ -337,17 +423,17 @@ test-ufw-docker--add-rule-skip-an-existing-rule-assert() {
test-ufw-docker--add-rule-modify-an-existing-rule-without-port() { test-ufw-docker--add-rule-modify-an-existing-rule-without-port() {
@mocktrue ufw-docker--list webapp "" tcp @mocktrue ufw-docker--list webapp "" tcp ""
@mocktrue ufw --dry-run route allow proto tcp from any to 172.18.0.4 comment "allow webapp" @mocktrue ufw --dry-run route allow proto tcp from any to 172.18.0.4 comment "allow webapp"
@mockfalse grep "^Skipping" @mockfalse grep "^Skipping"
load-ufw-docker-function ufw-docker--add-rule 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 "" tcp ""
} }
test-ufw-docker--add-rule-modify-an-existing-rule-without-port-assert() { test-ufw-docker--add-rule-modify-an-existing-rule-without-port-assert() {
ufw-docker--delete webapp "" tcp ufw-docker--delete webapp "" tcp ""
ufw route allow proto tcp from any to 172.18.0.4 comment "allow webapp" ufw route allow proto tcp from any to 172.18.0.4 comment "allow webapp"
} }
@@ -388,7 +474,7 @@ test-ufw-docker--list-name() {
ufw-docker--list foo ufw-docker--list foo
} }
test-ufw-docker--list-name-assert() { test-ufw-docker--list-name-assert() {
grep "# allow foo\\( [[:digit:]]\\+\\/\\(tcp\\|udp\\)\\)\\?\$" grep "# allow foo\\( [[:digit:]]\\+\\/\\(tcp\\|udp\\)\\)\\?\\( [[:graph:]]*\\)\\?\$"
} }
test-ufw-docker--list-name-udp() { test-ufw-docker--list-name-udp() {
@@ -397,7 +483,7 @@ test-ufw-docker--list-name-udp() {
ufw-docker--list foo "" udp ufw-docker--list foo "" udp
} }
test-ufw-docker--list-name-udp-assert() { test-ufw-docker--list-name-udp-assert() {
grep "# allow foo\\( [[:digit:]]\\+\\/\\(tcp\\|udp\\)\\)\\?\$" grep "# allow foo\\( [[:digit:]]\\+\\/\\(tcp\\|udp\\)\\)\\?\\( [[:graph:]]*\\)\\?\$"
} }
@@ -407,7 +493,7 @@ test-ufw-docker--list-name-80() {
ufw-docker--list foo 80 ufw-docker--list foo 80
} }
test-ufw-docker--list-name-80-assert() { test-ufw-docker--list-name-80-assert() {
grep "# allow foo\\( 80\\/tcp\\)\\?\$" grep "# allow foo\\( 80\\/tcp\\)\\?\\( [[:graph:]]*\\)\\?\$"
} }
@@ -417,7 +503,7 @@ test-ufw-docker--list-name-80-udp() {
ufw-docker--list foo 80 udp ufw-docker--list foo 80 udp
} }
test-ufw-docker--list-name-80-udp-assert() { test-ufw-docker--list-name-80-udp-assert() {
grep "# allow foo\\( 80\\/udp\\)\\?\$" grep "# allow foo\\( 80\\/udp\\)\\?\\( [[:graph:]]*\\)\\?\$"
} }
+43 -11
View File
@@ -11,7 +11,17 @@ GREP_REGEXP_INSTANCE_NAME="[-_.[:alnum:]]\\+"
DEFAULT_PROTO=tcp DEFAULT_PROTO=tcp
ufw_docker_agent=ufw-docker-agent ufw_docker_agent=ufw-docker-agent
ufw_docker_agent_image="${UFW_DOCKER_AGENT_IMAGE:-chaifeng/${ufw_docker_agent}:200812}" ufw_docker_agent_image="${UFW_DOCKER_AGENT_IMAGE:-chaifeng/${ufw_docker_agent}:220920-legacy}"
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() { function ufw-docker--status() {
ufw-docker--list "$GREP_REGEXP_INSTANCE_NAME" ufw-docker--list "$GREP_REGEXP_INSTANCE_NAME"
@@ -21,12 +31,18 @@ function ufw-docker--list() {
local INSTANCE_NAME="$1" local INSTANCE_NAME="$1"
local INSTANCE_PORT="${2:-}" local INSTANCE_PORT="${2:-}"
local PROTO="${3:-${DEFAULT_PROTO}}" local PROTO="${3:-${DEFAULT_PROTO}}"
local NETWORK="${4:-}"
if [[ -z "$INSTANCE_PORT" ]]; then if [[ -z "$INSTANCE_PORT" ]]; then
INSTANCE_PORT="[[:digit:]]\\+" INSTANCE_PORT="[[:digit:]]\\+"
PROTO="\\(tcp\\|udp\\)" PROTO="\\(tcp\\|udp\\)"
fi fi
ufw status numbered | grep "# allow ${INSTANCE_NAME}\\( ${INSTANCE_PORT}\\/${PROTO}\\)\\?\$"
if [[ -z "$NETWORK" ]]; then
NETWORK="[[:graph:]]*"
fi
ufw status numbered | grep "# allow ${INSTANCE_NAME}\\( ${INSTANCE_PORT}\\/${PROTO}\\)\\?\\( ${NETWORK}\\)\\?\$"
} }
function ufw-docker--list-number() { function ufw-docker--list-number() {
@@ -44,6 +60,7 @@ function ufw-docker--allow() {
local INSTANCE_NAME="$1" local INSTANCE_NAME="$1"
local INSTANCE_PORT="$2" local INSTANCE_PORT="$2"
local PROTO="$3" local PROTO="$3"
local NETWORK="${4:-}"
docker inspect "$INSTANCE_NAME" &>/dev/null || docker inspect "$INSTANCE_NAME" &>/dev/null ||
die "Docker instance \"$INSTANCE_NAME\" doesn't exist." die "Docker instance \"$INSTANCE_NAME\" doesn't exist."
@@ -52,6 +69,7 @@ function ufw-docker--allow() {
[[ -z "${INSTANCE_IP_ADDRESSES:-}" ]] && die "Could not find a running instance \"$INSTANCE_NAME\"." [[ -z "${INSTANCE_IP_ADDRESSES:-}" ]] && die "Could not find a running instance \"$INSTANCE_NAME\"."
mapfile -t INSTANCE_NETWORK_NAMES < <(docker inspect --format='{{range $k, $v := .NetworkSettings.Networks}}{{printf "%s\n" $k}}{{end}}' "$INSTANCE_NAME" 2>/dev/null | remove_blank_lines)
mapfile -t PORT_PROTO_LIST < <(docker inspect --format='{{range $p, $conf := .NetworkSettings.Ports}}{{with $conf}}{{$p}}{{"\n"}}{{end}}{{end}}' "$INSTANCE_NAME" | remove_blank_lines) 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 if [[ -z "${PORT_PROTO_LIST:-}" ]]; then
@@ -62,8 +80,14 @@ function ufw-docker--allow() {
RETVAL=1 RETVAL=1
for PORT_PROTO in "${PORT_PROTO_LIST[@]}"; do for PORT_PROTO in "${PORT_PROTO_LIST[@]}"; do
if [[ -z "$INSTANCE_PORT" || "$PORT_PROTO" = "${INSTANCE_PORT}/${PROTO}" ]]; then if [[ -z "$INSTANCE_PORT" || "$PORT_PROTO" = "${INSTANCE_PORT}/${PROTO}" ]]; then
ITER=0
for IP in "${INSTANCE_IP_ADDRESSES[@]}"; do for IP in "${INSTANCE_IP_ADDRESSES[@]}"; do
ufw-docker--add-rule "$INSTANCE_NAME" "$IP" "${PORT_PROTO%/*}" "${PORT_PROTO#*/}" INSTANCE_NETWORK="${INSTANCE_NETWORK_NAMES[$ITER]}"
ITER=$((ITER+1))
if [[ -n "$NETWORK" ]] && [[ "$NETWORK" != "$INSTANCE_NETWORK" ]]; then
continue
fi
ufw-docker--add-rule "$INSTANCE_NAME" "$IP" "${PORT_PROTO%/*}" "${PORT_PROTO#*/}" "${INSTANCE_NETWORK}"
RETVAL="$?" RETVAL="$?"
done done
fi fi
@@ -92,10 +116,11 @@ function ufw-docker--add-rule() {
local INSTANCE_IP_ADDRESS="$2" local INSTANCE_IP_ADDRESS="$2"
local PORT="$3" local PORT="$3"
local PROTO="$4" local PROTO="$4"
local NETWORK="${5:-}"
declare comment declare comment
echo "allow ${INSTANCE_NAME} ${PORT}/${PROTO}" echo "allow ${INSTANCE_NAME} ${PORT}/${PROTO} ${NETWORK}"
typeset -a UFW_OPTS typeset -a UFW_OPTS
UFW_OPTS=(route allow proto "${PROTO}" UFW_OPTS=(route allow proto "${PROTO}"
from any to "$INSTANCE_IP_ADDRESS") from any to "$INSTANCE_IP_ADDRESS")
@@ -104,12 +129,15 @@ function ufw-docker--add-rule() {
UFW_OPTS+=(port "${PORT}") UFW_OPTS+=(port "${PORT}")
comment="$comment ${PORT}/${PROTO}" comment="$comment ${PORT}/${PROTO}"
} }
[[ -n "$NETWORK" ]] && {
comment="$comment ${NETWORK}"
}
UFW_OPTS+=(comment "$comment") UFW_OPTS+=(comment "$comment")
if ufw-docker--list "$INSTANCE_NAME" "$PORT" "$PROTO" &>/dev/null; then if ufw-docker--list "$INSTANCE_NAME" "$PORT" "$PROTO" "$NETWORK" &>/dev/null; then
ufw --dry-run "${UFW_OPTS[@]}" | grep "^Skipping" && return 0 ufw --dry-run "${UFW_OPTS[@]}" | grep "^Skipping" && return 0
err "Remove outdated rule." err "Remove outdated rule."
ufw-docker--delete "$INSTANCE_NAME" "$PORT" "$PROTO" ufw-docker--delete "$INSTANCE_NAME" "$PORT" "$PROTO" "$NETWORK"
fi fi
echo ufw "${UFW_OPTS[@]}" echo ufw "${UFW_OPTS[@]}"
ufw "${UFW_OPTS[@]}" ufw "${UFW_OPTS[@]}"
@@ -341,8 +369,8 @@ function ufw-docker--install() {
function ufw-docker--help() { function ufw-docker--help() {
cat <<-EOF >&2 cat <<-EOF >&2
Usage: Usage:
ufw-docker <list|allow> [docker-instance-id-or-name [port[/tcp|/udp]]] 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]]] 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 allow <swarm-service-id-or-name <port</tcp|/udp>>>
ufw-docker service delete allow <swarm-service-id-or-name> ufw-docker service delete allow <swarm-service-id-or-name>
@@ -363,10 +391,11 @@ function ufw-docker--help() {
ufw-docker allow httpd ufw-docker allow httpd
ufw-docker allow httpd 80 ufw-docker allow httpd 80
ufw-docker allow httpd 80/tcp ufw-docker allow httpd 80/tcp
ufw-docker allow httpd 80/tcp default
ufw-docker delete allow httpd ufw-docker delete allow httpd
ufw-docker delete allow httpd 80/tcp 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 allow httpd 80/tcp
@@ -390,7 +419,7 @@ function die() {
# __main__ # __main__
if ! ufw status 2>/dev/null | grep -Fq "Status: active" ; then if ! ufw status 2>/dev/null | grep -Fq "Status: active" ; then
die "UFW is disabled or you are not root user." die "UFW is disabled or you are not root user, or mismatched iptables legacy/nf_tables, current $(iptables --version)"
fi fi
ufw_action="${1:-help}" ufw_action="${1:-help}"
@@ -418,10 +447,13 @@ case "$ufw_action" in
if [[ "$INSTANCE_PORT" = */udp ]]; then if [[ "$INSTANCE_PORT" = */udp ]]; then
PROTO=udp PROTO=udp
fi fi
shift || true
NETWORK="${1:-}"
INSTANCE_PORT="${INSTANCE_PORT%/*}" INSTANCE_PORT="${INSTANCE_PORT%/*}"
"ufw-docker--$ufw_action" "$INSTANCE_NAME" "$INSTANCE_PORT" "$PROTO" "ufw-docker--$ufw_action" "$INSTANCE_NAME" "$INSTANCE_PORT" "$PROTO" "$NETWORK"
;; ;;
service|raw-command|add-service-rule) service|raw-command|add-service-rule)
shift || true shift || true