Can not add new user in docker container with mounted /etc/passwd and /etc/shadow
Asked Answered
P

2

6

Example of the problem:

docker run -ti -v my_passwd:/etc/passwd -v my_shadow:/etc/shadow --rm centos
[root@681a5489f3b0 /]# useradd test # does not work !?
useradd: failure while writing changes to /etc/passwd
[root@681a5489f3b0 /]# ll /etc/passwd /etc/shadow # permission check
-rw-r--r-- 1 root root 157 Oct  8 10:17 /etc/passwd
-rw-r----- 1 root root 100 Oct  7 18:02 /etc/shadow

The similar problem arises when using passwd:

[root@681a5489f3b0 /]# passwd test
Changing password for user test.
New password: 
BAD PASSWORD: The password is shorter than 8 characters
Retype new password: 
passwd: Authentication token manipulation error

I have tried using the ubuntu image, but the same problem arises.

I can manually edit passwd file and shadow file from within container.

I am getting the same problem on following two machines:

Host OS: CentOS 7 - SELinux Disabled
Docker Version: 1.8.2, build 0a8c2e3

Host OS: CoreOS 766.4.0
Docker version: 1.7.1, build df2f73d-dirty

I've also opened issue on GitHub: https://github.com/docker/docker/issues/16857

Pram answered 8/10, 2015 at 10:39 Comment(0)
A
8

It's failing because passwd manipulates a temporary file, and then attempts to rename it to /etc/shadow. This fails because /etc/shadow is a mountpoint -- which cannot be replaced -- which results in this error (captured using strace):

102   rename("/etc/nshadow", "/etc/shadow") = -1 EBUSY (Device or resource busy)

You can reproduce this trivially from the command line:

# cd /etc
# touch foo
# mv foo shadow
mv: cannot move 'foo' to 'shadow': Device or resource busy

You could work around this by mounting a directory containing my_shadow and my_passwd somewhere else, and then symlinking /etc/passwd and /etc/shadow in the container appropriately:

$ docker run -it --rm -v $PWD/my_etc:/my_etc centos
[root@afbc739f588c /]# ln -sf /my_etc/my_passwd /etc/passwd
[root@afbc739f588c /]# ln -sf /my_etc/my_shadow /etc/shadow
[root@afbc739f588c /]# ls -l /etc/{shadow,passwd}
lrwxrwxrwx. 1 root root 17 Oct  8 17:48 /etc/passwd -> /my_etc/my_passwd
lrwxrwxrwx. 1 root root 17 Oct  8 17:48 /etc/shadow -> /my_etc/my_shadow
[root@afbc739f588c /]# passwd root
Changing password for user root.
New password: 
Retype new password: 
passwd: all authentication tokens updated successfully.
[root@afbc739f588c /]# 
Aurel answered 8/10, 2015 at 17:49 Comment(3)
With this solution useradd and adduser no longer work. How should this be fixed?Goudy
I've tried this and the symlink is simply removed and replaced by a copy of the shadow file in its place (in the container) so you won't get an update on the host. So if this ever worked, for debian/ubuntu its now definitely useless.Concelebrate
I have also tried and failed with this method, however it gave me just what I needed to make it work. Instead of symlinking /etc/{passwd,shadow} to /my_etc/, all you need to do is to copy the files back and forth: cp /metc/* /etc -> do your deeds -> cp /etc/{passwd,shadow} /metc/Trichina
Z
0

I ran into this problem today. After trying basically everything, I arrived at the unfortunate conclusion that there's no good way to make passwd, useradd, groupadd, etc. all happy without mounting the entire /etc directory. The one saving grace is that it likely won't take up too much space if you are using a minimised base (e.g. alpine, bitnami/minideb).


The following is a table of things I tried and why they each failed:

Method Problem
mount /my-path and symlink /etc/{passwd,group,shadow,gshadow} to /my-path/... useradd and groupadd failure (presumably userdel and groupdel will fail too)
mount /my-path and hard link /etc/{passwd,group,shadow,gshadow} to /my-path/... hard links cannot cross file system boundary
mount /etc/{passwd,group,shadow,gshadow} individually everything fails
install an init system like s6-overlay and use a file synchronisation program janky AF; potential race condition; inefficient; etc.

In the end I just had to sigh and take the L and mount the entire /etc directory. Honestly I recommend you do the same - it's not worth my time and neither is it worth yours.


If you are doing this, you probably want to copy the contents of /etc from the image to your host before you start your container for the first time, otherwise your /etc will just be an empty mount. For this, you can use this trick.

If you are using docker-compose or podman-compose, you can use variable substitution to temporarily mount the empty host directory into /tmp/etc (or anywhere else for that matter), so that it's not shadowing the real /etc when you perform the copy:

services:
  my-service:
    container_name: my-container
    volumes:
      - /dir/on/host:${DIVERT_DIR}/etc
# I'm using podman but it's the same thing with docker

# create temporary container without shadowing /etc
DIVERT_DIR=/tmp podman-compose up --no-start

# copy /etc to host
podman cp my-container:/etc /dir/on/host

# tear down temporary container
podman rm my-container

# create and start the actual container
podman-compose up
Zalucki answered 2/8, 2023 at 10:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.