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  }