"Mini-Firewall" Over EC2 Machines, AKA use Lambda function to update SecGroup with your IP address
Tl;Dr — in some setups, it makes sense to allow connection to EC2 machine only to your (home laptop) IP address. If you have dynamic IP, you can use Lambda function (+API GW) to update the IP in the security group automatically.
See snippet: https://gist.github.com/yuval1024/7aa78bf2fac234d0491c1695c0b5ff99

“Mini-Firewall”? Why?
So the idea is to get an extra layer of security and allow connection to EC2 machines only from a specific IP address. Create a new machine, create a new security group, and allow connection only from the specific IP address. So far, so good.
However, what happens when the IP changes? When IP changes, you need to update the security group — login into AWS console, find the security group, update the IP address. This can be a bit annoying.
So there’s another option — update the security group automatically, using Lambda function.
Lambda function
Steps: Create a Lambda function, which gets IP address and changes the security group. Create API Gateway, which calls the Lambda function. Add 2 links to your bookmarks — enable and disable access
- Enable access — call the “Enable” Lambda function which changes the IP address to your IP address (eg caller-IP address)
- Disable access — call the “Disable” Lambda function which changes the IP address to 8.8.8.8 (eg Google DNS…)
See change_secgroup_lambda.py for the code. It was written for Python 3.8 originally, but should work with newer versions.. We have lambda_handler which is the function being called by the Lambda infrastructure; and we have update_security_group which is the function which actually updates the security group.
Manual Installing
After installing Lambda function, go to Configuration -> Triggers.
And then click on “Add Trigger” and add API gateway function for the trigger.
Then go to API Gateway, and create a new API.
Then create a new resource, and then create a new method.
Then go to “Integration Request” and set “Body Mapping Templates” to “application/json” and add the following:
{
"action": "enable",
"name": "$input.params('name')"
}
Then go to “Method Response” and add 200 response. Then go to “Integration Response” and add 200 response. Then go to “Integration Request” and add the following:
{
"body-json" : { "action" : "enable", "name" : "$input.params('name')" },
"context" : {
"account-id" : "$context.identity.accountId",
"api-id" : "$context.apiId",
"api-key" : "$context.identity.apiKey",
"authorizer-principal-id" : "$context.authorizer.principalId",
"caller" : "$context.identity.caller",
"cognito-authentication-provider" : "$context.identity.cognitoAuthenticationProvider",
"cognito-authentication-type" : "$context.identity.cognitoAuthenticationType",
"cognito-identity-id" : "$context.identity.cognitoIdentityId",
"cognito-identity-pool-id" : "$context.identity.cognitoIdentityPoolId",
"http-method" : "$context.httpMethod",
"stage" : "$context.stage",
"source-ip" : "$context.identity.sourceIp",
"user" : "$context.identity.user",
"user-agent" : "$context.identity.userAgent",
"user-arn" : "$context.identity.userArn",
"request-id" : "$context.requestId",
"resource-id" : "$context.resourceId",
"resource-path" : "$context.resourcePath"
}
}
go to “Method Execution” and click on “Test” and then “Test” again. go to “Method Execution” and click on “Deploy API” and then “Deploy” again. go to “Stages” and click on “Prod” and then “Invoke URL” and copy the URL. go to “Resources” and click on “Actions” and then “Create Method” and select “GET” and click on the checkmark. go to “Method Execution” and click on “Test” and then “Test” again.
Automatic Installing, AKA productonize code using ChatGPT/Bard
So after we created the Lambda and API manually, we would like to “productionize” it, e.g. create code to create these things. We would use AWS CLI to inspect the existing Lambda and API, and then create the code to create them.
# let's get the APIS in region, then resource for the relevant one (ID pg6hm5xzu0), then 'enable' method for that resource (ID jiv1tt)
aws apigateway get-rest-apis --region us-west-2
aws apigateway get-resources --rest-api-id pg6hm5xzu0 --region us-west-2
aws apigateway get-method --rest-api-id pg6hm5xzu0 --resource-id jiv1tt --http-method GET --region us-west-2
Now after we gave ChatGPT/Bard the JSONs to reproduce, we have commands to reproduce it:
# create
aws apigateway create-rest-api --name 'add_rule_to_sg-yuval-reprod' --description 'Created by AWS Lambda' --endpoint-configuration types=REGIONAL --api-key-source HEADER --region us-west-2
# compare
aws apigateway get-resources --rest-api-id pg6hm5xzu0 --region us-west-2
aws apigateway get-resources --rest-api-id 3p06imz4sc --region us-west-2
And we can continue to the next steps, create method etc. And also continue and use Terraform or CloudFormation to create the API and Lambda.
FAQ
Q : Why not use a VPN? Eg developer should connect to AWS VPN and policy should allow connection only from the VPN? A : This is a good solution for an SMB/Enterprise, but for a solo developer/hobbyist just playing around, it’s an overkill.
Q : There is no need for authentication when calling the API (Eg when visiting the URLs)? A : Correct, this is by design; we exchange security for convenience. Worst case, we have another layer of security — the SSH certificate required for logging in — this is the main layer of protection.