Docker postgres image: How to restrict access from host network? - postgresql

I'm running a postgres server in a docker container within a custom docker network, and I found I can access it using psql from the host.
This seems to me to be undesirable from a security perspective since I thought the point of docker networks was to isolate access to the containers.
My thinking was that I would run my app in a separate container within the same docker network and publish ports on the app container only. That way, the app can be accessed from the outside world, but the database can't be.
My question is: Why is the 5432 port being published to host on the postgres container without me explicitly specifying that, and how can I "unpublish" this port?
And a related question would be: am I wrong that publishing port 5432 is a security concern in this case (or at least less secure than not publishing it)?
My container is running the official docker postgres image here: https://hub.docker.com/_/postgres/
Thanks for any help!
Edit: Here is the docker command I'm using to run the container:
docker run -d --restart=always --name=db.geppapp.com -e "POSTGRES_USER=<user>" -e "POSTGRES_PASSWORD=<password>" -e "POSTGRES_DB=gepp" -v "/mnt/storage/postgres-data:/var/lib/postgresql/data" --net=db postgres
Edit 2: My original question was not entirely correct as docker was not in fact publishing the port 5432 to the host but rather I was specifying the container's IP address as the host when connecting to postgres with psql as follows:
psql --host=<docker-assigned-ip> --username=<user> --dbname=gepp
So the thing preventing me from restricting access to the container from the host is in fact that an IP address is assigned to the container on the host network.

The Dockerfile of postgres exposes that port 5432, but that does not mean it makes the port of the container accessible to the host. To expose the port to host, you must use either the -p flag to publish a range of ports or the -P flag to publish all of the exposed ports. But I don't see that in your command.
Are you sure you are not accessing a local postgres and not the container postgres?
or Are you connected to the Host network? If docker container is set to network_mode: host or type of host, any port exposed in the container would be exposed on the docker host as well, without requiring the -p or -P docker run options.

Thanks everyone who commented and answered. I'm answering my own question with a summary of my best understanding based on the replies and other things I've read.
As I mentioned in the edit to my question the thing preventing me from restricting access to the container from the host is the IP assignment on the host network.
According to the docker docs: (https://docs.docker.com/config/containers/container-networking/)
By default, the container is assigned an IP address for every Docker
network it connects to.
In this case, the container is not technically "connected" to the host network but has an IP on that network anyway. David pointed out in comments that this only occurs on Linux, not on Windows or Mac (I haven't personally verified this).
So it would appear that due to the way docker networks are implemented in Docker Linux, an IP address is published to the host for all running containers, and there's no way to prevent this.
From a security perspective, since the docker host is always trusted, my understanding is that database security comes mainly from restricting access to the host itself (network and linux account security), and the native database credential security. Docker does not add another secure layer as I was initially thinking it might.

Related

Let PostgreSQL instance on host accept connections from docker bridge network without hardcoding docker IP address

The PostgreSQL database is just installed directly into the linux host machine (not as docker container).
In a docker container (built with docker compose) I have an application that needs to connect to the database.
The client container needs to be on a docker bridge network and cannot be on the host network directly because it needs to reach other containers on the bridge network.
I connect to the Postgres database using the
host.docker.internal hostname as described here.
From within that container I can reach the database no problem that way. But PostgreSQL needs to allow this connection in pg_hba.conf or else I get the error:
no pg_hba.conf entry for host "172.22.0.3"
Of course I can add that IP address to pg_hba.conf like done here but that won't give me a very stable solution because the IP address will not always be the same.
What would be the best practice?
Allow all connection from 172...* ?
Or...?
I'm pretty sure, docker has some dns service by default, and you can write hostname to pg_hba.conf instead of ip address. postgres tries to resolve those name, but maybe only when readin' the conf, so you may need to run pg_ctlcluster reload frequently, like on replacing the client container.
It's cleaner and more secure, if you open the postgres only for those containers who's actually need to connect. On the other hand, if the open port is ssl only and password protected, and your other containers can be considered as trusted, allowing all of them to connect is not something i would call high risk.
The only good practice is to move postgreSQL in a container as if you follow other good practice ( like running docker rootless-mode ) your container app is not suppose to access to any of you host interface.
For now I have gone with adding a samenet entry in the pg_hba.conf file. I am not sure if this is the best approach so I am happy to receive more suggestions.
# to enable local docker connections:
host all all samenet md5

rootless docker - communication between docker containers of different users

When using rootless Docker, Docker instances are running per user and not system-wide. How can I enable communication between Docker containers that have been started by different users?
Scenario is the following. The systems is setup with rootless Docker because multiple people are sharing the same workstation. Rootless Docker is used, so that users building their Docker images and running their containers will not interfer each other.
We also like to run a mongodb in a Docker container, that should be accessible for all users. How can this be done without system-wide Docker? We will be connected to the workstation via ssh and do not want to access the mongodb Docker container from outside the workstation (not being connected via ssh).
Using system-wide Docker is not an option.
Any suggestions how this can be done with root-less Docker?
Info:
Previously with the system-wide Docker we created a network (bridge) and used docker run --network=some_network_for_mongodb -d -p 27017:27017 mongodb:5.0.2
When using rootless Docker, Docker instances are running per user and not system-wide. How can I enable communication between Docker containers that have been started by different users?
The only way you'll be able to enable communication between containers started by different users is by publishing the necessary ports on the host. For example, if you want to create a MongoDB instance that's generally accessible, you could run:
docker run -p 27019:27019 ...
This binds port 27019 in the mongo container to port 27019 on the host. Any other container on the system would be able to connect to this service using an ip address of a host interface.
Of course, this will also open the port to outside connections. There are several ways of dealing with this:
Block the port in your firewall configuration.
Bind the port to a specific host interface on which it won't be available for outside access. For example, if you have a docker bridge docker0 at 172.17.0.1, you could run:
docker run -p 172.17.0.1:27019:27019 ...
This would only publish the port on 172.17.0.1. Other containers could access the service at 172.17.0.1:27019, but it wouldn't be available for outside access.

How to connect to containerized database with its IP?

I'm new to Docker. I successfully created a PostgreSQL container my-database and I am able to access it from SQLTools on my local machine with server address localhost and the port.
I got the containerized database's IP address from the following command:
docker container inspect my-database
But when I go back to SQLTools or the PHP web application (not containerized) and try to connect to my-database with the IP address I got above, it couldn't connect successfully.
What am I missing here?
FYI, I also created another container and was able to connect to my-database with the following way: Use the same network for both my-database and the second container.
It all depends on how you enable access to the database.
If your php service runs in the same machine, then localhost could work
If its on a different machine in the same network, then use the network IP assigned to that machine. If you have your php server in a totally different location, then you may want to use something like an nginx reverse proxy to your docker container.
So in your case you should get the ip:port where your db container runs and use that. Docker inspect shows the internal network ip which only helps other containers in the same virtual network connect to a container.
You never need the docker inspect IP address. You can only connect to it in two specific circumstances: if you're in a different container on the same Docker network, or if you're (a) not in a container at all, (b) on the same host, and (c) that host is running native Linux and not a different OS.
You've already identified the right answers. Say you start the container as
docker network create any-network-name
docker run \
--name database \
--net any-network-name \
-p 5432:5432 \
postgres
From another Docker container on the any-network-name network, you can use the database container name as a DNS name, and avoid the manual IP lookup; ignore any -p options and use the "normal" port for the service. From outside a container, you can use the host's DNS name or IP address (or, if it's the same host, localhost) and the first -p port number.
If you're running this in Docker Compose, it automatically creates a network for you and registers containers under their Compose service name; you do not need networks:, container_name:, or other similar settings.
version: '3.8'
services:
database:
image: postgres
ports: ['5432:5432']
application:
build: .
environment:
- PGHOST=postgres

Connect to Windows Postgres from Debian Docker container

I am running Postgres on a Windows 10 computer, and I want to connect to it from a Docker container. I've followed instructions from many sources and things should be working, but they're not.
Command line used to create Docker container:
docker run --rm -d --network=host --name mycontainer myimage
In postgresql.conf:
listen_addresses = '*'
In pg_hba.conf:
host all all 172.17.0.0/16 trust
In the bash shell of my container, I run:
psql -h 127.0.0.1
and I get the error:
psql: could not connect to server: Connection refused
Is the server running on host "127.0.0.1" and accepting TCP/IP connections on port 5432?
Needless to say, Postgres is definitely running on my computer and I am able to query it from local applications. What am I missing?
THIS WON'T WORK FOR DOCKER v18.03 AND ONWARDS
The answer is already there - From inside of a Docker container, how do I connect to the localhost of the machine?
This question is related to a mysql setup, but it should work for your case too.
FOR DOCKER v18.03 ONWARDS
Use host.docker.internal to refer to the host machine.
https://docs.docker.com/docker-for-windows/networking/#i-cannot-ping-my-containers
As you've discovered, --network-host doesn't work with Docker for Windows or Docker for Mac. It only works on Linux hosts.
One option for this scenario might be to host PostgreSql in a container, also. If you deploy them with a docker-compose file, you should be able to have two separate Docker containers (one for the database and one for your service) that are networked together. By default, docker-compose will expose containers to others in the same compose file using the container name as its DNS name.
You could also consider including the database in the same container as your service, but I think the docker-compose solution is better for several reasons:
It adheres to the best practice of each container having only a single process and single responsibility.
It means that you can easily change and re-deploy your service without having to recreate the database container.
Configure the connection inside your docker container with the real ip-address of your host or as workaround with a dns name

PostgreSQL in Docker - pg_hba.conf to allow access from host to container

I want to run PostgreSQL inside a Docker container. I am building my own Docker image, as I want to include PostgreSQL extensions. I should edit the pg_hba.conf configuration file to:
allow access originating from other containers
allow access originating from the host
The first is quite simple: I can add a rule for 172.17.0.0/16 if I am not mistaken.
But how can I approach the second? What does the IP (or range) looks like when the host connects to psql in a container?
Remark: I am starting the container via docker run -p 127.0.0.1:5432:5432, so in theory I could just allow all in pg_hba.conf because the port forwarding is only bound to 127.0.0.1. But I prefer this extra level of security in pg_hba.conf for the situation when I (probably by mistake) run the container via docker run -p 5432:5432. I hope this makes sense.
update
Actually, setting range 172.17.0.0/16 does not seem to be correct. For example, my container had IP 172.18.0.2 in my test. There does not seem to be a consensus on the default range or how to configure that range, according to my investigations so far.
check your docker0 bridge interface in your case it might be 172.18.0.0/16
make changes in postgresql.conf path will be same as pg_hba.conf.
listenaddress to "*"
Then in pg_hba.conf add rule as
host all all 172.18.0.0/16 md5.
run the docker with hostip : docker run -p :5432:5432
in this way other containser on same docker n/w can connect aswell as from host,but not from other hosts.