Centos 8 та Docker з nftables
Встановлення Docker в моєму варіанті дещо ускладнено.
Я не хочу використовувати дефолтовий firewalld і iptables.
Наразі firewalld не підтримує збереження правил nftables, які вносяться через nft, тому при рестарті все вилітає. 
А сам він не підтримує SNAT.
Плюс мені не подобається ідея ввімкнення маскарадінгу для всіх. Я хочу старий добрий Source NAT.
І завжди краще знати що відбувається під капотом та як працює, щоб не виникало сумних історій під час дебага.
Тому тут буде більше про nftables і як вимкнути в докері дефолтовий фаєрвол.
Ставимо докер. Додаємо репозиторій
~]# dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
Adding repo from: https://download.docker.com/linux/centos/docker-ce.repo

Встановлюємо
 
~]# dnf install docker-ce
Docker CE Stable - x86_64                                                                        41 kB/s |  14 kB     00:00    
Dependencies resolved.
================================================================================================================================
 Package                          Architecture  Version                                           Repository               Size
================================================================================================================================
Installing:
 docker-ce                        x86_64        3:20.10.7-3.el8                                   docker-ce-stable         27 M
Installing dependencies:
 container-selinux                noarch        2:2.162.0-1.module_el8.4.0+830+8027e1c4           appstream                52 k
 containerd.io                    x86_64        1.4.9-3.1.el8                                     docker-ce-stable         30 M
 docker-ce-cli                    x86_64        1:20.10.7-3.el8                                   docker-ce-stable         33 M
 docker-ce-rootless-extras        x86_64        20.10.7-3.el8                                     docker-ce-stable        9.2 M
 docker-scan-plugin               x86_64        0.8.0-3.el8                                       docker-ce-stable        4.2 M
 fuse-common                      x86_64        3.2.1-12.el8                                      baseos                   21 k
 fuse-overlayfs                   x86_64        1.4.0-3.module_el8.4.0+830+8027e1c4               appstream                72 k
 fuse3                            x86_64        3.2.1-12.el8                                      baseos                   50 k
 fuse3-libs                       x86_64        3.2.1-12.el8                                      baseos                   94 k
 libcgroup                        x86_64        0.41-19.el8                                       baseos                   70 k
 libslirp                         x86_64        4.3.1-1.module_el8.4.0+575+63b40ad7               appstream                69 k
 slirp4netns                      x86_64        1.1.8-1.module_el8.4.0+641+6116a774               appstream                51 k
 tar                              x86_64        2:1.30-5.el8                                      baseos                  838 k
Enabling module streams:
 container-tools                                rhel8                                                                          

Transaction Summary
================================================================================================================================
Install  14 Packages

Total download size: 104 M
Installed size: 424 M
Is this ok [y/N]: y

.....

Total                                                                                           9.0 MB/s | 104 MB     00:11     
warning: /var/cache/dnf/docker-ce-stable-fa9dc42ab4cec2f4/packages/containerd.io-1.4.9-3.1.el8.x86_64.rpm: Header V4 RSA/SHA512 Signature, key ID 621e9f35: NOKEY
Docker CE Stable - x86_64                                                                        10 kB/s | 1.6 kB     00:00    
Importing GPG key 0x621E9F35:
 Userid     : "Docker Release (CE rpm) <docker@docker.com>"
 Fingerprint: 060A 61C5 1B55 8A7F 742B 77AA C52F EB6B 621E 9F35
 From       : https://download.docker.com/linux/centos/gpg
Is this ok [y/N]: y

Встановлюємо docker-compose
~]# curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   633  100   633    0     0   1692      0 --:--:-- --:--:-- --:--:--  1692
100 12.1M  100 12.1M    0     0  5469k      0  0:00:02  0:00:02 --:--:-- 6980k

~]# chmod +x /usr/local/bin/docker-compose

За замовчуванням Centos 8 має firewalld та nftables в якості фаєрволу.
~]# cat /etc/firewalld/firewalld.conf |grep FirewallBackend
# FirewallBackend
FirewallBackend=nftables

Але він поки не розуміє source NAT. А ще має дуже заплутану схему таблиць та ланцюжків.
Тому я його просто вимикаю й маскую від інших, щоб не могли запустити.
~]# systemctl stop firewalld
~]# systemctl disable firewalld
Removed /etc/systemd/system/multi-user.target.wants/firewalld.service.
Removed /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service.
~]# systemctl mask firewalld
Created symlink /etc/systemd/system/firewalld.service → /dev/null.

А от nftables вмикаємо
~]# systemctl start nftables
~]# systemctl enable nftables

Вивести правила фільтру:
~]# nft list table ip filter
table ip filter {
        chain INPUT {
                type filter hook input priority filter; policy accept;
        }

        chain FORWARD {
                type filter hook forward priority filter; policy accept;
        }

        chain OUTPUT {
                type filter hook output priority filter; policy accept;
        }
}

Дефолтом краще для FORWARD поставити політику drop й дозволяти тільки тим, кому потрібно
~]# nft add chain filter FORWARD '{policy drop;}'

Docker поки не має підтримки nftables. Треба вимкнути в ньому iptables та налаштувати фаєрвол самому.

Доречі ip_forward має бути увімкнений
~]# sysctl -a|grep net.ipv4.ip_forward
net.ipv4.ip_forward = 1

зробимо окремий service файл для systemd
~]# cp /usr/lib/systemd/system/docker.service /etc/systemd/system/

Потрібно дещо змінити рядки для старту
~]# tail -n+$(cat /etc/systemd/system/docker.service -n|grep "\[Service\]"|sed 's/\|/ /'|awk '{print $1}') /etc/systemd/system/docker.service |head -n12
[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
EnvironmentFile=-/etc/sysconfig/docker
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock $DOCKER_ARGS
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always

Файл, в якому зазначаються опції
~]# cat /etc/sysconfig/docker 
DOCKER_ARGS=--iptables=false --ipv6=false --bip 172.17.0.1/16 --fixed-cidr 172.17.0.0/16

Запускаємо та перевіряємо
~]# systemctl daemon-reload
~]# systemctl start docker
~]# ps ax|grep docker
   7027 ?        Ssl    0:00 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --iptables=false --ipv6=false --bip 172.17.0.1/16 --fixed-cidr 172.17.0.0/16

Якщо демон докера вже запускався, то, щоб скинути всі правила, робимо 
~]# iptables -F
обережно, якщо iptables використовується чимось, то перевірте
~]# iptables -nvL

Опції присутні, дивимось мережу
~]# ip a l dev docker0
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:d9:fb:98:3b brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever

Для перевірки роботи можна запустити звичайний Web сервер.
~]# docker run -it --rm -d -p 8080:80 --name web nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
33847f680f63: Pull complete 
dbb907d5159d: Pull complete 
8a268f30c42a: Pull complete 
b10cf527a02d: Pull complete 
c90b090c213b: Pull complete 
1f41b2f2bf94: Pull complete 
Digest: sha256:8f335768880da6baf72b70c701002b45f4932acae8d574dedfddaf967fc3ac90
Status: Downloaded newer image for nginx:latest
1aa985a47ed44011712c00d3e68309297d32b4caf6a4b51852e7bd4b2005a55d

Доступ є, нжінкс віддає сторінку

Але ж треба перевірити чи є доступ в самих контейнерах
~]# docker exec -ti web /bin/bash
root@1aa985a47ed4:/# curl 1.1.1.1

Доступу немає
На хості маємо.
~]# tcpdump -ni docker0
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
22:17:34.837034 IP 172.17.0.2.36384 > 1.1.1.1.http: Flags [S], seq 1659458766, win 29200, options [mss 1460,sackOK,TS val 2264482116 ecr 0,nop,wscale 7], length 0
22:17:35.853350 IP 172.17.0.2.36384 > 1.1.1.1.http: Flags [S], seq 1659458766, win 29200, options [mss 1460,sackOK,TS val 2264483133 ecr 0,nop,wscale 7], length 0

Дивимось що там з nftables
~]# nft list table nat
table ip nat {
        chain PREROUTING {
                type nat hook prerouting priority dstnat; policy accept;
                fib daddr type local counter packets 9 bytes 540 jump DOCKER
        }

        chain INPUT {
                type nat hook input priority 100; policy accept;
        }

        chain POSTROUTING {
                type nat hook postrouting priority srcnat; policy accept;
                oifname != "docker0" @nh,96,16 44049 counter packets 6 bytes 360 masquerade 
        }

        chain OUTPUT {
                type nat hook output priority -100; policy accept;
                @nh,128,8 != 127 fib daddr type local counter packets 0 bytes 0 jump DOCKER
        }

        chain DOCKER {
                iifname "docker0" counter packets 0 bytes 0 return
        }
}

Є маскарадінг для докера. Вже щось.
Але мені в такому вигляді він не потрібен, бо не полюбляю коли хтось щось за мене вирішує.
Дивимось номер правила для видалення
~]# nft -a list chain nat POSTROUTING
table ip nat {
        chain POSTROUTING { # handle 3
                type nat hook postrouting priority srcnat; policy accept;
                oifname != "docker0" @nh,96,16 44049 counter packets 6 bytes 360 masquerade  # handle 14
        }
}

маємо handle 14, можна видаляти
~]# nft delete rule nat POSTROUTING handle 14
~]# nft -a list chain nat POSTROUTING
table ip nat {
        chain POSTROUTING { # handle 3
                type nat hook postrouting priority srcnat; policy accept;
        }
}

Таким же чином видаляємо інші правила й ланцюжки docker

А до аргументів запуску докера треба додати --ip-masq=false аби він сам не намагався такого утнути ще раз.
~]# cat /etc/sysconfig/docker 
DOCKER_ARGS=--iptables=false --ip-masq=false --ipv6=false --bip 172.17.0.1/16 --fixed-cidr 172.17.0.0/16

Можна перезапускати докер
~]# docker stop web
~]# systemctl restart docker
~]# docker run -it --rm -d -p 8080:80 --name web nginx
69c3a05b33a8a8c289428a386324c336c2e0c100aa7129e4857a09908ba9c498
~]# docker exec -ti web /bin/bash
root@69c3a05b33a8:/#

Ще у firewall-cmd додавалась зона docker, вона активна і там є інтерфейс docker0
~]# firewall-cmd --get-active-zones
docker
  interfaces: docker0
public
  interfaces: enp0s3

Якщо firewalld ще працює, її я теж приберу
~]# firewall-cmd --zone=docker --remove-interface=docker0
success
~]# firewall-cmd --permanent --zone=docker --remove-interface=docker0
Warning: NOT_ENABLED: docker0
success
~]# firewall-cmd --get-active-zones
public
  interfaces: enp0s3

Docker ще додав файл конфігурації до /etc/firewalld/zones
Його я теж видалив й перечитав конфіг firewall-cmd --reload

Ми все вимкнули, тому доступу з докера нікуди немає
Додаємо трансляцію nat POSTROUTING
~]# nft add rule nat POSTROUTING ip saddr 172.17.0.0/24 oif enp0s3 snat to 192.168.7.104
~]# nft list chain nat POSTROUTING
table ip nat {
        chain POSTROUTING {
                type nat hook postrouting priority srcnat; policy accept;
                ip saddr 172.17.0.0/24 oif "enp0s3" snat to 192.168.7.104
        }
}

Доступу все одно немає, бо у filter FORWARD політика drop (якщо accept, то пропускаємо)
~]# nft list chain filter FORWARD
table ip filter {
        chain FORWARD {
                type filter hook forward priority filter; policy drop;
        }
}

Дозволяємо FORWARD для мережі докера
~]# nft add rule ip filter FORWARD ip saddr 172.17.0.0/16 accept
~]# nft add rule ip filter FORWARD ip daddr 172.17.0.0/16 accept

Вмикаємо для старта в автоматичному режимі
~]# systemctl enable docker

Тепер постає питання збереження правил nftables під час старту firewalld.

Залишаються тільки дефолтові правила nftables та наш source NAT
Зберігаємо
~]# nft list ruleset >> /etc/sysconfig/nftables.conf

Переглянути
 
# nft list ruleset
table ip filter {
        chain INPUT {
                type filter hook input priority filter; policy accept;
        }

        chain FORWARD {
                type filter hook forward priority filter; policy drop;
                ip saddr 172.17.0.0/16 accept
                ip daddr 172.17.0.0/16 accept
        }

        chain OUTPUT {
                type filter hook output priority filter; policy accept;
        }
}
table ip6 filter {
        chain INPUT {
                type filter hook input priority filter; policy accept;
        }

        chain FORWARD {
                type filter hook forward priority filter; policy accept;
        }

        chain OUTPUT {
                type filter hook output priority filter; policy accept;
        }
}
table bridge filter {
        chain INPUT {
                type filter hook input priority filter; policy accept;
        }

        chain FORWARD {
                type filter hook forward priority filter; policy accept;
        }

        chain OUTPUT {
                type filter hook output priority filter; policy accept;
        }
}
table ip security {
        chain INPUT {
                type filter hook input priority 150; policy accept;
        }

        chain FORWARD {
                type filter hook forward priority 150; policy accept;
        }

        chain OUTPUT {
                type filter hook output priority 150; policy accept;
        }
}
table ip raw {
        chain PREROUTING {
                type filter hook prerouting priority raw; policy accept;
        }

        chain OUTPUT {
                type filter hook output priority raw; policy accept;
        }
}
table ip mangle {
        chain PREROUTING {
                type filter hook prerouting priority mangle; policy accept;
        }

        chain INPUT {
                type filter hook input priority mangle; policy accept;
        }

        chain FORWARD {
                type filter hook forward priority mangle; policy accept;
        }

        chain OUTPUT {
                type route hook output priority mangle; policy accept;
        }

        chain POSTROUTING {
                type filter hook postrouting priority mangle; policy accept;
        }
}
table ip nat {
        chain PREROUTING {
                type nat hook prerouting priority dstnat; policy accept;
        }

        chain INPUT {
                type nat hook input priority 100; policy accept;
        }

        chain POSTROUTING {
                type nat hook postrouting priority srcnat; policy accept;
                ip saddr 172.17.0.0/24 oif "enp0s3" snat to 192.168.7.104
        }

        chain OUTPUT {
                type nat hook output priority -100; policy accept;
        }
}
table ip6 security {
        chain INPUT {
                type filter hook input priority 150; policy accept;
        }

        chain FORWARD {
                type filter hook forward priority 150; policy accept;
        }

        chain OUTPUT {
                type filter hook output priority 150; policy accept;
        }
}
table ip6 raw {
        chain PREROUTING {
                type filter hook prerouting priority raw; policy accept;
        }

        chain OUTPUT {
                type filter hook output priority raw; policy accept;
        }
}
table ip6 mangle {
        chain PREROUTING {
                type filter hook prerouting priority mangle; policy accept;
        }

        chain INPUT {
                type filter hook input priority mangle; policy accept;
        }

        chain FORWARD {
                type filter hook forward priority mangle; policy accept;
        }

        chain OUTPUT {
                type route hook output priority mangle; policy accept;
        }

        chain POSTROUTING {
                type filter hook postrouting priority mangle; policy accept;
        }
}
table ip6 nat {
        chain PREROUTING {
                type nat hook prerouting priority dstnat; policy accept;
        }

        chain INPUT {
                type nat hook input priority 100; policy accept;
        }

        chain POSTROUTING {
                type nat hook postrouting priority srcnat; policy accept;
        }

        chain OUTPUT {
                type nat hook output priority -100; policy accept;
        }
}
table bridge nat {
        chain PREROUTING {
                type filter hook prerouting priority dstnat; policy accept;
        }

        chain OUTPUT {
                type filter hook output priority out; policy accept;
        }

        chain POSTROUTING {
                type filter hook postrouting priority srcnat; policy accept;
        }
}

В результаті: мені не довелось вмикати маскарадінг для всіх, я не використовував firewall-cmd
Ви маєте увійти під своїм обліковим записом

loading