0

When running Django in a container, I am unable to access it unless running with python manage.py runserver 0.0.0.0:8000. However, doing so allows anyone on my network to connect, which I do not want.

In my docker compose file, I have:

ports:
      - "8000:8000"

When accessing 127.0.0.1:8000 by just running python manage.py runserver, I get an ERR_EMPTY_RESPONSE error.

Is there any way of accessing my server without opening it to the whole network?

kayoz
  • 1,104
  • 12
  • 16

3 Answers3

2

In short: the process inside the container must bind to 0.0.0.0 to be reachable from outside the container at all; but you can then use Docker Compose's ports: option to control where it's actually reachable from. If you change it to "127.0.0.1:8000:8000" it will not be reachable from off-host.

Docker containers run in an isolated network. A typical setup might look like this:

 ^^^
Router
  | 192.168.1.1
  +--------------------+-- ...
  | 192.168.1.2        | 192.168.1.3
/- Host -----------\   Host 2
|    172.17.0.1    |
|     |            |
|     | 172.17.0.2 |
|  Container       |
\------------------/

As an implementation detail each container has its own private IP address. This is on an isolated network: the second host on 192.168.1.3/24 doesn't have any idea that the container-private network on 172.17.0.0/16 even exists much less how to route traffic there. Since each container has its own private network stack, each container also has its own notion of what localhost means.

(Compare this diagram to a server running directly on the host. It's reachable on 192.168.1.2, but only from the same network; a client on the far side of the router can't reach the 192.168.1.0/24 private network, and again the server is unreachable from the public Internet even if it's bound to "all interfaces".)

If you set the container process to bind only to 127.0.0.1, its own lo0 interface, it will not accept inbound traffic from the 172.17.0.2 eth0 interface, which means that nothing from the outside can ever reach it.

If you set the container process to bind to 0.0.0.0 ("all interfaces"), then it will accept traffic from the host, sending from 172.17.0.1 (probably a docker0 interface). That's not the same as accepting traffic from "the whole network", though: as previously discussed, host 2 still doesn't know how to route traffic there.

You can use the docker run -p or the Docker Compose ports: option to limit which interfaces Docker will publish ports to. If you change ports: to "127.0.0.1:8000:8000" then the container will be reachable from port 8000 on the host, but only via the host's loopback interface; again, there is no route for host 2 to send packets to the process.

David Maze
  • 130,717
  • 29
  • 175
  • 215
  • Thanks for the detailed explanation - this makes sense and it's working now! – kayoz Mar 29 '19 at 08:40
  • But if I want access the docker container from host 2 machine , how can I do it? – Sifat Amin Jul 10 '23 at 13:19
  • This question is specifically about disallowing that. If you `docker run -p 127.0.0.1:8000:8000` then the published port will only be accessible from non-container processes on the same host, not other machines in the network. – David Maze Jul 10 '23 at 13:40
0

do you run the python manage.py runserver directly or through the docker-compose command ? docker-compose run --rm [appname] sh -c 'python manage.py runserver' that's what you should do if you configured your Dockerfile and dockercompose.yml correctly.

docker-compose.yml example for a postgreqsl database:

version: "3"

services:
  app:
    build:
      context: .
    ports:
      - "8000:8000"
    volumes:
      - ./app:/app
    command: >
      sh -c "python manage.py wait_for_db &&
             python manage.py migrate &&
             python manage.py runserver 0.0.0.0:8000"
    environment:
      - DB_HOST=db
      - DB_NAME=app
      - DB_USER=postgres
      - DB_PASS=supersecretpassword
    depends_on:
      - db

  db:
    image: postgres:10-alpine
    environment:
      - POSTGRES_DB=app
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=supersecretpassword

Dockerfile :

FROM python:3.7-alpine
MAINTAINER Pshop

ENV PYTHONUNBUFFERED 1

COPY ./requirements.txt /requirements.txt
RUN apk add --update --no-cache postgresql-client
RUN apk add --update --no-cache --virtual .tmp-build-deps \
    gcc libc-dev linux-headers postgresql-dev
RUN pip install -r /requirements.txt
RUN apk del .tmp-build-deps

RUN mkdir /app
WORKDIR /app
COPY ./app /app

RUN adduser -D user
USER user

where app is your project name

i got this from this tutorial : https://www.udemy.com/django-python-advanced/

Paul Choppin
  • 116
  • 1
  • 11
  • The `python manage.py runserver` command is defined in docker-compose. I simply run `docker-compose up` – kayoz Mar 28 '19 at 18:38
  • Your example looks like it will work because you are running on `0.0.0.0:8000`. However, doing so allows anyone on your network to connect to your server, which is something that I do not want. – kayoz Mar 28 '19 at 18:40
  • Ok got it, actually it was more a stuggest/question than an answer but i can't comment. So sorry i can't help more – Paul Choppin Mar 28 '19 at 18:45
0

You can create a custom network in docker-compose, see the doc here. All you need is to find out or assign the IP of that network and access the container by typing its IP:port in your browser.

hurturk
  • 5,214
  • 24
  • 41
  • I've attempted this based on an answer here to get the IP of the running container: https://stackoverflow.com/questions/17157721/how-to-get-a-docker-containers-ip-address-from-the-host In my case, it's 172.26.0.3 but when I try connecting to http://172.26.0.3:8000/ I just get time outs. – kayoz Mar 28 '19 at 18:52
  • Does this work individually `docker run -p 127.0.0.1:8000:8000` ? I'm not sure if syntax is still valid but can be attempted with docker-compose without any networks. If so, I can update the answer. – hurturk Mar 28 '19 at 19:47
  • I've tried to run that, but my set-up is a bit complicated and I had a hard time doing it without docker-compose, so not completely sure I did it right. Still getting ERR_EMPTY_RESPONSE – kayoz Mar 28 '19 at 20:27
  • Also, do you have `ALLOWED_HOSTS = ['*']` in django's `settings.py` ? – hurturk Mar 28 '19 at 20:32