Skip to content

Commit

Permalink
feat(elasticloadbalancingv2): ALPN policy support for NLB listener (#…
Browse files Browse the repository at this point in the history
…15956)

Allow specifying the ALPN Policy
----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
project0 authored Sep 3, 2021
1 parent ad425d0 commit 5427578
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Duration, IResource, Resource } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { BaseListener, BaseListenerLookupOptions } from '../shared/base-listener';
import { HealthCheck } from '../shared/base-target-group';
import { Protocol, SslPolicy } from '../shared/enums';
import { AlpnPolicy, Protocol, SslPolicy } from '../shared/enums';
import { IListenerCertificate } from '../shared/listener-certificate';
import { validateNetworkProtocol } from '../shared/util';
import { NetworkListenerAction } from './network-listener-action';
Expand Down Expand Up @@ -65,6 +65,17 @@ export interface BaseNetworkListenerProps {
* @default - Current predefined security policy.
*/
readonly sslPolicy?: SslPolicy;


/**
* Application-Layer Protocol Negotiation (ALPN) is a TLS extension that is sent on the initial TLS handshake hello messages.
* ALPN enables the application layer to negotiate which protocols should be used over a secure connection, such as HTTP/1 and HTTP/2.
*
* Can only be specified together with Protocol TLS.
*
* @default - None
*/
readonly alpnPolicy?: AlpnPolicy;
}

/**
Expand Down Expand Up @@ -168,12 +179,17 @@ export class NetworkListener extends BaseListener implements INetworkListener {
throw new Error('Protocol must be TLS when certificates have been specified');
}

if (proto !== Protocol.TLS && props.alpnPolicy) {
throw new Error('Protocol must be TLS when alpnPolicy have been specified');
}

super(scope, id, {
loadBalancerArn: props.loadBalancer.loadBalancerArn,
protocol: proto,
port: props.port,
sslPolicy: props.sslPolicy,
certificates: props.certificates,
alpnPolicy: props.alpnPolicy ? [props.alpnPolicy] : undefined,
});

this.loadBalancer = props.loadBalancer;
Expand Down
35 changes: 33 additions & 2 deletions packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export enum ApplicationProtocol {
/**
* HTTPS
*/
HTTPS = 'HTTPS'
HTTPS = 'HTTPS',
}

/**
Expand Down Expand Up @@ -172,6 +172,37 @@ export enum TargetType {
LAMBDA = 'lambda',
}

/**
* Application-Layer Protocol Negotiation Policies for network load balancers.
* Which protocols should be used over a secure connection.
*/
export enum AlpnPolicy {
/**
* Negotiate only HTTP/1.*. The ALPN preference list is http/1.1, http/1.0
*/
HTTP1_ONLY = 'HTTP1Only',

/**
* Negotiate only HTTP/2. The ALPN preference list is h2
*/
HTTP2_ONLY = 'HTTP2Only',

/**
* Prefer HTTP/1.* over HTTP/2 (which can be useful for HTTP/2 testing). The ALPN preference list is http/1.1, http/1.0, h2
*/
HTTP2_OPTIONAL = 'HTTP2Optional',

/**
* Prefer HTTP/2 over HTTP/1.*. The ALPN preference list is h2, http/1.1, http/1.0
*/
HTTP2_PREFERRED = 'HTTP2Preferred',

/**
* Do not negotiate ALPN
*/
NONE = 'None',
}

/**
* Load balancing algorithmm type for target groups
*/
Expand All @@ -185,4 +216,4 @@ export enum TargetGroupLoadBalancingAlgorithmType {
* least_outstanding_requests
*/
LEAST_OUTSTANDING_REQUESTS = 'least_outstanding_requests',
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,50 @@ describe('tests', () => {
});
});

test('Trivial add TLS listener with ALPN', () => {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'Stack');
const lb = new elbv2.NetworkLoadBalancer(stack, 'LB', { vpc });
const cert = new acm.Certificate(stack, 'Certificate', {
domainName: 'example.com',
});

// WHEN
lb.addListener('Listener', {
port: 443,
protocol: elbv2.Protocol.TLS,
alpnPolicy: elbv2.AlpnPolicy.HTTP2_ONLY,
certificates: [elbv2.ListenerCertificate.fromCertificateManager(cert)],
sslPolicy: elbv2.SslPolicy.TLS12,
defaultTargetGroups: [
new elbv2.NetworkTargetGroup(stack, 'Group', { vpc, port: 80 }),
],
});

// THEN
expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', {
Protocol: 'TLS',
Port: 443,
AlpnPolicy: ['HTTP2Only'],
Certificates: [{ CertificateArn: { Ref: 'Certificate4E7ABB08' } }],
SslPolicy: 'ELBSecurityPolicy-TLS-1-2-2017-01',
});
});

test('Incompatible Protocol with ALPN', () => {
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'Stack');
const lb = new elbv2.NetworkLoadBalancer(stack, 'LB', { vpc });

expect(() => lb.addListener('Listener', {
port: 443,
protocol: elbv2.Protocol.TCP,
alpnPolicy: elbv2.AlpnPolicy.HTTP2_OPTIONAL,
defaultTargetGroups: [new elbv2.NetworkTargetGroup(stack, 'Group', { vpc, port: 80 })],
})).toThrow(/Protocol must be TLS when alpnPolicy have been specified/);
});

test('Invalid Protocol listener', () => {
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'Stack');
Expand Down

0 comments on commit 5427578

Please sign in to comment.