Skip to content
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

[BUG] Cannot initialize Playwright in rootless container #2770

Closed
1 task done
mu88 opened this issue Nov 28, 2023 · 14 comments · Fixed by #2782
Closed
1 task done

[BUG] Cannot initialize Playwright in rootless container #2770

mu88 opened this issue Nov 28, 2023 · 14 comments · Fixed by #2782
Labels

Comments

@mu88
Copy link

mu88 commented Nov 28, 2023

System info

  • Playwright Version: v1.40.0
  • Operating System: Docker (both on Windows using WSL2 or on a Raspberry Pi)
  • Browser: Chromium
  • Other info: /

Source code

  • I provided exact source code that allows reproducing the issue locally.

Link to the GitHub repository with the repro

https://github.com/mu88/ScreenshotCreator/tree/feature/repro-Playwright

Test file (self-contained)

/

Steps

  • Checkout the branch feature/repro-Playwright from https://github.com/mu88/ScreenshotCreator/
  • Run docker build --platform linux/amd64 -f .\src\ScreenshotCreator.Api\Dockerfile -t screenshotcreator:dev .
  • Run docker run --name screenshotcreator -p:8080:8080 screenshotcreator:dev

Expected

The app starts without errors.

Actual

Starting the app fails with the following error:

fail: Microsoft.Extensions.Hosting.Internal.Host[11]
      Hosting failed to start
      System.ComponentModel.Win32Exception (13): An error occurred trying to start process '/app/.playwright/node/linux-x64/playwright.sh' with working directory '/app'. Permission denied
         at System.Diagnostics.Process.ForkAndExecProcess(ProcessStartInfo startInfo, String resolvedFilename, String[] argv, String[] envp, String cwd, Boolean setCredentials, UInt32 userId, UInt32 groupId, UInt32[] groups, Int32& stdinFd, Int32& stdoutFd, Int32& stderrFd, Boolean usesTerminal, Boolean throwOnNoExec)
         at System.Diagnostics.Process.StartCore(ProcessStartInfo startInfo)
         at Microsoft.Playwright.Transport.StdIOTransport.StartProcessWithUTF8IOEncoding(Process process) in /_/src/Playwright/Transport/StdIOTransport.cs:line 167
         at Microsoft.Playwright.Transport.StdIOTransport..ctor() in /_/src/Playwright/Transport/StdIOTransport.cs:line 48
         at Microsoft.Playwright.Playwright.CreateAsync() in /_/src/Playwright/Playwright.cs:line 44
         at ScreenshotCreator.Logic.PlaywrightHelper.InitializePlaywrightAsync() in /src/src/ScreenshotCreator.Logic/PlaywrightHelper.cs:line 43

Comments

When either adding RUN chmod -R 777 /app (see here) or using a root container, everything works as expected.

mu88 added a commit to mu88/ScreenshotCreator that referenced this issue Nov 28, 2023
@mxschmitt
Copy link
Member

Looks like your user has no permissions to access /app. Have you tried the following instead of chmod?

RUN chown -R $APP_UID /app

This will give your user permission to /app.

@mu88
Copy link
Author

mu88 commented Dec 1, 2023

Correct me if I'm wrong, but doesn't chown change a directory's ownership, and therefore theoretically I could manipulate the /app directory using the app user? If yes, I shouldn't do that according to what I learned here:

That's kind of counter to the purpose of using a non-root user - the goal is that nothing in the application's binaries' directory can be changed.

-- @baronfel

@mxschmitt
Copy link
Member

I see! So the executable bit of /app/.playwright/node/linux-x64/playwright.sh seems not to be set correctly.

Investigation:

When we download the drivers before creating the nuget files we seem to be doing the right thing:

❯ ls -la src/Playwright/.drivers/linux/playwright.sh                 
-rwxr-xr-x  1 maxschmitt  staff  216 Nov 16 22:54 src/Playwright/.drivers/linux/playwright.sh

While on the user side the executable bit is only set for user:

❯ ls -la bin/Debug/net8.0/.playwright/node/darwin-arm64/playwright.sh
-rwxr--r--  1 maxschmitt  staff  222 Nov 21 12:58 bin/Debug/net8.0/.playwright/node/darwin-arm64/playwright.sh

Workaround would be RUN chmod +x /app/.playwright/node/linux-x64/playwright.sh instead of the chown/chmod which is already better while we figure out a better solution.

cc @campersau looks like when we pack the bit gets filtered out?

@mxschmitt mxschmitt added v1.41 and removed triaging labels Dec 1, 2023
@campersau
Copy link
Contributor

The problem is that NuGet pack and also extract does not preserve the executable bit, in fact any "external" attribute is ignored.
I have only seen an issue about the readonly attribute in the NuGet repro though: NuGet/Home#11286
So maybe you can open an issue for it as well?

It might be possible to workaround it by adding some more logic to Microsoft.Playwright.targets and set it manually there but it is not that easy.


NuGet uses the ZipArchive for packing/extracting the nupkg. And only "recently" APIs to get/set "external" attributes like the execution bit were made available:
https://apisof.net/catalog/a6f34fac-094f-db3b-e4bc-a1c7ed169602

Pack and extract only tries to preserve timestamps.

Pack:
https://github.com/NuGet/NuGet.Client/blob/6e01722cb9d4ede0baa3edc5131a90fe71b6c5fa/src/NuGet.Core/NuGet.Packaging/PackageCreation/Authoring/PackageBuilder.cs#L1011-L1031
https://github.com/NuGet/NuGet.Client/blob/6e01722cb9d4ede0baa3edc5131a90fe71b6c5fa/src/NuGet.Core/NuGet.Packaging/PackageCreation/Authoring/PackageBuilder.cs#L1223-L1237

Extract: https://github.com/NuGet/NuGet.Client/blob/6e01722cb9d4ede0baa3edc5131a90fe71b6c5fa/src/NuGet.Core/NuGet.Packaging/PackageExtraction/ZipArchiveExtensions.cs#L57-L66

@bbhxwl
Copy link

bbhxwl commented Jan 5, 2024

image

@mu88
Copy link
Author

mu88 commented Jan 29, 2024

@mxschmitt would it be possible to add chmod +x /app/.playwright/node/*/node as well? When using a rootless container, the permissions inside the container currently look like this:

app@d9c7ae175924:/app$ ls -lh .playwright/node/linux-x64/
total 92M
-rwxr--r-- 1 root root 92M Jan 19 23:07 node

...which leads to the following error when running my rootless container:

      System.ComponentModel.Win32Exception (13): An error occurred trying to start process '/app/.playwright/node/linux-x64/node' with working directory '/app'. Permission denied
         at System.Diagnostics.Process.ForkAndExecProcess(ProcessStartInfo startInfo, String resolvedFilename, String[] argv, String[] envp, String cwd, Boolean setCredentials, UInt32 userId, UInt32 groupId, UInt32[] groups, Int32& stdinFd, Int32& stdoutFd, Int32& stderrFd, Boolean usesTerminal, Boolean throwOnNoExec)
         at System.Diagnostics.Process.StartCore(ProcessStartInfo startInfo)
         at Microsoft.Playwright.Transport.StdIOTransport.StartProcessWithUTF8IOEncoding(Process process) in /_/src/Playwright/Transport/StdIOTransport.cs:line 168
         at Microsoft.Playwright.Transport.StdIOTransport..ctor() in /_/src/Playwright/Transport/StdIOTransport.cs:line 47
         at Microsoft.Playwright.Playwright.CreateAsync() in /_/src/Playwright/Playwright.cs:line 44
         at ScreenshotCreator.Logic.PlaywrightHelper.InitializePlaywrightAsync() in /src/src/ScreenshotCreator.Logic/PlaywrightHelper.cs:line 36
         at ScreenshotCreator.Logic.ScreenshotCreator.CreateScreenshotAsync(UInt32 width, UInt32 height) in /src/src/ScreenshotCreator.Logic/ScreenshotCreator.cs:line 14
         at ScreenshotCreator.Api.BackgroundScreenshotCreator.ExecuteAsync(CancellationToken stoppingToken) in /src/src/ScreenshotCreator.Api/BackgroundScreenshotCreator.cs:line 32
         at Microsoft.Extensions.Hosting.Internal.Host.<StartAsync>b__15_1(IHostedService service, CancellationToken token)
         at Microsoft.Extensions.Hosting.Internal.Host.ForeachService[T](IEnumerable`1 services, CancellationToken token, Boolean concurrent, Boolean abortOnFirstException, List`1 exceptions, Func`3 operation)
Unhandled exception. System.ComponentModel.Win32Exception (13): An error occurred trying to start process '/app/.playwright/node/linux-x64/node' with working directory '/app'. Permission denied
   at System.Diagnostics.Process.ForkAndExecProcess(ProcessStartInfo startInfo, String resolvedFilename, String[] argv, String[] envp, String cwd, Boolean setCredentials, UInt32 userId, UInt32 groupId, UInt32[] groups, Int32& stdinFd, Int32& stdoutFd, Int32& stderrFd, Boolean usesTerminal, Boolean throwOnNoExec)
   at System.Diagnostics.Process.StartCore(ProcessStartInfo startInfo)
   at Microsoft.Playwright.Transport.StdIOTransport.StartProcessWithUTF8IOEncoding(Process process) in /_/src/Playwright/Transport/StdIOTransport.cs:line 168
   at Microsoft.Playwright.Transport.StdIOTransport..ctor() in /_/src/Playwright/Transport/StdIOTransport.cs:line 47
   at Microsoft.Playwright.Playwright.CreateAsync() in /_/src/Playwright/Playwright.cs:line 44

This error disappears when using (see here):

FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
RUN chmod +x /app/.playwright/node/*/node
ENTRYPOINT ["dotnet", "ScreenshotCreator.Api.dll"]

@mxschmitt
Copy link
Member

We actually got rid of the playwright.sh file and do chmod +x to the node executable:

<Target Name="PlaywrightChmodExecutables" AfterTargets="CopyFilesToOutputDirectory;CopyFilesToPublishDirectory" Condition="!$([MSBuild]::IsOSPlatform('Windows'))">
<ItemGroup>
<_PlaywrightChmodItems Include="$([MSBuild]::EnsureTrailingSlash('$(OutputPath)')).playwright\node\*\node" />
<_PlaywrightChmodItems Include="$([MSBuild]::EnsureTrailingSlash('$(PublishDir)')).playwright\node\*\node" />
</ItemGroup>
<Exec Command="chmod +x &quot;%(_PlaywrightChmodItems.FullPath)&quot;" />
</Target>

Are you on 1.41.2?

@bbhxwl
Copy link

bbhxwl commented Jan 30, 2024

We actually got rid of the playwright.sh file and do chmod +x to the node executable:

<Target Name="PlaywrightChmodExecutables" AfterTargets="CopyFilesToOutputDirectory;CopyFilesToPublishDirectory" Condition="!$([MSBuild]::IsOSPlatform('Windows'))">
<ItemGroup>
<_PlaywrightChmodItems Include="$([MSBuild]::EnsureTrailingSlash('$(OutputPath)')).playwright\node\*\node" />
<_PlaywrightChmodItems Include="$([MSBuild]::EnsureTrailingSlash('$(PublishDir)')).playwright\node\*\node" />
</ItemGroup>
<Exec Command="chmod +x &quot;%(_PlaywrightChmodItems.FullPath)&quot;" />
</Target>

Are you on 1.41.2?

1.40.0

@mxschmitt
Copy link
Member

We actually got rid of the playwright.sh file and do chmod +x to the node executable:

<Target Name="PlaywrightChmodExecutables" AfterTargets="CopyFilesToOutputDirectory;CopyFilesToPublishDirectory" Condition="!$([MSBuild]::IsOSPlatform('Windows'))">
<ItemGroup>
<_PlaywrightChmodItems Include="$([MSBuild]::EnsureTrailingSlash('$(OutputPath)')).playwright\node\*\node" />
<_PlaywrightChmodItems Include="$([MSBuild]::EnsureTrailingSlash('$(PublishDir)')).playwright\node\*\node" />
</ItemGroup>
<Exec Command="chmod +x &quot;%(_PlaywrightChmodItems.FullPath)&quot;" />
</Target>

Are you on 1.41.2?

1.40.0

Could you try 1.41.2?

@mu88
Copy link
Author

mu88 commented Jan 30, 2024

I'm on 1.41.2 (see here)

@mu88
Copy link
Author

mu88 commented Feb 5, 2024

Do you have any idea what the problem might be, @mxschmitt ?

@mxschmitt
Copy link
Member

Some guesses would be that the user has no permission to run the driver aka. Node.js. Maybe the permission bits get lost when using FROM inside Docker? I would try tracking it backwards to see when the executable bit got lost. Or maybe try running it as root first, so it might be an ownership problem instead?

@mu88
Copy link
Author

mu88 commented Feb 9, 2024

Thx for that hint! You're right: when running COPY in a Dockerfile, the 'execute' bit gets lost (see here).
I was able to resolve the issue by moving the chmod command into my base image (see here) and configure PLAYWRIGHT_DRIVER_SEARCH_PATH. Now I'm even able to use the .NET SDK Container Building Tools for building the app image (see here).

Thx for your help and take care 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants