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

Revised keyVaultKeyExpiryNonRbac (2) #2106

Merged
merged 5 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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 @@ -1064,6 +1064,7 @@ module.exports = {
'rsaCertificateKeySize' : require(__dirname + '/plugins/azure/keyvaults/rsaCertificateKeySize.js'),
'keyVaultSecretExpiry' : require(__dirname + '/plugins/azure/keyvaults/keyVaultSecretExpiry.js'),
'keyVaultKeyExpiry' : require(__dirname + '/plugins/azure/keyvaults/keyVaultKeyExpiry.js'),
'keyVaultKeyExpiryNonRbac' : require(__dirname + '/plugins/azure/keyvaults/keyVaultKeyExpiryNonRbac.js'),
'allowedCertificateKeyTypes' : require(__dirname + '/plugins/azure/keyvaults/allowedCertificateKeyTypes.js'),
'appTierCmkInUse' : require(__dirname + '/plugins/azure/keyvaults/appTierCmkInUse.js'),
'keyVaultInUse' : require(__dirname + '/plugins/azure/keyvaults/keyVaultInUse.js'),
Expand Down
101 changes: 101 additions & 0 deletions plugins/azure/keyvaults/keyVaultKeyExpiryNonRbac.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
var async = require('async');
var helpers = require('../../../helpers/azure');

module.exports = {
title: 'Key Vault Key Expiry Non-RBAC',
alphadev4 marked this conversation as resolved.
Show resolved Hide resolved
category: 'Key Vaults',
domain: 'Application Integration',
severity: 'High',
description: 'Ensures that expiration date is set for all keys in non-RBAC Key Vaults.',
alphadev4 marked this conversation as resolved.
Show resolved Hide resolved
more_info: 'Setting an expiration date on keys helps in key lifecycle management and ensures that keys are rotated regularly.',
recommended_action: 'Modify keys in non-RBAC Key Vaults to have an expiration date set.',
link: 'https://learn.microsoft.com/en-us/azure/key-vault/about-keys-secrets-and-certificates',
apis: ['vaults:list', 'vaults:getKeys'],
settings: {
non_rbac_key_vault_key_expiry_fail: {
name: 'Key Vault Key Expiry Fail',
description: 'Return a failing result when key expiration date is within this number of days in the future',
regex: '^[1-9]{1}[0-9]{0,3}$',
default: '30'
}
},
realtime_triggers: ['microsoftkeyvault:vaults:write', 'microsoftkeyvault:vaults:delete'],

run: function(cache, settings, callback) {
var results = [];
var source = {};
var locations = helpers.locations(settings.govcloud);
var config = {
key_vault_key_expiry_fail: parseInt(settings.non_rbac_key_vault_key_expiry_fail || this.settings.non_rbac_key_vault_key_expiry_fail.default)
};

async.each(locations.vaults, function(location, rcb) {
var vaults = helpers.addSource(cache, source,
['vaults', 'list', location]);

if (!vaults) return rcb();

if (vaults.err || !vaults.data) {
helpers.addResult(results, 3, 'Unable to query for Key Vaults: ' + helpers.addError(vaults), location);
return rcb();
}

if (!vaults.data.length) {
helpers.addResult(results, 0, 'No Key Vaults found', location);
return rcb();
}

vaults.data.forEach(function(vault) {
if (!vault || !vault.properties) {
helpers.addResult(results, 3, 'Unable to read vault properties', location, vault.id);
return;
}

if (vault.properties.enableRbacAuthorization) {
return;
}

var keys = helpers.addSource(cache, source,
['vaults', 'getKeys', location, vault.id]);

if (!keys || keys.err || !keys.data) {
helpers.addResult(results, 3,
'Unable to query for Key Vault keys: ' + helpers.addError(keys), location, vault.id);
} else if (!keys.data.length) {
helpers.addResult(results, 0,
'No Key Vault keys found in non-RBAC vault', location, vault.id);
alphadev4 marked this conversation as resolved.
Show resolved Hide resolved
} else {
keys.data.forEach(function(key) {
var keyName = key.kid.substring(key.kid.lastIndexOf('/') + 1);
var keyId = `${vault.id}/keys/${keyName}`;

if (!key.attributes || !key.attributes.enabled) {
helpers.addResult(results, 0,
'Key in non-RBAC vault is not enabled', location, keyId);
alphadev4 marked this conversation as resolved.
Show resolved Hide resolved
} else if (key.attributes && (key.attributes.expires || key.attributes.exp)) {
let keyExpiry = key.attributes.exp ? key.attributes.exp * 1000 : key.attributes.expires;
let difference = Math.round((new Date(keyExpiry).getTime() - (new Date).getTime())/(24*60*60*1000));
if (difference > config.key_vault_key_expiry_fail) {
helpers.addResult(results, 0,
`Key in non-RBAC vault expires in ${difference} days`, location, keyId);
alphadev4 marked this conversation as resolved.
Show resolved Hide resolved
} else if (difference > 0){
helpers.addResult(results, 2,
`Key in non-RBAC vault expires in ${difference} days`, location, keyId);
alphadev4 marked this conversation as resolved.
Show resolved Hide resolved
} else {
helpers.addResult(results, 2,
`Key in non-RBAC vault expired ${Math.abs(difference)} days ago`, location, keyId);
alphadev4 marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
helpers.addResult(results, 0,
'Key expiration is not enabled in non-RBAC vault', location, keyId);
alphadev4 marked this conversation as resolved.
Show resolved Hide resolved
}
});
}
});

rcb();
}, function() {
callback(null, results, source);
});
}
};
175 changes: 175 additions & 0 deletions plugins/azure/keyvaults/keyVaultKeyExpiryNonRbac.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
var expect = require('chai').expect;
var auth = require('./keyVaultKeyExpiryNonRbac');

var keyExpiryPass = new Date();
keyExpiryPass.setMonth(keyExpiryPass.getMonth() + 2);

var keyExpiryFail = new Date();
keyExpiryFail.setMonth(keyExpiryFail.getMonth() + 1);

var keyExpired = new Date();
keyExpired.setMonth(keyExpired.getMonth() - 1);

const listKeyVaults = [
{
id: '/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.KeyVault/vaults/test-vault',
name: 'test-vault',
type: 'Microsoft.KeyVault/vaults',
location: 'eastus',
properties: {
enableRbacAuthorization: false,
vaultUri: 'https://test-vault.vault.azure.net/'
}
},
{
id: '/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.KeyVault/vaults/test-vault-2',
name: 'test-vault-2',
type: 'Microsoft.KeyVault/vaults',
location: 'eastus',
properties: {
enableRbacAuthorization: true,
vaultUri: 'https://test-vault-2.vault.azure.net/'
}
}
];

const getKeys = [
{
"attributes": {
"created": "2022-04-10T17:57:43+00:00",
"enabled": true,
"expires": null,
"notBefore": null,
"updated": "2022-04-10T17:57:43+00:00"
},
"kid": "https://test-vault.vault.azure.net/keys/test-key",
"name": "test-key"
},
{
"attributes": {
"created": "2022-04-10T17:57:43+00:00",
"enabled": true,
"expires": keyExpiryPass,
"notBefore": null,
"updated": "2022-04-10T17:57:43+00:00"
},
"kid": "https://test-vault.vault.azure.net/keys/test-key-2",
"name": "test-key-2"
},
{
"attributes": {
"created": "2022-04-10T17:57:43+00:00",
"enabled": true,
"expires": keyExpiryFail,
"notBefore": null,
"updated": "2022-04-10T17:57:43+00:00"
},
"kid": "https://test-vault.vault.azure.net/keys/test-key-3",
"name": "test-key-3"
},
{
"attributes": {
"created": "2022-04-10T17:57:43+00:00",
"enabled": true,
"expires": keyExpired,
"notBefore": null,
"updated": "2022-04-10T17:57:43+00:00"
},
"kid": "https://test-vault.vault.azure.net/keys/test-key-4",
"name": "test-key-4"
}
];

const createCache = (err, list, keys) => {
return {
vaults: {
list: {
'eastus': {
err: err,
data: list
}
},
getKeys: {
'eastus': {
'/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.KeyVault/vaults/test-vault': {
err: err,
data: keys
}
}
}
}
}
};

describe('keyVaultKeyExpiryNonRbac', function() {
describe('run', function() {
it('should give passing result if no key vaults found', function(done) {
const callback = (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(0);
expect(results[0].message).to.include('No Key Vaults found');
expect(results[0].region).to.equal('eastus');
done()
};

auth.run(createCache(null, [], {}), {}, callback);
});

it('should give passing result if no non-RBAC key vaults found', function(done) {
const callback = (err, results) => {
expect(results.length).to.equal(0); // No results since we skip RBAC vaults
done()
};

auth.run(createCache(null, [listKeyVaults[1]], []), {}, callback);
});

it('should give passing result if expiration is not set on keys in non-RBAC vault', function(done) {
const callback = (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(0);
expect(results[0].message).to.include('Key expiration is not enabled in non-RBAC vault');
alphadev4 marked this conversation as resolved.
Show resolved Hide resolved
expect(results[0].region).to.equal('eastus');
done()
};

auth.run(createCache(null, [listKeyVaults[0]], [getKeys[0]]), {}, callback);
});

it('should give passing result if expiry date is not yet reached in non-RBAC vault', function(done) {
const callback = (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(0);
expect(results[0].message).to.include('Key in non-RBAC vault expires in');
alphadev4 marked this conversation as resolved.
Show resolved Hide resolved
expect(results[0].region).to.equal('eastus');
done()
};

auth.run(createCache(null, [listKeyVaults[0]], [getKeys[1]]), { key_vault_key_expiry_fail: '30' }, callback);
});

it('should give failing result if the key has expired in non-RBAC vault', function(done) {
const callback = (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(2);
expect(results[0].message).to.include('Key in non-RBAC vault expired');
alphadev4 marked this conversation as resolved.
Show resolved Hide resolved
expect(results[0].region).to.equal('eastus');
done()
};

auth.run(createCache(null, [listKeyVaults[0]], [getKeys[3]]), { key_vault_key_expiry_fail: '40' }, callback);
});

it('should give failing result if the key expires within failure expiry date in non-RBAC vault', function(done) {
const callback = (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(2);
expect(results[0].message).to.include('Key in non-RBAC vault expires');
alphadev4 marked this conversation as resolved.
Show resolved Hide resolved
expect(results[0].region).to.equal('eastus');
done()
};

auth.run(createCache(null, [listKeyVaults[0]], [getKeys[2]]), { key_vault_key_expiry_fail: '40' }, callback);
});
});
});
Loading