如何获取AWS PHP SDK来查找存储在Elastic Beanstalk环境变量中的凭证?

时间:2019-12-14 11:59:50

标签: php amazon-web-services amazon-ec2 amazon-elastic-beanstalk

我正在使用PHP在AWS Elastic Beanstalk上运行Web应用程序。

该应用程序使用RDS MySQL数据库。我想通过AWS Secrets Manager存储数据库凭证。

我成功创建了秘密。

通过作曲家,我安装了AWS PHP SDK版本3.129。我运行了compatibility.test php文件来验证我的开发环境。一切都是绿色的(警告是出于性能原因请勿使用Xdebug)。

使用AWS文档,我编写了一个PHP类来获取秘密值。我为该课程写了单元测试。单元测试通过了,我能够使用RDS的用户名,密码等显示秘密值。

为了提供本地开发环境的凭据,我使用了存储在.aws目录中的凭据文件。在此文件中,我存储了:

  • aws_access_key_id
  • aws_secret_access_key

我在本地apache网络服务器上运行了应用程序,它能够从AWS Secrets Manager成功检索RDS凭证,并对RDS数据库进行MySQL调用。

我的最后一步是通过我的Elastic Beanstalk平台在AWS中运行该应用程序(该平台已经运行了几个月,并使用了硬编码的RDS凭证,我想停止使用它)。

我查看了解释"Credentials for the AWS SDK for PHP Version 3的AWS文档。该文档描述了“默认凭证提供者链”。

在标题为“使用环境变量的凭据”的this AWS document之后,我尝试(未成功)使用链中的第一个链接为环境变量。该文件指出:

  

如果您将应用程序托管在AWS Elastic Beanstalk上,则可以通过AWS Elastic Beanstalk控制台设置AWS_ACCESS_KEY_ID和AWS_SECRET_KEY环境变量,以便SDK可以自动使用这些凭据。

我通过AWS控制台使用了这些值作为变量:

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY

在测试期间,我尝试了两个访问密钥,并且我知道它们可以工作,因为我将它们用于其他目的,并且IAM控制台显示了这两个的最近使用情况。

运行测试时,我总是在AWS PHP SDK代码中遇到相同的异常,该异常报告“无法从/.aws/credentials读取凭据”。

这向我暗示:

  1. PHP SDK代码不在寻找我的环境变量
  2. 它试图找到他们,但找不到
  3. 它找到了他们,但无法使用

遇到异常时,我会收到“ Whoops”输出,其中显示了AWS_ACCESS_KEY_ID和AWS_SECRET_ACCESS_KEY环境变量及其输入时的准确值。

PHP SDK代码是正确的,因为它没有将我的凭据文件上传到Elastic Beanstalk,所以它找不到我的凭据文件。这样做对我来说在Elastic Beanstalk中是没有意义的,尽管它对于我的本地环境也很好。

以下是异常回溯中的最后几个调用:

  

Aws \ Exception \ CredentialsException   …/ vendor / aws / aws-sdk-php / src / Credentials / CredentialProvider.php691   21   Aws \ Credentials \ CredentialProvider拒绝   …/ vendor / aws / aws-sdk-php / src / Credentials / CredentialProvider.php424   20   Aws \ Credentials \ CredentialProvider Aws \ Credentials {关闭}   …/ vendor / aws / aws-sdk-php / src / Middleware.php121   19   Aws \ Middleware Aws {closure}   …/ vendor / aws / aws-sdk-php / src / RetryMiddleware.php269   18岁   Aws \ RetryMiddleware __调用   …/ vendor / aws / aws-sdk-php / src / Middleware.php206   17   Aws \ Middleware Aws {closure}   …/ vendor / aws / aws-sdk-php / src / StreamRequestPayloadMiddleware.php83

这是我在“凭据提供拒绝”中输入的代码:

            if (!is_readable($filename)) {
            return self::reject("Cannot read credentials from $filename");
        }

这是我为获取机密而编写的代码(带有一些无关的调试语句,稍后将删除它们):

    public function getSecretString(): ?string
{
    echo 'In getSecretString' . "\r\n";

    $result = null;

    $client = new SecretsManagerClient([
        'profile' => 'default',
        'version' => '2017-10-17',
        'region' => 'us-east-2'
    ]);

    echo 'Lets try' . "\r\n";

    try {
        $result = $client->getSecretValue([
            'SecretId' => $this->secretName,
        ]);
        echo 'Got a result' . "\r\n";
    } catch (AwsException $e) {
        echo 'FAILED!' . "\r\n";
        // output error message if fails
        echo $e->getMessage();
        echo "\n";
    }

    if ($result !== null) {
        echo 'We got our secret string!' . "\r\n" .
        $this->secretString = $result['SecretString'];
    } else {
        echo 'We DID NOT get our secret string!' . "\r\n" .
        $this->secretString = null;
    }

    print_r($this->secretString);
    echo "\r\n";
    return $this->secretString;
}

从我的调试看来,SDK只是忽略了除凭证文件以外的任何凭证提供方法。我尝试过:

  1. 在运行本地Web服务器时在本地计算机上设置本地环境变量。
  2. 将我的SDK客户端数组设置为仅查看env()方法。
  3. 对我的凭据进行硬编码。

设置我的信誉的这些方法都不起作用。

我还尝试使用旧版本的SDK,以查看是否最近引入了错误,该错误可以追溯到3.76.0。没什么。

以下是修改后的代码,其中显示了上面注释2以及上面注释3的尝试:

public function getSecretString(): ?string
{
    echo 'In getSecretString' . "\r\n";

    $result = null;

    // Only load credentials from environment variables
    //$provider = CredentialProvider::env();

    $client = new SecretsManagerClient([
        'profile' => 'default',
        'version' => '2017-10-17',
        'region' => 'us-east-2',
        //'credentials' => $provider,
        'credentials' => [
            'key'    => 'AKIAVGZKSJ5G76TTG2P4',
            'secret' => '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',
        ],
        // 'debug'   => true
    ]);

    try {
        $result = $client->getSecretValue([
            'SecretId' => $this->secretName,
        ]);
        echo 'Got a result' . "\r\n";
    } catch (AwsException $e) {
        echo 'FAILED!' . "\r\n";
        // output error message if fails
        echo $e->getMessage();
        echo "\n";
    }

    if ($result !== null) {
        echo 'We got our secret string!' . "\r\n" .
        $this->secretString = $result['SecretString'];
    } else {
        echo 'We DID NOT get our secret string!' . "\r\n" .
        $this->secretString = null;
    }
    return $this->secretString;
}

我还尝试了通过默认链获取凭据的第二种方法:承担IAM角色。我尝试遵循文档here,但坦白地发现它令人困惑。在我的Elastic Beanstalk Security配置中,我注意到了两个IAM角色:

  1. aws-elasticbeanstalk-ec2-role
  2. aws-elasticbeanstalk-service-role

我为这两个角色分配了以下策略:

  1. SecretsManagerReadWrite
  2. IAMFullAccess(因为当我分配上一个策略时,也有一条注释也要分配此策略)

分配这些IAM策略不能解决问题。

我的首选是通过Elastic Beanstalk环境变量解决此问题,因为它看起来非常简单,并且AWS文档明确指出它应该可以工作。另外,如果不需要大量脚本,则希望通过IAM策略解决它。

我该如何解决这个问题?

1 个答案:

答案 0 :(得分:0)

(代表问题作者发布了解决方案,以便将其从问题中移出)

在我的客户端SecretsManagerClient数组中,包含以下行:

PROJECT_ID@appspot.gserviceaccount.com

通过单步执行SDK代码,我观察到该行的存在被解释为“寻找凭据文件并跳过默认的凭据链”。

我删除了这一行,然后SDK处理了在我的环境变量中设置的凭据。