├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── img ├── provide.png └── referenced.png ├── lambda1 ├── build.sh ├── check.sh ├── config.sh ├── layer.zip ├── publish.sh ├── test.sh └── test │ └── index.js └── lambda2 ├── build.sh ├── check.sh ├── config.sh ├── layer.zip ├── publish.sh ├── test.sh └── test └── index.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: mhart 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | layer 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Michael Hart and LambCI contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Git (w/ ssh) binaries for AWS Lambda 2 | 3 | A [layer](https://aws.amazon.com/about-aws/whats-new/2018/11/aws-lambda-now-supports-custom-runtimes-and-layers/) 4 | for AWS Lambda that allows your functions to use `git` and `ssh` binaries. 5 | 6 | ## Getting Started 7 | 8 | You can add this layer to any Lambda function you want. 9 | `PATH` already includes `/opt/bin` in Lambda, which is where it will be mounted. 10 | 11 | Click on Layers and choose "Add a layer", and "Provide a layer version 12 | ARN" and enter the following ARN (replace `us-east-1` with the region of your Lambda): 13 | 14 | ``` 15 | arn:aws:lambda:us-east-1:553035198032:layer:git-lambda2:8 16 | ``` 17 | 18 | *Note:* If you're using an older runtime, such as `python2.7`, `python3.6` ,`python3.7`, `ruby2.5`, `java8`, `go1.x`, `dotnetcore2.1` 19 | or `provided` (custom runtime), then you'll need to use a slightly different layer: 20 | 21 | ``` 22 | arn:aws:lambda:us-east-1:553035198032:layer:git:14 23 | ``` 24 | 25 | (again, replacing `us-east-1` with the region of your Lambda) 26 | 27 | ![Provide layer ARN](https://raw.githubusercontent.com/lambci/git-lambda-layer/master/img/provide.png "Provide layer ARN screenshot") 28 | 29 | Then click Add, save your lambda and test it out! 30 | 31 | ![Referenced layers](https://raw.githubusercontent.com/lambci/git-lambda-layer/master/img/referenced.png "Referenced layer ARN screenshot") 32 | 33 | ## Simple example on Node.js w/ https 34 | 35 | ```js 36 | const { execSync } = require('child_process') 37 | 38 | exports.handler = async(event) => { 39 | execSync('rm -rf /tmp/*', { encoding: 'utf8', stdio: 'inherit' }) 40 | 41 | execSync('cd /tmp && git clone https://github.com/mhart/aws4', { encoding: 'utf8', stdio: 'inherit' }) 42 | 43 | return execSync('ls /tmp/aws4', { encoding: 'utf8' }).split('\n') 44 | } 45 | ``` 46 | 47 | ## Complex example on Node.js w/ ssh 48 | 49 | ```js 50 | const fs = require('fs') 51 | const { execSync } = require('child_process') 52 | 53 | exports.handler = async(event) => { 54 | execSync('rm -rf /tmp/*', { encoding: 'utf8', stdio: 'inherit' }) 55 | 56 | fs.writeFileSync('/tmp/known_hosts', 'github.com,192.30.252.*,192.30.253.*,192.30.254.*,192.30.255.* ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==') 57 | 58 | // Get this from a safe place, say SSM 59 | fs.writeFileSync('/tmp/id_rsa', `-----BEGIN RSA PRIVATE KEY----- 60 | ... 61 | -----END RSA PRIVATE KEY----- 62 | `) 63 | execSync('chmod 400 /tmp/id_rsa', { encoding: 'utf8', stdio: 'inherit' }) 64 | 65 | process.env.GIT_SSH_COMMAND = 'ssh -o UserKnownHostsFile=/tmp/known_hosts -i /tmp/id_rsa' 66 | 67 | execSync('git clone --depth 1 ssh://git@github.com/mhart/aws4.git /tmp/aws4', { encoding: 'utf8', stdio: 'inherit' }) 68 | 69 | return execSync('ls /tmp/aws4', { encoding: 'utf8' }).split('\n') 70 | } 71 | ``` 72 | `ssh` always tries to create a `.ssh` directory – this is something you can't avoid, nor can you specify your own destination for this – which means you'll see a warning similar to the following: 73 | ``` 74 | Could not create directory '/home/sbx_user1075/.ssh'. 75 | ``` 76 | You can ignore this warning – `ssh` should continue to execute past this point, assuming you have the `UserKnownHostsFile` option correct and it contains the signature of the host you're trying to connect to. Alternatively, you can use `-o StrictHostKeyChecking=no` if you're not concerned about MiTM attacks. 77 | 78 | ## Version ARNs for [Amazon Linux 2 runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html) 79 | 80 | | Git version | openssh version | ARN | 81 | | --- | --- | --- | 82 | | 2.29.0| OpenSSH_7.4p1, OpenSSL 1.0.2k-fips | `arn:aws:lambda::553035198032:layer:git-lambda2:8` | 83 | | 2.28.0| OpenSSH_7.4p1, OpenSSL 1.0.2k-fips | `arn:aws:lambda::553035198032:layer:git-lambda2:7` | 84 | | 2.26.2| OpenSSH_7.4p1, OpenSSL 1.0.2k-fips | `arn:aws:lambda::553035198032:layer:git-lambda2:6` | 85 | | 2.26.1 | OpenSSH_7.4p1, OpenSSL 1.0.2k-fips | `arn:aws:lambda::553035198032:layer:git-lambda2:5` | 86 | | 2.25.0 | OpenSSH_7.4p1, OpenSSL 1.0.2k-fips | `arn:aws:lambda::553035198032:layer:git-lambda2:4` | 87 | | 2.24.1 | OpenSSH_7.4p1, OpenSSL 1.0.2k-fips | `arn:aws:lambda::553035198032:layer:git-lambda2:3` | 88 | | 2.24.0 | OpenSSH_7.4p1, OpenSSL 1.0.2k-fips | `arn:aws:lambda::553035198032:layer:git-lambda2:2` | 89 | | 2.23.0 | OpenSSH_7.4p1, OpenSSL 1.0.2k-fips | `arn:aws:lambda::553035198032:layer:git-lambda2:1` | 90 | 91 | ## Version ARNs for all other runtimes 92 | 93 | | Git version | openssh version | ARN | 94 | | --- | --- | --- | 95 | | 2.29.0 | OpenSSH_7.4p1, OpenSSL 1.0.2k-fips | `arn:aws:lambda::553035198032:layer:git:14` | 96 | | 2.28.0 | OpenSSH_7.4p1, OpenSSL 1.0.2k-fips | `arn:aws:lambda::553035198032:layer:git:13` | 97 | | 2.26.2 | OpenSSH_7.4p1, OpenSSL 1.0.2k-fips | `arn:aws:lambda::553035198032:layer:git:12` | 98 | | 2.26.1 | OpenSSH_7.4p1, OpenSSL 1.0.2k-fips | `arn:aws:lambda::553035198032:layer:git:11` | 99 | | 2.25.0 | OpenSSH_7.4p1, OpenSSL 1.0.2k-fips | `arn:aws:lambda::553035198032:layer:git:10` | 100 | | 2.24.1 | OpenSSH_7.4p1, OpenSSL 1.0.2k-fips | `arn:aws:lambda::553035198032:layer:git:9` | 101 | | 2.24.0 | OpenSSH_7.4p1, OpenSSL 1.0.2k-fips | `arn:aws:lambda::553035198032:layer:git:8` | 102 | | 2.23.0 | OpenSSH_7.4p1, OpenSSL 1.0.2k-fips | `arn:aws:lambda::553035198032:layer:git:7` | 103 | | 2.21.0 | OpenSSH_7.4p1, OpenSSL 1.0.2k-fips | `arn:aws:lambda::553035198032:layer:git:6` | 104 | | 2.20.0 | OpenSSH_6.6.1p1, OpenSSL 1.0.1k-fips | `arn:aws:lambda::553035198032:layer:git:3` | 105 | | 2.19.2 | OpenSSH_6.6.1p1, OpenSSL 1.0.1k-fips | `arn:aws:lambda::553035198032:layer:git:2` | 106 | -------------------------------------------------------------------------------- /img/provide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambci/git-lambda-layer/3b4d3592afb537be4c1e291e8cf3b6a3f75d23d5/img/provide.png -------------------------------------------------------------------------------- /img/referenced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambci/git-lambda-layer/3b4d3592afb537be4c1e291e8cf3b6a3f75d23d5/img/referenced.png -------------------------------------------------------------------------------- /lambda1/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ./config.sh 4 | 5 | rm layer.zip 6 | 7 | docker run --rm -v "$PWD":/tmp/layer lambci/yumda:1 bash -c " 8 | yum install -y git-${GIT_VERSION} && \ 9 | cd /lambda/opt && \ 10 | zip -yr /tmp/layer/layer.zip . 11 | " 12 | -------------------------------------------------------------------------------- /lambda1/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . ./config.sh 4 | 5 | REGIONS="$(aws ssm get-parameters-by-path --path /aws/service/global-infrastructure/services/lambda/regions \ 6 | --query 'Parameters[].Value' --output text | tr '[:blank:]' '\n' | grep -v -e ^cn- -e ^us-gov- | sort -r)" 7 | 8 | for region in $REGIONS; do 9 | aws lambda list-layer-versions --region $region --layer-name $LAYER_NAME \ 10 | --query 'LayerVersions[*].[LayerVersionArn]' --output text 11 | done 12 | -------------------------------------------------------------------------------- /lambda1/config.sh: -------------------------------------------------------------------------------- 1 | export LAYER_NAME=git 2 | export GIT_VERSION=2.29.0 3 | -------------------------------------------------------------------------------- /lambda1/layer.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambci/git-lambda-layer/3b4d3592afb537be4c1e291e8cf3b6a3f75d23d5/lambda1/layer.zip -------------------------------------------------------------------------------- /lambda1/publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . ./config.sh 4 | 5 | DESCRIPTION="Git ${GIT_VERSION} and openssh binaries" 6 | FILENAME=${LAYER_NAME}-${GIT_VERSION}.zip 7 | 8 | REGIONS="$(aws ssm get-parameters-by-path --path /aws/service/global-infrastructure/services/lambda/regions \ 9 | --query 'Parameters[].Value' --output text | tr '[:blank:]' '\n' | grep -v -e ^cn- -e ^us-gov- | sort -r)" 10 | 11 | aws s3api put-object --bucket lambci --key layers/${FILENAME} --body layer.zip 12 | 13 | for region in $REGIONS; do 14 | aws s3api copy-object --region $region --copy-source lambci/layers/${FILENAME} \ 15 | --bucket lambci-${region} --key layers/${FILENAME} && \ 16 | aws lambda add-layer-version-permission --region $region --layer-name $LAYER_NAME \ 17 | --statement-id sid1 --action lambda:GetLayerVersion --principal '*' \ 18 | --version-number $(aws lambda publish-layer-version --region $region --layer-name $LAYER_NAME \ 19 | --content S3Bucket=lambci-${region},S3Key=layers/${FILENAME} \ 20 | --description "$DESCRIPTION" --query Version --output text) & 21 | done 22 | 23 | for job in $(jobs -p); do 24 | wait $job 25 | done 26 | -------------------------------------------------------------------------------- /lambda1/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf layer && unzip layer.zip -d layer 4 | 5 | cd test 6 | 7 | docker run --rm -v "$PWD":/var/task -v "$PWD"/../layer:/opt lambci/lambda:nodejs8.10 index.handler 8 | 9 | -------------------------------------------------------------------------------- /lambda1/test/index.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('child_process') 2 | 3 | exports.handler = async(event) => { 4 | execSync('rm -rf /tmp/*', { encoding: 'utf8', stdio: 'inherit' }) 5 | 6 | execSync('cd /tmp && git clone https://github.com/mhart/aws4', { encoding: 'utf8', stdio: 'inherit' }) 7 | 8 | execSync('ls -l /tmp/aws4', { encoding: 'utf8', stdio: 'inherit' }) 9 | 10 | execSync('ldd /opt/bin/git', { encoding: 'utf8', stdio: 'inherit' }) 11 | 12 | execSync('ldd /opt/bin/ssh', { encoding: 'utf8', stdio: 'inherit' }) 13 | 14 | execSync('ssh -V', { encoding: 'utf8', stdio: 'inherit' }) 15 | } 16 | -------------------------------------------------------------------------------- /lambda2/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ./config.sh 4 | 5 | rm layer.zip 6 | 7 | docker run --rm -v "$PWD":/tmp/layer lambci/yumda:2 bash -c " 8 | yum install -y git-${GIT_VERSION} && \ 9 | cd /lambda/opt && \ 10 | zip -yr /tmp/layer/layer.zip . 11 | " 12 | -------------------------------------------------------------------------------- /lambda2/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . ./config.sh 4 | 5 | REGIONS="$(aws ssm get-parameters-by-path --path /aws/service/global-infrastructure/services/lambda/regions \ 6 | --query 'Parameters[].Value' --output text | tr '[:blank:]' '\n' | grep -v -e ^cn- -e ^us-gov- | sort -r)" 7 | 8 | for region in $REGIONS; do 9 | aws lambda list-layer-versions --region $region --layer-name $LAYER_NAME \ 10 | --query 'LayerVersions[*].[LayerVersionArn]' --output text 11 | done 12 | -------------------------------------------------------------------------------- /lambda2/config.sh: -------------------------------------------------------------------------------- 1 | export LAYER_NAME=git-lambda2 2 | export GIT_VERSION=2.29.0 3 | -------------------------------------------------------------------------------- /lambda2/layer.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambci/git-lambda-layer/3b4d3592afb537be4c1e291e8cf3b6a3f75d23d5/lambda2/layer.zip -------------------------------------------------------------------------------- /lambda2/publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . ./config.sh 4 | 5 | DESCRIPTION="Git ${GIT_VERSION} and openssh binaries for Amazon Linux 2 Lambdas" 6 | FILENAME=${LAYER_NAME}-${GIT_VERSION}.zip 7 | 8 | REGIONS="$(aws ssm get-parameters-by-path --path /aws/service/global-infrastructure/services/lambda/regions \ 9 | --query 'Parameters[].Value' --output text | tr '[:blank:]' '\n' | grep -v -e ^cn- -e ^us-gov- | sort -r)" 10 | 11 | aws s3api put-object --bucket lambci --key layers/${FILENAME} --body layer.zip 12 | 13 | for region in $REGIONS; do 14 | aws s3api copy-object --region $region --copy-source lambci/layers/${FILENAME} \ 15 | --bucket lambci-${region} --key layers/${FILENAME} && \ 16 | aws lambda add-layer-version-permission --region $region --layer-name $LAYER_NAME \ 17 | --statement-id sid1 --action lambda:GetLayerVersion --principal '*' \ 18 | --version-number $(aws lambda publish-layer-version --region $region --layer-name $LAYER_NAME \ 19 | --content S3Bucket=lambci-${region},S3Key=layers/${FILENAME} \ 20 | --description "$DESCRIPTION" --query Version --output text) & 21 | done 22 | 23 | for job in $(jobs -p); do 24 | wait $job 25 | done 26 | -------------------------------------------------------------------------------- /lambda2/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf layer && unzip layer.zip -d layer 4 | 5 | cd test 6 | 7 | docker run --rm -v "$PWD":/var/task -v "$PWD"/../layer:/opt lambci/lambda:nodejs10.x index.handler 8 | 9 | -------------------------------------------------------------------------------- /lambda2/test/index.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('child_process') 2 | 3 | exports.handler = async(event) => { 4 | execSync('rm -rf /tmp/*', { encoding: 'utf8', stdio: 'inherit' }) 5 | 6 | execSync('cd /tmp && git clone https://github.com/mhart/aws4', { encoding: 'utf8', stdio: 'inherit' }) 7 | 8 | execSync('ls -l /tmp/aws4', { encoding: 'utf8', stdio: 'inherit' }) 9 | 10 | execSync('ldd /opt/bin/git', { encoding: 'utf8', stdio: 'inherit' }) 11 | 12 | execSync('ldd /opt/bin/ssh', { encoding: 'utf8', stdio: 'inherit' }) 13 | 14 | execSync('ssh -V', { encoding: 'utf8', stdio: 'inherit' }) 15 | } 16 | --------------------------------------------------------------------------------