1# Copyright The Cloud Custodian Authors.
2# SPDX-License-Identifier: Apache-2.0
3import re
4
5from c7n_gcp.actions import MethodAction
6from c7n_gcp.query import QueryResourceManager, TypeInfo
7
8from c7n_gcp.provider import resources
9from c7n.filters.core import ListItemFilter
10from c7n.utils import type_schema, local_session
11from c7n_gcp.utils import get_firewall_port_ranges
12
13
14@resources.register('vpc')
15class Network(QueryResourceManager):
16 """GCP resource: https://cloud.google.com/compute/docs/reference/rest/v1/networks
17 """
18 class resource_type(TypeInfo):
19 service = 'compute'
20 version = 'v1'
21 component = 'networks'
22 scope_template = "{}"
23 name = id = "name"
24 default_report_fields = [
25 "name", "description", "creationTimestamp",
26 "autoCreateSubnetworks", "IPv4Range", "gatewayIPv4"]
27 asset_type = "compute.googleapis.com/Network"
28 scc_type = "google.compute.Network"
29 urn_component = "vpc"
30
31 @staticmethod
32 def get(client, resource_info):
33 path_param_re = re.compile('.*?/projects/(.*?)/global/networks/(.*)')
34 project, network = path_param_re.match(
35 resource_info["resourceName"]).groups()
36 return client.execute_query(
37 'get', {'project': project, 'network': network})
38
39
40@Network.filter_registry.register('firewall')
41class VPCFirewallFilter(ListItemFilter):
42 schema = type_schema(
43 'firewall',
44 attrs={'$ref': '#/definitions/filters_common/list_item_attrs'}
45 )
46 annotate_items = True
47 permissions = ("vpcaccess.locations.list",)
48
49 def get_item_values(self, resource):
50 session = local_session(self.manager.session_factory)
51 client = session.client(service_name='compute', version='v1',
52 component='networks')
53 project = session.get_default_project()
54 firewalls = client.execute_query('getEffectiveFirewalls', {
55 'project': project, 'network': resource['name']}).get('firewalls')
56 return firewalls
57
58
59@resources.register('subnet')
60class Subnet(QueryResourceManager):
61 """GCP resource: https://cloud.google.com/compute/docs/reference/rest/v1/subnetworks
62 """
63 class resource_type(TypeInfo):
64 service = 'compute'
65 version = 'v1'
66 component = 'subnetworks'
67 enum_spec = ('aggregatedList', 'items.*.subnetworks[]', None)
68 name = id = "name"
69 default_report_fields = [
70 "name", "description", "creationTimestamp", "ipCidrRange",
71 "gatewayAddress", "region", "state"]
72 asset_type = "compute.googleapis.com/Subnetwork"
73 scc_type = "google.compute.Subnetwork"
74 metric_key = "resource.labels.subnetwork_name"
75 urn_component = "subnet"
76
77 @staticmethod
78 def get(client, resource_info):
79
80 path_param_re = re.compile(
81 '.*?projects/(.*?)/regions/(.*?)/subnetworks/(.*)')
82 project, region, subnet = path_param_re.match(
83 resource_info["resourceName"]).groups()
84 return client.execute_query(
85 'get', {'project': project, 'region': region, 'subnetwork': subnet})
86
87
88class SubnetAction(MethodAction):
89
90 path_param_re = re.compile(
91 '.*?/projects/(.*?)/regions/(.*?)/subnetworks/(.*)')
92
93 def get_resource_params(self, model, resource):
94 project, region, subnet = self.path_param_re.match(
95 resource['selfLink']).groups()
96 return {'project': project, 'region': region, 'subnetwork': subnet}
97
98
99@Subnet.action_registry.register('set-flow-log')
100class SetFlowLog(SubnetAction):
101 """Enable vpc flow logs on a subnet.
102
103 :example: Enable flow logs on all subnets
104
105 .. yaml:
106
107 policies:
108 - name: flow-active
109 resource: gcp.subnet
110 filters:
111 - enableFlowLogs: empty
112 actions:
113 - set-flow-log
114
115 """
116
117 schema = type_schema(
118 'set-flow-log',
119 state={'type': 'boolean', 'default': True})
120 method_spec = {'op': 'patch'}
121 method_perm = 'update'
122
123 def get_resource_params(self, m, r):
124 params = super(SetFlowLog, self).get_resource_params(m, r)
125 return {
126 'project': params['project'],
127 'region': params['region'],
128 'subnetwork': params['subnetwork'],
129 'body': {
130 'fingerprint': r['fingerprint'],
131 'enableFlowLogs': self.data.get('state', True)}
132 }
133
134
135@Subnet.action_registry.register('set-private-api')
136class SetGcpPrivateAccess(SubnetAction):
137 """Enable/Disable GCP Private IP Access for a subnet"""
138
139 schema = type_schema(
140 'set-gcp-private',
141 state={'type': 'boolean', 'default': True})
142 method_spec = {'op': 'setPrivateIpGoogleAccess'}
143
144 def get_resource_params(self, m, r):
145 params = super(SetGcpPrivateAccess, self).get_resource_params(m, r)
146 params['body'] = {
147 'privateIpGoogleAccess': self.data.get('state', True)}
148 return params
149
150
151@resources.register('firewall')
152class Firewall(QueryResourceManager):
153 """GCP resource: https://cloud.google.com/compute/docs/reference/rest/v1/firewalls
154 """
155 class resource_type(TypeInfo):
156 service = 'compute'
157 version = 'v1'
158 component = 'firewalls'
159 name = id = "name"
160 default_report_fields = [
161 name, "description", "network", "priority", "creationTimestamp",
162 "logConfig.enable", "disabled"]
163 asset_type = "compute.googleapis.com/Firewall"
164 scc_type = "google.compute.Firewall"
165 metric_key = 'metric.labels.firewall_name'
166 urn_component = "firewall"
167
168 @staticmethod
169 def get(client, resource_info):
170 return client.execute_query(
171 'get', {'project': resource_info['project_id'],
172 'firewall': resource_info['resourceName'].rsplit('/', 1)[-1]})
173
174 def augment(self, resources):
175 if not resources:
176 return []
177 return get_firewall_port_ranges(resources)
178
179
180@Firewall.action_registry.register('delete')
181class DeleteFirewall(MethodAction):
182 """Delete filtered Firewall Rules
183
184 :example: Delete firewall rule
185
186 .. yaml:
187
188 policies:
189 - name: delete-public-access-firewall-rules
190 resource: gcp.firewall
191 filters:
192 - type: value
193 key: sourceRanges
194 value: "0.0.0.0/0"
195 op: in
196 value_type: swap
197 actions:
198 - delete
199 """
200
201 schema = type_schema('delete')
202 method_spec = {'op': 'delete'}
203 path_param_re = re.compile('.*?/projects/(.*?)/global/firewalls/(.*)')
204
205 def get_resource_params(self, m, r):
206 project, resource_name = self.path_param_re.match(
207 r['selfLink']).groups()
208 return {'project': project, 'firewall': resource_name}
209
210
211@Firewall.action_registry.register('modify')
212class ModifyFirewall(MethodAction):
213 """Modify filtered Firewall Rules
214
215 :example: Enable logging on filtered firewalls
216
217 .. yaml:
218
219 policies:
220 - name: enable-firewall-logging
221 resource: gcp.firewall
222 filters:
223 - type: value
224 key: name
225 value: no-logging
226 actions:
227 - type: modify
228 logConfig:
229 enabled: true
230 """
231
232 schema = type_schema(
233 'modify',
234 **{'description': {'type': 'string'},
235 'network': {'type': 'string'},
236 'priority': {'type': 'number'},
237 'sourceRanges': {'type': 'array', 'items': {'type': 'string'}},
238 'destinationRanges': {'type': 'array', 'items': {'type': 'string'}},
239 'sourceTags': {'type': 'array', 'items': {'type': 'string'}},
240 'targetTags': {'type': 'array', 'items': {'type': 'string'}},
241 'sourceServiceAccounts': {'type': 'array', 'items': {'type': 'string'}},
242 'targetServiceAccounts': {'type': 'array', 'items': {'type': 'string'}},
243 'allowed': {'type': 'array', 'items': {'type': 'object'}},
244 'denied': {'type': 'array', 'items': {'type': 'object'}},
245 'direction': {'enum': ['INGRESS', 'EGRESS']},
246 'logConfig': {'type': 'object'},
247 'disabled': {'type': 'boolean'}})
248 method_spec = {'op': 'patch'}
249 permissions = ('compute.networks.updatePolicy', 'compute.firewalls.update')
250 path_param_re = re.compile('.*?/projects/(.*?)/global/firewalls/(.*)')
251
252 def get_resource_params(self, m, r):
253 project, resource_name = self.path_param_re.match(
254 r['selfLink']).groups()
255 return {'project': project, 'firewall': resource_name, 'body': self.data}
256
257
258@resources.register('router')
259class Router(QueryResourceManager):
260 """GCP resource: https://cloud.google.com/compute/docs/reference/rest/v1/routers
261 """
262 class resource_type(TypeInfo):
263 service = 'compute'
264 version = 'v1'
265 component = 'routers'
266 enum_spec = ('aggregatedList', 'items.*.routers[]', None)
267 name = id = 'name'
268 default_report_fields = [
269 "name", "description", "creationTimestamp", "region", "network"]
270 asset_type = "compute.googleapis.com/Router"
271 urn_component = "router"
272
273 @staticmethod
274 def get(client, resource_info):
275 return client.execute_command(
276 'get', {'project': resource_info['project_id'],
277 'region': resource_info['region'],
278 'router': resource_info['resourceName'].rsplit('/', 1)[-1]})
279
280
281@Router.action_registry.register('delete')
282class DeleteRouter(MethodAction):
283 """`Deletes <https://cloud.google.com/compute/docs/reference/rest/v1/routers/delete>`_ a router
284
285 :Example:
286
287 .. code-block:: yaml
288
289 policies:
290 - name: gcp-network-unattached-routers
291 description: Deletes unattached Cloud Routers
292 resource: gcp.router
293 filters:
294 - type: value
295 key: interfaces
296 value: absent
297 actions:
298 - delete
299 """
300
301 schema = type_schema('delete')
302 method_spec = {'op': 'delete'}
303 path_param_re = re.compile('.*?/projects/(.*?)/regions/(.*?)/routers/(.*)')
304
305 def get_resource_params(self, m, r):
306 project, region, router = self.path_param_re.match(r['selfLink']).groups()
307 return {'project': project, 'region': region, 'router': router}
308
309
310@resources.register('route')
311class Route(QueryResourceManager):
312 """GCP resource: https://cloud.google.com/compute/docs/reference/rest/v1/routes
313 """
314 class resource_type(TypeInfo):
315 service = 'compute'
316 version = 'v1'
317 component = 'routes'
318 enum_spec = ('list', 'items[]', None)
319 name = id = 'name'
320 default_report_fields = [
321 "name", "description", "creationTimestamp", "network", "priority", "destRange"]
322 asset_type = "compute.googleapis.com/Route"
323 urn_component = "route"
324
325 @staticmethod
326 def get(client, resource_info):
327 return client.execute_command(
328 'get', {'project': resource_info['project_id'],
329 'route': resource_info['resourceName'].rsplit('/', 1)[-1]})
330
331
332@resources.register('interconnect')
333class Interconnect(QueryResourceManager):
334 """GCP resource: https://cloud.google.com/compute/docs/reference/rest/v1/interconnects
335 """
336 class resource_type(TypeInfo):
337 service = 'compute'
338 version = 'v1'
339 component = 'interconnects'
340 enum_spec = ('list', 'items[]', None)
341 name = id = 'name'
342 default_report_fields = [
343 "name", "description", "creationTimestamp", "operationalStatus",
344 "linkType", "location"]
345 asset_type = "compute.googleapis.com/Interconnect"
346
347 @staticmethod
348 def get(client, resource_info):
349 return client.execute_command(
350 'get', {'project': resource_info['project_id'],
351 'interconnect': resource_info['resourceName'].rsplit('/', 1)[-1]})
352
353
354@resources.register('interconnect-attachment')
355class InterconnectAttachment(QueryResourceManager):
356 """GCP resource: https://cloud.google.com/compute/docs/reference/rest/v1/interconnectAttachments
357 """
358 class resource_type(TypeInfo):
359 service = 'compute'
360 version = 'v1'
361 component = 'interconnectAttachments'
362 enum_spec = ('aggregatedList', 'items.*.interconnectAttachments[]', None)
363 name = id = 'name'
364 default_report_fields = [
365 "name", "description", "creationTimestamp", "interconnect",
366 "router", "region", "operationalStatus"]
367 asset_type = "compute.googleapis.com/InterconnectAttachment"
368
369 @staticmethod
370 def get(client, resource_info):
371 project, region, name = re.match(
372 'projects/(.*?)/regions/(.*?)/interconnectAttachments/(.*?)',
373 resource_info['resourceName']).groups()
374
375 return client.execute_command(
376 'get', {'project': project,
377 'interconnectAttachment': name,
378 'region': region})