Skip to content

Token file not found temporarily after refresh on Kubernetes #2014

@alecwcp

Description

@alecwcp

Confirm by changing [ ] to [x] below to ensure that it's a bug:

Describe the bug
When using the PHP SDK within a Kubernetes pod, using IAM roles for service accounts (https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html), with the default provider chain, the credentials are fetched by \Aws\Credentials\AssumeRoleWithWebIdentityCredentialProvider from the file identified by the environment variable with name \Aws\Credentials\CredentialProvider::ENV_TOKEN_FILE (in our case, the file is /var/run/secrets/eks.amazonaws.com/serviceaccount/token - this is coming from using IAM roles for service accounts). At some point after a token refresh the next time the SDK attempts to fetch the token

$token = file_get_contents($this->tokenFile);
it isn't found, the credentials provider goes down the chain and ends up throwing Aws\Exception\CredentialsException with message

Error retrieving credentials from the instance profile metadata server. (cURL error 7: Failed to connect to 169.254.169.254 port 80: Connection refused (see https://curl.haxx.se/libcurl/c/libcurl-errors.html))

Version of AWS SDK for PHP?
3.133.6

Version of PHP (php -v)?
7.3.13

To Reproduce (observed behavior)
Create a Kubernetes pod using the default provider chain (retrieving credentials from a token file) and set a short refresh period for the token (to see the issue sooner). The code below recreates the client on each loop - in production code we only create it once now, but the issue still occurs because we're using PHP FPM and the realpath cache stays with the child worker, so is not cleared on each request

<?php
require 'vendor/autoload.php';
use Aws\S3\S3Client;
use Aws\Exception\AwsException;
use Aws\Credentials\CredentialProvider;
const WAIT_TIME_IN_SECONDS = 10;
while (true) {
    //Create a S3Client
    $s3Client = new S3Client([
        //'profile' => 'default',
        'region' => 'us-west-2',
        'version' => '2006-03-01'
    ]);
    $date = date('Y-m-d H:i:s');
    echo sprintf('%s Sleeping %s seconds%s', $date, WAIT_TIME_IN_SECONDS, PHP_EOL);
    sleep(WAIT_TIME_IN_SECONDS);
    //Listing all S3 Bucket
    $buckets = $s3Client->listBuckets();
    foreach ($buckets['Buckets'] as $bucket) {
//        echo sprintf(' %s%s', $bucket['Name'], PHP_EOL);
    }
}

Expected behavior
The token file should always be successfully retrieved if it is present.

Screenshots
If applicable, add screenshots to help explain your problem.

Additional context
The issue appears to be related to the realpath cache (the issue relates to reading Kubernetes secrets, which this post highlights as hitting issues with the realpath cache https://pracucci.com/php-realpath-cache-and-kubernetes-secrets-configmap-updates.html) .
The token file is a symlink. When the token is refreshed this symlink changes, meaning the PHP realpath cache entry for this becomes invalid. So the next time the line

$token = file_get_contents($this->tokenFile);
is reached the token file isn't found.

We alleviated the issue by manually clearing the realpath cache for this file before creating the SDK client, but it is still hit upon occasion.
We believe that changing the highlighted line in \Aws\Credentials\AssumeRoleWithWebIdentityCredentialProvider to

$token = file_get_contents($this->tokenFile);
if (false === $token) {
    clearstatcache(true, $this->tokenFile);
    $token = file_get_contents($this->tokenFile);
}

would resolve this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugThis issue is a bug.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions