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

feat(iot-alpha): support for account audit configuration #31661

Merged
merged 14 commits into from
Oct 7, 2024
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
48 changes: 48 additions & 0 deletions packages/@aws-cdk/aws-iot-alpha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,51 @@ new iot.Logging(this, 'Logging', {
```

**Note**: All logs are forwarded to the `AWSIotLogsV2` log group in CloudWatch.

## Audit

An [AWS IoT Device Defender audit looks](https://docs.aws.amazon.com/iot-device-defender/latest/devguide/device-defender-audit.html) at account- and device-related settings and policies to ensure security measures are in place.
An audit can help you detect any drifts from security best practices or access policies.

### Account Audit Configuration

The IoT audit includes [various audit checks](https://docs.aws.amazon.com/iot-device-defender/latest/devguide/device-defender-audit-checks.html), and it is necessary to configure settings to enable those checks.

You can enable an account audit configuration with the following code:

```ts
// Audit notification are sent to the SNS topic
declare const targetTopic: sns.ITopic;

new iot.AccountAuditConfiguration(this, 'AuditConfiguration', {
targetTopic,
});
```

By default, all audit checks are enabled, but it is also possible to enable only specific audit checks.

```ts
new iot.AccountAuditConfiguration(this, 'AuditConfiguration', {
checkConfiguration: {
// enabled
authenticatedCognitoRoleOverlyPermissiveCheck: true,
// enabled by default
caCertificateExpiringCheck: undefined,
// disabled
caCertificateKeyQualityCheck: false,
conflictingClientIdsCheck: false,
deviceCertificateExpiringCheck: false,
deviceCertificateKeyQualityCheck: false,
deviceCertificateSharedCheck: false,
intermediateCaRevokedForActiveDeviceCertificatesCheck: false,
ioTPolicyPotentialMisConfigurationCheck: false,
iotPolicyOverlyPermissiveCheck: false,
iotRoleAliasAllowsAccessToUnusedServicesCheck: false,
iotRoleAliasOverlyPermissiveCheck: false,
loggingDisabledCheck: false,
revokedCaCertificateStillActiveCheck: false,
revokedDeviceCertificateStillActiveCheck: false,
unauthenticatedCognitoRoleOverlyPermissiveCheck: false,
},
});
```
3 changes: 2 additions & 1 deletion packages/@aws-cdk/aws-iot-alpha/awslint.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"exclude": [
"no-unused-type:@aws-cdk/aws-iot-alpha.ActionConfig",
"props-physical-name:@aws-cdk/aws-iot-alpha.LoggingProps"
"props-physical-name:@aws-cdk/aws-iot-alpha.LoggingProps",
"props-physical-name:@aws-cdk/aws-iot-alpha.AccountAuditConfigurationProps"
]
}
287 changes: 287 additions & 0 deletions packages/@aws-cdk/aws-iot-alpha/lib/audit-configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
import { Resource, Stack, IResource } from 'aws-cdk-lib/core';
import { Construct } from 'constructs';
import * as iot from 'aws-cdk-lib/aws-iot';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as sns from 'aws-cdk-lib/aws-sns';

/**
* Represents AWS IoT Audit Configuration
*/
export interface IAccountAuditConfiguration extends IResource {
/**
* The account ID
* @attribute
*/
readonly accountId: string;
}

/**
* The types of audit checks
*
* @see https://docs.aws.amazon.com/iot-device-defender/latest/devguide/device-defender-audit-checks.html
*/
export interface CheckConfiguration {
/**
* Checks the permissiveness of an authenticated Amazon Cognito identity pool role.
*
* For this check, AWS IoT Device Defender audits all Amazon Cognito identity pools that have been used to connect to the AWS IoT message broker
* during the 31 days before the audit is performed.
*
* @default true
*/
readonly authenticatedCognitoRoleOverlyPermissiveCheck?: boolean;

/**
* Checks if a CA certificate is expiring.
*
* This check applies to CA certificates expiring within 30 days or that have expired.
*
* @default true
*/
readonly caCertificateExpiringCheck?: boolean;

/**
* Checks the quality of the CA certificate key.
*
* The quality checks if the key is in a valid format, not expired, and if the key meets a minimum required size.
*
* This check applies to CA certificates that are ACTIVE or PENDING_TRANSFER.
*
* @default true
*/
readonly caCertificateKeyQualityCheck?: boolean;

/**
* Checks if multiple devices connect using the same client ID.
*
* @default true
*/
readonly conflictingClientIdsCheck?: boolean;

/**
* Checks if a device certificate is expiring.
*
* This check applies to device certificates expiring within 30 days or that have expired.
*
* @default true
*/
readonly deviceCertificateExpiringCheck?: boolean;

/**
* Checks the quality of the device certificate key.
*
* The quality checks if the key is in a valid format, not expired, signed by a registered certificate authority,
* and if the key meets a minimum required size.
*
* @default true
*/
readonly deviceCertificateKeyQualityCheck?: boolean;

/**
* Checks if multiple concurrent connections use the same X.509 certificate to authenticate with AWS IoT.
*
* @default true
*/
readonly deviceCertificateSharedCheck?: boolean;

/**
* Checks if device certificates are still active despite being revoked by an intermediate CA.
*
* @default true
*/
readonly intermediateCaRevokedForActiveDeviceCertificatesCheck?: boolean;

/**
* Checks the permissiveness of a policy attached to an authenticated Amazon Cognito identity pool role.
*
* @default true
*/
readonly iotPolicyOverlyPermissiveCheck?: boolean;

/**
* Checks if an AWS IoT policy is potentially misconfigured.
*
* Misconfigured policies, including overly permissive policies, can cause security incidents like allowing devices access to unintended resources.
*
* This check is a warning for you to make sure that only intended actions are allowed before updating the policy.
*
* @default true
*/
readonly ioTPolicyPotentialMisConfigurationCheck?: boolean;

/**
* Checks if a role alias has access to services that haven't been used for the AWS IoT device in the last year.
*
* @default true
*/
readonly iotRoleAliasAllowsAccessToUnusedServicesCheck?: boolean;

/**
* Checks if the temporary credentials provided by AWS IoT role aliases are overly permissive.
*
* @default true
*/
readonly iotRoleAliasOverlyPermissiveCheck?: boolean;

/**
* Checks if AWS IoT logs are disabled.
*
* @default true
*/
readonly loggingDisabledCheck?: boolean;

/**
* Checks if a revoked CA certificate is still active.
*
* @default true
*/
readonly revokedCaCertificateStillActiveCheck?: boolean;

/**
* Checks if a revoked device certificate is still active.
*
* @default true
*/
readonly revokedDeviceCertificateStillActiveCheck?: boolean;

/**
* Checks if policy attached to an unauthenticated Amazon Cognito identity pool role is too permissive.
*
* @default true
*/
readonly unauthenticatedCognitoRoleOverlyPermissiveCheck?: boolean;
}

/**
* Properties for defining AWS IoT Audit Configuration
*/
export interface AccountAuditConfigurationProps {
/**
* The target SNS topic to which audit notifications are sent.
*
* @default - no notifications are sent
*/
readonly targetTopic?: sns.ITopic;

/**
* Specifies which audit checks are enabled and disabled for this account.
*
* @default - all checks are enabled
*/
readonly checkConfiguration?: CheckConfiguration;
}

/**
* Defines AWS IoT Audit Configuration
*/
export class AccountAuditConfiguration extends Resource implements IAccountAuditConfiguration {
/**
* Import an existing AWS IoT Audit Configuration
*
* @param scope The parent creating construct (usually `this`)
* @param id The construct's name
* @param accountId The account ID
*/
public static fromAccountId(scope: Construct, id: string, accountId: string): IAccountAuditConfiguration {
class Import extends Resource implements IAccountAuditConfiguration {
public readonly accountId = accountId;
}
return new Import(scope, id);
}

/**
* The account ID
* @attribute
*/
public readonly accountId: string;

constructor(scope: Construct, id: string, props?: AccountAuditConfigurationProps) {
super(scope, id);

this.accountId = Stack.of(this).account;

const auditRole = new iam.Role(this, 'AuditRole', {
assumedBy: new iam.ServicePrincipal('iot.amazonaws.com'),
managedPolicies: [
iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSIoTDeviceDefenderAudit'),
],
});

new iot.CfnAccountAuditConfiguration(this, 'Resource', {
accountId: this.accountId,
auditCheckConfigurations: this.renderAuditCheckConfigurations(props?.checkConfiguration),
auditNotificationTargetConfigurations: this.renderAuditNotificationTargetConfigurations(props?.targetTopic),
roleArn: auditRole.roleArn,
});
}

/**
* Render the audit notification target configurations
*/
private renderAuditNotificationTargetConfigurations(
targetTopic?: sns.ITopic,
): iot.CfnAccountAuditConfiguration.AuditNotificationTargetConfigurationsProperty | undefined {
if (!targetTopic) {
return undefined;
}

const notificationRole = new iam.Role(this, 'NotificationRole', {
assumedBy: new iam.ServicePrincipal('iot.amazonaws.com'),
inlinePolicies: {
NotificationPolicy: new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
actions: ['sns:Publish'],
resources: [targetTopic.topicArn],
}),
],
}),
},
});

return {
sns: {
enabled: true,
targetArn: targetTopic.topicArn,
roleArn: notificationRole.roleArn,
},
};
}

/**
* Render the audit check configurations
*/
private renderAuditCheckConfigurations(checkConfiguration?: CheckConfiguration): iot.CfnAccountAuditConfiguration.AuditCheckConfigurationsProperty {
return {
authenticatedCognitoRoleOverlyPermissiveCheck:
this.renderAuditCheckConfiguration(checkConfiguration?.authenticatedCognitoRoleOverlyPermissiveCheck),
caCertificateExpiringCheck: this.renderAuditCheckConfiguration(checkConfiguration?.caCertificateExpiringCheck),
caCertificateKeyQualityCheck: this.renderAuditCheckConfiguration(checkConfiguration?.caCertificateKeyQualityCheck),
conflictingClientIdsCheck: this.renderAuditCheckConfiguration(checkConfiguration?.conflictingClientIdsCheck),
deviceCertificateExpiringCheck: this.renderAuditCheckConfiguration(checkConfiguration?.deviceCertificateExpiringCheck),
deviceCertificateKeyQualityCheck: this.renderAuditCheckConfiguration(checkConfiguration?.deviceCertificateKeyQualityCheck),
deviceCertificateSharedCheck: this.renderAuditCheckConfiguration(checkConfiguration?.deviceCertificateSharedCheck),
intermediateCaRevokedForActiveDeviceCertificatesCheck:
this.renderAuditCheckConfiguration(checkConfiguration?.intermediateCaRevokedForActiveDeviceCertificatesCheck),
iotPolicyOverlyPermissiveCheck: this.renderAuditCheckConfiguration(checkConfiguration?.iotPolicyOverlyPermissiveCheck),
ioTPolicyPotentialMisConfigurationCheck: this.renderAuditCheckConfiguration(checkConfiguration?.ioTPolicyPotentialMisConfigurationCheck),
iotRoleAliasAllowsAccessToUnusedServicesCheck:
this.renderAuditCheckConfiguration(checkConfiguration?.iotRoleAliasAllowsAccessToUnusedServicesCheck),
iotRoleAliasOverlyPermissiveCheck: this.renderAuditCheckConfiguration(checkConfiguration?.iotRoleAliasOverlyPermissiveCheck),
loggingDisabledCheck: this.renderAuditCheckConfiguration(checkConfiguration?.loggingDisabledCheck),
revokedCaCertificateStillActiveCheck: this.renderAuditCheckConfiguration(checkConfiguration?.revokedCaCertificateStillActiveCheck),
revokedDeviceCertificateStillActiveCheck:
this.renderAuditCheckConfiguration(checkConfiguration?.revokedDeviceCertificateStillActiveCheck),
unauthenticatedCognitoRoleOverlyPermissiveCheck:
this.renderAuditCheckConfiguration(checkConfiguration?.unauthenticatedCognitoRoleOverlyPermissiveCheck),
};
}

/**
* Render the audit check configuration
*/
private renderAuditCheckConfiguration(check?: boolean): iot.CfnAccountAuditConfiguration.AuditCheckConfigurationProperty | undefined {
return check === false ? undefined : { enabled: true };
}
}

1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-iot-alpha/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './action';
export * from './audit-configuration';
export * from './iot-sql';
export * from './logging';
export * from './topic-rule';
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-iot-alpha/rosetta/default.ts-fixture
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as actions from '@aws-cdk/aws-iot-actions-alpha';
import * as iot from '@aws-cdk/aws-iot-alpha';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as sns from 'aws-cdk-lib/aws-sns'

class Fixture extends Stack {
constructor(scope: Construct, id: string) {
Expand Down
Loading