Anything that is insecure by default in 2020 should be killed off IMO.
In general I would recommend putting some kind of protection between machines running Docker (and especially orchestrators) and the internet. This could be cloud provider mechanisms (security groups, ACLs, etc), a firewall appliance, NAT gateway configuration, etc. depending on the situation. It's not necessary, but it makes the situation easier to audit/validate, and more layers of protection seldom hurt. If nothing else it means that much of the time you'll need to make a mistake in two places instead of one, in order to have an unintended exposure.
Use nmap to evaluate your policy from the outside, don't try to validate it in your head by inspection.
"The EXPOSE instruction does not actually publish the port. It functions as a type of documentation between the person who builds the image and the person who runs the container, about which ports are intended to be published."
Second paragraph:Although the similar words with different meanings no doubt contribute to confusion.
The fact it's not, is why we're seeing major attacks/leaks/etc almost every single day now.
A secure by default product is a dead on arrival product.
I'm 58 and bald, so channeling yoda is easy: a valuable lesson you need to learn, and learn it the cheap way or the expensive way you can.
The cheap way is to invest in a cheap VPS for a month, fire up sshd and a webserver, and then check the logs when the month is up.
The expensive way is to carry on treating security as an afterthought. It'll cost you your pride, your reputation, and possibly your career.
When you use ufw with a default DENY policy, you tend to assume that whatever isn't explicitly listed gets DENIED. This is not the case with Docker, and I think it's just a matter of time until someone loses big because of this issue.
But, after some search, I found that simply setting "network_mode" to "host" could solve my problem. So, I ended up not to deal with iptable.
If someone want to deploy some self-hosted service, I would suggest using traefik v2. Outfit docker quite well especially with letsencrypt.