8

I have a VPN client in my Docker container (ubuntu:18.04).

The client must do the following:

mv /etc/resolv.conf /etc/resolv.conf.orig

Then the client should create new /etc/resolv.conf with their DNS servers. However, the move fails with an error:

mv: cannot move '/etc/resolv.conf' to '/etc/resolv.conf.orig': Device or resource busy

Can this be fixed? Thank you advance.

P.S.: I can 't change the VPN client code.

Danila Kiver
  • 3,418
  • 1
  • 21
  • 31
d.jok.er
  • 79
  • 1
  • 1
  • 2

2 Answers2

14

Within the Docker container the /etc/resolv.conf file is not an ordinary regular file. Docker manages it in a special manner: the container engine writes container-specific configuration into the file outside of the container and bind-mounts it to /etc/resolv.conf inside the container.

When your VPN client runs mv /etc/resolv.conf /etc/resolv.conf.orig, things boil down to the rename(2) syscall (or similar call from this family), and, according to the manpage for this syscall, EBUSY (Device or resource busy) error could be returned by few reasons, including the situation when the original file is a mountpoint:

EBUSY

The rename fails because oldpath or newpath is a directory that is in use by some process (perhaps as current working directory, or as root directory, or because it was open for reading) or is in use by the system (for example as mount point), while the system considers this an error. (Note that there is no requirement to return EBUSY in such cases — there is nothing wrong with doing the rename anyway — but it is allowed to return EBUSY if the system cannot otherwise handle such situations.)

Though there is a remark that the error is not guaranteed to be produced in such circumstances, it seems that it always fires for bind-mount targets (I guess that probably this happens here):

$ touch sourcefile destfile
$ sudo mount --bind sourcefile destfile
$ mv destfile anotherfile
mv: cannot move 'destfile' to 'anotherfile': Device or resource busy

So, similarly, you cannot move /etc/resolv.conf inside the container, for it is a bind-mount, and there is no straight solution.

Given that the bind-mount of /etc/resolv.conf is a read-write mount, not a read-only one, it is still possible to overwrite this file:

$ mount | grep resolv.conf
/dev/sda1 on /etc/resolv.conf type ext4 (rw,relatime)

So, the possible fix could be to try copying this file to the .orig backup and then rewriting the original one instead of renaming the original file and then re-creating it.

Unfortunately, this does not meet your restrictions (I can 't change the VPN client code.), so I bet that you are out of luck here.

Danila Kiver
  • 3,418
  • 1
  • 21
  • 31
5

Any method that requires moving a file onto /etc/resolv.conf fails in docker container. The workaround is to rewrite the original file instead of moving or renaming a modified version onto it.

For example, use the following at a bash prompt:

(rc=$(sed 's/^\(nameserver 192\.168\.\)/# \1/' /etc/resolv.conf)
     echo "$rc" > /etc/resolv.conf)

This works by rewriting /etc/resolv.conf as follows:

  • read and modify the current contents of /etc/resov.conf through the stream editor, sed
  • the sed script in this example is for commenting out lines starting with nameserver 192.168.
  • save the updated contents in a variable, rc
  • overwrite the original file /etc/resolv.conf with updated contents in "$rc"

The command list is in parentheses to operate in a sub-shell to avoid polluting the current shell's name space with a variable name rc, just in case it happens to be in use.

Note that this command did not require sudo since it took advantage of the super user privileges available by default inside the container. Otherwise, use

sudo sh -c 'rc=$(sed 's/^\(nameserver 192\.168\.\)/# \1/' /etc/resolv.conf); echo "$rc" > /etc/resolv.conf'

as Michael Johansen clarifies below.

Note that sed -i (editing in-place) involves moving the updated file onto the original and will not work.

But if the visual editor, vi, is available in the container, editing and saving /etc/resolv.conf with vi works, since vi modifies the original file directly.

user3076105
  • 336
  • 3
  • 6
  • 2
    Please include an explanation with your answer to help readers understand how this works, and solves the problem. You can click the edit button at the bottom of your answer to add an explanation. Additionally, you may find it beneficial reading [how to answer](https://stackoverflow.com/help/how-to-answer) – Freddy Mcloughlan Jun 23 '22 at 23:11
  • 1
    I got permission denied, so I had to use sudo: `sudo sh -c 'rc=$(sed "s/^\(nameserver 192\.168\.\)/# \1/" /etc/resolv.conf); echo "$rc" > /etc/resolv.conf'` – Michael Johansen Mar 21 '23 at 10:05