Docker compose and Dockerfile error - you must be root

Hello,
i’m try to install and setup iptables inside a container (zerotier)

i have a container with zerotier that works fine
but i would use iptables inside a container
so i test with

version: '2'

services:
  zerotier-one:
    image: zerotier/zerotier:latest
    container_name: zerotier-one
    restart: unless-stopped
    cap_add:
      - SYS_ADMIN
      - NET_ADMIN
    devices:
      - /dev/net/tun
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
    volumes:
      - ./zerotier-one:/var/lib/zerotier-one
      #- ./init.sh:/init.sh
    build:
      dockerfile: ./Dockerfile
    #entrypoint: sh -c "sh /init.sh"
    #command: sh -c "sh /init.sh"
    #tty: true

and Dockerfile

FROM zerotier/zerotier:latest

RUN apt-get update ;
RUN apt-get install iptables sudo -y ;

COPY init.sh /init.sh
RUN chmod 755 /init.sh
RUN sh -c 'sh /init.sh'
#CMD ["sh", "/init.sh"]

and init.sh

#!/bin/sh
#apt-get update ;
#apt-get install iptables sudo -y ;
PHY_IFACE=eth0; ZT_IFACE=ztxxxxxxxx
sudo iptables -t nat -A POSTROUTING -o $PHY_IFACE -j MASQUERADE
sudo iptables -A FORWARD -i $PHY_IFACE -o $ZT_IFACE -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i $ZT_IFACE -o $PHY_IFACE -j ACCEPT

but when i use
docker compose up --build -d
i have this error

 => [zerotier-one internal] load build definition from Dockerfile                                                                                                                       0.0s
 => => transferring dockerfile: 767B                                                                                                                                                    0.0s
 => [zerotier-one internal] load metadata for docker.io/zerotier/zerotier:latest                                                                                                        0.0s
 => [zerotier-one internal] load .dockerignore                                                                                                                                          0.0s
 => => transferring context: 2B                                                                                                                                                         0.0s
 => [zerotier-one 1/6] FROM docker.io/zerotier/zerotier:latest                                                                                                                          0.0s
 => [zerotier-one internal] load build context                                                                                                                                          0.0s
 => => transferring context: 29B                                                                                                                                                        0.0s
 => CACHED [zerotier-one 2/6] RUN apt-get update ;                                                                                                                                      0.0s
 => CACHED [zerotier-one 3/6] RUN apt-get install iptables sudo -y ;                                                                                                                    0.0s
 => CACHED [zerotier-one 4/6] COPY init.sh /init.sh                                                                                                                                     0.0s
 => CACHED [zerotier-one 5/6] RUN chmod 755 /init.sh                                                                                                                                    0.0s
 => ERROR [zerotier-one 6/6] RUN sh -c 'sh /init.sh'                                                                                                                                    0.4s
------
 > [zerotier-one 6/6] RUN sh -c 'sh /init.sh':
0.373 iptables v1.8.7 (nf_tables): Could not fetch rule set generation id: Permission denied (you must be root)
0.375
0.394 iptables v1.8.7 (nf_tables): Couldn't load match `state':No such file or directory
0.395
0.396 Try `iptables -h' or 'iptables --help' for more information.
0.414 iptables v1.8.7 (nf_tables): Could not fetch rule set generation id: Permission denied (you must be root)
0.416
------
failed to solve: process "/bin/sh -c sh -c 'sh /init.sh'" did not complete successfully: exit code: 4

strange is if i open a container
docker exec -it zerotier-one /bin/bash
and type
sh /init.sh
works fine…

so how i can run this file or command after container is up?

thank you in advance

It isn’t strange. You got the error message during the build process where it doesn’t make sense to configure the network. You create an image and only when you run the container can you do anything with the container network.

Why did you replace CMD with the RUN instruction?

i try to use only init.sh (without Dockerfile) but don’t work.

i try to mount with volume in docker-compose.yml
and “decomment”

 entrypoint: sh -c "sh /init.sh"
    command: sh -c "sh /init.sh"

one at a time , of course.
but init.sh doesn’t run…

thank you

Why not just

command: /init.sh

What you did is started a shell in which you started another shell to execute /init.sh whch had to run and stop as the shell stops after you executed the commands. My recommended code will not solve that. You have to keep the container alive by running a command in the foreground until you stop the container. And that command is missing in the end of the script.

thank yuo for reply.

so i now change docker compose without Dockerfile

version: '2'

services:
  zerotier-one:
    image: zerotier/zerotier:latest
    container_name: zerotier-one
    restart: unless-stopped
    cap_add:
      - SYS_ADMIN
      - NET_ADMIN
    devices:
      - /dev/net/tun
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
    volumes:
      - ./zerotier-one:/var/lib/zerotier-one
      - ./init.sh:/init.sh
    #build:
      #dockerfile: ./Dockerfile
    #entrypoint: sh -c "sh /init.sh"
    command: /init.sh
    #tty: true

and init.sh

!/bin/sh
apt-get update ;
apt-get install iptables sudo -y ;
PHY_IFACE=eth0; ZT_IFACE=ztxxxxxxx
sudo iptables -t nat -A POSTROUTING -o $PHY_IFACE -j MASQUERADE
sudo iptables -A FORWARD -i $PHY_IFACE -o $ZT_IFACE -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i $ZT_IFACE -o $PHY_IFACE -j ACCEPT
tail -f /dev/null

i read to keep container alive i can use tail -f /dev/null at end of script, so i add on init.sh
but when i start container e login.
i can’t see iptables installed… so rules can’t be executed…

i try command entrypoint Dockerfile, i can’t understand how i can run a script after container is started…
in other work how i can run automatically these commands (that manual works)

docker exec -it zerotier-one /bin/bash
/init.sh

thank you in advance

i try to execute

docker compose down ; docker compose up

and in console i see

Attaching to zerotier-one
=> Configuring networks to join
=> Joining networks from command line: [/init.sh]
===> Configuring join: [/init.sh]
=> Starting ZeroTier
=> Writing healthcheck for networks: [/init.sh]

but command try to join networks?
why doesn’t it run normally?

instead if i use in docker-compose.yml

    entrypoint: /init.sh

i have iptables and rules, but not work zerotier…
seems entrypoint exclude zerotier… maybe iptables is installed before zerotier executable…
so can i start container , and “auto” execute init.sh after 30s . when zerotier is working…

thanks

First you wanted to run iptables in a RUN instruction which should have been in the CMD, and now you want to run apt in the CMD instruction which should be in the RUN instruction. Don’t install anything in a running container. That should be in the image already.

You also need to learn to debug your containers. We can’t recognize all the mistakes just by reading your scripts and Dockerfile. IYou have to check the logs using docker logs and if necessary add the lines to your script line by line and after each new line, you check what happened in the container by running commands that cn check the result of the previous command. Don’t use apt in the init script, but if you wan to chek if something was installed, you can run

dpkg -l | grep packagename

It is also a good practice to start your scripts this way:

#!/bin/sh

set -eu -o pipefail

That way when one line fails, or there is a missing variable, the script will stop and you will see what was the last line that worked. For debugging you can also add -x like

#!/bin/sh

set -eux -o pipefail

So it will also show all the executed commands even if those commands don’t have any output.

About using commands and the entrypoinnt, you can read my blogpost: