paketo-buildpacks / builder-jammy-base

A Cloud Native Buildpacks (CNB) builder with the Paketo Jammy Jellyfish Base stack and Paketo buildpacks.
Apache License 2.0
19 stars 9 forks source link

Permission problem with using Jammy builder #375

Open hirosat opened 1 year ago

hirosat commented 1 year ago

Expected Behavior

We can run the container without permission trouble in case using React APP (npm start for development). As a successful example, I will show the case of using bionic based builder.

$ pack build test-bionic --builder paketobuildpacks/builder:base
(snip..)
Successfully built image test-bionic

$ docker run -p 3000:3000 test-bionic:latest
(snip..)
Starting the development server...
(snip..)
Compiled successfully!
(snip..)
webpack compiled successfully
Compiling...
Compiled successfully!
webpack compiled successfully

Current Behavior

Failed to run the container with permission trouble when using Jammy based builder.

$ pack build test-jammy --builder paketobuildpacks/builder-jammy-base
(snip..)
Successfully built image test-jammy

$ docker run -p 3000:3000 test-jammy:latest
(snip..)
Starting the development server...
(snip..)
Failed to compile.

[eslint] EACCES: permission denied, open '/workspace/node_modules/.cache/.eslintcache'
ERROR in [eslint] EACCES: permission denied, open '/workspace/node_modules/.cache/.eslintcache'

webpack compiled with 1 error

I guess #278 may be related.

Possible Solution

I will probably go back to bionic based builder for a while for React development. However, bionic is not good for a security perspective. So I hope this issue will be fixed.

Steps to Reproduce

  1. Prepare react app
    $ npx create-react-app my-app
    (snip..)
    Happy hacking!
    $ cd my-app
    $ ls -a
    .                 .git              README.md         package-lock.json public
    ..                .gitignore        node_modules      package.json      src
  2. Just try Expected (or Current) Behavior

Motivations

I think it's a very basic scenario when we develop React App. If we can't do it, we need to go back dockerfile.

hirosat commented 1 year ago

Additional Note

The directory structure of the container at build time is created with uid1001, but the actual user is cnb(uid1002), which seems to cause problems when writing a file such as .eslintcache after startup.

$ docker exec -it 9c925c0c6253 bash
cnb@9c925c0c6253:/workspace$ id
uid=1002(cnb) gid=1000(cnb) groups=1000(cnb)
cnb@9c925c0c6253:/workspace$ ls -al
total 732
drwxrwxrwx 6 1001 cnb    4096 Jan  1  1980 .
drwxr-xr-x 1 root root   4096 Oct 24 15:51 ..
-rw-r--r-- 1 1001 cnb      27 Jan  1  1980 .env
drwxr-xr-x 7 1001 cnb    4096 Jan  1  1980 .git
-rw-r--r-- 1 1001 cnb    2230 Jan  1  1980 .gitignore
-rw-r--r-- 1 1001 cnb    3359 Jan  1  1980 README.md
drwxr-xr-x 3 1001 cnb    4096 Jan  1  1980 build
lrwxrwxrwx 1 1001 cnb      69 Jan  1  1980 node_modules -> /tmp/4d6b8328f31f0ade280f708051b532cf52e67649547d1c558ce7df2ecb94a806
-rw-r--r-- 1 1001 cnb  698792 Jan  1  1980 package-lock.json
-rw-r--r-- 1 1001 cnb     821 Jan  1  1980 package.json
drwxr-xr-x 2 1001 cnb    4096 Jan  1  1980 public
drwxr-xr-x 2 1001 cnb    4096 Jan  1  1980 src
-rw-r--r-- 1 1001 cnb     130 Jan  1  1980 start.sh
9c925c0c6253:/workspace$ ls -al /workspace/node_modules/.cache/.eslintcache
-rw-r--r-- 1 1001 cnb 1089 Jan  1  1980 /workspace/node_modules/.cache/.eslintcache
c0d1ngm0nk3y commented 1 year ago

@hirosat Thanks for the for the perfect steps to reproduce :)

For bionic, the build und the run user were the same (uid 1000). This changed with jammy and the build (uid 1001) and the run user (uid 1002) are different. Unfortunately, no writable .cache folder exists, so when eslint tries to create it, it fails due to permission.

workaround: You could run the container with the build user, e.g. docker run -u 1001 -p 3000:3000 test-jammy:latest

possible solution: It sounds to me as if a .cache folder should already exist and it should be writable for the group cnb. I will look into this.

hirosat commented 1 year ago

@c0d1ngm0nk3y Thank you for your reply! Your explanation was very helpful. However, if the expected run user is cnb (uid 1002), is it possible to change the directory's owner to the last step of the builder process, like the following?:

$ chown -R 1002 /workspace

Or it would be more appropriate if the build user could also be configured with id1002.

c0d1ngm0nk3y commented 1 year ago

@hirosat Having the whole workspace writable for the run user would somehow be against the original idea of using different users for build and run in the first place.

Although, the same problem should occur when using a r/o root file system. The question is WHY eslint needs to run at runtime and not only at buildtime. Is it not "only" a linter?

Another question, you can help me with. How/when did you decide to change from bionic to jammy. Because I think the usage of bionic is quite hidden and we could/should help there.

hirosat commented 1 year ago

@c0d1ngm0nk3y

Having the whole workspace writable for the run user would somehow be against the original idea of using different users for build and run in the first place.

I think the actual problem is that the runuser is not an expected user ID. I suggested chown than chmod because where it is writable is not a current issue.

Although, the same problem should occur when using a r/o root file system. The question is WHY eslint needs to run at runtime and not only at buildtime. Is it not "only" a linter?

Usecases for react: For development, it is likely to use npm start on the local environment to start the Webpack Dev Server and enable real-time debugging. The eslint is used in this case, and a cache is written to re-test based on changes in the source code. For production, it is likely to use npm build to render static files and copy these files to a web server, such as apache or nginx.

Why: To share a prototype frontend code with others, it is preferable to be able to build by using a development way, rather than preparing such a web server. Essentially, eslint is unnecessary in such cases, but it is a pain to consider a workaround for it. BTW, I'm not sure "only" a linter is need writeble.

Another question, you can help me with. How/when did you decide to change from bionic to jammy. Because I think the usage of bionic is quite hidden and we could/should help there.

I was decided by using pack builder suggest. Previously bionic(paketobuildpacks/builder:base) was suggested and a few months ago it was changed to jammy.

c0d1ngm0nk3y commented 1 year ago

Thanks for the explanation.

Having the whole workspace writable for the run user would somehow be against the original idea of using different users for build and run in the first place.

I think the actual problem is that the runuser is not an expected user ID. I suggested chown than chmod because where it is writable is not a current issue.

Actually i do think that the run user has the expected user id and the node_modules do not have the permissions to be changed by the run user (only by build user).

The permissions of these symlinks in between might be confusing.

cnb@6242c05f0a37:/workspace$ id
uid=1002(cnb) gid=1000(cnb) groups=1000(cnb)
cnb@6242c05f0a37:/workspace$ ls -la node_modules
lrwxrwxrwx 1 1001 cnb 69 Jan  1  1980 node_modules -> /tmp/4d6b8328f31f0ade280f708051b532cf52e67649547d1c558ce7df2ecb94a806
cnb@6242c05f0a37:/workspace$ ls -la /tmp/4d6b8328f31f0ade280f708051b532cf52e67649547d1c558ce7df2ecb94a806
lrwxrwxrwx 1 cnb cnb 65 Oct 31 08:31 /tmp/4d6b8328f31f0ade280f708051b532cf52e67649547d1c558ce7df2ecb94a806 -> /layers/paketo-buildpacks_npm-install/launch-modules/node_modules
cnb@6242c05f0a37:/workspace$ ls -la /layers/paketo-buildpacks_npm-install/launch-modules
total 52
drwxr-xr-x   5 1001 cnb  4096 Jan  1  1980 .
drwxr-xr-x   3 1001 cnb  4096 Jan  1  1980 ..
drwxr-xr-x   2 1001 cnb  4096 Jan  1  1980 env.launch
drwxr-xr-x   2 1001 cnb  4096 Jan  1  1980 exec.d
drwxr-xr-x 854 1001 cnb 36864 Jan  1  1980 node_modules

Although, the same problem should occur when using a r/o root file system. The question is WHY eslint needs to run at runtime and not only at buildtime. Is it not "only" a linter?

Usecases for react: For development, it is likely to use npm start on the local environment to start the Webpack Dev Server and enable real-time debugging. The eslint is used in this case, and a cache is written to re-test based on changes in the source code. For production, it is likely to use npm build to render static files and copy these files to a web server, such as apache or nginx.

Why: To share a prototype frontend code with others, it is preferable to be able to build by using a development way, rather than preparing such a web server. Essentially, eslint is unnecessary in such cases, but it is a pain to consider a workaround for it. BTW, I'm not sure "only" a linter is need writeble.

Thanks for the explanation. Yes, I agree that this seems not to be specific to eslint. I was just wondering why a linter comes up in this.

Another question, you can help me with. How/when did you decide to change from bionic to jammy. Because I think the usage of bionic is quite hidden and we could/should help there.

I was decided by using pack builder suggest. Previously bionic(paketobuildpacks/builder:base) was suggested and a few months ago it was changed to jammy.

Thanks.

hirosat commented 1 year ago

Maybe I misunderstood. So you are intentionally making node_modules read-only and intentionally mounting them as a different user? In general, I thought that node_modules is an area that can be managed by the user(cnb), so what is the reason for making it read-only like that?

c0d1ngm0nk3y commented 1 year ago

Got it.

The idea of splitting the run and build user is originated here.

@paketo-buildpacks/nodejs-maintainers Having a group writable node_module folder doesn't sound right to me. Why should the run user want/need to change it. It is an image after all. WDYT?

But the node_nodules/.cache folder is a bit different and seems to be standard to be used. Any concerns in making is group writable?

I guess this use case will just not work with a readonly root filesystem, but I don't see a chance.

hirosat commented 1 year ago

Now I understood the background. Thank you for your kindly explanation. Yes. I would be happy if you could make the group writable for all configurations under the node_nodules/.cache directory, at least in this case. Thank you in advance.

hirosat commented 11 months ago

@c0d1ngm0nk3y Would you fix it to close this ticket?

hirosat commented 9 months ago

@c0d1ngm0nk3y Any updates?

c0d1ngm0nk3y commented 7 months ago

@hirosat Sorry for the VERY late reply :( I created a draft pr for the fix, I just wanna check the approach with the maintainers first before I clean the pr up.

But basically, the build would always create the .cache folder as symlink to tmp, so it would work for a different user as well as when a read-only file-system is used.

c0d1ngm0nk3y commented 1 month ago

Fixed with the next release of npm-install/nodejs buildpack