1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-03-03 14:52:21 +02:00

Reload GCS credentials before renewing authentication token.

This allows the service key to be updated while a command is running. The new key should be written atomically and ideally the old key should remain valid for some period of time to avoid a race condition if the old token happens to expire at the same time that the new key is being generated.
This commit is contained in:
David Steele 2023-08-07 13:30:50 +01:00 committed by GitHub
parent cbafcfabf2
commit 995a8e9669
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 31 additions and 10 deletions

View File

@ -15,6 +15,21 @@
<release-list>
<release date="XXXX-XX-XX" version="2.48dev" title="Under Development">
<release-core-list>
<release-improvement-list>
<release-item>
<github-pull-request id="2072"/>
<release-item-contributor-list>
<release-item-ideator id="daniel.farina"/>
<release-item-contributor id="david.steele"/>
<release-item-reviewer id="stephen.frost"/>
</release-item-contributor-list>
<p>Reload GCS credentials before renewing authentication token.</p>
</release-item>
</release-improvement-list>
</release-core-list>
</release>
<release date="2023-07-24" version="2.47" title="Performance Improvements and Bug Fixes">

View File

@ -659,6 +659,8 @@
<list-item><id>service</id> - Service account from locally stored key.</list-item>
<list-item><id>token</id> - For local testing, e.g. <file>fakegcs</file>.</list-item>
</list>
<p>When <br-option>repo-gcs-key-type=service</br-option> the credentials will be reloaded when the authentication token is renewed.</p>
</text>
<example>auto</example>

View File

@ -89,8 +89,7 @@ struct StorageGcs
size_t chunkSize; // Block size for resumable upload
StorageGcsKeyType keyType; // Auth key type
const String *credential; // Credential (client email)
const String *privateKey; // Private key in PEM format
const String *key; // Key (value depends on key type)
String *token; // Token
time_t tokenTimeExpire; // Token expiration time (if service auth)
HttpUrl *authUrl; // URL for authentication server
@ -177,6 +176,13 @@ storageGcsAuthJwt(StorageGcs *this, time_t timeBegin)
MEM_CONTEXT_TEMP_BEGIN()
{
// Load client email and private key
KeyValue *const kvKey = varKv(jsonToVar(strNewBuf(storageGetP(storageNewReadP(storagePosixNewP(FSLASH_STR), this->key)))));
const String *const clientEmail = varStr(kvGet(kvKey, GCS_JSON_CLIENT_EMAIL_VAR));
const String *const privateKeyRaw = varStr(kvGet(kvKey, GCS_JSON_PRIVATE_KEY_VAR));
CHECK(FormatError, clientEmail != NULL && privateKeyRaw != NULL, "credentials missing");
// Add claim
strCatEncode(
result, encodingBase64Url,
@ -184,7 +190,7 @@ storageGcsAuthJwt(StorageGcs *this, time_t timeBegin)
strNewFmt(
"{\"iss\":\"%s\",\"scope\":\"https://www.googleapis.com/auth/devstorage.read%s\",\"aud\":\"%s\""
",\"exp\":%" PRIu64 ",\"iat\":%" PRIu64 "}",
strZ(this->credential), this->write ? "_write" : "_only", strZ(httpUrl(this->authUrl)),
strZ(clientEmail), this->write ? "_write" : "_only", strZ(httpUrl(this->authUrl)),
(uint64_t)timeBegin + 3600, (uint64_t)timeBegin)));
// Sign with RSA key
@ -198,7 +204,7 @@ storageGcsAuthJwt(StorageGcs *this, time_t timeBegin)
{
// Load key
bio = BIO_new(BIO_s_mem());
BIO_write((BIO *)bio, strZ(this->privateKey), (int)strSize(this->privateKey));
BIO_write((BIO *)bio, strZ(privateKeyRaw), (int)strSize(privateKeyRaw));
privateKey = PEM_read_bio_PrivateKey((BIO *)bio, NULL, NULL, NULL);
cryptoError(privateKey == NULL, "unable to read PEM");
@ -999,15 +1005,13 @@ storageGcsNew(
// Read data from file for service keys
case storageGcsKeyTypeService:
{
KeyValue *kvKey = varKv(jsonToVar(strNewBuf(storageGetP(storageNewReadP(storagePosixNewP(FSLASH_STR), key)))));
this->credential = varStr(kvGet(kvKey, GCS_JSON_CLIENT_EMAIL_VAR));
this->privateKey = varStr(kvGet(kvKey, GCS_JSON_PRIVATE_KEY_VAR));
const KeyValue *const kvKey = varKv(
jsonToVar(strNewBuf(storageGetP(storageNewReadP(storagePosixNewP(FSLASH_STR), key)))));
const String *const uri = varStr(kvGet(kvKey, GCS_JSON_TOKEN_URI_VAR));
CHECK(FormatError, uri != NULL, "uri missing");
CHECK(FormatError, this->credential != NULL && this->privateKey != NULL && uri != NULL, "credentials missing");
this->key = strDup(key);
this->authUrl = httpUrlNewParseP(uri, .type = httpProtocolTypeHttps);
this->authClient = httpClientNew(
tlsClientNewP(
sckClientNew(httpUrlHost(this->authUrl), httpUrlPort(this->authUrl), timeout, timeout),