Skip to content
Advertisement

AWS: How to get PHP on an EC2 instance to read a Secrets Manager secret?

I’ve set up an EC2 instance running PHP. For testing only, the instance is in a public subnet with a Security Group that allows All Traffic to 0.0.0.0/0. The Route Table has the default local route to 10.0.0.0/16 (the VPC’s CIDR block) and a route to the Internet Gateway at 0.0.0.0/0. The NACL associated with the subnet allows All Traffic in and out at 0.0.0.0/0. I know this is wide open but I wanted to ensure that the problem I’m encountering isn’t related to Security Groups and NACLs.

I created a Secrets Manager secret MySecret-xxxxx and have attached an IAM role to the instance with the following policy to allow the instance to access the secret:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetResourcePolicy",
                "secretsmanager:GetSecretValue",
                "secretsmanager:DescribeSecret",
                "secretsmanager:ListSecretVersionIds"
            ],
            "Resource": "arn:aws:secretsmanager:eu-west-2:xxxxxxxxx:secret:MySecret-xxxxx"
        }
    ]
}

I’ve installed the AWS SDK for PHP on the instance in a subfolder called sdks, and lastly created a “Hello World” index.php file that works perfectly well until I try to run getSecretValue in a simplified version of the setup information that AWS provides . This is the PHP code:

<?php
    require 'sdks/aws/aws-autoloader.php';

    use AwsSecretsManagerSecretsManagerClient;
    use AwsExceptionAwsException;

    $client = new SecretsManagerClient( [
        'profile' => 'default',
        'version' => 'latest',
        'region' => 'eu-west-2'
    ] );

    $secretName = 'MySecret-xxxxx';

    echo '<h1>Hello World</h1>';

    $result = $client->getSecretValue([
        'SecretId' => $secretName,
    ]);
?>

As soon as I include the $result = $client->getSecretValue([... block of code, I get an HTTP ERROR 500 error message, although it works perfectly well without it. I ran aws secretsmanager get-secret-value --secret-id MySecret-xxxxx --region eu-west-2 on the CLI and that returned the secret details properly.

Advertisement

Answer

Finally figured it out – although I had created a credentials file in the /home/ec2-user/.aws folder on the EC2 instance, I still had to retrieve the credentials via the SDK. Why this is excluded from the Secrets Manager example code that AWS provide is beyond me. The full working code now looks like this:

<?php
    require 'vendor/autoload.php';

    use AwsCredentialsCredentialProvider;
    use AwsSecretsManagerSecretsManagerClient;
    use AwsExceptionAwsException;

    $provider = CredentialProvider::defaultProvider();

    $client = new SecretsManagerClient( [
        'credentials' => $provider,
        'version' => 'latest',
        'region' => 'eu-west-2'
    ] );

    $secretName = 'MySecret-xxxxx';

    try {
        $result = $client->getSecretValue( [
            'SecretId' => $secretName,
        ] );
    } catch ( AwsException $e ) {
        $error = $e->getAwsErrorCode();
        if ( $error == 'DecryptionFailureException' ) { // Can't decrypt the protected secret text using the provided AWS KMS key.
            throw $e;
        }
        if ( $error == 'InternalServiceErrorException' ) { // An error occurred on the server side.
            throw $e;
        }
        if ( $error == 'InvalidParameterException' ) { // Invalid parameter value.
            throw $e;
        }
        if ( $error == 'InvalidRequestException' ) { // Parameter value is not valid for the current state of the resource.
            throw $e;
        }
        if ( $error == 'ResourceNotFoundException' ) { // Requested resource not found
            throw $e;
        }
    }
    // Decrypts secret using the associated KMS CMK, depends on whether the secret is a string or binary.
    if ( isset( $result[ 'SecretString' ] ) ) {
        $secret = $result[ 'SecretString' ];
    } else {
        $secret = base64_decode( $result[ 'SecretBinary' ] );
    }

    // Decode the secret json
    $secrets = json_decode( $secret, true );

echo( '<p>hostname/ipaddress: ' . $secrets[ 'host' ] . '</p><p>username: ' . $secrets[ 'username' ] . '</p><p>password: ' . $secrets[ 'password' ] . '</p><p>dbname: ' . $secrets[ 'dbname' ] . '</p>' );
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement