Skip to content

Commit 418ebea

Browse files
committed
Update the GCP provider to allow users to manage their own firewall rules
cluster: Update the scripts to include the new variables providers/gce: Update the config to include the new `FirewallRulesManagement` string that can be set to Enabled or Disabled. This variable will allow users to skip the creation, deletion, and updates to firewall rules when set to Disabled. Users may not want or have the ability to add the permissions to perform these actions on their service account. When this is the case the firewall rules should be pre created and managed by someone with permissions to achieve the same goal.
1 parent 2e53900 commit 418ebea

File tree

8 files changed

+224
-39
lines changed

8 files changed

+224
-39
lines changed

cluster/gce/gci/configure-helper.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,14 @@ EOF
958958
use_cloud_config="true"
959959
cat <<EOF >>/etc/gce.conf
960960
regional = ${MULTIMASTER}
961+
EOF
962+
fi
963+
# FirewallRulesManagement indicates whethere the firewall rules are
964+
# managed (enabled) or not (disabled) for the provider.
965+
if [[ -n "${FIREWALLRULESMANAGEMENT:-}" ]]; then
966+
use_cloud_config="true"
967+
cat <<EOF >>/etc/gce.conf
968+
firewall-rules-management = ${FIREWALLRULESMANAGEMENT}
961969
EOF
962970
fi
963971
if [[ -n "${GCE_ALPHA_FEATURES:-}" ]]; then

cluster/gce/util.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,6 +1190,7 @@ KUBE_DOCKER_REGISTRY: $(yaml-quote "${KUBE_DOCKER_REGISTRY:-}")
11901190
KUBE_ADDON_REGISTRY: $(yaml-quote "${KUBE_ADDON_REGISTRY:-}")
11911191
MULTIZONE: $(yaml-quote "${MULTIZONE:-}")
11921192
MULTIMASTER: $(yaml-quote "${MULTIMASTER:-}")
1193+
FIREWALLRULESMANAGEMENT: $(yaml-quote "${FIREWALLRULESMANAGEMENT:-}")
11931194
NON_MASQUERADE_CIDR: $(yaml-quote "${NON_MASQUERADE_CIDR:-}")
11941195
ENABLE_DEFAULT_STORAGE_CLASS: $(yaml-quote "${ENABLE_DEFAULT_STORAGE_CLASS:-}")
11951196
# (TODO/cloud-provider-gcp): Need to figure out how to inject this

providers/gce/gce.go

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,17 @@ const clusterStackIPV4 StackType = "IPV4"
116116
// The underlying VPC's stack type could be either IPV6 or dual stack IPV4_IPV6.
117117
const clusterStackIPV6 StackType = "IPV6"
118118

119+
// FirewallRulesManagement indicates how firewall rules are managed by the provider.
120+
type FirewallRulesManagement string
121+
122+
// firewallRulesManagementEnabled indicates that the firewall rules should be managed by the provider.
123+
// This includes firewall rule creation, deletion, and updates.
124+
const firewallRulesManagementEnabled FirewallRulesManagement = "Enabled"
125+
126+
// firewallRulesManagementDisabled indicates that the firewall rules should not be managed by the provider.
127+
// This includes firewall rule creation, deletion, and updates.
128+
const firewallRulesManagementDisabled FirewallRulesManagement = "Disabled"
129+
119130
// Cloud is an implementation of Interface, LoadBalancer and Instances for Google Compute Engine.
120131
type Cloud struct {
121132
// ClusterID contains functionality for getting (and initializing) the ingress-uid. Call Cloud.Initialize()
@@ -209,6 +220,8 @@ type Cloud struct {
209220

210221
// enableRBSDefaultForL4NetLB disable Service controller from picking up services by default
211222
enableRBSDefaultForL4NetLB bool
223+
224+
firewallRulesManagement FirewallRulesManagement
212225
}
213226

214227
// ConfigGlobal is the in memory representation of the gce.conf config data
@@ -246,6 +259,10 @@ type ConfigGlobal struct {
246259
// Default to none.
247260
// For example: MyFeatureFlag
248261
AlphaFeatures []string `gcfg:"alpha-features"`
262+
263+
// FirewallRulesManagement indicates whether the provider should handle all firewall
264+
// operations, such as creation, deletion, and updates.
265+
FirewallRulesManagement string `gcfg:"firewall-rules-management"`
249266
}
250267

251268
// ConfigFile is the struct used to parse the /etc/gce.conf configuration file.
@@ -273,13 +290,14 @@ type CloudConfig struct {
273290
SubnetworkName string
274291
SubnetworkURL string
275292
// DEPRECATED: Do not rely on this value as it may be incorrect.
276-
SecondaryRangeName string
277-
NodeTags []string
278-
NodeInstancePrefix string
279-
TokenSource oauth2.TokenSource
280-
UseMetadataServer bool
281-
AlphaFeatureGate *AlphaFeatureGate
282-
StackType string
293+
SecondaryRangeName string
294+
NodeTags []string
295+
NodeInstancePrefix string
296+
TokenSource oauth2.TokenSource
297+
UseMetadataServer bool
298+
AlphaFeatureGate *AlphaFeatureGate
299+
StackType string
300+
FirewallRulesManagement string
283301
}
284302

285303
func init() {
@@ -370,6 +388,7 @@ func generateCloudConfig(configFile *ConfigFile) (cloudConfig *CloudConfig, err
370388
cloudConfig.NodeTags = configFile.Global.NodeTags
371389
cloudConfig.NodeInstancePrefix = configFile.Global.NodeInstancePrefix
372390
cloudConfig.AlphaFeatureGate = NewAlphaFeatureGate(configFile.Global.AlphaFeatures)
391+
cloudConfig.FirewallRulesManagement = configFile.Global.FirewallRulesManagement
373392
}
374393

375394
// retrieve projectID and zone
@@ -573,6 +592,7 @@ func CreateGCECloud(config *CloudConfig) (*Cloud, error) {
573592
metricsCollector: newLoadBalancerMetrics(),
574593
projectsBasePath: getProjectsBasePath(service.BasePath),
575594
stackType: StackType(config.StackType),
595+
firewallRulesManagement: FirewallRulesManagement(config.FirewallRulesManagement),
576596
}
577597

578598
gce.manager = &gceServiceManager{gce}

providers/gce/gce_fake.go

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -30,30 +30,32 @@ import (
3030

3131
// TestClusterValues holds the config values for the fake/test gce cloud object.
3232
type TestClusterValues struct {
33-
ProjectID string
34-
Region string
35-
ZoneName string
36-
SecondaryZoneName string
37-
ClusterID string
38-
ClusterName string
39-
OnXPN bool
40-
Regional bool
41-
NetworkURL string
42-
SubnetworkURL string
43-
StackType StackType
33+
ProjectID string
34+
Region string
35+
ZoneName string
36+
SecondaryZoneName string
37+
ClusterID string
38+
ClusterName string
39+
OnXPN bool
40+
Regional bool
41+
NetworkURL string
42+
SubnetworkURL string
43+
StackType StackType
44+
FirewallRulesManagement FirewallRulesManagement
4445
}
4546

4647
// DefaultTestClusterValues Creates a reasonable set of default cluster values
4748
// for generating a new test fake GCE cloud instance.
4849
func DefaultTestClusterValues() TestClusterValues {
4950
return TestClusterValues{
50-
ProjectID: "test-project",
51-
Region: "us-central1",
52-
ZoneName: "us-central1-b",
53-
SecondaryZoneName: "us-central1-c",
54-
ClusterID: "test-cluster-id",
55-
ClusterName: "Test-Cluster-Name",
56-
StackType: clusterStackIPV4,
51+
ProjectID: "test-project",
52+
Region: "us-central1",
53+
ZoneName: "us-central1-b",
54+
SecondaryZoneName: "us-central1-c",
55+
ClusterID: "test-cluster-id",
56+
ClusterName: "Test-Cluster-Name",
57+
StackType: clusterStackIPV4,
58+
FirewallRulesManagement: firewallRulesManagementEnabled,
5759
}
5860
}
5961

@@ -75,20 +77,21 @@ func NewFakeGCECloud(vals TestClusterValues) *Cloud {
7577
panic(err)
7678
}
7779
gce := &Cloud{
78-
region: vals.Region,
79-
service: service,
80-
managedZones: []string{vals.ZoneName},
81-
localZone: vals.ZoneName,
82-
projectID: vals.ProjectID,
83-
networkProjectID: vals.ProjectID,
84-
ClusterID: fakeClusterID(vals.ClusterID),
85-
onXPN: vals.OnXPN,
86-
metricsCollector: newLoadBalancerMetrics(),
87-
projectsBasePath: getProjectsBasePath(service.BasePath),
88-
regional: vals.Regional,
89-
networkURL: vals.NetworkURL,
90-
unsafeSubnetworkURL: vals.SubnetworkURL,
91-
stackType: vals.StackType,
80+
region: vals.Region,
81+
service: service,
82+
managedZones: []string{vals.ZoneName},
83+
localZone: vals.ZoneName,
84+
projectID: vals.ProjectID,
85+
networkProjectID: vals.ProjectID,
86+
ClusterID: fakeClusterID(vals.ClusterID),
87+
onXPN: vals.OnXPN,
88+
metricsCollector: newLoadBalancerMetrics(),
89+
projectsBasePath: getProjectsBasePath(service.BasePath),
90+
regional: vals.Regional,
91+
networkURL: vals.NetworkURL,
92+
unsafeSubnetworkURL: vals.SubnetworkURL,
93+
stackType: vals.StackType,
94+
firewallRulesManagement: vals.FirewallRulesManagement,
9295
}
9396
c := cloud.NewMockGCE(&gceProjectRouter{gce})
9497
gce.c = c

providers/gce/gce_firewall.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ func newFirewallMetricContext(request string) *metricContext {
3232

3333
// GetFirewall returns the Firewall by name.
3434
func (g *Cloud) GetFirewall(name string) (*compute.Firewall, error) {
35+
if g.firewallRulesManagement == firewallRulesManagementDisabled {
36+
return nil, nil
37+
}
38+
3539
ctx, cancel := cloud.ContextWithCallTimeout()
3640
defer cancel()
3741

@@ -42,6 +46,10 @@ func (g *Cloud) GetFirewall(name string) (*compute.Firewall, error) {
4246

4347
// CreateFirewall creates the passed firewall
4448
func (g *Cloud) CreateFirewall(f *compute.Firewall) error {
49+
if g.firewallRulesManagement == firewallRulesManagementDisabled {
50+
return nil
51+
}
52+
4553
ctx, cancel := cloud.ContextWithCallTimeout()
4654
defer cancel()
4755

@@ -51,6 +59,10 @@ func (g *Cloud) CreateFirewall(f *compute.Firewall) error {
5159

5260
// DeleteFirewall deletes the given firewall rule.
5361
func (g *Cloud) DeleteFirewall(name string) error {
62+
if g.firewallRulesManagement == firewallRulesManagementDisabled {
63+
return nil
64+
}
65+
5466
ctx, cancel := cloud.ContextWithCallTimeout()
5567
defer cancel()
5668

@@ -60,6 +72,10 @@ func (g *Cloud) DeleteFirewall(name string) error {
6072

6173
// UpdateFirewall applies the given firewall as an update to an existing service.
6274
func (g *Cloud) UpdateFirewall(f *compute.Firewall) error {
75+
if g.firewallRulesManagement == firewallRulesManagementDisabled {
76+
return nil
77+
}
78+
6379
ctx, cancel := cloud.ContextWithCallTimeout()
6480
defer cancel()
6581

@@ -69,6 +85,10 @@ func (g *Cloud) UpdateFirewall(f *compute.Firewall) error {
6985

7086
// PatchFirewall applies the given firewall as an update to an existing service.
7187
func (g *Cloud) PatchFirewall(f *compute.Firewall) error {
88+
if g.firewallRulesManagement == firewallRulesManagementDisabled {
89+
return nil
90+
}
91+
7292
ctx, cancel := cloud.ContextWithCallTimeout()
7393
defer cancel()
7494

providers/gce/gce_loadbalancer_external.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,11 @@ func translateAffinityType(affinityType v1.ServiceAffinity) string {
964964
}
965965

966966
func (g *Cloud) firewallNeedsUpdate(name, serviceName, ipAddress string, ports []v1.ServicePort, sourceRanges utilnet.IPNetSet) (exists bool, needsUpdate bool, err error) {
967+
if g.firewallRulesManagement == firewallRulesManagementDisabled {
968+
klog.V(2).Infof("firewallNeedsUpdate(%v): firewall rules are unmanaged", name)
969+
return false, false, nil
970+
}
971+
967972
fw, err := g.GetFirewall(MakeFirewallName(name))
968973
if err != nil {
969974
if isHTTPErrorCode(err, http.StatusNotFound) {
@@ -1009,6 +1014,11 @@ func (g *Cloud) firewallNeedsUpdate(name, serviceName, ipAddress string, ports [
10091014
}
10101015

10111016
func (g *Cloud) ensureHTTPHealthCheckFirewall(svc *v1.Service, serviceName, ipAddress, region, clusterID string, hosts []*gceInstance, hcName string, hcPort int32, isNodesHealthCheck bool) error {
1017+
if g.firewallRulesManagement == firewallRulesManagementDisabled {
1018+
klog.V(2).Infof("ensureHTTPHealthCheckFirewall(%v): firewall rules are unmanaged", hcName)
1019+
return nil
1020+
}
1021+
10121022
// Prepare the firewall params for creating / checking.
10131023
desc := fmt.Sprintf(`{"kubernetes.io/cluster-id":"%s"}`, clusterID)
10141024
if !isNodesHealthCheck {
@@ -1087,6 +1097,17 @@ func (g *Cloud) createFirewall(svc *v1.Service, name, desc, destinationIP string
10871097
if err != nil {
10881098
return err
10891099
}
1100+
1101+
if g.firewallRulesManagement == firewallRulesManagementDisabled {
1102+
klog.V(2).Infof("createFirewall(%v): firewall rules are unmanaged", name)
1103+
project := g.NetworkProjectID()
1104+
if project == "" {
1105+
project = g.ProjectID()
1106+
}
1107+
g.raiseFirewallChangeNeededEvent(svc, FirewallToGCloudCreateCmd(firewall, project))
1108+
return nil
1109+
}
1110+
10901111
if err = g.CreateFirewall(firewall); err != nil {
10911112
if isHTTPErrorCode(err, http.StatusConflict) {
10921113
return nil

providers/gce/gce_loadbalancer_external_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1902,6 +1902,104 @@ func TestFirewallNeedsUpdate(t *testing.T) {
19021902
}
19031903
}
19041904

1905+
func TestDisabledFirewallOperations(t *testing.T) {
1906+
vals := DefaultTestClusterValues()
1907+
vals.FirewallRulesManagement = firewallRulesManagementDisabled
1908+
gce, err := fakeGCECloud(vals)
1909+
require.NoError(t, err)
1910+
1911+
fw, err := gce.GetFirewall(MakeFirewallName("test"))
1912+
assert.NoError(t, err)
1913+
assert.Nil(t, fw)
1914+
1915+
ipnet, err := utilnet.ParseIPNets("0.0.0.0/0")
1916+
require.NoError(t, err)
1917+
1918+
ports := []v1.ServicePort{
1919+
{Name: "port1", Protocol: v1.ProtocolTCP, Port: int32(80), TargetPort: intstr.FromInt(80)},
1920+
{Name: "port2", Protocol: v1.ProtocolTCP, Port: int32(81), TargetPort: intstr.FromInt(81)},
1921+
{Name: "port3", Protocol: v1.ProtocolTCP, Port: int32(82), TargetPort: intstr.FromInt(82)},
1922+
{Name: "port4", Protocol: v1.ProtocolTCP, Port: int32(84), TargetPort: intstr.FromInt(84)},
1923+
{Name: "port5", Protocol: v1.ProtocolTCP, Port: int32(85), TargetPort: intstr.FromInt(85)},
1924+
{Name: "port6", Protocol: v1.ProtocolTCP, Port: int32(86), TargetPort: intstr.FromInt(86)},
1925+
{Name: "port7", Protocol: v1.ProtocolTCP, Port: int32(88), TargetPort: intstr.FromInt(87)},
1926+
}
1927+
1928+
firewall, err := gce.firewallObject(MakeFirewallName("test"), "Test Description", "0.0.0.0/0", ipnet, ports, nil)
1929+
1930+
err = gce.CreateFirewall(firewall)
1931+
assert.NoError(t, err)
1932+
1933+
err = gce.UpdateFirewall(firewall)
1934+
assert.NoError(t, err)
1935+
1936+
err = gce.PatchFirewall(firewall)
1937+
assert.NoError(t, err)
1938+
1939+
err = gce.DeleteFirewall(MakeFirewallName("test"))
1940+
assert.NoError(t, err)
1941+
}
1942+
1943+
func TestDisabledFirewallNeedsUpdate(t *testing.T) {
1944+
t.Parallel()
1945+
1946+
vals := DefaultTestClusterValues()
1947+
vals.FirewallRulesManagement = firewallRulesManagementDisabled
1948+
gce, err := fakeGCECloud(vals)
1949+
require.NoError(t, err)
1950+
svc := fakeLoadbalancerService("")
1951+
1952+
svc, err = gce.client.CoreV1().Services(svc.Namespace).Create(context.TODO(), svc, metav1.CreateOptions{})
1953+
require.NoError(t, err)
1954+
1955+
svc.Spec.Ports = []v1.ServicePort{
1956+
{Name: "port1", Protocol: v1.ProtocolTCP, Port: int32(80), TargetPort: intstr.FromInt(80)},
1957+
{Name: "port2", Protocol: v1.ProtocolTCP, Port: int32(81), TargetPort: intstr.FromInt(81)},
1958+
{Name: "port3", Protocol: v1.ProtocolTCP, Port: int32(82), TargetPort: intstr.FromInt(82)},
1959+
{Name: "port4", Protocol: v1.ProtocolTCP, Port: int32(84), TargetPort: intstr.FromInt(84)},
1960+
{Name: "port5", Protocol: v1.ProtocolTCP, Port: int32(85), TargetPort: intstr.FromInt(85)},
1961+
{Name: "port6", Protocol: v1.ProtocolTCP, Port: int32(86), TargetPort: intstr.FromInt(86)},
1962+
{Name: "port7", Protocol: v1.ProtocolTCP, Port: int32(88), TargetPort: intstr.FromInt(87)},
1963+
}
1964+
1965+
status, err := createExternalLoadBalancer(gce, svc, []string{"test-node-1"}, vals.ClusterName, vals.ClusterID, vals.ZoneName)
1966+
require.NotNil(t, status)
1967+
require.NoError(t, err)
1968+
svcName := "/" + svc.ObjectMeta.Name
1969+
1970+
ipAddr := status.Ingress[0].IP
1971+
lbName := gce.GetLoadBalancerName(context.TODO(), "", svc)
1972+
1973+
ipnet, err := utilnet.ParseIPNets("0.0.0.0/0")
1974+
require.NoError(t, err)
1975+
1976+
fw, err := gce.GetFirewall(MakeFirewallName(lbName))
1977+
require.NoError(t, err)
1978+
1979+
for desc := range map[string]struct {
1980+
hasErr bool
1981+
}{
1982+
"need to update port-ranges ": {},
1983+
} {
1984+
t.Run(desc, func(t *testing.T) {
1985+
fw, err = gce.GetFirewall(MakeFirewallName(lbName))
1986+
assert.NoError(t, err)
1987+
assert.Nil(t, fw)
1988+
1989+
exists, needsUpdate, err := gce.firewallNeedsUpdate(
1990+
lbName,
1991+
svcName,
1992+
ipAddr,
1993+
svc.Spec.Ports,
1994+
ipnet)
1995+
1996+
assert.Equal(t, false, exists, "firewall should not exist")
1997+
assert.Equal(t, false, needsUpdate, "firewall should not exist, no update needed")
1998+
assert.NoError(t, err)
1999+
})
2000+
}
2001+
}
2002+
19052003
func TestDeleteWrongNetworkTieredResourcesSucceedsWhenNotFound(t *testing.T) {
19062004
t.Parallel()
19072005

0 commit comments

Comments
 (0)