mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-16 03:48:09 +00:00
enable configurable virtual network ranges (#1268)
This commit is contained in:
@ -645,6 +645,10 @@ Each event will be submitted via HTTP POST to the user provided URL.
|
|||||||
"allowed_aad_tenants": [
|
"allowed_aad_tenants": [
|
||||||
"00000000-0000-0000-0000-000000000000"
|
"00000000-0000-0000-0000-000000000000"
|
||||||
],
|
],
|
||||||
|
"network_config": {
|
||||||
|
"address_space": "10.0.0.0/8",
|
||||||
|
"subnet": "10.0.0.0/16"
|
||||||
|
},
|
||||||
"proxy_vm_sku": "Standard_B2s"
|
"proxy_vm_sku": "Standard_B2s"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -752,6 +756,9 @@ Each event will be submitted via HTTP POST to the user provided URL.
|
|||||||
"extensions": {
|
"extensions": {
|
||||||
"$ref": "#/definitions/AzureVmExtensionConfig"
|
"$ref": "#/definitions/AzureVmExtensionConfig"
|
||||||
},
|
},
|
||||||
|
"network_config": {
|
||||||
|
"$ref": "#/definitions/NetworkConfig"
|
||||||
|
},
|
||||||
"proxy_vm_sku": {
|
"proxy_vm_sku": {
|
||||||
"default": "Standard_B2s",
|
"default": "Standard_B2s",
|
||||||
"title": "Proxy Vm Sku",
|
"title": "Proxy Vm Sku",
|
||||||
@ -791,6 +798,22 @@ Each event will be submitted via HTTP POST to the user provided URL.
|
|||||||
],
|
],
|
||||||
"title": "KeyvaultExtensionConfig",
|
"title": "KeyvaultExtensionConfig",
|
||||||
"type": "object"
|
"type": "object"
|
||||||
|
},
|
||||||
|
"NetworkConfig": {
|
||||||
|
"properties": {
|
||||||
|
"address_space": {
|
||||||
|
"default": "10.0.0.0/8",
|
||||||
|
"title": "Address Space",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"subnet": {
|
||||||
|
"default": "10.0.0.0/16",
|
||||||
|
"title": "Subnet",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "NetworkConfig",
|
||||||
|
"type": "object"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -5804,6 +5827,9 @@ Each event will be submitted via HTTP POST to the user provided URL.
|
|||||||
"extensions": {
|
"extensions": {
|
||||||
"$ref": "#/definitions/AzureVmExtensionConfig"
|
"$ref": "#/definitions/AzureVmExtensionConfig"
|
||||||
},
|
},
|
||||||
|
"network_config": {
|
||||||
|
"$ref": "#/definitions/NetworkConfig"
|
||||||
|
},
|
||||||
"proxy_vm_sku": {
|
"proxy_vm_sku": {
|
||||||
"default": "Standard_B2s",
|
"default": "Standard_B2s",
|
||||||
"title": "Proxy Vm Sku",
|
"title": "Proxy Vm Sku",
|
||||||
@ -5895,6 +5921,22 @@ Each event will be submitted via HTTP POST to the user provided URL.
|
|||||||
"title": "KeyvaultExtensionConfig",
|
"title": "KeyvaultExtensionConfig",
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
"NetworkConfig": {
|
||||||
|
"properties": {
|
||||||
|
"address_space": {
|
||||||
|
"default": "10.0.0.0/8",
|
||||||
|
"title": "Address Space",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"subnet": {
|
||||||
|
"default": "10.0.0.0/16",
|
||||||
|
"title": "Subnet",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "NetworkConfig",
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
"NoReproReport": {
|
"NoReproReport": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"error": {
|
"error": {
|
||||||
|
@ -13,10 +13,11 @@ from msrestazure.azure_exceptions import CloudError
|
|||||||
from msrestazure.tools import parse_resource_id
|
from msrestazure.tools import parse_resource_id
|
||||||
from onefuzztypes.enums import ErrorCode
|
from onefuzztypes.enums import ErrorCode
|
||||||
from onefuzztypes.models import Error
|
from onefuzztypes.models import Error
|
||||||
|
from onefuzztypes.primitives import Region
|
||||||
|
|
||||||
from .creds import get_base_resource_group
|
from .creds import get_base_resource_group
|
||||||
|
from .network import Network
|
||||||
from .network_mgmt_client import get_network_client
|
from .network_mgmt_client import get_network_client
|
||||||
from .subnet import create_virtual_network, get_subnet_id
|
|
||||||
from .vmss import get_instance_id
|
from .vmss import get_instance_id
|
||||||
|
|
||||||
|
|
||||||
@ -63,12 +64,12 @@ def delete_ip(resource_group: str, name: str) -> Any:
|
|||||||
return network_client.public_ip_addresses.begin_delete(resource_group, name)
|
return network_client.public_ip_addresses.begin_delete(resource_group, name)
|
||||||
|
|
||||||
|
|
||||||
def create_ip(resource_group: str, name: str, location: str) -> Any:
|
def create_ip(resource_group: str, name: str, region: Region) -> Any:
|
||||||
logging.info("creating ip for %s:%s in %s", resource_group, name, location)
|
logging.info("creating ip for %s:%s in %s", resource_group, name, region)
|
||||||
|
|
||||||
network_client = get_network_client()
|
network_client = get_network_client()
|
||||||
params: Dict[str, Union[str, Dict[str, str]]] = {
|
params: Dict[str, Union[str, Dict[str, str]]] = {
|
||||||
"location": location,
|
"location": region,
|
||||||
"public_ip_allocation_method": "Dynamic",
|
"public_ip_allocation_method": "Dynamic",
|
||||||
}
|
}
|
||||||
if "ONEFUZZ_OWNER" in os.environ:
|
if "ONEFUZZ_OWNER" in os.environ:
|
||||||
@ -93,21 +94,24 @@ def delete_nic(resource_group: str, name: str) -> Optional[Any]:
|
|||||||
return network_client.network_interfaces.begin_delete(resource_group, name)
|
return network_client.network_interfaces.begin_delete(resource_group, name)
|
||||||
|
|
||||||
|
|
||||||
def create_public_nic(resource_group: str, name: str, location: str) -> Optional[Error]:
|
def create_public_nic(
|
||||||
logging.info("creating nic for %s:%s in %s", resource_group, name, location)
|
resource_group: str, name: str, region: Region
|
||||||
|
) -> Optional[Error]:
|
||||||
|
logging.info("creating nic for %s:%s in %s", resource_group, name, region)
|
||||||
|
|
||||||
network_client = get_network_client()
|
network = Network(region)
|
||||||
subnet_id = get_subnet_id(resource_group, location)
|
subnet_id = network.get_id()
|
||||||
if not subnet_id:
|
if subnet_id is None:
|
||||||
return create_virtual_network(resource_group, location, location)
|
network.create()
|
||||||
|
return None
|
||||||
|
|
||||||
ip = get_ip(resource_group, name)
|
ip = get_ip(resource_group, name)
|
||||||
if not ip:
|
if not ip:
|
||||||
create_ip(resource_group, name, location)
|
create_ip(resource_group, name, region)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
"location": location,
|
"location": region,
|
||||||
"ip_configurations": [
|
"ip_configurations": [
|
||||||
{
|
{
|
||||||
"name": "myIPConfig",
|
"name": "myIPConfig",
|
||||||
@ -119,6 +123,7 @@ def create_public_nic(resource_group: str, name: str, location: str) -> Optional
|
|||||||
if "ONEFUZZ_OWNER" in os.environ:
|
if "ONEFUZZ_OWNER" in os.environ:
|
||||||
params["tags"] = {"OWNER": os.environ["ONEFUZZ_OWNER"]}
|
params["tags"] = {"OWNER": os.environ["ONEFUZZ_OWNER"]}
|
||||||
|
|
||||||
|
network_client = get_network_client()
|
||||||
try:
|
try:
|
||||||
network_client.network_interfaces.begin_create_or_update(
|
network_client.network_interfaces.begin_create_or_update(
|
||||||
resource_group, name, params
|
resource_group, name, params
|
||||||
|
@ -4,43 +4,68 @@
|
|||||||
# Licensed under the MIT License.
|
# Licensed under the MIT License.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import uuid
|
||||||
from typing import Optional, Union
|
from typing import Optional, Union
|
||||||
|
|
||||||
from msrestazure.azure_exceptions import CloudError
|
from msrestazure.azure_exceptions import CloudError
|
||||||
from onefuzztypes.enums import ErrorCode
|
from onefuzztypes.enums import ErrorCode
|
||||||
from onefuzztypes.models import Error
|
from onefuzztypes.models import Error, NetworkConfig
|
||||||
from onefuzztypes.primitives import Region
|
from onefuzztypes.primitives import Region
|
||||||
|
|
||||||
|
from ..config import InstanceConfig
|
||||||
from .creds import get_base_resource_group
|
from .creds import get_base_resource_group
|
||||||
from .subnet import create_virtual_network, delete_subnet, get_subnet_id
|
from .subnet import create_virtual_network, get_subnet_id
|
||||||
|
|
||||||
|
# This was generated randomly and should be preserved moving forwards
|
||||||
|
NETWORK_GUID_NAMESPACE = uuid.UUID("372977ad-b533-416a-b1b4-f770898e0b11")
|
||||||
|
|
||||||
|
|
||||||
class Network:
|
class Network:
|
||||||
def __init__(self, region: Region):
|
def __init__(self, region: Region):
|
||||||
self.group = get_base_resource_group()
|
self.group = get_base_resource_group()
|
||||||
self.region = region
|
self.region = region
|
||||||
|
self.network_config = InstanceConfig.fetch().network_config
|
||||||
|
|
||||||
|
# Network names will be calculated from the address_space/subnet
|
||||||
|
# *except* if they are the original values. This allows backwards
|
||||||
|
# compatibility to existing configs if you don't change the network
|
||||||
|
# configs.
|
||||||
|
if (
|
||||||
|
self.network_config.address_space
|
||||||
|
== NetworkConfig.__fields__["address_space"].default
|
||||||
|
and self.network_config.subnet == NetworkConfig.__fields__["subnet"].default
|
||||||
|
):
|
||||||
|
self.name: str = self.region
|
||||||
|
else:
|
||||||
|
network_id = uuid.uuid5(
|
||||||
|
NETWORK_GUID_NAMESPACE,
|
||||||
|
"|".join(
|
||||||
|
[self.network_config.address_space, self.network_config.subnet]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
self.name = f"{self.region}-{network_id}"
|
||||||
|
|
||||||
def exists(self) -> bool:
|
def exists(self) -> bool:
|
||||||
return self.get_id() is not None
|
return self.get_id() is not None
|
||||||
|
|
||||||
def get_id(self) -> Optional[str]:
|
def get_id(self) -> Optional[str]:
|
||||||
return get_subnet_id(self.group, self.region)
|
return get_subnet_id(self.group, self.name, self.name)
|
||||||
|
|
||||||
def create(self) -> Union[None, Error]:
|
def create(self) -> Union[None, Error]:
|
||||||
if not self.exists():
|
if not self.exists():
|
||||||
result = create_virtual_network(self.group, self.region, self.region)
|
result = create_virtual_network(
|
||||||
|
self.group, self.name, self.region, self.network_config
|
||||||
|
)
|
||||||
if isinstance(result, CloudError):
|
if isinstance(result, CloudError):
|
||||||
error = Error(
|
error = Error(
|
||||||
code=ErrorCode.UNABLE_TO_CREATE_NETWORK, errors=[result.message]
|
code=ErrorCode.UNABLE_TO_CREATE_NETWORK, errors=[result.message]
|
||||||
)
|
)
|
||||||
logging.error(
|
logging.error(
|
||||||
"network creation failed: %s- %s",
|
"network creation failed: %s:%s- %s",
|
||||||
|
self.name,
|
||||||
self.region,
|
self.region,
|
||||||
error,
|
error,
|
||||||
)
|
)
|
||||||
return error
|
return error
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def delete(self) -> None:
|
|
||||||
delete_subnet(self.group, self.region)
|
|
||||||
|
@ -10,21 +10,23 @@ from typing import Any, Optional, Union, cast
|
|||||||
from azure.core.exceptions import ResourceNotFoundError
|
from azure.core.exceptions import ResourceNotFoundError
|
||||||
from msrestazure.azure_exceptions import CloudError
|
from msrestazure.azure_exceptions import CloudError
|
||||||
from onefuzztypes.enums import ErrorCode
|
from onefuzztypes.enums import ErrorCode
|
||||||
from onefuzztypes.models import Error
|
from onefuzztypes.models import Error, NetworkConfig
|
||||||
|
from onefuzztypes.primitives import Region
|
||||||
|
|
||||||
from .network_mgmt_client import get_network_client
|
from .network_mgmt_client import get_network_client
|
||||||
|
|
||||||
|
|
||||||
def get_subnet_id(resource_group: str, name: str) -> Optional[str]:
|
def get_subnet_id(resource_group: str, name: str, subnet_name: str) -> Optional[str]:
|
||||||
network_client = get_network_client()
|
network_client = get_network_client()
|
||||||
try:
|
try:
|
||||||
subnet = network_client.subnets.get(resource_group, name, name)
|
subnet = network_client.subnets.get(resource_group, name, subnet_name)
|
||||||
return cast(str, subnet.id)
|
return cast(str, subnet.id)
|
||||||
except (CloudError, ResourceNotFoundError):
|
except (CloudError, ResourceNotFoundError):
|
||||||
logging.info(
|
logging.info(
|
||||||
"subnet missing: resource group: %s name: %s",
|
"subnet missing: resource group:%s name:%s subnet_name:%s",
|
||||||
resource_group,
|
resource_group,
|
||||||
name,
|
name,
|
||||||
|
subnet_name,
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -43,20 +45,23 @@ def delete_subnet(resource_group: str, name: str) -> Union[None, CloudError, Any
|
|||||||
|
|
||||||
|
|
||||||
def create_virtual_network(
|
def create_virtual_network(
|
||||||
resource_group: str, name: str, location: str
|
resource_group: str,
|
||||||
|
name: str,
|
||||||
|
region: Region,
|
||||||
|
network_config: NetworkConfig,
|
||||||
) -> Optional[Error]:
|
) -> Optional[Error]:
|
||||||
logging.info(
|
logging.info(
|
||||||
"creating subnet - resource group: %s name: %s location: %s",
|
"creating subnet - resource group:%s name:%s region:%s",
|
||||||
resource_group,
|
resource_group,
|
||||||
name,
|
name,
|
||||||
location,
|
region,
|
||||||
)
|
)
|
||||||
|
|
||||||
network_client = get_network_client()
|
network_client = get_network_client()
|
||||||
params = {
|
params = {
|
||||||
"location": location,
|
"location": region,
|
||||||
"address_space": {"address_prefixes": ["10.0.0.0/8"]},
|
"address_space": {"address_prefixes": [network_config.address_space]},
|
||||||
"subnets": [{"name": name, "address_prefix": "10.0.0.0/16"}],
|
"subnets": [{"name": name, "address_prefix": network_config.subnet}],
|
||||||
}
|
}
|
||||||
if "ONEFUZZ_OWNER" in os.environ:
|
if "ONEFUZZ_OWNER" in os.environ:
|
||||||
params["tags"] = {"OWNER": os.environ["ONEFUZZ_OWNER"]}
|
params["tags"] = {"OWNER": os.environ["ONEFUZZ_OWNER"]}
|
||||||
|
@ -399,11 +399,11 @@ def create_vmss(
|
|||||||
|
|
||||||
@cached(ttl=60)
|
@cached(ttl=60)
|
||||||
@retry_on_auth_failure()
|
@retry_on_auth_failure()
|
||||||
def list_available_skus(location: str) -> List[str]:
|
def list_available_skus(region: Region) -> List[str]:
|
||||||
compute_client = get_compute_client()
|
compute_client = get_compute_client()
|
||||||
|
|
||||||
skus: List[ResourceSku] = list(
|
skus: List[ResourceSku] = list(
|
||||||
compute_client.resource_skus.list(filter="location eq '%s'" % location)
|
compute_client.resource_skus.list(filter="location eq '%s'" % region)
|
||||||
)
|
)
|
||||||
sku_names: List[str] = []
|
sku_names: List[str] = []
|
||||||
for sku in skus:
|
for sku in skus:
|
||||||
@ -411,7 +411,7 @@ def list_available_skus(location: str) -> List[str]:
|
|||||||
if sku.restrictions is not None:
|
if sku.restrictions is not None:
|
||||||
for restriction in sku.restrictions:
|
for restriction in sku.restrictions:
|
||||||
if restriction.type == ResourceSkuRestrictionsType.location and (
|
if restriction.type == ResourceSkuRestrictionsType.location and (
|
||||||
location.upper() in [v.upper() for v in restriction.values]
|
region.upper() in [v.upper() for v in restriction.values]
|
||||||
):
|
):
|
||||||
available = False
|
available = False
|
||||||
break
|
break
|
||||||
|
@ -92,8 +92,8 @@ def not_ok(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def redirect(location: str) -> HttpResponse:
|
def redirect(url: str) -> HttpResponse:
|
||||||
return HttpResponse(status_code=302, headers={"Location": location})
|
return HttpResponse(status_code=302, headers={"Location": url})
|
||||||
|
|
||||||
|
|
||||||
def convert_error(err: ValidationError) -> Error:
|
def convert_error(err: ValidationError) -> Error:
|
||||||
|
@ -794,6 +794,11 @@ class Task(BaseModel):
|
|||||||
user_info: Optional[UserInfo]
|
user_info: Optional[UserInfo]
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkConfig(BaseModel):
|
||||||
|
address_space: str = Field(default="10.0.0.0/8")
|
||||||
|
subnet: str = Field(default="10.0.0.0/16")
|
||||||
|
|
||||||
|
|
||||||
class KeyvaultExtensionConfig(BaseModel):
|
class KeyvaultExtensionConfig(BaseModel):
|
||||||
keyvault_name: str
|
keyvault_name: str
|
||||||
cert_name: str
|
cert_name: str
|
||||||
@ -835,9 +840,8 @@ class InstanceConfig(BaseModel):
|
|||||||
allow_pool_management: bool = Field(default=True)
|
allow_pool_management: bool = Field(default=True)
|
||||||
|
|
||||||
allowed_aad_tenants: List[UUID]
|
allowed_aad_tenants: List[UUID]
|
||||||
|
network_config: NetworkConfig = Field(default_factory=NetworkConfig)
|
||||||
extensions: Optional[AzureVmExtensionConfig]
|
extensions: Optional[AzureVmExtensionConfig]
|
||||||
|
|
||||||
proxy_vm_sku: str = Field(default="Standard_B2s")
|
proxy_vm_sku: str = Field(default="Standard_B2s")
|
||||||
|
|
||||||
def update(self, config: "InstanceConfig") -> None:
|
def update(self, config: "InstanceConfig") -> None:
|
||||||
|
Reference in New Issue
Block a user