使用 Amazon Linux 2 个挂钩配置 Elastic Beanstalk 以通过 SSH 访问私有 git 存储库

Configuring Elastic Beanstalk for SSH access to private git repo using Amazon Linux 2 hooks

假设我们有一个名为 shared_package 的自定义 Python 包,位于私有存储库中,托管在 github 或 bitbucket 上。我们的私有存储库配置为通过 SSH 进行只读访问,如所述here for github and here for bitbucket.

我们的另一个项目,恰当地命名为 dependent_project,依赖于此 shared_package,并且需要部署到 AWS Elastic Beanstalk (EB)。我们的环境使用最新的“Python on Amazon Linux 2”平台,我们使用pipenv作为包管理器。

出于各种原因,我们最方便的方法是直接从我们的在线 git 存储库安装 shared_package,如 here for pipenv and here for pip 所述。 dependent_projectPipfile 看起来像这样:

[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
shared_package = {git = "ssh://bitbucket.org/our_username/shared_package.git", editable = true, ref = "2021.0"}

[dev-packages]
awsebcli = "*"

[requires]
python_version = "3.8"

这在我们的本地开发系统上运行良好,但是在将 dependent_project 部署到 Elastic Beanstalk 时,pipenv 安装失败并显示:Permission denied (publickey).

由此引出问题:

如何使用 Amazon Linux 2 platform hooks 配置 Elastic Beanstalk 环境,以便 pipenv 可以通过 SSH 从私人在线 git 存储库成功安装包?

可以在以下讨论中找到一些拼图,但这些不使用亚马逊 Linux 2 个平台挂钩:

总结

假设我们已经定义了以下 Elastic Beanstalk 环境属性,并且 bitbucket public 密钥文件和我们的私钥文件都已上传到指定的 S3 存储桶:

S3_BUCKET_NAME="my_bucket"
REPO_HOST_NAME="bitbucket.org"
REPO_HOST_PUBLIC_KEY_NAME="bitbucket_public_key"
REPO_PRIVATE_KEY_NAME="my_private_key"

然后可以使用 .platform/hooks/prebuild:

中的这个钩子完成配置
#!/bin/bash

# git is required to install our python packages directly from bitbucket
yum -y install git

# file paths (platform hooks are executed as root)
SSH_KNOWN_HOSTS_FILE="/root/.ssh/known_hosts"
SSH_CONFIG_FILE="/root/.ssh/config"
PRIVATE_KEY_FILE="/root/.ssh/$REPO_PRIVATE_KEY_NAME"

# remove any existing (stale) keys for our host from the known_hosts file
[ -f $SSH_KNOWN_HOSTS_FILE ] && ssh-keygen -R $REPO_HOST_NAME

# read the (fresh) host key from S3 file and append to known_hosts file
aws s3 cp "s3://$S3_BUCKET_NAME/$REPO_HOST_PUBLIC_KEY_NAME" - >> $SSH_KNOWN_HOSTS_FILE

# copy our private key from S3 to our instance
aws s3 cp "s3://$S3_BUCKET_NAME/$REPO_PRIVATE_KEY_NAME" $PRIVATE_KEY_FILE

# create an ssh config file to point to the private key file
tee $SSH_CONFIG_FILE <<HERE
Host $REPO_HOST_NAME
    User git
    Hostname $REPO_HOST_NAME
    IdentityFile $PRIVATE_KEY_FILE
HERE

# file permissions must be restricted
chmod 600 $SSH_CONFIG_FILE
chmod 600 $PRIVATE_KEY_FILE

注意此文件需要执行权限 (chmod +x <file path>)。

详细解释

继续阅读以了解详细的原理。

Git

要访问 git 存储库,我们的 Elastic Beanstalk 环境需要安装 git。 这可以在平台挂钩中使用 yum 完成(-y 假设每个问题都为“是”):

yum -y install git

SSH 密钥

在我们的 Elastic Beanstalk (EB) 实例和例如一个 bitbucket 存储库,我们需要三个 SSH 密钥:

  • bitbucket.org 的 public 密钥,用于验证我们正在连接到受信任的主机。

    要获得 bitbucket.org 的 public 密钥,以适合 known_hosts 的格式,我们可以使用 ssh-keyscan。 为了安全起见,我们 should verify this key 使用“可信”来源。 在我们的例子中,我们能做的最好的事情就是将 public 密钥指纹与 bitbucket (or github) 网站上发布的“官方”指纹进行比较。 可以使用 ssh-keygen 从 public 密钥计算指纹,例如

    ssh-keyscan -t rsa bitbucket.org | ssh-keygen -lf -
    
  • 我们存储库的私有密钥和public密钥。

    一对密钥,由私钥和public密钥组成,可以使用ssh-keygen生成。 私钥必须保密,必须将 public 密钥复制到 bitbucket 存储库的“访问密钥”列表中,如 bitbucket docs 中所述。 请注意,创建密钥对 不带 密码是最方便的,否则我们的脚本也需要处理密码。

在 AWS 上存储密钥

部署期间,public bitbucket 主机密钥和我们的私有 repo 密钥需要在 EB 环境中可用。 private 密钥是秘密的,因此它 不应 存储在源代码中,也不应以其他方式进行版本控制。

最方便的选择是将键值存储为 EB environment properties(即环境变量),因为这些在部署期间很容易获得。 原则上,这个可以完成,例如使用 base64 编码将多行私钥存储在单行环境中 属性。 但是,所有 EB 环境 属性 键和值组合的总大小为 limited to a mere 4096 bytes,这基本上排除了此选项。

另一种方法是将密钥文件存储在 AWS S3 上的安全私有存储桶中。 documentation 描述了如何设置 IAM 角色以授予对 EC2 实例的 S3 存储桶的访问权限。该文档确实提供了一个配置示例,但这使用 .ebextensions 并且不适用于 .platform 挂钩。

简而言之,我们可以创建一个具有默认设置(启用“阻止 public 访问”,无自定义权限)的基本 S3 存储桶,并将 SSH 密钥文件上传到该存储桶。 然后,使用 AWS IAM Web 控制台,select aws-elasticbeanstalk-ec2-role(或者最好创建一个自定义角色),并附加 AmazonS3ReadOnlyAccess 策略。

在部署到 Elastic Beanstalk 期间,我们可以使用 .platform 挂钩使用 aws cli.

将密钥文件从 S3 存储桶下载到 EC2 实例

要测试 EC2 和 S3 之间的连接,我们可以使用 eb ssh 连接到 EC2 实例,然后使用 aws s3 ls s3://<bucket name> 列出存储桶内容。

正在更新known_hosts

为了表明 bitbucket.org 是受信任的主机,它的 public 密钥需要添加到我们实例上的 known_hosts 文件中。 在我们的平台挂钩脚本中,我们删除主机的所有现有 public 密钥,以防它们过时,并用 S3 上文件中的当前密钥替换它们:

SSH_KNOWN_HOSTS_FILE="/root/.ssh/known_hosts"
[ -f $SSH_KNOWN_HOSTS_FILE ] && ssh-keygen -R $REPO_HOST_NAME
aws s3 cp "s3://$S3_BUCKET_NAME/$REPO_HOST_PUBLIC_KEY_NAME" - >> $SSH_KNOWN_HOSTS_FILE

指定私钥

私钥可以从S3下载如下,需要限制文件权限:

PRIVATE_KEY_FILE="/root/.ssh/$REPO_PRIVATE_KEY_NAME"
aws s3 cp "s3://$S3_BUCKET_NAME/$REPO_PRIVATE_KEY_NAME" $PRIVATE_KEY_FILE
chmod 600 $PRIVATE_KEY_FILE

还需要一个 SSH 配置文件来指向私钥:

tee $SSH_CONFIG_FILE <<HERE
Host $REPO_HOST_NAME
    User git
    Hostname $REPO_HOST_NAME
    IdentityFile $PRIVATE_KEY_FILE
HERE
chmod 600 $SSH_CONFIG_FILE

再次,文件 permissions must be restricted

最终脚本显示在顶部的摘要中。 该脚本可以存储在例如作为项目文件夹中的 .platform/hooks/prebuild/01_configure_bitbucket_ssh.sh

挂钩和配置挂钩

请注意,Amazon Linux 2 使用 .platform/hooks 进行正常部署,使用 .platform/confighooks 进行配置部署。 通常,在这两种情况下都需要使用相同的脚本。 为了防止代码重复,我们的 .platform/confighooks/prebuild/01_configure_bitbucket_ssh.sh 可能如下所示:

#!/bin/bash
source "/var/app/current/.platform/hooks/prebuild/01_configure_bitbucket_ssh.sh"

请注意,应用程序代码最终出现在实例的 /var/app/current 中。