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(appmesh): add listener TLS certificates for VirtualNodes and VirtualGateways #11863

Merged
merged 12 commits into from Jan 12, 2021
Merged
38 changes: 38 additions & 0 deletions packages/@aws-cdk/aws-appmesh/README.md
Expand Up @@ -241,6 +241,44 @@ The `backends` property can be added with `node.addBackend()`. We define a virtu

The `backendsDefaultClientPolicy` property are added to the node while creating the virtual node. These are virtual node's service backends client policy defaults.

## Adding TLS to a listener

The `tlsCertificate` property can be added to a Virtual Node listener or Virtual Gateway listener to add TLS configuration.
A certificate from AWS Certificate Manager can be incorporated or a customer provided certificate can be specified with a `certificateChain` path file and a `privateKey` file path.

```typescript
alexbrjo marked this conversation as resolved.
Show resolved Hide resolved
import * as certificatemanager from '@aws-cdk/aws-certificatemanager';

// A Virtual Node with listener TLS from an ACM provided certificate
const cert = new certificatemanager.Certificate(this, 'cert', {...});

const node = new appmesh.VirtualNode(stack, 'node', {
mesh,
dnsHostName: 'node',
listeners: [appmesh.VirtualNodeListener.grpc({
port: 80,
tlsCertificate: appmesh.TlsCertificate.acm({
certificate: cert,
tlsMode: TlsMode.STRICT,
}),
})],
});

// A Virtual Gateway with listener TLS from a customer provided file certificate
const gateway = new appmesh.VirtualGateway(this, 'gateway', {
mesh: mesh,
listeners: [appmesh.VirtualGatewayListener.grpc({
port: 8080,
tlsCertificate: appmesh.TlsCertificate.file({
certificateChain: 'path/to/certChain',
privateKey: 'path/to/privateKey',
tlsMode: TlsMode.STRICT,
}),
})],
virtualGatewayName: 'gateway',
});
```

## Adding a Route

A `route` is associated with a virtual router, and it's used to match requests for a virtual router and distribute traffic accordingly to its associated virtual nodes.
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-appmesh/lib/index.ts
Expand Up @@ -5,6 +5,7 @@ export * from './route';
export * from './service-discovery';
export * from './route-spec';
export * from './shared-interfaces';
export * from './tls-certificate';
export * from './virtual-node';
export * from './virtual-router';
export * from './virtual-router-listener';
Expand Down
173 changes: 173 additions & 0 deletions packages/@aws-cdk/aws-appmesh/lib/tls-certificate.ts
@@ -0,0 +1,173 @@
import * as acm from '@aws-cdk/aws-certificatemanager';
import * as cdk from '@aws-cdk/core';
import { CfnVirtualNode } from './appmesh.generated';

/**
* Enum of supported TLS modes
*/
export enum TlsMode {
/**
* Only accept encrypted traffic
*/
STRICT = 'STRICT',

/**
* Accept encrypted and plaintext traffic.
*/
PERMISSIVE = 'PERMISSIVE',

/**
* TLS is disabled, only accept plaintext traffic.
*/
DISABLED = 'DISABLED',
}

/**
* A wrapper for the tls config returned by {@link TlsCertificate.bind}
*/
export interface TlsCertificateConfig {
/**
* The CFN shape for a listener TLS certificate
*/
readonly tlsCertificate: CfnVirtualNode.ListenerTlsCertificateProperty,

/**
* The TLS mode.
*/
readonly tlsMode: TlsMode;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be optional, and should be defaulted by the classes that use this interface.

}

/**
* ACM Certificate Properties
*/
export interface AcmCertificateOptions {
/**
* The TLS mode.
*/
readonly tlsMode: TlsMode;
skinny85 marked this conversation as resolved.
Show resolved Hide resolved

/**
* The ACM certificate
*/
readonly certificate: acm.ICertificate;
}

/**
* File Certificate Properties
*/
export interface FileCertificateOptions {
/**
* The TLS mode.
*/
readonly tlsMode: TlsMode;

/**
* The file path of the certificate chain file.
*/
readonly certificateChainPath: string;

/**
* The file path of the private key file.
*/
readonly privateKeyPath: string;
}
alexbrjo marked this conversation as resolved.
Show resolved Hide resolved

/**
* Represents a TLS certificate
*/
export abstract class TlsCertificate {
/**
* Returns an File TLS Certificate
*/
public static file(props: FileCertificateOptions): TlsCertificate {
return new FileTlsCertificate(props);
}

/**
* Returns an ACM TLS Certificate
*/
public static acm(props: AcmCertificateOptions): TlsCertificate {
return new AcmTlsCertificate(props);
}

/**
* Returns TLS certificate based provider.
*/
public abstract bind(_scope: cdk.Construct): TlsCertificateConfig;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can safely call this scope, the "unused parameter" validation does not apply to abstract methods 🙂.


}

/**
* Represents a ACM provided TLS certificate
*/
class AcmTlsCertificate extends TlsCertificate {
/**
* The TLS mode.
*
* @default - TlsMode.DISABLED
*/
readonly tlsMode: TlsMode;

/**
* The ARN of the ACM certificate
*/
readonly acmCertificate: acm.ICertificate;

constructor(props: AcmCertificateOptions) {
super();
this.tlsMode = props.tlsMode ? props.tlsMode : TlsMode.DISABLED;
this.acmCertificate = props.certificate;
}

bind(_scope: cdk.Construct): TlsCertificateConfig {
return {
tlsCertificate: {
acm: {
certificateArn: this.acmCertificate.certificateArn,
},
},
tlsMode: this.tlsMode,
};
}
}

/**
* Represents a file provided TLS certificate
*/
class FileTlsCertificate extends TlsCertificate {
/**
* The TLS mode.
*
* @default - TlsMode.DISABLED
*/
readonly tlsMode: TlsMode;

/**
* The file path of the certificate chain file.
*/
readonly certificateChain: string;

/**
* The file path of the private key file.
*/
readonly privateKey: string;

constructor(props: FileCertificateOptions) {
super();
this.tlsMode = props.tlsMode ? props.tlsMode : TlsMode.DISABLED;
alexbrjo marked this conversation as resolved.
Show resolved Hide resolved
this.certificateChain = props.certificateChainPath;
this.privateKey = props.privateKeyPath;
}

bind(_scope: cdk.Construct): TlsCertificateConfig {
return {
tlsCertificate: {
file: {
certificateChain: this.certificateChain,
privateKey: this.privateKey,
},
},
tlsMode: this.tlsMode,
};
}
}