Why Do I Need a Language Specific Runtime in a Lambda Container?
ACM.303 Why doesn’t Lambda just run any OCI Compliant container?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
⚙️ Part of my series on Automating Cybersecurity Metrics. The Code.
🔒 Related Stories: Container Security | Lambda | Deploying a Static Website
💻 Free Content on Jobs in Cybersecurity | ✉️ Sign up for the Email List
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In the last post I wrote about trying to test a container for a Lambda function locally, outside of Lambda.
In this post, I’m digging into why Lambda doesn’t simply run any OCI compliant container. I wrote about container engines and container runtimes in a prior post and wanted to dig into this a bit more.
Wouldn’t it be nice if you could just drop your container into Lambda and it would just run? Why doesn’t that work?
Running a container that runs commands in a Bash shell
Here’s my scenario. I want to run a container that runs some bash commands in Lambda. I created such a container but can I just upload that container to Lambda and it will work? No.
We have to include a Lambda runtime in our container, and then use the runtime to execute our code. If we don’t include a Lambda runtime in our container, when Lambda executes the container it will fail (I presume — I haven’t actually tested that). But I know it won’t work correctly as a Lambda function should. When Lambda runs the container, it is expecting certain API calls. Without those API calls, the Lambda service doesn’t function properly.
Lambda has different runtimes for different programming languages. If you are running a Lambda function, you can choose from the following runtimes:
- Node
- Python
- Java
- .NET
- Go
- Ruby
- Custom (build your own)
The specific versions are listed here:
Lambda runtimes
Learn about the languages that Lambda supports through runtimes.
docs.aws.amazon.com
If you are using a container, you specify the runtime and Linux distribution in the container image.
In order to run a bash script, I could try to include one of the Lambda runtimes for Python or Go and then try to use one of those languages to execute my bash script, but that seems like an unnecessary layer and complexity. I also don’t know if it will work due to some Lambda restrictions, missing libraries, etc. I don’t want to spend time troubleshooting all that.
I looked into using the Github API with one of the available programming languages, but it doesn’t have all the same functionality as the git command line options. What I really want to do is just create a container that executes the commands in a bash script via a Lambda function.
So what do I need to do to get a runtime to execute a bash script directly?
In order to understand all this, we need to dive into the Lambda architecture a bit more.
AWS Lambda Architecture and Lambda Runtimes
The Lambda Runtime API is an interface between your code and the Lambda service. The API is used to report back to the Lambda service and update the Lambda console, for instance.
As you can see from the picture above, a Lambda runtime communicates with Lambda APIs to report back to the Lambda service. This is basically what a Lambda runtime does:
- Executes code to initialize your function with information from the Lambda service, such as your environment variables that you deployed with your Lambda function (if any).
- Execute your function.
- Report the status of your function back to Lambda including the results of the execution.
An AWS Lambda function was intended to be literally — a function in a specific programming language. It’s a function that is written in a specific language that takes some inputs and runs and returns a value. That’s why it is language specific.
If you use a Python runtime, it’s expecting to execute a Python function. If you use a golang runtime, it is expecting to execute a Go function. The function that Lambda runs is called the handler.
It’s not intended to be an entire application with multiple function calls or multiple applications. At least that was not the initial intention, but we may be able to push some boundaries with a container. :)
What if we didn’t include a runtime in our container? If you just ran a container on Lambda with no interaction with the Lambda service, then the console would never show you any information when you executed the function. It wouldn’t show you the logs or other information we looked at in the console in the prior posts. And, it would likely just fail because Lambda is expecting certain API calls.
Lambda Runtime API
Let’s look at the Lambda Runtime API. The Lambda Runtime API has four methods:
- Next invocation: The runtime sends this message to Lambda to request an invocation event. The response body contains the payload from the invocation, which is a JSON document that contains event data from the function trigger. The response headers contain additional data about the invocation.
- Invocation response: After the function has run to completion, the runtime sends an invocation response to Lambda. For synchronous invocations, Lambda sends the response to the client.
- Initialization error: If the function returns an error or the runtime encounters an error during initialization, the runtime uses this method to report the error to Lambda.
- Invocation error: If the function returns an error or the runtime encounters an error, the runtime uses this method to report the error to Lambda.
Those are pretty self-explanatory. You can send a message to the Lambda service to update the console with the status of your function’s execution. Whatever runtime you are using needs to make the appropriate API calls.
Let’s take a look at the container we were using in the last few posts. Where was the Lambda runtime getting invoked to make those API calls? As you recall I was trying to test a container both within Lambda and locally in the various posts. If you look at the entry point to the container, you will see that it runs a script called entry.sh and passes in a parameter “app.handler”:
What’s in the entry.sh script? It’s calling the Lambda Runtime Interface Client (awslambdaric) with or without the Lambda Runtime Interface Emulator (RIE).
As you can see the container is not executing the code in the container directly that was pushed to the app folder in the last post. It is executing the Lambda runtime client and passing in the handler, or function name, that the Lambda runtime should execute. The first variable passed into the script is referenced as $1 and refers to “app.handler” passed in by the CMD line above. In the case of a local execution, the function is supposed to call the runtime emulator.
As I’m looking at this now, perhaps something was missing and the container didn’t actually follow the correct code path. What I just realized looking at this more closely is that the code is checking for a particular variable, AWS_LAMBDA_RUNTIME_API, to be set to true, in which case the container will run the local test path with the Lambda Runtime Interface Emulator.
However, I don’t see where that variable is set or used in the blog post. I figured out with further testing, that the variable gets set if your’e running the container on the Lambda platform. If you’re not, then the value isn’t set. So the code is basically saying, “If that value is not set then use the local emulator.”
Anyway, as you can see, to execute a Python function, you run the Python runtime interface client and pass in the function name. If you pass in bash to that client, it would try to execute the bash as Python and that’s not going to work out so well.
So it looks like we need a Lambda runtime that works with bash. But there isn’t one? Oh noooos. What can we do? We could create a custom Lambda runtime. A custom Lambda runtime will do all those things that the Python runtime is doing for us above, except that it will execute a bash function instead of a Python function.
Luckily, we don’t need to figure this out entirely from scratch. There’s an AWS tutorial showing how to do that.
I’m going to try that out in the next post, but I’m going to try to use it in a container.
Follow for updates.
Teri Radichel | © 2nd Sight Lab 2024
About Teri Radichel:
~~~~~~~~~~~~~~~~~~~~
⭐️ Author: Cybersecurity for Executives in the Age of Cloud
⭐️ Presentations: Presentations by Teri Radichel
⭐️ Recognition: SANS Award, AWS Security Hero, IANS Faculty
⭐️ Certifications: SANS ~ GSE 240
⭐️ Education: BA Business, Master of Software Engineering, Master of Infosec
⭐️ Company: Penetration Tests, Assessments, Phone Consulting ~ 2nd Sight Lab
Need Help With Cybersecurity, Cloud, or Appication Security?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
🔒 Request a penetration test or security assessment
🔒 Schedule a consulting call
🔒 Cybersecurity Speaker for Presenation
Follow for more stories like this:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
❤️ Medium: Teri Radichel
❤️ Sign Up For Email
❤️ Twitter: @teriradichel
❤️ Mastodon: @[email protected]
❤️ Facebook: 2nd Sight Lab
❤️ YouTube: @2ndsightlab