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

chore: support nlb with sg #26744

Closed
wants to merge 2 commits into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Aws, Annotations, Duration, Token } from '../../../core';
import { ApplicationELBMetrics } from '../elasticloadbalancingv2-canned-metrics.generated';
import {
BaseTargetGroupProps, ITargetGroup, loadBalancerNameFromListenerArn, LoadBalancerTargetProps,
TargetGroupAttributes, TargetGroupBase, TargetGroupImportProps,
TargetGroupAttributes, TargetGroupBase, TargetGroupImportProps, ConnectableMember,
} from '../shared/base-target-group';
import { ApplicationProtocol, ApplicationProtocolVersion, Protocol, TargetType, TargetGroupLoadBalancingAlgorithmType } from '../shared/enums';
import { ImportedTargetGroupBase } from '../shared/imported';
Expand Down Expand Up @@ -582,21 +582,6 @@ export class ApplicationTargetGroup extends TargetGroupBase implements IApplicat
}
}

/**
* A connectable member of a target group
*/
interface ConnectableMember {
/**
* The connectable member
*/
connectable: ec2.IConnectable;

/**
* The port (range) the member is listening on
*/
portRange: ec2.Port;
}

/**
* A Target Group for Application Load Balancers
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { NetworkListenerAction } from './network-listener-action';
import { NetworkListenerCertificate } from './network-listener-certificate';
import { INetworkLoadBalancer } from './network-load-balancer';
import { INetworkLoadBalancerTarget, INetworkTargetGroup, NetworkTargetGroup } from './network-target-group';
import * as ec2 from '../../../aws-ec2';
import * as cxschema from '../../../cloud-assembly-schema';
import { Duration, Resource, Lazy } from '../../../core';
import * as cxapi from '../../../cx-api';
import { BaseListener, BaseListenerLookupOptions, IListener } from '../shared/base-listener';
import { HealthCheck } from '../shared/base-target-group';
import { AlpnPolicy, Protocol, SslPolicy } from '../shared/enums';
Expand Down Expand Up @@ -68,7 +70,7 @@ export interface BaseNetworkListenerProps {
readonly sslPolicy?: SslPolicy;

/**
* Application-Layer Protocol Negotiation (ALPN) is a TLS extension that is sent on the initial TLS handshake hello messages.
* Network-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.
Expand Down Expand Up @@ -137,24 +139,15 @@ export class NetworkListener extends BaseListener implements INetworkListener {
loadBalancerType: cxschema.LoadBalancerType.NETWORK,
});

class LookedUp extends Resource implements INetworkListener {
public listenerArn = props.listenerArn;
}

return new LookedUp(scope, id);
return new LookedUpNetworkListener(scope, id, props);
}

/**
* Import an existing listener
*/
public static fromNetworkListenerArn(scope: Construct, id: string, networkListenerArn: string): INetworkListener {
class Import extends Resource implements INetworkListener {
public listenerArn = networkListenerArn;
}

return new Import(scope, id);
public static fromNetworkListenerAttributes(scope: Construct, id: string, attrs: NetworkListenerAttributes): INetworkListener {
return new ImportedNetworkListener(scope, id, attrs);
}

/**
* The load balancer this listener is attached to
*/
Expand All @@ -170,6 +163,11 @@ export class NetworkListener extends BaseListener implements INetworkListener {
*/
private readonly protocol: Protocol;

/**
* Manage connections to this ApplicationListener
*/
public readonly connections?: ec2.Connections;

constructor(scope: Construct, id: string, props: NetworkListenerProps) {
const certs = props.certificates || [];
const proto = props.protocol || (certs.length > 0 ? Protocol.TLS : Protocol.TCP);
Expand Down Expand Up @@ -217,6 +215,10 @@ export class NetworkListener extends BaseListener implements INetworkListener {
}
}

registerConnectable(connectable: ec2.IConnectable, portRange: ec2.Port): void {
this.connections?.allowTo(connectable, portRange, 'Load balancer to target');
}

/**
* Add one or more certificates to this listener.
*
Expand Down Expand Up @@ -307,10 +309,122 @@ export class NetworkListener extends BaseListener implements INetworkListener {
}
}

/**
* Properties to reference an existing listener
*/
export interface NetworkListenerAttributes {
/**
* ARN of the listener
*/
readonly listenerArn: string;

/**
* Security group of the load balancer this listener is associated with
*/
readonly securityGroup: ec2.ISecurityGroup;

/**
* The default port on which this listener is listening
*/
readonly defaultPort?: number;

/**
* Whether the imported security group allows all outbound traffic or not when
* imported using `securityGroupId`
*
* Unless set to `false`, no egress rules will be added to the security group.
*
* @default true
*
* @deprecated use `securityGroup` instead
*/
readonly securityGroupAllowsAllOutbound?: boolean;
}

abstract class ExternalNetworkListener extends Resource implements INetworkListener {
/**
* Connections object.
*/
public abstract readonly connections: ec2.Connections;

/**
* ARN of the listener
*/
public abstract readonly listenerArn: string;

constructor(scope: Construct, id: string) {
super(scope, id);
}

/**
* Register that a connectable that has been added to this load balancer.
*
* Don't call this directly. It is called by NetworkTargetGroup.
*/
public registerConnectable(connectable: ec2.IConnectable, portRange: ec2.Port): void {
this.connections.allowTo(connectable, portRange, 'Load balancer to target');
}

/**
* Add one or more certificates to this listener.
*/
public addCertificates(id: string, certificates: IListenerCertificate[]): void {
new NetworkListenerCertificate(this, id, {
listener: this,
certificates,
});
}
};

/**
* Properties to reference an existing listener
*/
export interface INetworkListener extends IListener {
/**
* Register that a connectable that has been added to this load balancer.
*
* Don't call this directly. It is called by NetworkListenerTargetGroup.
*/
registerConnectable(connectable: ec2.IConnectable, portRange: ec2.Port): void;
}

class LookedUpNetworkListener extends ExternalNetworkListener {
public readonly listenerArn: string;
public readonly connections: ec2.Connections;

constructor(scope: Construct, id: string, props: cxapi.LoadBalancerListenerContextResponse) {
super(scope, id);

this.listenerArn = props.listenerArn;
this.connections = new ec2.Connections({
defaultPort: ec2.Port.tcp(props.listenerPort),
});

for (const securityGroupId of props.securityGroupIds) {
const securityGroup = ec2.SecurityGroup.fromLookupById(this, `SecurityGroup-${securityGroupId}`, securityGroupId);
this.connections.addSecurityGroup(securityGroup);
}
}
}

/**
* An imported application listener.
*/
class ImportedNetworkListener extends ExternalNetworkListener {
public readonly listenerArn: string;
public readonly connections: ec2.Connections;

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

this.listenerArn = props.listenerArn;
const defaultPort = props.defaultPort !== undefined ? ec2.Port.tcp(props.defaultPort) : undefined;

this.connections = new ec2.Connections({
securityGroups: [props.securityGroup],
defaultPort,
});
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { BaseNetworkListenerProps, NetworkListener } from './network-listener';
import * as cloudwatch from '../../../aws-cloudwatch';
import * as ec2 from '../../../aws-ec2';
import * as cxschema from '../../../cloud-assembly-schema';
import { Resource } from '../../../core';
import { Resource, Lazy } from '../../../core';
import * as cxapi from '../../../cx-api';
import { NetworkELBMetrics } from '../elasticloadbalancingv2-canned-metrics.generated';
import { BaseLoadBalancer, BaseLoadBalancerLookupOptions, BaseLoadBalancerProps, ILoadBalancerV2 } from '../shared/base-load-balancer';
Expand All @@ -19,6 +19,18 @@ export interface NetworkLoadBalancerProps extends BaseLoadBalancerProps {
* @default false
*/
readonly crossZoneEnabled?: boolean;

/**
* Security group to associate with this load balancer
*
* @description Security groups support on Network Load Balancers can only be enabled at creation by including at least one security group.
* You can change security groups after creation. The security groups for your load balancer must allow it to communicate with registered targets on both the listener port and the health check port.
* For PrivateLink Network Load Balancers, security group rules are enforced on PrivateLink traffic; however, you can turn off inbound rule evaluation after creation within the load balancer’s Security tab or using the API
*
* @default A security group is created
*/
readonly securityGroup?: ec2.ISecurityGroup;

}

/**
Expand Down Expand Up @@ -166,14 +178,20 @@ export class NetworkLoadBalancer extends BaseLoadBalancer implements INetworkLoa

return new Import(scope, id, { environmentFromArn: attrs.loadBalancerArn });
}

public readonly connections?: ec2.Connections;
public readonly metrics: INetworkLoadBalancerMetrics;

constructor(scope: Construct, id: string, props: NetworkLoadBalancerProps) {
super(scope, id, props, {
type: 'network',
securityGroups: Lazy.list({ produce: () => this.connections?.securityGroups.map(sg => sg.securityGroupId) }),
});

if (props.securityGroup) {
const securityGroups = [props.securityGroup];
this.connections = new ec2.Connections({ securityGroups });
}

this.metrics = new NetworkLoadBalancerMetrics(this, this.loadBalancerFullName);
if (props.crossZoneEnabled) { this.setAttribute('load_balancing.cross_zone.enabled', 'true'); }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Construct } from 'constructs';
import { INetworkListener } from './network-listener';
import * as cloudwatch from '../../../aws-cloudwatch';
import * as ec2 from '../../../aws-ec2';
import * as cdk from '../../../core';
import {
BaseTargetGroupProps, HealthCheck, ITargetGroup, loadBalancerNameFromListenerArn, LoadBalancerTargetProps,
TargetGroupAttributes, TargetGroupBase, TargetGroupImportProps,
TargetGroupAttributes, TargetGroupBase, TargetGroupImportProps, ConnectableMember,
} from '../shared/base-target-group';
import { Protocol } from '../shared/enums';
import { ImportedTargetGroupBase } from '../shared/imported';
Expand Down Expand Up @@ -146,6 +147,7 @@ export class NetworkTargetGroup extends TargetGroupBase implements INetworkTarge
return NetworkTargetGroup.fromTargetGroupAttributes(scope, id, props);
}

private readonly connectableMembers: ConnectableMember[];
private readonly listeners: INetworkListener[];
private _metrics?: INetworkTargetGroupMetrics;

Expand All @@ -158,6 +160,7 @@ export class NetworkTargetGroup extends TargetGroupBase implements INetworkTarge
port: props.port,
});

this.connectableMembers = [];
this.listeners = [];

if (props.proxyProtocolV2 != null) {
Expand Down Expand Up @@ -191,12 +194,32 @@ export class NetworkTargetGroup extends TargetGroupBase implements INetworkTarge
}
}

/**
* Register a connectable as a member of this target group.
*
* Don't call this directly. It will be called by load balancing targets.
*/
public registerConnectable(connectable: ec2.IConnectable, portRange?: ec2.Port) {
portRange = portRange || ec2.Port.tcp(this.defaultPort);

// Notify all listeners that we already know about of this new connectable.
// Then remember for new listeners that might get added later.
this.connectableMembers.push({ connectable, portRange });
for (const listener of this.listeners) {
listener.registerConnectable(connectable, portRange);
}
}

/**
* Register a listener that is load balancing to this target group.
*
* Don't call this directly. It will be called by listeners.
*/
public registerListener(listener: INetworkListener) {
// Then remember for new connectables that might get added later.
for (const member of this.connectableMembers) {
listener.registerConnectable(member.connectable, member.portRange);
}
this.loadBalancerAttachedDependencies.add(listener);
this.listeners.push(listener);
}
Expand Down Expand Up @@ -313,6 +336,13 @@ export interface INetworkTargetGroup extends ITargetGroup {
*/
registerListener(listener: INetworkListener): void;

/**
* Register a connectable as a member of this target group.
*
* Don't call this directly. It will be called by load balancing targets.
*/
registerConnectable(connectable: ec2.IConnectable, portRange?: ec2.Port): void;

/**
* Add a load balancing target to this target group
*/
Expand Down Expand Up @@ -347,6 +377,10 @@ class ImportedNetworkTargetGroup extends ImportedTargetGroupBase implements INet
// Nothing to do, we know nothing of our members
}

public registerConnectable(_connectable: ec2.IConnectable, _portRange?: ec2.Port | undefined): void {
cdk.Annotations.of(this).addWarning('Cannot register connectable on imported target group -- security groups might need to be updated manually');
}

public addTarget(...targets: INetworkLoadBalancerTarget[]) {
for (const target of targets) {
const result = target.attachToNetworkTargetGroup(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,3 +440,18 @@ export function loadBalancerNameFromListenerArn(listenerArn: string) {
const arnParts = cdk.Fn.split('/', listenerArn);
return `${cdk.Fn.select(1, arnParts)}/${cdk.Fn.select(2, arnParts)}/${cdk.Fn.select(3, arnParts)}`;
}

/**
* A connectable member of a target group
*/
export interface ConnectableMember {
/**
* The connectable member
*/
connectable: ec2.IConnectable;

/**
* The port (range) the member is listening on
*/
portRange: ec2.Port;
}
Loading