-
-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Run Docker container as unprivileged user, allow PUID/PGID selection #722
base: master
Are you sure you want to change the base?
Conversation
|
||
set -eu | ||
|
||
# If the first argument looks like a flag, assume we want to run changedetection |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmmm what is the problem you're trying to solve here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming you're referring to the line below: If the container is e.g. run with docker-compose run changedetection -c
, this would run python ./changedetection.py -d /datastore -c
inside the container. It's mostly a convention, nothing that's necessary, just nice to have. If you prefer it removed, no worries. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see PR comments - mainly about being sure it's actually /datastore
?
5534b50
to
314c310
Compare
Dockerfile
Outdated
libxslt-dev \ | ||
zlib1g-dev && \ | ||
rm -rf /var/lib/apt/lists/*; \ | ||
useradd -u 911 -U -d /datastore -s /bin/false changedetection && \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why 911
? :) Is it better to choose a UID > 1000 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's the standard one used by linuxserver.io and makes it a system user, but a higher default one like 3000 is fine as well imho. Would you prefer the default changed? It can be chosen freely anyways (docker-entrypoint.sh:L16-20).
also need to test this on an existing install which used root.root |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unsure
@lkubb biggest one so far, gosu not found |
0f6a310
to
e26de1f
Compare
hmm when it's running as test, it needs to use |
somehow needs a var passed to |
a) Introduce a new env var |
This is fine if theres a comment above like |
I added |
needs but why not just |
Because usually, there is no good reason for an application to be able to modify itself. We could of course disable all that with a build arg specifically for the workflow, but I would be very hesitant to |
haha I love tests, what looked like a simple PR always shows up something
so |
From what I can tell, it creates it as ============================= test session starts ==============================
platform linux -- Python 3.10.9, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /app/changedetectionio, configfile: pytest.ini
plugins: flask-1.2.0
collected 1 item
tests/test_backup.py::test_backup
[...]
> with open("download.zip", 'wb') as f:
E PermissionError: [Errno 13] Permission denied: 'download.zip' |
Oops right you are, that test needs fixing so its using the |
Or better is that it doesnt even write the file to disk and just stores it memory |
9b718cd
to
29afe61
Compare
Honestly, I got tired of explaining at some point and decided to run the container in Podman rootless with userns remapping. I rebased on master once again, let's see if the tests still work (@dgtlmoon needs to approve them though). It has been a while since I created this PR, hope I didn't break anything during the rebase. |
thanks - running again and will test manually |
It seems another test tries to write to the app directory, need to have a look @dgtlmoon It's this fixture: @pytest.fixture(scope='function')
def measure_memory_usage(request):
memory_usage = {"peak": 0, "stop": False}
tracker_thread = Thread(target=track_memory, args=(memory_usage,))
tracker_thread.start()
yield
memory_usage["stop"] = True
tracker_thread.join()
# Note: ru_maxrss is in kilobytes on Unix-based systems
max_memory_used = memory_usage["peak"] / 1024 # Convert to MB
s = f"Peak memory used by the test {request.node.fspath} - '{request.node.name}': {max_memory_used:.2f} MB"
logger.debug(s)
with open("test-memory.log", 'a') as f:
f.write(f"{s}\n") I can't find a reference to Edit: If you prefer to leave it as-is, we always the option of setting |
to be honest the whole memory test thing is not needed.. you can remove it, or on another PR, it didnt really add anything interesting :) |
29afe61
to
467315d
Compare
Alright, I chose to just remove writing the file since the rest of the code does not interfere. |
agreed, no problems, some things seem like a nice idea - but just get in the way |
sometimes there are "random" fails, i'll keep an eye on it |
👍 It's still complaining that |
... to be able to install extra packages via pip
The SMTP tests were failing because |
Alright, all tests are passing now. For most setups, the entrypoint script should automatically chown the datastore to the correct user. The one exception is in case it is a network share mount, where it might fail. In this case, admins must ensure the correct In case this logic causes any problems, it can be skipped completely by setting the I'd really like to wrap this PR up soon since it has been going for nearly two and a half years. :) |
amazing thanks, will have a play with it - yeah its one of those "might break something at some time for somewhere" commits but it NEEDS to be in the codebase |
can you think of some test cases to try? like i already have a datastore owned by some user and... |
Unless otherwise noted, everything assumes 0755/0644 dir/file permissions (or 0 for others, just not 7/6). First, I would verify everything works as before when Then, with a root-owned (populated) local datastore:
Then chown -R the datastore to 1234:5678 and do 1 again. You should check that running the container rootless (still?) works with 1 (/4) (so root here is actually the local user account, PUID/PGID are relative to the user namespace). I'd probably check that correctly setup NFS/SAMBA mounts work. Tbh I'm not familiar with the application architecture, so I'm not sure if there are any other paths that should be tested.
Edit2: The entrypoint now only checks if the datastore is writable and tries to chown -R it if it's not. |
better than doing it every time, some datasets are pretty big |
This got lost during a rebase, it's not necessary anymore since the env var is set inside the container by default and changedetection respects it.
Alright, the entrypoint now checks writability of the datastore by the designated PUID:PGID combination and only intervenes if that fails. I'll adjust the proposed test cases above. |
# On Windows, create and use a default path. | ||
if os.name == 'nt': | ||
elif os.name == 'nt': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldnt this stay as if
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it would then not respect the DATASTORE_PATH
defined by the user (or the default one set during container build time).
Currently, the container process runs with root privileges. This cannot be changed, even by specifying PUID/PGID (as suggested in
docker-compose.yml
).This PR migrates to running as an unprivileged user and makes it possible to specify PUID/PGID environment variables to choose UID/GID. Migration of existing data owned by root is handled transparently.
A side-effect is that the /app directory is read-only to the python process, I'm not sure if that is a problem since I'm not familiar with the architecture of changedetection.
I also took the liberty to clean up the Dockerfile (and apt cache) a bit, hope that's not a problem. :) Please feel free to suggest changes or deny the PR, if it does not fit your vision.
Fixes #565.