Skip to content

Commit 6f151ad

Browse files
authored
CDPCAM-83 Add cross-account role actions validation for Azure (#61)
* CDPCAM-83 Add cross-account role actions validation for Azure Signed-off-by: Mahendra Korepu <mkorepu@cloudera.com>
1 parent 1b87cc1 commit 6f151ad

File tree

5 files changed

+568
-0
lines changed

5 files changed

+568
-0
lines changed

cdpctl/templates/config_azure.yml.j2

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ env:
3838
## The Ranger Audit Identity to be used for the environment
3939
# example: azure-cdp-ranger_audit-identity
4040
ranger_audit: {{ info['env:azure:role:name:ranger_audit']|default("", true) }}
41+
42+
## The Cloudera Management Console Operator Identity to be used for the environment
43+
# example: azure-cdp-cross_account-identity
44+
cross_account: {{ info['env:azure:role:name:cross_account']|default("", true) }}
4145
storage:
4246
## The Storage Account used for the environment
4347
name: {{ info['env:azure:storage:name']|default("", true) }}

cdpctl/validation/infra/conftest.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,3 +440,89 @@ def azure_assumer_required_resource_group_actions() -> List[str]:
440440
"Microsoft.Support/operationsstatus/read",
441441
"Microsoft.Support/operations/read",
442442
]
443+
444+
445+
@pytest.fixture
446+
def azure_cross_account_required_resource_group_actions() -> List[str]:
447+
"""Get the Azure actions needed for the cross account identity."""
448+
return [
449+
"Microsoft.Storage/storageAccounts/read",
450+
"Microsoft.Storage/storageAccounts/write",
451+
"Microsoft.Storage/storageAccounts/blobServices/write",
452+
"Microsoft.Storage/storageAccounts/blobServices/containers/delete",
453+
"Microsoft.Storage/storageAccounts/blobServices/containers/read",
454+
"Microsoft.Storage/storageAccounts/blobServices/containers/write",
455+
"Microsoft.Storage/storageAccounts/fileServices/write",
456+
"Microsoft.Storage/storageAccounts/listkeys/action",
457+
"Microsoft.Storage/storageAccounts/regeneratekey/action",
458+
"Microsoft.Storage/storageAccounts/delete",
459+
"Microsoft.Storage/locations/deleteVirtualNetworkOrSubnets/action",
460+
"Microsoft.Network/virtualNetworks/read",
461+
"Microsoft.Network/virtualNetworks/write",
462+
"Microsoft.Network/virtualNetworks/delete",
463+
"Microsoft.Network/virtualNetworks/subnets/read",
464+
"Microsoft.Network/virtualNetworks/subnets/write",
465+
"Microsoft.Network/virtualNetworks/subnets/delete",
466+
"Microsoft.Network/virtualNetworks/subnets/join/action",
467+
"Microsoft.Network/publicIPAddresses/read",
468+
"Microsoft.Network/publicIPAddresses/write",
469+
"Microsoft.Network/publicIPAddresses/delete",
470+
"Microsoft.Network/publicIPAddresses/join/action",
471+
"Microsoft.Network/networkInterfaces/read",
472+
"Microsoft.Network/networkInterfaces/write",
473+
"Microsoft.Network/networkInterfaces/delete",
474+
"Microsoft.Network/networkInterfaces/join/action",
475+
"Microsoft.Network/networkInterfaces/ipconfigurations/read",
476+
"Microsoft.Network/networkSecurityGroups/read",
477+
"Microsoft.Network/networkSecurityGroups/write",
478+
"Microsoft.Network/networkSecurityGroups/delete",
479+
"Microsoft.Network/networkSecurityGroups/join/action",
480+
"Microsoft.Network/virtualNetworks/subnets/joinViaServiceEndpoint/action",
481+
"Microsoft.Network/loadBalancers/delete",
482+
"Microsoft.Network/loadBalancers/read",
483+
"Microsoft.Network/loadBalancers/write",
484+
"Microsoft.Network/loadBalancers/backendAddressPools/join/action",
485+
"Microsoft.Compute/availabilitySets/read",
486+
"Microsoft.Compute/availabilitySets/write",
487+
"Microsoft.Compute/availabilitySets/delete",
488+
"Microsoft.Compute/disks/read",
489+
"Microsoft.Compute/disks/write",
490+
"Microsoft.Compute/disks/delete",
491+
"Microsoft.Compute/images/read",
492+
"Microsoft.Compute/images/write",
493+
"Microsoft.Compute/images/delete",
494+
"Microsoft.Compute/virtualMachines/read",
495+
"Microsoft.Compute/virtualMachines/write",
496+
"Microsoft.Compute/virtualMachines/delete",
497+
"Microsoft.Compute/virtualMachines/start/action",
498+
"Microsoft.Compute/virtualMachines/restart/action",
499+
"Microsoft.Compute/virtualMachines/deallocate/action",
500+
"Microsoft.Compute/virtualMachines/powerOff/action",
501+
"Microsoft.Compute/virtualMachines/vmSizes/read",
502+
"Microsoft.Authorization/roleAssignments/read",
503+
"Microsoft.Resources/subscriptions/resourceGroups/read",
504+
"Microsoft.Resources/deployments/read",
505+
"Microsoft.Resources/deployments/write",
506+
"Microsoft.Resources/deployments/delete",
507+
"Microsoft.Resources/deployments/operations/read",
508+
"Microsoft.Resources/deployments/operationstatuses/read",
509+
"Microsoft.Resources/deployments/exportTemplate/action",
510+
"Microsoft.Resources/subscriptions/read",
511+
"Microsoft.ManagedIdentity/userAssignedIdentities/read",
512+
"Microsoft.ManagedIdentity/userAssignedIdentities/assign/action",
513+
"Microsoft.DBforPostgreSQL/servers/write",
514+
"Microsoft.DBforPostgreSQL/servers/delete",
515+
"Microsoft.DBforPostgreSQL/servers/virtualNetworkRules/write",
516+
"Microsoft.Resources/deployments/cancel/action",
517+
]
518+
519+
520+
@pytest.fixture
521+
def azure_cross_account_required_resource_group_data_actions() -> List[str]:
522+
"""Get the Azure data actions needed for the cross account identity."""
523+
return [
524+
"Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read",
525+
"Microsoft.Storage/storageAccounts/blobServices/containers/blobs/write",
526+
"Microsoft.Storage/storageAccounts/blobServices/containers/blobs/delete",
527+
"Microsoft.Storage/storageAccounts/blobServices/containers/blobs/add/action",
528+
]
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
#!/usr/bin/env python3
2+
###
3+
# CLOUDERA CDP Control (cdpctl)
4+
#
5+
# (C) Cloudera, Inc. 2021-2021
6+
# All rights reserved.
7+
#
8+
# Applicable Open Source License: GNU AFFERO GENERAL PUBLIC LICENSE
9+
#
10+
# NOTE: Cloudera open source products are modular software products
11+
# made up of hundreds of individual components, each of which was
12+
# individually copyrighted. Each Cloudera open source product is a
13+
# collective work under U.S. Copyright Law. Your license to use the
14+
# collective work is as provided in your written agreement with
15+
# Cloudera. Used apart from the collective work, this file is
16+
# licensed for your use pursuant to the open source license
17+
# identified above.
18+
#
19+
# This code is provided to you pursuant a written agreement with
20+
# (i) Cloudera, Inc. or (ii) a third-party authorized to distribute
21+
# this code. If you do not have a written agreement with Cloudera nor
22+
# with an authorized and properly licensed third party, you do not
23+
# have any rights to access nor to use this code.
24+
#
25+
# Absent a written agreement with Cloudera, Inc. (“Cloudera”) to the
26+
# contrary, A) CLOUDERA PROVIDES THIS CODE TO YOU WITHOUT WARRANTIES OF ANY
27+
# KIND; (B) CLOUDERA DISCLAIMS ANY AND ALL EXPRESS AND IMPLIED
28+
# WARRANTIES WITH RESPECT TO THIS CODE, INCLUDING BUT NOT LIMITED TO
29+
# IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND
30+
# FITNESS FOR A PARTICULAR PURPOSE; (C) CLOUDERA IS NOT LIABLE TO YOU,
31+
# AND WILL NOT DEFEND, INDEMNIFY, NOR HOLD YOU HARMLESS FOR ANY CLAIMS
32+
# ARISING FROM OR RELATED TO THE CODE; AND (D)WITH RESPECT TO YOUR EXERCISE
33+
# OF ANY RIGHTS GRANTED TO YOU FOR THE CODE, CLOUDERA IS NOT LIABLE FOR ANY
34+
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE OR
35+
# CONSEQUENTIAL DAMAGES INCLUDING, BUT NOT LIMITED TO, DAMAGES
36+
# RELATED TO LOST REVENUE, LOST PROFITS, LOSS OF INCOME, LOSS OF
37+
# BUSINESS ADVANTAGE OR UNAVAILABILITY, OR LOSS OR CORRUPTION OF
38+
# DATA.
39+
#
40+
# Source File Name: validate_azure_cross_account_identity.py
41+
###
42+
"""Validation of Azure Cross Account Identity."""
43+
# "Cross Account Identity" is equivalent to "Cloudera Management Console Operator Identity" # noqa: D401,E501
44+
# Customer facing text changed to "Cloudera Management Console Operator Identity" to
45+
# match the documentation
46+
47+
from typing import Any, Dict, List
48+
49+
import pytest
50+
from azure.mgmt.authorization import AuthorizationManagementClient
51+
from azure.mgmt.resource import ResourceManagementClient
52+
53+
from cdpctl.validation import fail, get_config_value
54+
from cdpctl.validation.azure_utils import (
55+
check_for_actions,
56+
get_client,
57+
get_resource_group_scope,
58+
get_role_assignments,
59+
)
60+
from cdpctl.validation.infra.issues import (
61+
AZURE_IDENTITY_MISSING_ACTIONS_FOR_LOCATION,
62+
AZURE_IDENTITY_MISSING_DATA_ACTIONS_FOR_LOCATION,
63+
)
64+
65+
_cross_account_info = {}
66+
67+
68+
@pytest.fixture(name="cross_account_info")
69+
def cross_account_info_fixture():
70+
"""Get the info for the Cross Account Identity."""
71+
return _cross_account_info
72+
73+
74+
@pytest.fixture(autouse=True, name="resource_client")
75+
def resource_client_fixture(config: Dict[str, Any]) -> ResourceManagementClient:
76+
"""Return an Azure Resource Client."""
77+
return get_client("resource", config)
78+
79+
80+
@pytest.fixture(autouse=True, name="auth_client")
81+
def auth_client_fixture(config: Dict[str, Any]) -> AuthorizationManagementClient:
82+
"""Return an Azure Auth Client."""
83+
return get_client("auth", config)
84+
85+
86+
@pytest.mark.azure
87+
@pytest.mark.infra
88+
def azure_cross_account_identity_exists_validation(
89+
config: Dict[str, Any],
90+
auth_client: AuthorizationManagementClient,
91+
resource_client: ResourceManagementClient,
92+
) -> None: # pragma: no cover
93+
"""Azure Cloudera Management Console Operator Identity exists."""
94+
95+
_cross_account_info["sub_id"] = get_config_value(
96+
config=config, key="infra:azure:subscription_id"
97+
)
98+
_cross_account_info["rg_name"] = get_config_value(
99+
config=config, key="infra:azure:metagroup:name"
100+
)
101+
_cross_account_info["name"] = get_config_value(
102+
config=config, key="env:azure:role:name:cross_account"
103+
)
104+
105+
_cross_account_info["assignments"] = get_role_assignments(
106+
auth_client=auth_client,
107+
resource_client=resource_client,
108+
identity_name=_cross_account_info["name"],
109+
subscription_id=_cross_account_info["sub_id"],
110+
resource_group=_cross_account_info["rg_name"],
111+
)
112+
113+
114+
@pytest.mark.azure
115+
@pytest.mark.infra
116+
@pytest.mark.dependency(depends=["azure_cross_account_identity_exists_validation"])
117+
def azure_cross_account_rg_actions_validation(
118+
auth_client: AuthorizationManagementClient,
119+
azure_cross_account_required_resource_group_actions: List[str],
120+
cross_account_info,
121+
) -> None: # pragma: no cover
122+
"""Cloudera Management Console Operator Identity has the necessary actions on the resource group.""" # noqa: D401,E501
123+
124+
proper_scope = get_resource_group_scope(
125+
subscription_id=cross_account_info["sub_id"],
126+
resource_group=cross_account_info["rg_name"],
127+
)
128+
129+
missing_actions, _ = check_for_actions(
130+
auth_client=auth_client,
131+
role_assigments=cross_account_info["assignments"],
132+
proper_scope=proper_scope,
133+
required_actions=azure_cross_account_required_resource_group_actions,
134+
required_data_actions=[],
135+
)
136+
137+
if missing_actions:
138+
fail(
139+
AZURE_IDENTITY_MISSING_ACTIONS_FOR_LOCATION,
140+
subjects=[
141+
cross_account_info["name"],
142+
proper_scope, # noqa: E501
143+
],
144+
resources=missing_actions,
145+
)
146+
147+
148+
@pytest.mark.azure
149+
@pytest.mark.infra
150+
@pytest.mark.dependency(depends=["azure_cross_account_identity_exists_validation"])
151+
def azure_cross_account_rg_data_actions_validation(
152+
auth_client: AuthorizationManagementClient,
153+
azure_cross_account_required_resource_group_data_actions: List[str],
154+
cross_account_info,
155+
) -> None: # pragma: no cover
156+
"""Cloudera Management Console Operator Identity has the necessary data actions on the resource group.""" # noqa: D401,E501
157+
158+
proper_scope = get_resource_group_scope(
159+
subscription_id=cross_account_info["sub_id"],
160+
resource_group=cross_account_info["rg_name"],
161+
)
162+
163+
_, missing_data_actions = check_for_actions(
164+
auth_client=auth_client,
165+
role_assigments=cross_account_info["assignments"],
166+
proper_scope=proper_scope,
167+
required_actions=[],
168+
required_data_actions=azure_cross_account_required_resource_group_data_actions,
169+
)
170+
if missing_data_actions:
171+
fail(
172+
AZURE_IDENTITY_MISSING_DATA_ACTIONS_FOR_LOCATION,
173+
subjects=[
174+
cross_account_info["name"],
175+
proper_scope, # noqa: E501
176+
],
177+
resources=missing_data_actions,
178+
)

tests/validation/infra/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
"""Import validation fixtures."""
4545
from cdpctl.validation.infra.conftest import (
4646
autoscaling_resources_needed_actions,
47+
azure_cross_account_required_resource_group_actions,
48+
azure_cross_account_required_resource_group_data_actions,
4749
azure_data_required_actions,
4850
azure_data_required_data_actions,
4951
cdp_cidrs,

0 commit comments

Comments
 (0)