sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/services/inboundnatrules/spec.go (about) 1 /* 2 Copyright 2021 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package inboundnatrules 18 19 import ( 20 "context" 21 22 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4" 23 "github.com/pkg/errors" 24 "k8s.io/utils/ptr" 25 ) 26 27 // InboundNatSpec defines the specification for an inbound NAT rule. 28 type InboundNatSpec struct { 29 Name string 30 LoadBalancerName string 31 ResourceGroup string 32 FrontendIPConfigurationID *string 33 SSHFrontendPort *int32 34 } 35 36 // ResourceName returns the name of the inbound NAT rule. 37 func (s *InboundNatSpec) ResourceName() string { 38 return s.Name 39 } 40 41 // ResourceGroupName returns the name of the resource group. 42 func (s *InboundNatSpec) ResourceGroupName() string { 43 return s.ResourceGroup 44 } 45 46 // OwnerResourceName returns the name of the load balancer associated with an inbound NAT rule. 47 func (s *InboundNatSpec) OwnerResourceName() string { 48 return s.LoadBalancerName 49 } 50 51 // Parameters returns the parameters for the inbound NAT rule. 52 func (s *InboundNatSpec) Parameters(ctx context.Context, existing interface{}) (parameters interface{}, err error) { 53 if existing != nil { 54 if _, ok := existing.(armnetwork.InboundNatRule); !ok { 55 return nil, errors.Errorf("%T is not an armnetwork.InboundNatRule", existing) 56 } 57 // Skip updating the existing inbound NAT rule 58 return nil, nil 59 } 60 61 if s.FrontendIPConfigurationID == nil { 62 return nil, errors.Errorf("FrontendIPConfigurationID is not set") 63 } 64 65 rule := armnetwork.InboundNatRule{ 66 Name: ptr.To(s.ResourceName()), 67 Properties: &armnetwork.InboundNatRulePropertiesFormat{ 68 BackendPort: ptr.To[int32](22), 69 EnableFloatingIP: ptr.To(false), 70 IdleTimeoutInMinutes: ptr.To[int32](4), 71 FrontendIPConfiguration: &armnetwork.SubResource{ 72 ID: s.FrontendIPConfigurationID, 73 }, 74 Protocol: ptr.To(armnetwork.TransportProtocolTCP), 75 FrontendPort: s.SSHFrontendPort, 76 }, 77 } 78 79 return rule, nil 80 } 81 82 func getAvailableSSHFrontendPort(portsInUse map[int32]struct{}) (int32, error) { 83 // NAT rules need to use a unique port. Since we need one NAT rule per control plane and we expect to have 1, 3, 5, maybe 9 control planes, there should never be more than 9 ports in use. 84 // This is an artificial limit of 20 ports that we can pick from, which should be plenty enough (in reality we should never reach that limit). 85 // These NAT rules are used for SSH purposes which is why we start at 22 and then use 2201, 2202, etc. 86 var i int32 = 22 87 if _, ok := portsInUse[22]; ok { 88 for i = 2201; i < 2220; i++ { 89 if _, ok := portsInUse[i]; !ok { 90 // Found available port 91 return i, nil 92 } 93 } 94 return i, errors.Errorf("No available SSH Frontend ports") 95 } 96 97 return i, nil 98 }