Skip to content

Conversation

@FrancoisCapon
Copy link
Contributor

@FrancoisCapon FrancoisCapon commented Sep 1, 2025

Hello,

  • Usability: the Docker container user is the same as the Docker host, so there are no longer any file permission issues
$ id -u ; id -g 1000 1000 ... $ ls -ltrn total 12 -rw-r--r-- 1 1000 1000 19 sept. 1 10:53 mkdocs.yml drwxr-xr-x 2 1000 1000 4096 sept. 1 10:54 docs drwxr-xr-x 5 1000 1000 4096 sept. 1 10:54 site 
  • Security: by default, Docker publishes to 0.0.0.0, so the Docker host exposes the documentation to all other hosts

Important

Publishing container ports is insecure by default. Meaning, when you publish a container's ports it becomes available not only to the Docker host, but to the outside world as well.

If you include the localhost IP address (127.0.0.1, or ::1) with the publish flag, only the Docker host and its containers can access the published container port.

https://docs.docker.com/engine/network/#published-ports

$ ... -p 8000:8000 ... $ ss -ltrn State Recv-Q Send-Q Local Address:Port Peer Address:Port Process LISTEN 0 4096 0.0.0.0:8000 0.0.0.0:* LISTEN 0 4096 [::]:8000 [::]:* $ curl -I 192.168.1.30:8000 HTTP/1.0 200 OK Date: Mon, 01 Sep 2025 09:46:27 GMT Server: WSGIServer/0.2 CPython/3.11.13 Content-Type: text/html Content-Length: 11125 $ curl -I localhost:8000 HTTP/1.0 200 OK Date: Mon, 01 Sep 2025 09:46:29 GMT Server: WSGIServer/0.2 CPython/3.11.13 Content-Type: text/html Content-Length: 11125 $ ... -p 127.0.0.1:8000:8000 ... $ ss -ltrn State Recv-Q Send-Q Local Address:Port Peer Address:Port Process LISTEN 0 4096 localhost:8000 0.0.0.0:* $ curl -I 192.168.1.30:8000 curl: (7) Failed to connect to 192.168.1.30 port 8000 after 0 ms: Couldn't connect to server $ curl -I localhost:8000 HTTP/1.0 200 OK Date: Mon, 01 Sep 2025 09:44:57 GMT Server: WSGIServer/0.2 CPython/3.11.13 Content-Type: text/html Content-Length: 11125 
@FrancoisCapon
Copy link
Contributor Author

I added the docker documentation reference of the ports publication on the initial comment.

@squidfunk
Copy link
Owner

Thanks for the PR! Could you please elaborate a little more why the changes are necessary?

Please keep in mind that we want to keep the getting started guide as simple as possible, and this does not include having a hardened setup. The Docker image is only intended for previewing, not for production. Additionally, please understand that we're currently very busy with bigger picture topics, so this change has to wait for a bit.

@FrancoisCapon
Copy link
Contributor Author

Thanks for your response.

  • By default the docker container user is root so the files writes belong to root and the docker host user can not handle it, that's here that my students stop using the docker image!
    drwxr-xr-x 2 root root 4096 sept. 9 10:57 docs -rw-r--r-- 1 root root 19 sept. 9 10:57 mkdocs.ym 

The Docker image is only intended for previewing, not for production.

  • I know, but security by default is always the best policy. Your host can potentially exposed your "secret" documentation to other hosts (lan or bigger network) when all you need to do is add 127.0.0.1 to avoid that (docs.docker.com: Publishing container ports is insecure by default.)

  • The command lines can be monoline if you think it's simpler I can change it

    docker run --rm -u $(id -u):$(id -g) -v $(pwd):/docs squidfunk/mkdocs-material new . docker run --rm -u $(id -u):$(id -g) -v $(pwd):/docs -p 127.0.0.1:8000:8000 squidfunk/mkdocs-material docker run --rm -u $(id -u):$(id -g) -v $(pwd):/docs -p 127.0.0.1:8000:8000 squidfunk/mkdocs-material build 

Additionally, please understand that we're currently very busy with bigger picture topics, so this change has to wait for a bit.

Yes, I understand.

Copy link
Owner

@squidfunk squidfunk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again for the PR and providing more information. I agree regarding security by default, and I wasn't aware that we can scope this to be less permissive. I thought exposing on 0.0.0.0 is the only thing that works. Does this also mean we can just use 127.0.0.1 in Docker, and map that through as well?

Please also read the comments, I have some minor remarks.

After you've [installed] Material for MkDocs, you can bootstrap your project
documentation using the `mkdocs` executable. Go to the directory where you want
your project to be located and enter:
After you've [installed] Material for MkDocs, you can bootstrap your documentation project then preview your documentation and finaly build your site using the `mkdocs` executable.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain why this change is necessary? It doesn't have anything to do with what you described in your PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I used the page's table of contents to navigate between commands, and I can't find the first one because there is no header for it. So I added it, but you're right, it's not mentioned in the PR goal, only in a commit message (explain the steps in the introduction 60675a1). I can delete it and create a specific PR if you want?

=== "Unix"

```
docker run --rm \
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please format this as:

docker run --rm \ ... (two spaces) \ .... (and so on) 
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean ?

docker run --rm \ --user $(id -u):$(id -g) \ --volume $(pwd):/docs \ squidfunk/mkdocs-material \ new . 
```
docker run --rm \
--user $(id -u):$(id -g) \
--volume $(pwd):/docs \
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you remove -it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-it are not needed for running a containerized web service, so I removed it (the “only necessary features” principle).

🧩 Breakdown of -it
-i stands for interactive: It keeps the standard input (stdin) open so you can interact with the container.
-t stands for pseudo-TTY: It allocates a terminal for the container, which makes it behave like a real terminal session.
🔍 Why use -it ?
Ideal for debugging or exploring a container manually
Useful when you want to run shell commands inside a container
Copilot 🤖

--user $(id -u):$(id -g) \
--volume $(pwd):/docs \
squidfunk/mkdocs-material \
new . # bootstrap project
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the comment, this is not necessary, as it's described in the paragraph above.

--user $(id -u):$(id -g) \
--volume $(pwd):/docs \
--publish 127.0.0.1:8000:8000 \
squidfunk/mkdocs-material
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest leaving the serve command uncommented, as I don't want to explain to students why to use serve with mkdocs command and why not serve with the container!

 docker run --rm \ --user $(id -u):$(id -g) \ --volume $(pwd):/docs \ --publish 127.0.0.1:8000:8000 \ squidfunk/mkdocs-material serve 

I realize that there is no explanation that the server must be stopped using Ctrl + C, regardless of the execution context chosen. Can I add it in the futur update of this PR ?

docker run --rm \
--user $(id -u):$(id -g) \
--volume $(pwd):/docs \
squidfunk/mkdocs-material \
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay

@FrancoisCapon
Copy link
Contributor Author

FrancoisCapon commented Oct 7, 2025

I thought exposing on 0.0.0.0 is the only thing that works. Does this also mean we can just use 127.0.0.1 in Docker, and map that through as well?

  • When you use an IP you use the NIC "associated" with this IP
  • The 127.0.0.1 use a loopback NIC and can only communicate with himself (https://en.wikipedia.org/wiki/Localhost)
  • So if you bind a service to 127.0.0.1 in a container, the service can only be accessible into the container on its lo interface
$ docker run --entrypoint "/sbin/ip" squidfunk/mkdocs-material address 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 38: eth0@if39: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever 
  • When you run a container using docker run without specifying a network, Docker automatically connects it to the default bridge network with a "container" NIC (example: eth0@if39 above) and docker set automatically a IP to that NIC.
  • The service must be bind to this IP, and because this IP is dynamic the service is binded to the special 0.0.0.0 IP (https://en.wikipedia.org/wiki/0.0.0.0#Binding) ; the service will be binded to lo (we don't care) and the container NIC (ex: eth0@if39).
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4c265467c646 squidfunk/mkdocs-material "/sbin/tini -- mkdoc…" 5 minutes ago Up 5 minutes 127.0.0.1:8000->8000/tcp relaxed_booth $ docker exec -u 0 relaxed_booth apk add iproute2 fetch https://dl-cdn.alpinelinux.org/alpine/v3.21/main/x86_64/APKINDEX.tar.gz ... OK: 40 MiB in 91 packages $ docker exec relaxed_booth ss -ltp State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess LISTEN 0 5 0.0.0.0:8000 0.0.0.0:* users:(("mkdocs",pid=7,fd=3)) 
  • Now, with for example --publish 127.0.0.1:1234:5678 all the outgoing TCP network traffic on port 1234 from the lo NIC host will be "routed" to the container NIC on port 5678 and vice versa for the response. The host think talking to localhost on port 1234 but in fact it talk to the container on port 5678!

Tip

So to put it on a nutshell:

  • CMD ["serve", "--dev-addr=0.0.0.0:8000"]
  • always bind the containerized service to 0.0.0.0 for technical reason
  • --publish 127.0.0.1:8000:8000
  • always map only your loopback host NIC to the container for security reason
Copy link
Contributor Author

@FrancoisCapon FrancoisCapon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First of all, sorry for the first delay and and sorry for the "pending" delay, I think that my returns pending your review not pending my submission as a lot of other GitHub users (https://github.com/orgs/community/discussions/10369).

After much looking, I found the place to "Submit" my review on the "files changed" tab in the upper right hand corner.

Yes, my feedback is now public and submitted 😅.

After you've [installed] Material for MkDocs, you can bootstrap your project
documentation using the `mkdocs` executable. Go to the directory where you want
your project to be located and enter:
After you've [installed] Material for MkDocs, you can bootstrap your documentation project then preview your documentation and finaly build your site using the `mkdocs` executable.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I used the page's table of contents to navigate between commands, and I can't find the first one because there is no header for it. So I added it, but you're right, it's not mentioned in the PR goal, only in a commit message (explain the steps in the introduction 60675a1). I can delete it and create a specific PR if you want?

=== "Unix"

```
docker run --rm \
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean ?

docker run --rm \ --user $(id -u):$(id -g) \ --volume $(pwd):/docs \ squidfunk/mkdocs-material \ new . 
```
docker run --rm \
--user $(id -u):$(id -g) \
--volume $(pwd):/docs \
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-it are not needed for running a containerized web service, so I removed it (the “only necessary features” principle).

🧩 Breakdown of -it
-i stands for interactive: It keeps the standard input (stdin) open so you can interact with the container.
-t stands for pseudo-TTY: It allocates a terminal for the container, which makes it behave like a real terminal session.
🔍 Why use -it ?
Ideal for debugging or exploring a container manually
Useful when you want to run shell commands inside a container
Copilot 🤖

--user $(id -u):$(id -g) \
--volume $(pwd):/docs \
--publish 127.0.0.1:8000:8000 \
squidfunk/mkdocs-material
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest leaving the serve command uncommented, as I don't want to explain to students why to use serve with mkdocs command and why not serve with the container!

 docker run --rm \ --user $(id -u):$(id -g) \ --volume $(pwd):/docs \ --publish 127.0.0.1:8000:8000 \ squidfunk/mkdocs-material serve 

I realize that there is no explanation that the server must be stopped using Ctrl + C, regardless of the execution context chosen. Can I add it in the futur update of this PR ?

docker run --rm \
--user $(id -u):$(id -g) \
--volume $(pwd):/docs \
squidfunk/mkdocs-material \
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay

@squidfunk
Copy link
Owner

Thanks for your comments. Given the level of involvement this PR seems to need, I'm afraid it has to wait. I currently have no time to review it and assess its impact on our users, as we're currently focusing on finalizing our foundational work. Once that's out the door, I'll try to set aside some time to review it, but I must admit I'm having a hard time assessing those changes given how much has been written. Thus, please don't expect it to be merged soon. We'll revisit it when possible.

@squidfunk
Copy link
Owner

Material for MkDocs is now in maintenance mode. Please understand that we don't consider merging this PR anymore.

@squidfunk squidfunk closed this Nov 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants