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

Add sslPolicy for NLB to change listener's security policy #9666

Merged
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
5 changes: 4 additions & 1 deletion docs/cluster_spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

The `Cluster` resource contains the specification of the cluster itself.

The complete list of keys can be found at the [Cluster](https://pkg.go.dev/k8s.io/kops/pkg/apis/kOps#ClusterSpec) reference page.
The complete list of keys can be found at the [Cluster](https://pkg.go.dev/k8s.io/kops/pkg/apis/kops#ClusterSpec) reference page.

On this page, we will expand on the more important configuration keys.

Expand Down Expand Up @@ -49,6 +49,8 @@ spec:

You can use a valid SSL Certificate for your API Server Load Balancer. Currently, only AWS is supported.

Also, you can change listener's [security policy](https://docs.aws.amazon.com/sdk-for-go/api/service/elbv2/#CreateListenerInput) by `sslPolicy`. Currently, only AWS Network Load Balancer is supported.

Note that when using `sslCertificate`, client certificate authentication, such as with the credentials generated via `kOps export kubecfg`, will not work through the load balancer. As of kOps 1.19, a `kubecfg` that bypasses the load balancer may be created with the `--internal` flag to `kops update cluster` or `kOps export kubecfg`. Security groups may need to be opened to allow access from the clients to the master instances' port TCP/443, for example by using the `additionalSecurityGroups` field on the master instance groups.

```yaml
Expand All @@ -57,6 +59,7 @@ spec:
loadBalancer:
type: Public
sslCertificate: arn:aws:acm:<region>:<accountId>:certificate/<uuid>
sslPolicy: ELBSecurityPolicy-TLS-1-2-2017-01
```

*Openstack only*
Expand Down
3 changes: 3 additions & 0 deletions k8s/crds/kops.k8s.io_clusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ spec:
sslCertificate:
description: SSLCertificate allows you to specify the ACM cert to be used the LB
type: string
sslPolicy:
description: SSLPolicy allows you to overwrite the LB listener's Security Policy
type: string
type:
description: Type of load balancer to create may Public or Internal.
type: string
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/kops/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,8 @@ type LoadBalancerAccessSpec struct {
UseForInternalApi bool `json:"useForInternalApi,omitempty"`
// SSLCertificate allows you to specify the ACM cert to be used the LB
SSLCertificate string `json:"sslCertificate,omitempty"`
// SSLPolicy allows you to overwrite the LB listener's Security Policy
SSLPolicy *string `json:"sslPolicy,omitempty"`
// CrossZoneLoadBalancing allows you to enable the cross zone load balancing
CrossZoneLoadBalancing *bool `json:"crossZoneLoadBalancing,omitempty"`
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/kops/v1alpha2/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,8 @@ type LoadBalancerAccessSpec struct {
UseForInternalApi bool `json:"useForInternalApi,omitempty"`
// SSLCertificate allows you to specify the ACM cert to be used the LB
SSLCertificate string `json:"sslCertificate,omitempty"`
// SSLPolicy allows you to overwrite the LB listener's Security Policy
SSLPolicy *string `json:"sslPolicy,omitempty"`
// CrossZoneLoadBalancing allows you to enable the cross zone load balancing
CrossZoneLoadBalancing *bool `json:"crossZoneLoadBalancing,omitempty"`
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/kops/v1alpha2/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions pkg/apis/kops/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions pkg/apis/kops/validation/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func awsValidateCluster(c *kops.Cluster) field.ErrorList {
if c.Spec.API != nil {
if c.Spec.API.LoadBalancer != nil {
allErrs = append(allErrs, awsValidateAdditionalSecurityGroups(field.NewPath("spec", "api", "loadBalancer", "additionalSecurityGroups"), c.Spec.API.LoadBalancer.AdditionalSecurityGroups)...)
allErrs = append(allErrs, awsValidateSSLPolicy(field.NewPath("spec", "api", "loadBalancer", "sslPolicy"), c.Spec.API.LoadBalancer)...)
}
}

Expand Down Expand Up @@ -142,3 +143,18 @@ func awsValidateMixedInstancesPolicy(path *field.Path, spec *kops.MixedInstances

return errs
}

func awsValidateSSLPolicy(fieldPath *field.Path, spec *kops.LoadBalancerAccessSpec) field.ErrorList {
allErrs := field.ErrorList{}

if spec.SSLPolicy != nil {
if spec.Class != kops.LoadBalancerClassNetwork {
allErrs = append(allErrs, field.Forbidden(fieldPath, "sslPolicy should be specified with Network Load Balancer"))
}
if spec.SSLCertificate == "" {
allErrs = append(allErrs, field.Forbidden(fieldPath, "sslPolicy should not be specified without SSLCertificate"))
}
}

return allErrs
}
5 changes: 5 additions & 0 deletions pkg/apis/kops/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions pkg/model/awsmodel/api_loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,16 @@ func (b *APILoadBalancerBuilder) Build(c *fi.ModelBuilderContext) error {
if lbSpec.SSLCertificate != "" {
listeners["443"].SSLCertificateID = lbSpec.SSLCertificate
nlbListeners[0].Port = 8443
nlbListeners = append(nlbListeners, &awstasks.NetworkLoadBalancerListener{

nlbListener := &awstasks.NetworkLoadBalancerListener{
Port: 443,
TargetGroupName: b.NLBTargetGroupName("tls"),
SSLCertificateID: lbSpec.SSLCertificate,
})
}
if lbSpec.SSLPolicy != nil {
FrankYang0529 marked this conversation as resolved.
Show resolved Hide resolved
nlbListener.SSLPolicy = *lbSpec.SSLPolicy
}
nlbListeners = append(nlbListeners, nlbListener)
}

if lbSpec.SecurityGroupOverride != nil {
Expand Down
3 changes: 2 additions & 1 deletion tests/integration/update_cluster/complex/cloudformation.json
Original file line number Diff line number Diff line change
Expand Up @@ -1192,7 +1192,8 @@
"Ref": "AWSElasticLoadBalancingV2LoadBalancerapicomplexexamplecom"
},
"Port": 443,
"Protocol": "TLS"
"Protocol": "TLS",
"SslPolicy": "ELBSecurityPolicy-2016-08"
}
},
"AWSElasticLoadBalancingV2Listenerapicomplexexamplecom8443": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ spec:
crossZoneLoadBalancing: true
class: Network
sslCertificate: arn:aws:acm:us-test-1:000000000000:certificate/123456789012-1234-1234-1234-12345678
sslPolicy: ELBSecurityPolicy-2016-08
kubernetesApiAccess:
- 1.1.1.0/24
- 2001:0:8500::/40
Expand Down
1 change: 1 addition & 0 deletions tests/integration/update_cluster/complex/in-v1alpha2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ spec:
crossZoneLoadBalancing: true
class: Network
sslCertificate: arn:aws:acm:us-test-1:000000000000:certificate/123456789012-1234-1234-1234-12345678
sslPolicy: ELBSecurityPolicy-2016-08
kubernetesApiAccess:
- 1.1.1.0/24
- 2001:0:8500::/40
Expand Down
1 change: 1 addition & 0 deletions tests/integration/update_cluster/complex/kubernetes.tf
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ resource "aws_lb_listener" "api-complex-example-com-443" {
load_balancer_arn = aws_lb.api-complex-example-com.id
port = 443
protocol = "TLS"
ssl_policy = "ELBSecurityPolicy-2016-08"
}

resource "aws_lb_listener" "api-complex-example-com-8443" {
Expand Down
15 changes: 15 additions & 0 deletions upup/pkg/fi/cloudup/awstasks/network_load_balancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ type NetworkLoadBalancerListener struct {
Port int
TargetGroupName string
SSLCertificateID string
SSLPolicy string
}

func (e *NetworkLoadBalancerListener) mapToAWS(targetGroups []*TargetGroup, loadBalancerArn string) (*elbv2.CreateListenerInput, error) {
Expand Down Expand Up @@ -110,6 +111,9 @@ func (e *NetworkLoadBalancerListener) mapToAWS(targetGroups []*TargetGroup, load
CertificateArn: aws.String(e.SSLCertificateID),
})
l.Protocol = aws.String(elbv2.ProtocolEnumTls)
if e.SSLPolicy != "" {
l.SslPolicy = aws.String(e.SSLPolicy)
}
} else {
l.Protocol = aws.String(elbv2.ProtocolEnumTcp)
}
Expand Down Expand Up @@ -369,6 +373,9 @@ func (e *NetworkLoadBalancer) Find(c *fi.Context) (*NetworkLoadBalancer, error)
actualListener.Port = int(aws.Int64Value(l.Port))
if len(l.Certificates) != 0 {
actualListener.SSLCertificateID = aws.StringValue(l.Certificates[0].CertificateArn) // What if there is more then one certificate, can we just grab the default certificate? we don't set it as default, we only set the one.
if l.SslPolicy != nil {
actualListener.SSLPolicy = aws.StringValue(l.SslPolicy)
}
}

// This will need to be rearranged when we recognized multiple listeners and target groups per NLB
Expand Down Expand Up @@ -689,6 +696,7 @@ type terraformNetworkLoadBalancerListener struct {
Port int64 `json:"port" cty:"port"`
Protocol string `json:"protocol" cty:"protocol"`
CertificateARN *string `json:"certificate_arn,omitempty" cty:"certificate_arn"`
SSLPolicy *string `json:"ssl_policy,omitempty" cty:"ssl_policy"`
DefaultAction []terraformNetworkLoadBalancerListenerAction `json:"default_action" cty:"default_action"`
}

Expand Down Expand Up @@ -730,6 +738,9 @@ func (_ *NetworkLoadBalancer) RenderTerraform(t *terraform.TerraformTarget, a, e
if listener.SSLCertificateID != "" {
listenerTF.CertificateARN = &listener.SSLCertificateID
listenerTF.Protocol = elbv2.ProtocolEnumTls
if listener.SSLPolicy != "" {
listenerTF.SSLPolicy = &listener.SSLPolicy
}
} else {
listenerTF.Protocol = elbv2.ProtocolEnumTcp
}
Expand Down Expand Up @@ -764,6 +775,7 @@ type cloudformationNetworkLoadBalancerListener struct {
LoadBalancerARN *cloudformation.Literal `json:"LoadBalancerArn"`
Port int64 `json:"Port"`
Protocol string `json:"Protocol"`
SSLPolicy *string `json:"SslPolicy,omitempty"`
}

type cloudformationNetworkLoadBalancerListenerCertificate struct {
Expand Down Expand Up @@ -811,6 +823,9 @@ func (_ *NetworkLoadBalancer) RenderCloudformation(t *cloudformation.Cloudformat
{CertificateArn: listener.SSLCertificateID},
}
listenerCF.Protocol = elbv2.ProtocolEnumTls
if listener.SSLPolicy != "" {
listenerCF.SSLPolicy = &listener.SSLPolicy
}
} else {
listenerCF.Protocol = elbv2.ProtocolEnumTcp
}
Expand Down