serverless: Windows: Uploaded binaries cannot be executed

Attaching a binary file to the project on the Windows platform should behave similarly to Linux, in which that given the correct permissions on the file it should be executable by the Lambda server (or similar Serverless binary compatible server)

A workaround for this issue is to use Bash for Windows to zip the project (with the file given 777) and upload manually

  • What did you expect should have happened? The file attached should be executable by Lambda (or similar Serverless binary compatible server)

Although I expect it to be executable the way Windows defines what is/is not executable is different to Linux, so the permissions on the file may be harder to utilise, a proposed idea is to have a configuration file marking files/folders as executable

  • What was the config you used? N/A
  • What stacktrace or error message from your provider did you see? [Error: spawn EACCES] code: 'EACCES', errno: 'EACCES', syscall: 'spawn' when trying to launch the process

Similar or dependent issues: #1631

Proposed solution

(Added by maintainers after investigating the matter)

  1. As proposed below in https://github.com/serverless/serverless/issues/3557#issuecomment-315525767 by @ilanc, when packaging on Windows ensure each packaged file is executable (as we’re packaging for environment with already properly restricted access, so there seems no risks in doing that) File permissions are setup here: https://github.com/serverless/serverless/blob/03940254385e138eb40f2f25bd56fcdbee0c3a22/lib/plugins/package/lib/zipService.js#L105-L109

  2. Revert tweak introduced here https://github.com/serverless/serverless/pull/5813 for golang runtime. It’s hacky and obsolete with above solution

PR’s welcome!

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 2
  • Comments: 33 (7 by maintainers)

Commits related to this issue

Most upvoted comments

Some kind of workaround is really needed because go applications can not be used if deployed using win. Execution fails: { "errorMessage": "fork/exec /var/task/bin/main: permission denied", "errorType": "PathError" } executable option is good but not flexible I would suggest mode:

package:
  include:
    - some-shared-lib.js
    - sox
  mode: 0744 # change mode for all files
  mode: 
    0644:
      - sox # change mode per file

I completely agree with you, but having to build on a foreign system without a workaround feels pretty gross and mandating more complex build/deployment for it to work is painful (whereas right now you can just start working on something small and simple and get it up and running easily)

I’d say the way executable are currently being handled is equally insecure as right now it is unclear what the serverless deploy command is doing, if I was working on a developer machine to do my deployments, I would often have “looser” file permissions than I would on a normal build server/production environment, it would be easy to accidentally introduce something as too permissive

My proposed idea would be more to make everything non-executable (even if ithe file is 777 or similar on the deployment machine) unless specified in the config, if worried you could even remove globbing and just have listed files

For example:

package:
  include:
    - some-shared-lib.js
    - sox
   executable:
    - sox

It would make deployments more consistent regardless of the operating system running the deployment, and in general going out of your way to make something more permissible is often more secure (if that is a concern)

I’d like to engage with the Serverless community on this issue. This issue has come up in the serverless-chrome project from developers deploying from Windows. We have a thread tracking the issue as it relates to the project here, but we’re a bit stuck. Our solutions so far:

  • Provide a Docker image to deploy from — equal parts cool and lame
  • Hack Serverless’ zipService.js to chmod files as executable when added to the zip during packaging. This one is perhaps relevant to this Issue thread—if we knew when we should set the file as executable. Kind of what what @mattisdada is suggesting.

What’s the community’s opinion on adding a package.executable configuration options to serverless.yml?

@medikoo considering the issue is opened for a while already, I’ll take a chance to work on this.

BTW this appears to be the solution which docker uses, when building docker images on my windows box i get the following warning:

SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.

Windows file permissions don’t have rwx like unix does. So there’s always a problem when you zip on windows and try unzip to unix and want a certain permission mode to result. In particular windows gets the exec permission wrong, so zipping unix binaries on windows and unzipping on unix gives you non-executable binaries. Most zip libraries have encountered this in the past - see archiver, JSZip, gulp-zip - and have their own workarounds.

In serverless-chrome’s case you need serverless-chrome.zip\headless-chrome\headless_shell to be unzipped into a lambda with +x permissions. With the existing serverless code (and windows pokey permission system) you wind up with rw- i.e. 666 (or perhaps 644). My hack was to make serverless set the file permission on all files inserted into the zip to have 777 permissions. It means that all of these will be rwx inside the lambda:

  • serverless-chrome.zip\handler.js
  • serverless-chrome.zip\headless-chrome\headless_shell
  • serverless-chrome.zip\headless-chrome\libosmesa.so

This isn’t a problem in serverless-chrome’s case but it may impact other serverless apps which need to preserve permissions. NB Given that you cannot set unix file permissions on a windows dev machine I would say that anyone who is using windows as a dev platform doesn’t have this requirement.

Hence the quickest hack for windows dev and deployment seems to be to have a check in serverless\lib\plugins\package\lib\zipService.js to say:

  1. if unix - then use file permissions normally
  2. else if windows - always set to 777

Summary:

  • As things currently stand with serverless - you can’t use windows as your development platform whenever you have unix executables to deploy (like headless_shell)
  • Using the proposed hack - windows dev is possible but all files will have exec permission even when they’re not executable. AFAIK this doesn’t seem to cause any problems.

Amazon provides a tool for zipping the files on a windows machine so they work in lambda. Could serverless just be modified to use this tool when zipping on windows? https://github.com/aws/aws-lambda-go

Is this really something that can be done without adding much techical debt? I think an operating system that has no knowledge of UNIX access permissions cannot be mapped easily to UNIX.

The above mentioned idea, specifying rights for folders might open severe security issues as the executable bit as well as the world-readable/writable would be applied without control. How much AWS will pretend that the Lambda environment is safe itself, copying files/folders without proper permissions there is a risk.

Imo deployments that require special UNIX specific access rights should be done on a compatible system (e.g. developing locally on Windows, but leaving the infrastructure - CI/CD - on a Linux based machine). As Git supports umask flags it is no problem to have the binaries checked in correctly on Windows systems and checked out and handled correctly on the build server.

Just my 2 cents.

I’ve outlined an implementation proposal in main readme, through which we should tackle this issue finally.

PR’s welcome!

So, for golang on windows support (#5813) I partially did this. Maybe that code can be extended to read from a list of files in package.executables(or something) to chmod +x while zipping.

There is no hack required. My docker solution above gives you a command line that is 100% compatible with serverless deployments.

@afkhalid that was an older solution - it involves forking the serverless source and changing the way it uses archiver. I abandoned that idea in favour of my new approach which is to put all dependencies on S3 and manually copy the files into the lambda when the function starts up. That way you can control the permissions and you also reduce the size of your deployment bundle. Replace with google or azure equivalents if you’re not on AWS.

If you want to hack the serverless source I made these changes - but bear in mind this is out of date, I haven’t looked at the serverless source in a while: https://github.com/ilanc/serverless/commit/fa3b00f2255bc9693fd9b7238c3524ed9d3d317f

I hit this as well.

The upstream aws-lambda-go documentation has a special tool: build-lambda-zip to help Windows folks build a correct zip file. Maybe serverless can reuse that?

Tested on Windows 10, serverless 1.28.0:

# Have to build by hand because I don't have `make` on Windows.
> echo $Env:GOOS
linux
> go build -ldflags="-s -w" -o bin/world .\world\
> go build -ldflags="-s -w" -o bin/hello .\hello\

# Deploy, so far so good...
> serverless deploy
... successful ...

# But cannot run it:
> serverless invoke -f hello -l
{
    "errorMessage": "fork/exec /var/task/bin/hello: permission denied",
    "errorType": "PathError"
}
...

In the end I used https://github.com/rayhaanq/win-go-zipper, a zipper written in go, but which compiles to an EXE. This zipper gives the right permissions. This means that I first build the lambdas with the go compiler and then zip each. Finally when I deploy them with serverless.yml, I use artifacts. For example

myfunction: package: artifact: myfunction.zip
handler: bin/…

Is there an actual solution for the problem of uploading go binaries from Windows to AWS. I too am getting the error { "errorMessage": "fork/exec /var/task/bin/main: permission denied", "errorType": "PathError" }. My executables permissions inside the zip is rw, whereas the file’s permission outside of the zip (prior to zipping) is rwx

I think if someone gets remote access to your lambda you’ve got bigger problems to worry about than whether the handler.js is writable. That said you could just as easily default to 744 instead of 777.