mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-15 11:28:09 +00:00
Update the registration logic to print manual steps when adal authentication fails (#447)
Mitigate the deployment issue related to the conditional access policy. The registration logic is updated to use the old rbac python library when possible. The deployment will print some manual step for operations that cannot be automated
This commit is contained in:
@ -67,7 +67,6 @@ from registration import (
|
||||
add_application_password,
|
||||
assign_scaleset_role,
|
||||
authorize_application,
|
||||
get_application,
|
||||
register_application,
|
||||
update_pool_registration,
|
||||
)
|
||||
@ -329,10 +328,11 @@ class Client:
|
||||
|
||||
(password_id, password) = self.create_password(app.object_id)
|
||||
|
||||
onefuzz_cli_app_uuid = uuid.UUID(ONEFUZZ_CLI_APP)
|
||||
cli_app = get_application(onefuzz_cli_app_uuid)
|
||||
cli_app = client.applications.list(filter="appId eq '%s'" % ONEFUZZ_CLI_APP)
|
||||
|
||||
if cli_app is None:
|
||||
onefuzz_cli_app_uuid = uuid.UUID(ONEFUZZ_CLI_APP)
|
||||
|
||||
if not cli_app:
|
||||
logger.info(
|
||||
"Could not find the default CLI application under the current "
|
||||
"subscription, creating a new one"
|
||||
|
@ -12,6 +12,7 @@ from enum import Enum
|
||||
from typing import Any, Dict, List, NamedTuple, Optional, Tuple
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
import adal # type: ignore
|
||||
import requests
|
||||
from azure.common.client_factory import get_client_from_cli_profile
|
||||
from azure.common.credentials import get_cli_profile
|
||||
@ -19,12 +20,21 @@ from azure.graphrbac import GraphRbacManagementClient
|
||||
from azure.graphrbac.models import (
|
||||
Application,
|
||||
ApplicationCreateParameters,
|
||||
ApplicationUpdateParameters,
|
||||
AppRole,
|
||||
PasswordCredential,
|
||||
RequiredResourceAccess,
|
||||
ResourceAccess,
|
||||
ServicePrincipal,
|
||||
)
|
||||
from functional import seq
|
||||
from msrest.serialization import TZ_UTC
|
||||
|
||||
FIX_URL = (
|
||||
"https://ms.portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/"
|
||||
"ApplicationMenuBlade/ProtectAnAPI/appId/%s/isMSAApp/"
|
||||
)
|
||||
|
||||
logger = logging.getLogger("deploy")
|
||||
|
||||
|
||||
@ -70,6 +80,13 @@ def query_microsoft_graph(
|
||||
)
|
||||
|
||||
|
||||
def get_graph_client() -> GraphRbacManagementClient:
|
||||
client: GraphRbacManagementClient = get_client_from_cli_profile(
|
||||
GraphRbacManagementClient
|
||||
)
|
||||
return client
|
||||
|
||||
|
||||
class ApplicationInfo(NamedTuple):
|
||||
client_id: UUID
|
||||
client_secret: str
|
||||
@ -85,7 +102,7 @@ def register_application(
|
||||
registration_name: str, onefuzz_instance_name: str, approle: OnefuzzAppRole
|
||||
) -> ApplicationInfo:
|
||||
logger.info("retrieving the application registration %s" % registration_name)
|
||||
client = get_client_from_cli_profile(GraphRbacManagementClient)
|
||||
client = get_graph_client()
|
||||
apps: List[Application] = list(
|
||||
client.applications.list(filter="displayName eq '%s'" % registration_name)
|
||||
)
|
||||
@ -132,7 +149,7 @@ def create_application_credential(application_name: str) -> str:
|
||||
""" Add a new password to the application registration """
|
||||
|
||||
logger.info("creating application credential for '%s'" % application_name)
|
||||
client = get_client_from_cli_profile(GraphRbacManagementClient)
|
||||
client = get_graph_client()
|
||||
apps: List[Application] = list(
|
||||
client.applications.list(filter="displayName eq '%s'" % application_name)
|
||||
)
|
||||
@ -148,7 +165,7 @@ def create_application_registration(
|
||||
) -> Application:
|
||||
""" Create an application registration """
|
||||
|
||||
client = get_client_from_cli_profile(GraphRbacManagementClient)
|
||||
client = get_graph_client()
|
||||
apps: List[Application] = list(
|
||||
client.applications.list(filter="displayName eq '%s'" % onefuzz_instance_name)
|
||||
)
|
||||
@ -189,21 +206,15 @@ def create_application_registration(
|
||||
atttempts = atttempts - 1
|
||||
try:
|
||||
time.sleep(5)
|
||||
query_microsoft_graph(
|
||||
method="PATCH",
|
||||
resource="applications/%s" % registered_app.object_id,
|
||||
body={
|
||||
"publicClient": {
|
||||
"redirectUris": [
|
||||
"https://%s.azurewebsites.net" % onefuzz_instance_name
|
||||
]
|
||||
},
|
||||
"isFallbackPublicClient": True,
|
||||
},
|
||||
|
||||
client = get_graph_client()
|
||||
update_param = ApplicationUpdateParameters(
|
||||
reply_urls=["https://%s.azurewebsites.net" % onefuzz_instance_name]
|
||||
)
|
||||
client.applications.patch(registered_app.object_id, update_param)
|
||||
|
||||
break
|
||||
except GraphQueryError as err:
|
||||
if err.status_code == 404:
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
authorize_application(UUID(registered_app.app_id), UUID(app.app_id))
|
||||
@ -232,6 +243,26 @@ def add_application_password(app_object_id: UUID) -> Tuple[str, str]:
|
||||
raise Exception("unable to create password")
|
||||
|
||||
|
||||
def add_application_password_legacy(app_object_id: UUID) -> Tuple[str, str]:
|
||||
key = str(uuid4())
|
||||
password = str(uuid4())
|
||||
|
||||
client = get_graph_client()
|
||||
password_cred = [
|
||||
PasswordCredential(
|
||||
start_date="%s" % datetime.now(TZ_UTC).strftime("%Y-%m-%dT%H:%M.%fZ"),
|
||||
end_date="%s"
|
||||
% (datetime.now(TZ_UTC) + timedelta(days=365)).strftime(
|
||||
"%Y-%m-%dT%H:%M.%fZ"
|
||||
),
|
||||
key_id=key,
|
||||
value=password,
|
||||
)
|
||||
]
|
||||
client.applications.update_password_credentials(app_object_id, password_cred)
|
||||
return (key, password)
|
||||
|
||||
|
||||
def add_application_password_impl(app_object_id: UUID) -> Tuple[str, str]:
|
||||
key = uuid4()
|
||||
password_request = {
|
||||
@ -245,13 +276,15 @@ def add_application_password_impl(app_object_id: UUID) -> Tuple[str, str]:
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
password: Dict = query_microsoft_graph(
|
||||
method="POST",
|
||||
resource="applications/%s/addPassword" % app_object_id,
|
||||
body=password_request,
|
||||
)
|
||||
|
||||
return (str(key), password["secretText"])
|
||||
except adal.AdalError:
|
||||
return add_application_password_legacy(app_object_id)
|
||||
|
||||
|
||||
def get_application(app_id: UUID) -> Optional[Any]:
|
||||
@ -271,9 +304,10 @@ def authorize_application(
|
||||
onefuzz_app_id: UUID,
|
||||
permissions: List[str] = ["user_impersonation"],
|
||||
) -> None:
|
||||
try:
|
||||
onefuzz_app = get_application(onefuzz_app_id)
|
||||
if onefuzz_app is None:
|
||||
logger.error("Application '%s' not found" % onefuzz_app_id)
|
||||
logger.error("Application '%s' not found", onefuzz_app_id)
|
||||
return
|
||||
|
||||
scopes = seq(onefuzz_app["api"]["oauth2PermissionScopes"]).filter(
|
||||
@ -302,9 +336,14 @@ def authorize_application(
|
||||
method="PATCH",
|
||||
resource="applications/%s" % onefuzz_app["id"],
|
||||
body={
|
||||
"api": {"preAuthorizedApplications": preAuthorizedApplications.to_list()}
|
||||
"api": {
|
||||
"preAuthorizedApplications": preAuthorizedApplications.to_list()
|
||||
}
|
||||
},
|
||||
)
|
||||
except adal.AdalError:
|
||||
logger.warning("*** Browse to: %s", FIX_URL % onefuzz_app_id)
|
||||
logger.warning("*** Then add the client application %s", registration_app_id)
|
||||
|
||||
|
||||
def create_and_display_registration(
|
||||
@ -330,12 +369,70 @@ def update_pool_registration(onefuzz_instance_name: str) -> None:
|
||||
)
|
||||
|
||||
|
||||
def assign_scaleset_role_manually(
|
||||
onefuzz_instance_name: str, scaleset_name: str
|
||||
) -> None:
|
||||
|
||||
client = get_graph_client()
|
||||
apps: List[Application] = list(
|
||||
client.applications.list(filter="displayName eq '%s'" % onefuzz_instance_name)
|
||||
)
|
||||
|
||||
if not apps:
|
||||
raise Exception("onefuzz app registration not found")
|
||||
|
||||
app = apps[0]
|
||||
appId = app.app_id
|
||||
|
||||
onefuzz_service_principals: List[ServicePrincipal] = list(
|
||||
client.service_principals.list(filter="appId eq '%s'" % appId)
|
||||
)
|
||||
|
||||
if not onefuzz_service_principals:
|
||||
raise Exception("onefuzz app service principal not found")
|
||||
onefuzz_service_principal = onefuzz_service_principals[0]
|
||||
|
||||
scaleset_service_principals: List[ServicePrincipal] = list(
|
||||
client.service_principals.list(filter="displayName eq '%s'" % scaleset_name)
|
||||
)
|
||||
|
||||
if not scaleset_service_principals:
|
||||
raise Exception("scaleset service principal not found")
|
||||
scaleset_service_principal = scaleset_service_principals[0]
|
||||
|
||||
scaleset_service_principal.app_roles
|
||||
app_roles: List[AppRole] = [
|
||||
role for role in app.app_roles if role.value == OnefuzzAppRole.ManagedNode.value
|
||||
]
|
||||
|
||||
if not app_roles:
|
||||
raise Exception(
|
||||
"ManagedNode role not found in the OneFuzz application "
|
||||
"registration. Please redeploy the instance"
|
||||
)
|
||||
|
||||
body = '{ "principalId": "%s", "resourceId": "%s", "appRoleId": "%s"}' % (
|
||||
scaleset_service_principal.object_id,
|
||||
onefuzz_service_principal.object_id,
|
||||
app_roles[0].id,
|
||||
)
|
||||
|
||||
query = (
|
||||
"az rest --method post --url https://graph.microsoft.com/v1.0/servicePrincipals/%s/appRoleAssignedTo --body '%s' --headers \"Content-Type\"=application/json"
|
||||
% (scaleset_service_principal.object_id, body)
|
||||
)
|
||||
|
||||
logger.warning(
|
||||
"execute the following query in the azure portal bash shell : \n%s" % query
|
||||
)
|
||||
|
||||
|
||||
def assign_scaleset_role(onefuzz_instance_name: str, scaleset_name: str) -> None:
|
||||
"""
|
||||
Allows the nodes in the scaleset to access the service by assigning
|
||||
their managed identity to the ManagedNode Role
|
||||
"""
|
||||
|
||||
try:
|
||||
onefuzz_service_appId = query_microsoft_graph(
|
||||
method="GET",
|
||||
resource="applications",
|
||||
@ -401,6 +498,8 @@ def assign_scaleset_role(onefuzz_instance_name: str, scaleset_name: str) -> None
|
||||
"appRoleId": managed_node_role["id"],
|
||||
},
|
||||
)
|
||||
except adal.AdalError:
|
||||
assign_scaleset_role_manually(onefuzz_instance_name, scaleset_name)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
@ -12,3 +12,4 @@ azure-storage-queue==12.1.3
|
||||
cryptography<3.0.0,>=2.3.1
|
||||
pyfunctional==1.4.2
|
||||
pyopenssl==19.1.0
|
||||
adal~=1.2.5
|
Reference in New Issue
Block a user