Socket
Socket
Sign inDemoInstall

@economist/aws-key-rotator

Package Overview
Dependencies
37
Maintainers
25
Versions
6
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.3 to 1.1.0

CHANGELOG.md

16

lib/keyRotation/keyRotator.d.ts

@@ -23,3 +23,3 @@ import { IAM } from "aws-sdk";

/**
* Rotate the given Access Keys for the given IAM User.
* Performs the core key rotation steps and attempts to self-heal if there are any errors.
* @param user the IAM User that the Access Keys belong to

@@ -30,2 +30,16 @@ * @param keys the Access Keys to rotate

/**
* Performs the core key rotation steps: creating a new key, propagating it as required and
* deleting any old keys.
* @param user the IAM User that the Access Keys belong to
* @param keys the Access Keys to rotate
*/
private performCoreKeyRotation;
/**
* Performs a self-healing step by deleting any inactive keys and then re-running the
* core key rotation
* @param user the IAM User that the Access Keys belong to
* @param keys the Access Keys to rotate
*/
private selfHeal;
/**
* Creates a new Access Key and updates the relevant CircleCI environment variables.

@@ -32,0 +46,0 @@ * @param user the IAM User to create a new Access Key for

62

lib/keyRotation/keyRotator.js

@@ -20,3 +20,3 @@ "use strict";

console.error(`There was an error during key rotation: ${JSON.stringify(err)}`);
return Promise.reject(err);
throw err;
});

@@ -29,3 +29,3 @@ };

this.getExistingKeys = (user) => {
console.log(`Retrieving existing keys for User ${user}`);
console.log(`Retrieving existing keys for ${user}`);
const params = {

@@ -37,8 +37,8 @@ UserName: user,

.then((data) => {
console.log(`Retrieved the following keys for User ${user}: ${JSON.stringify(data.AccessKeyMetadata)}`);
return Promise.resolve(data.AccessKeyMetadata);
console.log(`Retrieved the following keys for ${user}: ${JSON.stringify(data.AccessKeyMetadata)}`);
return data.AccessKeyMetadata;
});
};
/**
* Rotate the given Access Keys for the given IAM User.
* Performs the core key rotation steps and attempts to self-heal if there are any errors.
* @param user the IAM User that the Access Keys belong to

@@ -48,14 +48,36 @@ * @param keys the Access Keys to rotate

this.performKeyRotation = (user, keys) => {
return this.createNewKey(user)
.then((key) => this.handleNewKey(user, key))
.then(() => this.deleteKeys(user, keys))
return this.performCoreKeyRotation(user, keys)
.catch((err) => {
// Try to self-heal by removing any inactive keys but still throw an error
// as we haven't created/handled the new key correctly
console.log(`Attempting to delete inactive keys`);
return this.deleteKeys(user, keys, (key) => key.Status === keyStatus_1.INACTIVE)
.then(() => Promise.reject(err));
console.error(`There was an error during key rotation: ${JSON.stringify(err)}`);
return this.selfHeal(user, keys);
});
};
/**
* Performs the core key rotation steps: creating a new key, propagating it as required and
* deleting any old keys.
* @param user the IAM User that the Access Keys belong to
* @param keys the Access Keys to rotate
*/
this.performCoreKeyRotation = (user, keys) => {
console.log(`Beginning key rotation.`);
return this.createNewKey(user)
.then((key) => this.handleNewKey(user, key))
.then(() => {
console.log(`Deleting old keys.`);
return this.deleteKeys(user, keys);
})
.then(() => console.log(`Key rotation for ${user} completed succesfully.`));
};
/**
* Performs a self-healing step by deleting any inactive keys and then re-running the
* core key rotation
* @param user the IAM User that the Access Keys belong to
* @param keys the Access Keys to rotate
*/
this.selfHeal = (user, keys) => {
console.log(`Attempting to self-heal by deleting any inactive keys`);
return this.deleteKeys(user, keys, (key) => key.Status === keyStatus_1.INACTIVE)
.then(() => this.performCoreKeyRotation(user, keys));
};
/**
* Creates a new Access Key and updates the relevant CircleCI environment variables.

@@ -65,3 +87,3 @@ * @param user the IAM User to create a new Access Key for

this.createNewKey = (user) => {
console.log(`Creating a new Access Key for User: ${user}`);
console.log(`Creating a new Access Key for ${user}`);
const params = {

@@ -75,3 +97,3 @@ UserName: user,

console.log(`Created a new Access Key with ID: ${newKey.AccessKeyId}`);
return Promise.resolve(newKey);
return newKey;
});

@@ -86,7 +108,9 @@ };

this.handleNewKey = (user, key) => {
console.log(`Handling the newly created key.`);
return this.newKeyHandler(key)
.then(() => console.log(`Successfully handled the new key.`))
.catch((err) => {
console.error(`New Key Handler failed with error: ${JSON.stringify(err)}. New key will be deleted.`);
return this.deleteKey(user, key)
.then(() => Promise.reject(err));
.then(() => { throw err; });
});

@@ -115,3 +139,3 @@ };

return Promise.all(promises)
.then(() => Promise.resolve());
.then(() => { return; });
};

@@ -124,3 +148,3 @@ /**

this.deleteKey = (user, key) => {
console.log(`Deleting Access Key: ${key.AccessKeyId} for User ${user}`);
console.log(`Deleting Access Key: ${key.AccessKeyId} for ${user}`);
const params = {

@@ -134,3 +158,3 @@ AccessKeyId: key.AccessKeyId,

console.log(`Deleted Access Key: ${params.AccessKeyId}`);
return Promise.resolve(data);
return data;
});

@@ -137,0 +161,0 @@ };

{
"name": "@economist/aws-key-rotator",
"version": "1.0.3",
"version": "1.1.0",
"description": "AWS Access Key Rotation",

@@ -16,3 +16,4 @@ "keywords": [

"clean-build": "npm run clean && npm run build",
"test": "jest --coverage"
"test": "jest --coverage",
"prepublishOnly": "npm run clean-build"
},

@@ -19,0 +20,0 @@ "author": {

@@ -72,3 +72,3 @@ # aws-key-rotator

Any errors that occur after the user's existing key(s) have been retrieved will trigger a clean-up stage that attempts to delete any inactive keys from the list of existing keys. AWS restricts users to having at most 2 Access Keys at any one time therefore rotation will fail if 2 keys are present as a new key cannot be created. Deleting inactive keys ensures that the [KeyRotator](#KeyRotator) can "self-heal" from this state and should allow it run successfully on its next attempt.
Any errors that occur after the user's existing key(s) have been retrieved will trigger a clean-up stage that attempts to delete any inactive keys from the list of existing keys and then performs a re-run of the core rotation steps. AWS restricts users to having at most 2 Access Keys at any one time, therefore rotation will fail if 2 keys are present as a new key cannot be created. Deleting inactive keys ensures that the [KeyRotator](#KeyRotator) can "self-heal" from this state and should allow it to successfully rotate the keys on the re-run.

@@ -75,0 +75,0 @@ Additionally, if the user-defined [NewKeyHandler](#NewKeyHandler) function returns a rejected `Promise`, indicating that the required handling failed, then the [KeyRotator](#KeyRotator) will delete the newly created Access Key ensuring that the user is not left with an unusable, active Access Key.

@@ -104,17 +104,16 @@ import { Callback } from "aws-lambda";

.then(() => {
fail();
done();
})
.catch((err) => {
// Expect there to be 1 key
expect(keys.length).toBe(1);
// Expect existing inactive key to have been removed
// Expect existing keys to have been removed
expect(keys.indexOf(existingInactiveKey)).toBe(-1);
expect(keys.indexOf(existingActiveKey)).toBe(-1);
// Expect existing active key to be present and still active
expect(existingActiveKey.Status).toBe(ACTIVE);
expect(keys.indexOf(existingActiveKey)).toBeGreaterThanOrEqual(0);
// Expect new key to be present and active
expect(newKey.Status).toBe(ACTIVE);
expect(keys.indexOf(newKey)).toBeGreaterThanOrEqual(0);
done();
})
.catch((err) => {
fail();
});

@@ -132,15 +131,16 @@ });

.then(() => {
fail();
done();
})
.catch((err) => {
// Expect there to be 2 keys
expect(keys.length).toBe(0);
// Expect there to be 1 key
expect(keys.length).toBe(1);
// Expect both existing keys to have been removed
// Expect existing keys to have been removed
expect(keys.indexOf(firstExistingKey)).toBe(-1);
expect(keys.indexOf(secondExistingKey)).toBe(-1);
// Expect new key to be present and active
expect(newKey.Status).toBe(ACTIVE);
expect(keys.indexOf(newKey)).toBeGreaterThanOrEqual(0);
done();
})
.catch((err) => {
fail();
});

@@ -159,3 +159,2 @@ });

fail();
done();
})

@@ -182,3 +181,2 @@ .catch((err) => {

.then(() => {
fail();
done();

@@ -204,3 +202,2 @@ })

fail();
done();
})

@@ -229,3 +226,2 @@ .catch((err) => {

fail();
done();
})

@@ -254,3 +250,2 @@ .catch((err) => {

fail();
done();
})

@@ -257,0 +252,0 @@ .catch((err) => {

@@ -31,3 +31,3 @@ import { AWSError, IAM } from "aws-sdk";

console.error(`There was an error during key rotation: ${JSON.stringify(err)}`);
return Promise.reject(err);
throw err;
});

@@ -41,3 +41,3 @@ }

private getExistingKeys = (user: string): Promise<AccessKeyMetadata[]> => {
console.log(`Retrieving existing keys for User ${user}`);
console.log(`Retrieving existing keys for ${user}`);
const params: ListAccessKeysRequest = {

@@ -50,4 +50,4 @@ UserName: user,

.then((data) => {
console.log(`Retrieved the following keys for User ${user}: ${JSON.stringify(data.AccessKeyMetadata)}`);
return Promise.resolve(data.AccessKeyMetadata);
console.log(`Retrieved the following keys for ${user}: ${JSON.stringify(data.AccessKeyMetadata)}`);
return data.AccessKeyMetadata;
});

@@ -57,3 +57,3 @@ }

/**
* Rotate the given Access Keys for the given IAM User.
* Performs the core key rotation steps and attempts to self-heal if there are any errors.
* @param user the IAM User that the Access Keys belong to

@@ -63,11 +63,6 @@ * @param keys the Access Keys to rotate

private performKeyRotation = (user: string, keys: AccessKeyMetadata[]) => {
return this.createNewKey(user)
.then((key) => this.handleNewKey(user, key))
.then(() => this.deleteKeys(user, keys))
return this.performCoreKeyRotation(user, keys)
.catch((err) => {
// Try to self-heal by removing any inactive keys but still throw an error
// as we haven't created/handled the new key correctly
console.log(`Attempting to delete inactive keys`);
return this.deleteKeys(user, keys, (key) => key.Status === INACTIVE)
.then(() => Promise.reject(err));
console.error(`There was an error during key rotation: ${JSON.stringify(err)}`);
return this.selfHeal(user, keys);
});

@@ -77,2 +72,31 @@ }

/**
* Performs the core key rotation steps: creating a new key, propagating it as required and
* deleting any old keys.
* @param user the IAM User that the Access Keys belong to
* @param keys the Access Keys to rotate
*/
private performCoreKeyRotation = (user: string, keys: AccessKeyMetadata[]) => {
console.log(`Beginning key rotation.`);
return this.createNewKey(user)
.then((key) => this.handleNewKey(user, key))
.then(() => {
console.log(`Deleting old keys.`);
return this.deleteKeys(user, keys);
})
.then(() => console.log(`Key rotation for ${user} completed succesfully.`));
}
/**
* Performs a self-healing step by deleting any inactive keys and then re-running the
* core key rotation
* @param user the IAM User that the Access Keys belong to
* @param keys the Access Keys to rotate
*/
private selfHeal = (user: string, keys: AccessKeyMetadata[]) => {
console.log(`Attempting to self-heal by deleting any inactive keys`);
return this.deleteKeys(user, keys, (key) => key.Status === INACTIVE)
.then(() => this.performCoreKeyRotation(user, keys));
}
/**
* Creates a new Access Key and updates the relevant CircleCI environment variables.

@@ -82,3 +106,3 @@ * @param user the IAM User to create a new Access Key for

private createNewKey = (user: string): Promise<AccessKey> => {
console.log(`Creating a new Access Key for User: ${user}`);
console.log(`Creating a new Access Key for ${user}`);

@@ -94,3 +118,3 @@ const params: CreateAccessKeyRequest = {

console.log(`Created a new Access Key with ID: ${newKey.AccessKeyId}`);
return Promise.resolve(newKey);
return newKey;
});

@@ -106,7 +130,9 @@ }

private handleNewKey = (user: string, key: AccessKey) => {
console.log(`Handling the newly created key.`);
return this.newKeyHandler(key)
.then(() => console.log(`Successfully handled the new key.`))
.catch((err) => {
console.error(`New Key Handler failed with error: ${JSON.stringify(err)}. New key will be deleted.`);
return this.deleteKey(user, key)
.then(() => Promise.reject(err));
.then(() => { throw err; });
});

@@ -140,3 +166,3 @@ }

return Promise.all(promises)
.then(() => Promise.resolve());
.then(() => { return; });
}

@@ -150,3 +176,3 @@

private deleteKey = (user: string, key: AccessKeyMetadata) => {
console.log(`Deleting Access Key: ${key.AccessKeyId} for User ${user}`);
console.log(`Deleting Access Key: ${key.AccessKeyId} for ${user}`);
const params: DeleteAccessKeyRequest = {

@@ -161,5 +187,5 @@ AccessKeyId: key.AccessKeyId!,

console.log(`Deleted Access Key: ${params.AccessKeyId}`);
return Promise.resolve(data);
return data;
});
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc