Files
onefuzz/src/api-service/__app__/onefuzzlib/extension.py
2021-07-13 12:08:07 -04:00

366 lines
11 KiB
Python

#!/usr/bin/env python
#
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
import os
from typing import List, Optional
from uuid import UUID, uuid4
from onefuzztypes.enums import OS, AgentMode
from onefuzztypes.models import AgentConfig, Pool, ReproConfig, Scaleset
from onefuzztypes.primitives import Container, Extension, Region
from .azure.containers import get_container_sas_url, get_file_sas_url, save_blob
from .azure.creds import get_instance_id, get_instance_url
from .azure.monitor import get_monitor_settings
from .azure.queue import get_queue_sas
from .azure.storage import StorageType
from .reports import get_report
def generic_extensions(region: Region, vm_os: OS) -> List[Extension]:
extensions = [monitor_extension(region, vm_os)]
depedency = dependency_extension(region, vm_os)
if depedency:
extensions.append(depedency)
return extensions
def monitor_extension(region: Region, vm_os: OS) -> Extension:
settings = get_monitor_settings()
if vm_os == OS.windows:
return {
"name": "OMSExtension",
"publisher": "Microsoft.EnterpriseCloud.Monitoring",
"type": "MicrosoftMonitoringAgent",
"typeHandlerVersion": "1.0",
"location": region,
"autoUpgradeMinorVersion": True,
"settings": {"workspaceId": settings["id"]},
"protectedSettings": {"workspaceKey": settings["key"]},
}
elif vm_os == OS.linux:
return {
"name": "OMSExtension",
"publisher": "Microsoft.EnterpriseCloud.Monitoring",
"type": "OmsAgentForLinux",
"typeHandlerVersion": "1.12",
"location": region,
"autoUpgradeMinorVersion": True,
"settings": {"workspaceId": settings["id"]},
"protectedSettings": {"workspaceKey": settings["key"]},
}
raise NotImplementedError("unsupported os: %s" % vm_os)
def dependency_extension(region: Region, vm_os: OS) -> Optional[Extension]:
if vm_os == OS.windows:
extension = {
"name": "DependencyAgentWindows",
"publisher": "Microsoft.Azure.Monitoring.DependencyAgent",
"type": "DependencyAgentWindows",
"typeHandlerVersion": "9.5",
"location": region,
"autoUpgradeMinorVersion": True,
}
return extension
else:
# TODO: dependency agent for linux is not reliable
# extension = {
# "name": "DependencyAgentLinux",
# "publisher": "Microsoft.Azure.Monitoring.DependencyAgent",
# "type": "DependencyAgentLinux",
# "typeHandlerVersion": "9.5",
# "location": vm.region,
# "autoUpgradeMinorVersion": True,
# }
return None
def build_scaleset_script(pool: Pool, scaleset: Scaleset) -> str:
commands = []
extension = "ps1" if pool.os == OS.windows else "sh"
filename = f"{scaleset.scaleset_id}/scaleset-setup.{extension}"
sep = "\r\n" if pool.os == OS.windows else "\n"
if pool.os == OS.windows and scaleset.auth is not None:
ssh_key = scaleset.auth.public_key.strip()
ssh_path = "$env:ProgramData/ssh/administrators_authorized_keys"
commands += [f'Set-Content -Path {ssh_path} -Value "{ssh_key}"']
save_blob(
Container("vm-scripts"), filename, sep.join(commands) + sep, StorageType.config
)
return get_file_sas_url(
Container("vm-scripts"), filename, StorageType.config, read=True
)
def build_pool_config(pool: Pool) -> str:
config = AgentConfig(
pool_name=pool.name,
onefuzz_url=get_instance_url(),
heartbeat_queue=get_queue_sas(
"node-heartbeat",
StorageType.config,
add=True,
),
instance_telemetry_key=os.environ.get("APPINSIGHTS_INSTRUMENTATIONKEY"),
microsoft_telemetry_key=os.environ.get("ONEFUZZ_TELEMETRY"),
instance_id=get_instance_id(),
)
multi_tenant_domain = os.environ.get("MULTI_TENANT_DOMAIN")
if multi_tenant_domain:
config.multi_tenant_domain = multi_tenant_domain
filename = f"{pool.name}/config.json"
save_blob(
Container("vm-scripts"),
filename,
config.json(),
StorageType.config,
)
return get_file_sas_url(
Container("vm-scripts"),
filename,
StorageType.config,
read=True,
)
def update_managed_scripts() -> None:
commands = [
"azcopy sync '%s' instance-specific-setup"
% (
get_container_sas_url(
Container("instance-specific-setup"),
StorageType.config,
read=True,
list=True,
)
),
"azcopy sync '%s' tools"
% (
get_container_sas_url(
Container("tools"), StorageType.config, read=True, list=True
)
),
]
save_blob(
Container("vm-scripts"),
"managed.ps1",
"\r\n".join(commands) + "\r\n",
StorageType.config,
)
save_blob(
Container("vm-scripts"),
"managed.sh",
"\n".join(commands) + "\n",
StorageType.config,
)
def agent_config(
region: Region, vm_os: OS, mode: AgentMode, *, urls: Optional[List[str]] = None
) -> Extension:
update_managed_scripts()
if urls is None:
urls = []
if vm_os == OS.windows:
urls += [
get_file_sas_url(
Container("vm-scripts"),
"managed.ps1",
StorageType.config,
read=True,
),
get_file_sas_url(
Container("tools"),
"win64/azcopy.exe",
StorageType.config,
read=True,
),
get_file_sas_url(
Container("tools"),
"win64/setup.ps1",
StorageType.config,
read=True,
),
get_file_sas_url(
Container("tools"),
"win64/onefuzz.ps1",
StorageType.config,
read=True,
),
]
to_execute_cmd = (
"powershell -ExecutionPolicy Unrestricted -File win64/setup.ps1 "
"-mode %s" % (mode.name)
)
extension = {
"name": "CustomScriptExtension",
"type": "CustomScriptExtension",
"publisher": "Microsoft.Compute",
"location": region,
"force_update_tag": uuid4(),
"type_handler_version": "1.9",
"auto_upgrade_minor_version": True,
"settings": {"commandToExecute": to_execute_cmd, "fileUris": urls},
"protectedSettings": {},
}
return extension
elif vm_os == OS.linux:
urls += [
get_file_sas_url(
Container("vm-scripts"),
"managed.sh",
StorageType.config,
read=True,
),
get_file_sas_url(
Container("tools"),
"linux/azcopy",
StorageType.config,
read=True,
),
get_file_sas_url(
Container("tools"),
"linux/setup.sh",
StorageType.config,
read=True,
),
]
to_execute_cmd = "sh setup.sh %s" % (mode.name)
extension = {
"name": "CustomScript",
"publisher": "Microsoft.Azure.Extensions",
"type": "CustomScript",
"typeHandlerVersion": "2.1",
"location": region,
"autoUpgradeMinorVersion": True,
"force_update_tag": uuid4(),
"settings": {"commandToExecute": to_execute_cmd, "fileUris": urls},
"protectedSettings": {},
}
return extension
raise NotImplementedError("unsupported OS: %s" % vm_os)
def fuzz_extensions(pool: Pool, scaleset: Scaleset) -> List[Extension]:
urls = [build_pool_config(pool), build_scaleset_script(pool, scaleset)]
fuzz_extension = agent_config(scaleset.region, pool.os, AgentMode.fuzz, urls=urls)
extensions = generic_extensions(scaleset.region, pool.os)
extensions += [fuzz_extension]
return extensions
def repro_extensions(
region: Region,
repro_os: OS,
repro_id: UUID,
repro_config: ReproConfig,
setup_container: Optional[Container],
) -> List[Extension]:
# TODO - what about contents of repro.ps1 / repro.sh?
report = get_report(repro_config.container, repro_config.path)
if report is None:
raise Exception("invalid report: %s" % repro_config)
if report.input_blob is None:
raise Exception("unable to perform reproduction without an input blob")
commands = []
if setup_container:
commands += [
"azcopy sync '%s' ./setup"
% (
get_container_sas_url(
setup_container, StorageType.corpus, read=True, list=True
)
),
]
urls = [
get_file_sas_url(
repro_config.container, repro_config.path, StorageType.corpus, read=True
),
get_file_sas_url(
report.input_blob.container,
report.input_blob.name,
StorageType.corpus,
read=True,
),
]
repro_files = []
if repro_os == OS.windows:
repro_files = ["%s/repro.ps1" % repro_id]
task_script = "\r\n".join(commands)
script_name = "task-setup.ps1"
else:
repro_files = ["%s/repro.sh" % repro_id, "%s/repro-stdout.sh" % repro_id]
commands += ["chmod -R +x setup"]
task_script = "\n".join(commands)
script_name = "task-setup.sh"
save_blob(
Container("task-configs"),
"%s/%s" % (repro_id, script_name),
task_script,
StorageType.config,
)
for repro_file in repro_files:
urls += [
get_file_sas_url(
Container("repro-scripts"),
repro_file,
StorageType.config,
read=True,
),
get_file_sas_url(
Container("task-configs"),
"%s/%s" % (repro_id, script_name),
StorageType.config,
read=True,
),
]
base_extension = agent_config(region, repro_os, AgentMode.repro, urls=urls)
extensions = generic_extensions(region, repro_os)
extensions += [base_extension]
return extensions
def proxy_manager_extensions(region: Region, proxy_id: UUID) -> List[Extension]:
urls = [
get_file_sas_url(
Container("proxy-configs"),
"%s/%s/config.json" % (region, proxy_id),
StorageType.config,
read=True,
),
get_file_sas_url(
Container("tools"),
"linux/onefuzz-proxy-manager",
StorageType.config,
read=True,
),
]
base_extension = agent_config(region, OS.linux, AgentMode.proxy, urls=urls)
extensions = generic_extensions(region, OS.linux)
extensions += [base_extension]
return extensions