github.com/openshift/installer@v1.4.17/pkg/infrastructure/gcp/clusterapi/firewallrules.go (about) 1 package clusterapi 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 "github.com/sirupsen/logrus" 9 "google.golang.org/api/compute/v1" 10 11 gcpconfig "github.com/openshift/installer/pkg/asset/installconfig/gcp" 12 "github.com/openshift/installer/pkg/infrastructure/clusterapi" 13 "github.com/openshift/installer/pkg/types" 14 ) 15 16 const ( 17 // gcpFirewallPermission is the role/permission to create or skip the creation of 18 // firewall rules for GCP during a xpn installation. 19 gcpFirewallPermission = "compute.firewalls.create" 20 ) 21 22 func getEtcdPorts() []*compute.FirewallAllowed { 23 return []*compute.FirewallAllowed{ 24 { 25 IPProtocol: "tcp", 26 Ports: []string{ 27 "2379-2380", 28 }, 29 }, 30 } 31 } 32 33 func getHealthChecksPorts() []*compute.FirewallAllowed { 34 return []*compute.FirewallAllowed{ 35 { 36 IPProtocol: "tcp", 37 Ports: []string{ 38 "6080", 39 "6443", 40 "22624", 41 }, 42 }, 43 } 44 } 45 46 func getControlPlanePorts() []*compute.FirewallAllowed { 47 return []*compute.FirewallAllowed{ 48 { 49 IPProtocol: "tcp", 50 Ports: []string{ 51 "22623", // Ignition 52 }, 53 }, 54 { 55 IPProtocol: "tcp", 56 Ports: []string{ 57 "10257", // Kube manager 58 }, 59 }, 60 { 61 IPProtocol: "tcp", 62 Ports: []string{ 63 "10259", // Kube scheduler 64 }, 65 }, 66 } 67 } 68 69 func getInternalClusterPorts() []*compute.FirewallAllowed { 70 return []*compute.FirewallAllowed{ 71 { 72 IPProtocol: "tcp", 73 Ports: []string{ 74 "30000-32767", // k8s NodePorts 75 }, 76 }, 77 { 78 IPProtocol: "udp", 79 Ports: []string{ 80 "30000-32767", // k8s NodePorts 81 }, 82 }, 83 { 84 IPProtocol: "tcp", 85 Ports: []string{ 86 "9000-9999", // host-level services 87 }, 88 }, 89 { 90 IPProtocol: "udp", 91 Ports: []string{ 92 "9000-9999", // host-level services 93 }, 94 }, 95 { 96 IPProtocol: "udp", 97 Ports: []string{ 98 "4789", "6081", // VXLAN and GENEVE 99 }, 100 }, 101 { 102 IPProtocol: "udp", 103 Ports: []string{ 104 "500", "4500", // IKE and IKE(NAT-T) 105 }, 106 }, 107 { 108 IPProtocol: "tcp", 109 Ports: []string{ 110 "10250", // kubelet secure 111 }, 112 }, 113 { 114 IPProtocol: "esp", 115 }, 116 } 117 } 118 119 func getAPIPorts() []*compute.FirewallAllowed { 120 return []*compute.FirewallAllowed{ 121 { 122 IPProtocol: "tcp", 123 Ports: []string{ 124 "6443", // kube-apiserver 125 }, 126 }, 127 } 128 } 129 130 func getInternalNetworkPorts() []*compute.FirewallAllowed { 131 return []*compute.FirewallAllowed{ 132 { 133 IPProtocol: "tcp", 134 Ports: []string{ 135 "22", // SSH 136 }, 137 }, 138 { 139 IPProtocol: "icmp", 140 }, 141 } 142 } 143 144 func getBootstrapSSHPorts() []*compute.FirewallAllowed { 145 return []*compute.FirewallAllowed{ 146 { 147 IPProtocol: "tcp", 148 Ports: []string{ 149 "22", // SSH 150 }, 151 }, 152 { 153 IPProtocol: "icmp", 154 }, 155 } 156 } 157 158 // addFirewallRule creates the firewall rule and adds it the compute's firewalls. 159 func addFirewallRule(ctx context.Context, name, network, projectID string, ports []*compute.FirewallAllowed, srcTags, targetTags, srcRanges []string) error { 160 service, err := NewComputeService() 161 if err != nil { 162 return err 163 } 164 165 ctx, cancel := context.WithTimeout(ctx, time.Minute*3) 166 defer cancel() 167 168 firewallRule := &compute.Firewall{ 169 Name: name, 170 Description: resourceDescription, 171 Direction: "INGRESS", 172 Network: network, 173 Allowed: ports, 174 SourceTags: srcTags, 175 TargetTags: targetTags, 176 } 177 if len(srcTags) > 0 { 178 firewallRule.SourceTags = srcTags 179 } 180 if len(srcRanges) > 0 { 181 firewallRule.SourceRanges = srcRanges 182 } 183 184 op, err := service.Firewalls.Insert(projectID, firewallRule).Context(ctx).Do() 185 if err != nil { 186 return fmt.Errorf("failed to create %s firewall rule: %w", name, err) 187 } 188 189 if err := WaitForOperationGlobal(ctx, projectID, op); err != nil { 190 return fmt.Errorf("failed to wait for inserting %s firewall rule: %w", name, err) 191 } 192 193 return nil 194 } 195 196 // deleteFirewallRule deletes the firewall rule identified by name. 197 func deleteFirewallRule(ctx context.Context, name, projectID string) error { 198 service, err := NewComputeService() 199 if err != nil { 200 return err 201 } 202 203 ctx, cancel := context.WithTimeout(ctx, time.Minute*3) 204 defer cancel() 205 206 op, err := service.Firewalls.Delete(projectID, name).Context(ctx).Do() 207 if err != nil { 208 return fmt.Errorf("failed to delete %s firewall rule: %w", name, err) 209 } 210 211 if err := WaitForOperationGlobal(ctx, projectID, op); err != nil { 212 return fmt.Errorf("failed to wait for delete %s firewall rule: %w", name, err) 213 } 214 215 return nil 216 } 217 218 // createFirewallRules creates the rules needed between the worker and master nodes. 219 func createFirewallRules(ctx context.Context, in clusterapi.InfraReadyInput, network string) error { 220 if projID := in.InstallConfig.Config.GCP.NetworkProjectID; projID != "" { 221 client, err := gcpconfig.NewClient(context.Background()) 222 if err != nil { 223 return fmt.Errorf("failed to create client during firewall rule creation: %w", err) 224 } 225 226 permissions, err := client.GetProjectPermissions(ctx, projID, []string{ 227 gcpFirewallPermission, 228 }) 229 if err != nil { 230 return fmt.Errorf("failed to find project permissions during firewall creation: %w", err) 231 } 232 233 if !permissions.Has(gcpFirewallPermission) { 234 logrus.Warnf("failed to find permission %s, skipping firewall rule creation", gcpFirewallPermission) 235 return nil 236 } 237 } 238 239 projectID := in.InstallConfig.Config.Platform.GCP.ProjectID 240 if in.InstallConfig.Config.Platform.GCP.NetworkProjectID != "" { 241 projectID = in.InstallConfig.Config.Platform.GCP.NetworkProjectID 242 } 243 workerTag := fmt.Sprintf("%s-worker", in.InfraID) 244 masterTag := fmt.Sprintf("%s-control-plane", in.InfraID) 245 246 // control-plane rules are needed for worker<->master communication for worker provisioning 247 firewallName := fmt.Sprintf("%s-control-plane", in.InfraID) 248 srcTags := []string{workerTag, masterTag} 249 targetTags := []string{masterTag} 250 srcRanges := []string{} 251 if err := addFirewallRule(ctx, firewallName, network, projectID, getControlPlanePorts(), srcTags, targetTags, srcRanges); err != nil { 252 return err 253 } 254 255 // etcd are needed for master communication for etcd nodes 256 firewallName = fmt.Sprintf("%s-etcd", in.InfraID) 257 srcTags = []string{masterTag} 258 targetTags = []string{masterTag} 259 srcRanges = []string{} 260 if err := addFirewallRule(ctx, firewallName, network, projectID, getEtcdPorts(), srcTags, targetTags, srcRanges); err != nil { 261 return err 262 } 263 264 // Add a single firewall rule to allow the Google Cloud Engine health checks to access all of the services. 265 // This rule enables the ingress load balancers to determine the health status of their instances. 266 firewallName = fmt.Sprintf("%s-health-checks", in.InfraID) 267 srcTags = []string{} 268 targetTags = []string{masterTag} 269 srcRanges = []string{"35.191.0.0/16", "130.211.0.0/22"} 270 if in.InstallConfig.Config.Publish == types.ExternalPublishingStrategy { 271 // public installs require additional google ip addresses for health checks 272 srcRanges = append(srcRanges, []string{"209.85.152.0/22", "209.85.204.0/22"}...) 273 } 274 if err := addFirewallRule(ctx, firewallName, network, projectID, getHealthChecksPorts(), srcTags, targetTags, srcRanges); err != nil { 275 return err 276 } 277 278 // internal-cluster rules are needed for worker<->master communication for k8s nodes 279 firewallName = fmt.Sprintf("%s-internal-cluster", in.InfraID) 280 srcTags = []string{workerTag, masterTag} 281 targetTags = []string{workerTag, masterTag} 282 srcRanges = []string{} 283 if err := addFirewallRule(ctx, firewallName, network, projectID, getInternalClusterPorts(), srcTags, targetTags, srcRanges); err != nil { 284 return err 285 } 286 287 // api rules are needed to access the kube-apiserver on master nodes 288 firewallName = fmt.Sprintf("%s-api", in.InfraID) 289 srcTags = []string{} 290 targetTags = []string{masterTag} 291 srcRanges = []string{} 292 if err := addFirewallRule(ctx, firewallName, network, projectID, getAPIPorts(), srcTags, targetTags, srcRanges); err != nil { 293 return err 294 } 295 296 // internal-network rules are used to access ssh and icmp over the machine network 297 firewallName = fmt.Sprintf("%s-internal-network", in.InfraID) 298 srcTags = []string{} 299 targetTags = []string{workerTag, masterTag} 300 machineCIDR := in.InstallConfig.Config.Networking.MachineNetwork[0].CIDR.String() 301 srcRanges = []string{machineCIDR} 302 err := addFirewallRule(ctx, firewallName, network, projectID, getInternalNetworkPorts(), srcTags, targetTags, srcRanges) 303 304 return err 305 } 306 307 // createBootstrapFirewallRules creates the rules needed for the bootstrap node. 308 func createBootstrapFirewallRules(ctx context.Context, in clusterapi.InfraReadyInput, network string) error { 309 projectID := in.InstallConfig.Config.Platform.GCP.ProjectID 310 if in.InstallConfig.Config.Platform.GCP.NetworkProjectID != "" { 311 projectID = in.InstallConfig.Config.Platform.GCP.NetworkProjectID 312 } 313 firewallName := fmt.Sprintf("%s-bootstrap-in-ssh", in.InfraID) 314 srcTags := []string{} 315 bootstrapTag := fmt.Sprintf("%s-control-plane", in.InfraID) 316 targetTags := []string{bootstrapTag} 317 var srcRanges []string 318 if in.InstallConfig.Config.Publish == types.ExternalPublishingStrategy { 319 srcRanges = []string{"0.0.0.0/0"} 320 } else { 321 machineCIDR := in.InstallConfig.Config.Networking.MachineNetwork[0].CIDR.String() 322 srcRanges = []string{machineCIDR} 323 } 324 return addFirewallRule(ctx, firewallName, network, projectID, getBootstrapSSHPorts(), srcTags, targetTags, srcRanges) 325 } 326 327 // removeBootstrapFirewallRules removes the rules created for the bootstrap node. 328 func removeBootstrapFirewallRules(ctx context.Context, infraID, projectID string) error { 329 firewallName := fmt.Sprintf("%s-bootstrap-in-ssh", infraID) 330 return deleteFirewallRule(ctx, firewallName, projectID) 331 }