Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

F/GCP-FunctionDefaultSeviceAccount #2052

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -1552,6 +1552,7 @@ module.exports = {
'instanceNodeCount' : require(__dirname + '/plugins/google/spanner/instanceNodeCount.js'),

'httpTriggerRequireHttps' : require(__dirname + '/plugins/google/cloudfunctions/httpTriggerRequireHttps.js'),
'functionDefaultServiceAccount' : require(__dirname + '/plugins/google/cloudfunctions/functionDefaultServiceAccount.js'),
'ingressAllTrafficDisabled' : require(__dirname + '/plugins/google/cloudfunctions/ingressAllTrafficDisabled.js'),
'cloudFunctionLabelsAdded' : require(__dirname + '/plugins/google/cloudfunctions/cloudFunctionLabelsAdded.js'),
'functionAllUsersPolicy' : require(__dirname + '/plugins/google/cloudfunctions/functionAllUsersPolicy.js'),
Expand Down
55 changes: 55 additions & 0 deletions plugins/google/cloudfunctions/functionDefaultServiceAccount.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
var async = require('async');
var helpers = require('../../../helpers/google');

module.exports = {
title: 'Cloud Function Default Service Account',
category: 'Cloud Functions',
domain: 'Serverless',
severity: 'Medium',
description: 'Ensures that Cloud Functions are not using the default service account.',
more_info: 'Using the default service account for Cloud Functions can lead to privilege escalation and overly permissive access. It is recommended to use a user-managed service account for each function in a project instead of the default service account. A managed service account allows more precise access control by granting only the necessary permissions through Identity and Access Management (IAM).',
link: 'https://cloud.google.com/functions/docs/securing/function-identity',
recommended_action: 'Ensure that no Cloud Functions are using the default service account.',
apis: ['functions:list'],
realtime_triggers: ['functions.CloudFunctionsService.UpdateFunction', 'functions.CloudFunctionsService.CreateFunction', 'functions.CloudFunctionsService.DeleteFunction'],

run: function(cache, settings, callback) {
var results = [];
var source = {};
var regions = helpers.regions();

async.each(regions.functions, (region, rcb) => {
var functions = helpers.addSource(cache, source,
['functions', 'list', region]);

if (!functions) return rcb();

if (functions.err || !functions.data) {
helpers.addResult(results, 3,
'Unable to query for Google Cloud Functions: ' + helpers.addError(functions), region, null, null, functions.err);
return rcb();
}

if (!functions.data.length) {
helpers.addResult(results, 0, 'No Google Cloud functions found', region);
return rcb();
}

functions.data.forEach(func => {
if (!func.name) return;

if (func.serviceAccountEmail && func.serviceAccountEmail.endsWith('@appspot.gserviceaccount.com')) {
helpers.addResult(results, 2,
'Cloud Function is using default service account', region, func.name);
} else {
helpers.addResult(results, 0,
'Cloud Function is not using default service account', region, func.name);
}
});

rcb();
}, function() {
callback(null, results, source);
});
}
};
117 changes: 117 additions & 0 deletions plugins/google/cloudfunctions/functionDefaultServiceAccount.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
var expect = require('chai').expect;
var plugin = require('./functionDefaultServiceAccount');


const functions = [
{
"name": "projects/my-test-project/locations/us-central1/functions/function-1",
"status": "ACTIVE",
"entryPoint": "helloWorld",
"timeout": "60s",
"availableMemoryMb": 256,
"updateTime": "2021-09-24T06:18:15.265Z",
"runtime": "nodejs14",
"ingressSettings": "ALLOW_ALL",
"serviceAccountEmail": "[email protected]"
},
{
"name": "projects/my-test-project/locations/us-central1/functions/function-1",
"status": "ACTIVE",
"entryPoint": "helloWorld",
"timeout": "60s",
"availableMemoryMb": 256,
"updateTime": "2021-09-24T06:18:15.265Z",
"versionId": "1",
"runtime": "nodejs14",
"ingressSettings": "ALLOW_INTERNAL_AND_GCLB",
"labels": { 'deployment-tool': 'console-cloud' },
"serviceAccountEmail": "[email protected]"

}
];

const createCache = (list, err) => {
return {
functions: {
list: {
'us-central1': {
err: err,
data: list
}
}
}
}
};

describe('functionDefaultServiceAccount', function () {
describe('run', function () {
it('should give passing result if no cloud functions found', function (done) {
const callback = (err, results) => {
expect(results.length).to.be.above(0);
expect(results[0].status).to.equal(0);
expect(results[0].message).to.include('No Google Cloud functions found');
expect(results[0].region).to.equal('us-central1');
done()
};

const cache = createCache(
[],
null
);

plugin.run(cache, {}, callback);
});

it('should give unknown result if unable to query for Google Cloud functions', function (done) {
const callback = (err, results) => {
expect(results.length).to.be.above(0);
expect(results[0].status).to.equal(3);
expect(results[0].message).to.include('Unable to query for Google Cloud Functions');
expect(results[0].region).to.equal('us-central1');
done()
};

const cache = createCache(
[],
{message: 'error'},
);

plugin.run(cache, {}, callback);
});

it('should give passing result if google cloud function is not using default service account', function (done) {
const callback = (err, results) => {
expect(results.length).to.be.above(0);
expect(results[0].status).to.equal(0);
expect(results[0].message).to.include('Cloud Function is not using default service account');
expect(results[0].region).to.equal('us-central1');
done()
};

const cache = createCache(
[functions[1]],
null
);

plugin.run(cache, {}, callback);
});

it('should give failing result if google cloud function is using service account', function (done) {
const callback = (err, results) => {
expect(results.length).to.be.above(0);
expect(results[0].status).to.equal(2);
expect(results[0].message).to.include('Cloud Function is using default service account');
expect(results[0].region).to.equal('us-central1');
done();
};

const cache = createCache(
[functions[0]],
null );

plugin.run(cache, {}, callback);
});

})
});

Loading