假设我们在github或bitbucket上托管的私有存储库中有一个名为shared_package
的自定义Python包。我们的私有存储库被配置为通过SSH进行只读访问,例如这里描述的github和这里描述的bitbucket。
我们的另一个项目,恰当地命名为dependent_project
,依赖于这个shared_package
,需要部署到AWS Elastic Beanstalk(EB)。我们的环境使用最新的";Python on Amazon Linux 2";平台,我们使用pipenv
作为包管理器。
由于各种原因,我们最方便的方法是直接从我们的在线git存储库安装shared_package
,如这里对pipenv和这里对pip所述。我们的dependent_project
的Pipfile
看起来像这样:
[[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平台挂钩配置Elastic Beanstalk环境,以便pipenv
能够通过SSH成功安装来自私人在线git repo的包
以下讨论中可以找到一些谜题,但这些都没有使用AmazonLinux2平台挂钩:
- 使用AWS Elastic Beanstalk和Ruby容器设置私有Github访问
- 在Elastic Beanstalk上设置github私有回购访问的SSH密钥
- 处理node.js私有模块依赖关系的推荐方法是什么
- BitBucket:主机密钥验证失败
- 授予S3对Elastic Beanstalk实例的访问权限
- 如何使用Amazon AWS Elastic Beanstalk部署私有python pip依赖关系
- AWS Elastic Beanstalk中的Python:私有包依赖关系
摘要
假设我们已经定义了以下Elastic Beanstalk环境属性,并且bitbucket公钥文件和我们的私钥文件都已上传到指定的S3 bucket:
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连接,我们需要三个SSH密钥:
bitbucket.org的公共密钥,用于验证我们是否正在连接到受信任的主机。
为了获得bitbucket.org的公钥,我们可以使用ssh-keyscan以适合
known_hosts
的格式。为了安全起见,我们应该使用";"可信";来源在我们的情况下,我们能做的最好的事情是将公钥指纹与";官方的";一个发布在bitbucket(或github)网站上。指纹可以使用ssh-keygen
从公钥计算,例如ssh-keyscan -t rsa bitbucket.org | ssh-keygen -lf -
我们存储库的私钥和公钥。
可以使用
ssh-keygen
生成由私钥和公钥组成的密钥对。私钥必须保密,公钥必须复制到";访问密钥";对于bitbucket存储库,如bitbucket文档中所述。请注意,创建一个没有密码短语的密钥对最方便,否则我们的脚本也需要处理密码短语。
在AWS上存储密钥
部署期间,EB环境中需要提供公共比特桶主机密钥和我们的私有回购密钥。私有密钥是秘密的,因此它不应存储在源代码中,也不应以其他方式进行版本控制。
最方便的选择是将键值存储为EB环境属性(即环境变量),因为这些属性在部署过程中很容易获得。原则上,这可以完成,例如使用base64
编码将多行私钥存储在单行环境属性中。然而,所有EB环境属性键和值组合的总大小仅限于4096字节,这基本上排除了此选项。
另一种选择是将密钥文件存储在AWS S3上的安全私有存储桶中。该文档描述了如何设置IAM角色,该角色为EC2实例授予对S3存储桶的访问权限。文档确实提供了一个配置示例,但它使用.ebextensions
,不适用于.platform
挂钩。
简而言之,我们可以创建一个具有默认设置的基本S3存储桶("阻止公共访问"已启用,没有自定义权限),并将SSH密钥文件上传到该存储桶。然后,使用AWS IAM web控制台,选择aws-elasticbeanstalk-ec2-role
(或者,最好创建一个自定义角色),并附加AmazonS3ReadOnlyAccess
策略。
在部署到Elastic Beanstalk的过程中,我们可以使用.platform
挂钩,使用aws-cli将S3 bucket中的密钥文件下载到EC2实例。
为了测试EC2和S3之间的连接,我们可以使用eb-ssh连接到EC2实例,然后使用aws s3 ls s3://<bucket name>
列出bucket内容。
更新已知主机
为了表明bitbucket.org是一个受信任的主机,需要将其公钥添加到我们实例上的known_hosts
文件中。在我们的平台挂钩脚本中,我们删除主机的任何现有公钥,以防它们过时,并用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
同样,必须限制文件权限。
最后的脚本显示在顶部的摘要中。该脚本可以例如作为.platform/hooks/prebuild/01_configure_bitbucket_ssh.sh
存储在项目文件夹中。
挂钩和配置挂钩
请注意,AmazonLinux2使用.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
中。