Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/c7n/resources/efs.py: 48%

190 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-08 06:51 +0000

1# Copyright The Cloud Custodian Authors. 

2# SPDX-License-Identifier: Apache-2.0 

3import json 

4 

5from c7n.actions import Action, BaseAction 

6from c7n.exceptions import PolicyValidationError 

7from c7n.filters.kms import KmsRelatedFilter 

8from c7n.filters import Filter 

9from c7n.manager import resources 

10from c7n.filters.vpc import SecurityGroupFilter, SubnetFilter, NetworkLocation 

11from c7n.filters.policystatement import HasStatementFilter 

12from c7n.query import ( 

13 QueryResourceManager, ChildResourceManager, TypeInfo, DescribeSource, ConfigSource 

14) 

15from c7n.tags import universal_augment 

16from c7n.utils import local_session, type_schema, get_retry 

17from .aws import shape_validate 

18from c7n.filters.backup import ConsecutiveAwsBackupsFilter 

19 

20 

21class EFSDescribe(DescribeSource): 

22 

23 def augment(self, resources): 

24 return universal_augment(self.manager, resources) 

25 

26 

27@resources.register('efs') 

28class ElasticFileSystem(QueryResourceManager): 

29 

30 class resource_type(TypeInfo): 

31 service = 'efs' 

32 enum_spec = ('describe_file_systems', 'FileSystems', None) 

33 id = 'FileSystemId' 

34 name = 'Name' 

35 date = 'CreationTime' 

36 dimension = 'FileSystemId' 

37 arn_type = 'file-system' 

38 permission_prefix = arn_service = 'elasticfilesystem' 

39 filter_name = 'FileSystemId' 

40 filter_type = 'scalar' 

41 universal_taggable = True 

42 config_type = cfn_type = 'AWS::EFS::FileSystem' 

43 arn = 'FileSystemArn' 

44 

45 source_mapping = { 

46 'describe': EFSDescribe, 

47 'config': ConfigSource 

48 } 

49 

50 

51@resources.register('efs-mount-target') 

52class ElasticFileSystemMountTarget(ChildResourceManager): 

53 

54 class resource_type(TypeInfo): 

55 service = 'efs' 

56 parent_spec = ('efs', 'FileSystemId', None) 

57 enum_spec = ('describe_mount_targets', 'MountTargets', None) 

58 permission_prefix = 'elasticfilesystem' 

59 name = id = 'MountTargetId' 

60 arn = False 

61 cfn_type = 'AWS::EFS::MountTarget' 

62 supports_trailevents = True 

63 

64 

65@ElasticFileSystemMountTarget.filter_registry.register('subnet') 

66class Subnet(SubnetFilter): 

67 

68 RelatedIdsExpression = "SubnetId" 

69 

70 

71@ElasticFileSystemMountTarget.filter_registry.register('security-group') 

72class SecurityGroup(SecurityGroupFilter): 

73 

74 efs_group_cache = None 

75 

76 RelatedIdsExpression = "" 

77 

78 def get_related_ids(self, resources): 

79 

80 if self.efs_group_cache: 

81 group_ids = set() 

82 for r in resources: 

83 group_ids.update( 

84 self.efs_group_cache.get(r['MountTargetId'], ())) 

85 return list(group_ids) 

86 

87 client = local_session(self.manager.session_factory).client('efs') 

88 groups = {} 

89 group_ids = set() 

90 retry = get_retry(('Throttled',), 12) 

91 

92 for r in resources: 

93 groups[r['MountTargetId']] = retry( 

94 client.describe_mount_target_security_groups, 

95 MountTargetId=r['MountTargetId'])['SecurityGroups'] 

96 group_ids.update(groups[r['MountTargetId']]) 

97 

98 self.efs_group_cache = groups 

99 return list(group_ids) 

100 

101 

102@ElasticFileSystemMountTarget.filter_registry.register('network-location', NetworkLocation) 

103 

104 

105@ElasticFileSystem.filter_registry.register('kms-key') 

106class KmsFilter(KmsRelatedFilter): 

107 

108 RelatedIdsExpression = 'KmsKeyId' 

109 

110 

111@ElasticFileSystem.action_registry.register('delete') 

112class Delete(Action): 

113 

114 schema = type_schema('delete') 

115 permissions = ('elasticfilesystem:DescribeMountTargets', 

116 'elasticfilesystem:DeleteMountTarget', 

117 'elasticfilesystem:DeleteFileSystem') 

118 

119 def process(self, resources): 

120 client = local_session(self.manager.session_factory).client('efs') 

121 self.unmount_filesystems(resources) 

122 retry = get_retry(('FileSystemInUse',), 12) 

123 for r in resources: 

124 retry(client.delete_file_system, FileSystemId=r['FileSystemId']) 

125 

126 def unmount_filesystems(self, resources): 

127 client = local_session(self.manager.session_factory).client('efs') 

128 for r in resources: 

129 if not r['NumberOfMountTargets']: 

130 continue 

131 for t in client.describe_mount_targets( 

132 FileSystemId=r['FileSystemId'])['MountTargets']: 

133 client.delete_mount_target(MountTargetId=t['MountTargetId']) 

134 

135 

136@ElasticFileSystem.action_registry.register('configure-lifecycle-policy') 

137class ConfigureLifecycle(BaseAction): 

138 """Enable/disable lifecycle policy for efs. 

139 

140 :example: 

141 

142 .. code-block:: yaml 

143 

144 policies: 

145 - name: efs-apply-lifecycle 

146 resource: efs 

147 actions: 

148 - type: configure-lifecycle-policy 

149 state: enable 

150 rules: 

151 - 'TransitionToIA': 'AFTER_7_DAYS' 

152 

153 """ 

154 schema = type_schema( 

155 'configure-lifecycle-policy', 

156 state={'enum': ['enable', 'disable']}, 

157 rules={ 

158 'type': 'array', 

159 'items': {'type': 'object'}}, 

160 required=['state']) 

161 

162 permissions = ('elasticfilesystem:PutLifecycleConfiguration',) 

163 shape = 'PutLifecycleConfigurationRequest' 

164 

165 def validate(self): 

166 if self.data.get('state') == 'enable' and 'rules' not in self.data: 

167 raise PolicyValidationError( 

168 'rules are required to enable lifecycle configuration %s' % (self.manager.data)) 

169 if self.data.get('state') == 'disable' and 'rules' in self.data: 

170 raise PolicyValidationError( 

171 'rules not required to disable lifecycle configuration %s' % (self.manager.data)) 

172 if self.data.get('rules'): 

173 attrs = {} 

174 attrs['LifecyclePolicies'] = self.data['rules'] 

175 attrs['FileSystemId'] = 'PolicyValidator' 

176 return shape_validate(attrs, self.shape, 'efs') 

177 

178 def process(self, resources): 

179 client = local_session(self.manager.session_factory).client('efs') 

180 op_map = {'enable': self.data.get('rules'), 'disable': []} 

181 for r in resources: 

182 try: 

183 client.put_lifecycle_configuration( 

184 FileSystemId=r['FileSystemId'], 

185 LifecyclePolicies=op_map.get(self.data.get('state'))) 

186 except client.exceptions.FileSystemNotFound: 

187 continue 

188 

189 

190@ElasticFileSystem.filter_registry.register('lifecycle-policy') 

191class LifecyclePolicy(Filter): 

192 """Filters efs based on the state of lifecycle policies 

193 

194 :example: 

195 

196 .. code-block:: yaml 

197 

198 policies: 

199 - name: efs-filter-lifecycle 

200 resource: efs 

201 filters: 

202 - type: lifecycle-policy 

203 state: present 

204 value: AFTER_7_DAYS 

205 

206 """ 

207 schema = type_schema( 

208 'lifecycle-policy', 

209 state={'enum': ['present', 'absent']}, 

210 value={'type': 'string'}, 

211 required=['state']) 

212 

213 permissions = ('elasticfilesystem:DescribeLifecycleConfiguration',) 

214 

215 def process(self, resources, event=None): 

216 resources = self.fetch_resources_lfc(resources) 

217 if self.data.get('value'): 

218 config = {'TransitionToIA': self.data.get('value')} 

219 if self.data.get('state') == 'present': 

220 return [r for r in resources if config in r.get('c7n:LifecyclePolicies')] 

221 return [r for r in resources if config not in r.get('c7n:LifecyclePolicies')] 

222 else: 

223 if self.data.get('state') == 'present': 

224 return [r for r in resources if r.get('c7n:LifecyclePolicies')] 

225 return [r for r in resources if r.get('c7n:LifecyclePolicies') == []] 

226 

227 def fetch_resources_lfc(self, resources): 

228 client = local_session(self.manager.session_factory).client('efs') 

229 for r in resources: 

230 try: 

231 lfc = client.describe_lifecycle_configuration( 

232 FileSystemId=r['FileSystemId']).get('LifecyclePolicies') 

233 r['c7n:LifecyclePolicies'] = lfc 

234 except client.exceptions.FileSystemNotFound: 

235 continue 

236 return resources 

237 

238 

239@ElasticFileSystem.filter_registry.register('check-secure-transport') 

240class CheckSecureTransport(Filter): 

241 """Find EFS that does not enforce secure transport 

242 

243 :Example: 

244 

245 .. code-block:: yaml 

246 

247 - name: efs-securetransport-check-policy 

248 resource: efs 

249 filters: 

250 - check-secure-transport 

251 

252 To configure an EFS to enforce secure transport, set up the appropriate 

253 Effect and Condition for its policy document. For example: 

254 

255 .. code-block:: json 

256 

257 { 

258 "Sid": "efs-statement-b3f6b59b-d938-4001-9154-508f67707073", 

259 "Effect": "Deny", 

260 "Principal": { "AWS": "*" }, 

261 "Action": "*", 

262 "Condition": { 

263 "Bool": { "aws:SecureTransport": "false" } 

264 } 

265 } 

266 """ 

267 

268 schema = type_schema('check-secure-transport') 

269 permissions = ('elasticfilesystem:DescribeFileSystemPolicy',) 

270 

271 policy_annotation = 'c7n:Policy' 

272 

273 def get_policy(self, client, resource): 

274 if self.policy_annotation in resource: 

275 return resource[self.policy_annotation] 

276 try: 

277 result = client.describe_file_system_policy( 

278 FileSystemId=resource['FileSystemId']) 

279 except client.exceptions.PolicyNotFound: 

280 return None 

281 resource[self.policy_annotation] = json.loads(result['Policy']) 

282 return resource[self.policy_annotation] 

283 

284 def securetransport_check_policy(self, client, resource): 

285 policy = self.get_policy(client, resource) 

286 if not policy: 

287 return True 

288 

289 statements = policy['Statement'] 

290 if isinstance(statements, dict): 

291 statements = [statements] 

292 

293 for s in statements: 

294 try: 

295 effect = s['Effect'] 

296 secureTransportValue = s['Condition']['Bool']['aws:SecureTransport'] 

297 if ((effect == 'Deny' and secureTransportValue == 'false') or 

298 (effect == 'Allow' and secureTransportValue == 'true')): 

299 return False 

300 except (KeyError, TypeError): 

301 pass 

302 

303 return True 

304 

305 def process(self, resources, event=None): 

306 c = local_session(self.manager.session_factory).client('efs') 

307 results = [r for r in resources if self.securetransport_check_policy(c, r)] 

308 self.log.info( 

309 "%d of %d EFS policies don't enforce secure transport", 

310 len(results), len(resources)) 

311 return results 

312 

313 

314@ElasticFileSystem.filter_registry.register('has-statement') 

315class EFSHasStatementFilter(HasStatementFilter): 

316 

317 def __init__(self, data, manager=None): 

318 super().__init__(data, manager) 

319 self.policy_attribute = 'c7n:Policy' 

320 

321 def process(self, resources, event=None): 

322 resources = [self.policy_annotate(r) for r in resources] 

323 return super().process(resources, event) 

324 

325 def policy_annotate(self, resource): 

326 client = local_session(self.manager.session_factory).client('efs') 

327 if self.policy_attribute in resource: 

328 return resource 

329 try: 

330 result = client.describe_file_system_policy( 

331 FileSystemId=resource['FileSystemId']) 

332 resource[self.policy_attribute] = result['Policy'] 

333 except client.exceptions.PolicyNotFound: 

334 resource[self.policy_attribute] = None 

335 return resource 

336 return resource 

337 

338 def get_std_format_args(self, fs): 

339 return { 

340 'fs_arn': fs['FileSystemArn'], 

341 'account_id': self.manager.config.account_id, 

342 'region': self.manager.config.region 

343 } 

344 

345 

346ElasticFileSystem.filter_registry.register('consecutive-aws-backups', ConsecutiveAwsBackupsFilter)