it has limitations in a sense that it does require the service account created from the gcp project where apigee is… in my use case i want to be able to use a cloud function and cloud run from a separate project
Ahh, I see. I understand.
I think having KVM with a key from a SA generated from a different project would be powerful - i haven’t really found a nice way to for apigee work with serverless on gcp.
Good feedback. I agree with you.
can you paste the actually code you used to store the certificate in KVM on apigee… this step is giving me issues
Yes. I suggest that you do something a little different. Rather than loading just the private key into the KVM, load the entire JSON. The entire file you’ve downloaded from the cloud console, or created with the gcloud command.
In that case, you want something like this:
curl -i -X POST \
-H "Authorization: Bearer $TOKEN" \
$endpoint/kvm-admin/v1/organizations/ORG/environments/ENV/keyvaluemaps/KVMNAME/entries \
-d key=sakeyjson --data-urlencode value@./my-sacreds.json
That curl command posts a form to the endpoint, telling it what key/value pair to load. The key (name) in this case is sakeyjson
. The value of that KVM entry will be the entire contents of the file “my-sacreds.json” in the current directory. That entire contents will be something like this:
{
"type": "service_account",
"project_id": "cluster",
"private_key_id": "93158289b2734d823aaeba3b1e4a48a15aaac",
"client_email": "sa-id-123@ourcluster.iam.gserviceaccount.com",
"client_id": "31167058558367844",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/sa-id-123%40ourcluster.iam.gserviceaccount.com",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQE...8K5WjX\n-----END PRIVATE KEY-----\n"
}
If the KVMNAME in that curl command was “settings”, THEN, at runtime in the API Proxy you need to load that thing into a variable. Like so:
<KeyValueMapOperations name='KVM-Get-SAKey-JSON'>
<Scope>environment</Scope>
<ExpiryTimeInSecs>240</ExpiryTimeInSecs>
<MapName>settings</MapName>
<Get assignTo='private.sakeyjson'>
<Key>
<Parameter>sakeyjson</Parameter>
</Key>
</Get>
</KeyValueMapOperations>
But that’s not enough. You then need to extract the things out of that JSON into individual variables. You can do that like so:
<Javascript name='JS-Shred-SAKey-JSON' timeLimit='200' >
<Properties>
<!-- this prefix should be "private" to obscure the data in Trace -->
<Property name='output-prefix'>private</Property>
<Property name='source'>private.sakeyjson</Property>
</Properties>
<Source>
function varname(propertyName) {
return properties['output-prefix'] + '.' + propertyName;
}
try {
var obj = JSON.parse(context.getVariable(properties.source));
for (var p in obj) {
context.setVariable(varname(p), obj[p]);
// for diagnostics purposes only. Remove for production use.
context.setVariable('SHREDDED.' + varname(p), obj[p]);
}
}
catch (e) {
context.setVariable('extract_error', "bad inbound message");
context.setVariable('extract_exception', e.toString());
}
</Source>
</Javascript>
At this point you have a variable named “private.private_key” holding the PEM-encoded representation of the private key, and other “private” variables holding the other properties from that json. So you can generate the JWT:
<GenerateJWT name="Generate-JWT-2">
<Algorithm>RS256</Algorithm>
<PrivateKey>
<Value ref="private.private_key"/>
</PrivateKey>
<Issuer ref="private.client_email"/>
<Audience ref="private.token_uri"/>
<ExpiresIn>300s</ExpiresIn>
<AdditionalClaims>
<Claim name="scope" type="string">https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/datastore</Claim>
</AdditionalClaims>
<OutputVariable>output_jwt</OutputVariable>
</GenerateJWT>