172.21.0.1 is the IP address of the Pi-hole network gateway: "Gateway":"172.21.0.1" and I don't see anything actually running on that gateway IP address.
That's a docker DNS address that would only resolve if the container is using the docker DNS server, I believe it's 127.0.0.11.
That would not query the host when you are using a bridge network, which docker compose is creating.
I think your main issue and I believe it's been noted previously, is that you have unbound locked to interface: 127.0.0.1 so it truly will only see the loopback interface on the host. Doesn't matter what access control you modify if unbound isn't going to see any of the packets.
If you review the documentation for unbound.conf you will find the way to set this up:
# listen on all interfaces, answer queries from the local subnet.
interface: 0.0.0.0
interface: ::0
access-control: 10.0.0.0/8 allow
access-control: 2001:DB8::/64 allow
interface: <ip address or interface name [@port]>
Interface to use to connect to the network. This interface is
listened to for queries from clients, and answers to clients are
given from it. Can be given multiple times to work on several
interfaces. If none are given the default is to listen to local-
host. If an interface name is used instead of an ip address,
the list of ip addresses on that interface are used. The inter-
faces are not changed on a reload (kill -HUP) but only on
restart. A port number can be specified with @port (without
spaces between interface and port number), if not specified the
default port (from port) is used.
My suggestion is to set the inteface to listen on all interfaces as documented and see if this resolves your issue. You can then look to locking things down further if you so desire.
Why do you tie unbound to Docker's internal gateway address?
As mentioned earlier:
What's the value of your ${FTLCONF_LOCAL_IPV4}?
Since that should be the IP of the machine that runs both your bare-metal unbound and your dockered Pi-hole, that should be the IP to use.
OK, in /etc/unbound/unbound.conf.d/pihole.conf, I have:
interface: 91.xx.xx.xx # my public IP
Inside Pihole docker (sudo docker exec -it pihole bash) I can now successfully run a query:
dig mail.google.com @91.xx.xx.xx -p 5335
Now, regarding the PIHOLE_DNS_=${FTLCONF_LOCAL_IPV4}#5335 - in PiHole settings it is 192.168.1.10#5335.
And the problem is, that PiHole is not working (meaning: DNS querying is not working).
Also, I run into another problem... I said docker-compose down -v and docker-compose up -d --force-recreate and now I have different subnet inside docker. IP is 172.23.0.2/16 (before that it was 172.21.0.2). This is some normal docker behaviour or what?
How to set unbound access-control rules and ufw rules then? Because it seems that every time I restart PiHole docker, I get another (higher) subnet...
I guess it is OK if I put access-control: 172.16.0.0/12 allow in unbound config (/etc/unbound/unbound.conf.d/pihole.conf) and set ufw rule as ufw allow from 172.16.0.0/12 to any port 5335?
And then there should be PIHOLE_DNS_=91.xx.xx.xx#5335 in docker-compose.yml?
Well, the problem is, that on a host machine I do not have this interface:
ip a | grep 192.168.1.
So when I put interface: 192.168.1.10 in the unbound config, unbound does not start because of an error.
I have no idea where this IP 192.168.1.10 (from ${FTLCONF_LOCAL_IPV4}) came from.
Anyway, I have ufw rule allowing access to unbound only from private 172 addresses (sudo ufw allow from 172.16.0.0/12 to any port 5335). I know it is not ideal solution, but have no idea where does this 192.168.1.10 come from...