Skip to content

Commit

Permalink
Revised keyVaultKeyExpiryNonRbac (2)
Browse files Browse the repository at this point in the history
  • Loading branch information
AkhtarAmir authored and AkhtarAmir committed Nov 6, 2024
1 parent 3d86c5d commit e939cd0
Show file tree
Hide file tree
Showing 3 changed files with 278 additions and 1 deletion.
3 changes: 2 additions & 1 deletion exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -1063,7 +1063,8 @@ module.exports = {
'manageKeyAccessAndPermissions' : require(__dirname + '/plugins/azure/keyvaults/manageKeyAccessAndPermissions.js'),
'rsaCertificateKeySize' : require(__dirname + '/plugins/azure/keyvaults/rsaCertificateKeySize.js'),
'keyVaultSecretExpiry' : require(__dirname + '/plugins/azure/keyvaults/keyVaultSecretExpiry.js'),
'keyVaultKeyExpiry' : require(__dirname + '/plugins/azure/keyvaults/keyVaultKeyExpiry.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',
category: 'Key Vaults',
domain: 'Application Integration',
severity: 'High',
description: 'Ensures that expiration date is set for all keys in Non-RBAC Key Vaults.',
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: {
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.key_vault_key_expiry_fail || this.settings.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);
} 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);
} 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);
} else if (difference > 0){
helpers.addResult(results, 2,
`Key in non-RBAC vault expires in ${difference} days`, location, keyId);
} else {
helpers.addResult(results, 2,
`Key in non-RBAC vault expired ${Math.abs(difference)} days ago`, location, keyId);
}
} else {
helpers.addResult(results, 0,
'Key expiration is not enabled in non-RBAC vault', location, keyId);
}
});
}
});

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');
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');
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');
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');
expect(results[0].region).to.equal('eastus');
done()
};

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

0 comments on commit e939cd0

Please sign in to comment.