Esempio n. 1
0
def build_arena(unit_id):
    """
    Builds an arena of student servers and a common compute environment according to the specification referenced in
    the workout_unit datastore
    :param unit_id: The workout_id key in the datastore holding the build specification
    :return: None
    """
    key = ds_client.key('cybergym-unit', unit_id)
    unit = ds_client.get(key)
    # This can sometimes happen when debugging a Unit ID and the Datastore record no longer exists.
    arena = unit['arena']
    if not arena:
        print('No unit %s exists in the data store' % unit_id)
        return

    if 'state' not in unit or not unit['state']:
        state_transition(entity=unit, new_state=BUILD_STATES.START)

    # # Parse the assessment specification to obtain any startup scripts for the workout.
    # startup_scripts = None
    # if unit['assessment']:
    #     startup_scripts = get_startup_scripts(workout_id=workout_id, assessment=workout['assessment'])
    # # Create the networks and subnets
    # First create the student servers
    print('Creating student servers')
    build_student_servers(unit_id=unit_id, workouts=unit['workouts'],
                          student_entry_type=arena['student_entry_type'],
                          student_entry_server=arena['student_entry'],
                          student_entry_username=arena['student_entry_username'],
                          student_entry_password=arena['student_entry_password'],
                          network_type=arena['student_network_type'])

    # STATE: BUILDING_ARENA_NETWORKS
    if check_ordered_arenas_state(unit, BUILD_STATES.BUILDING_ARENA_NETWORKS):
        state_transition(entity=unit, new_state=BUILD_STATES.BUILDING_ARENA_NETWORKS)
        if arena['networks']:
            print('Creating additional arena networks')
            create_network(networks=arena['networks'], build_id=unit_id)
        state_transition(entity=unit, new_state=BUILD_STATES.COMPLETED_ARENA_NETWORKS)

    # STATE: BUILDING_ARENA_SERVERS
    if check_ordered_arenas_state(unit, BUILD_STATES.BUILDING_ARENA_SERVERS):
        state_transition(entity=unit, new_state=BUILD_STATES.BUILDING_ARENA_SERVERS)
        print('Creating additional servers')
        i = 101
        for server in arena['servers']:
            server_name = "%s-%s" % (unit_id, server['name'])
            sshkey = server["sshkey"]
            guac_path = server['guac_path']
            tags = server['tags']
            machine_type = server["machine_type"]
            network_routing = server["network_routing"]
            # If a nic is not specified, then add the server to the student-network.
            if server['nics']:
                nics = []
                for n in server['nics']:
                    if 'network' not in n:
                        n['network'] = student_network_name
                    if 'internal_IP' not in n:
                        n['internal_IP'] = f'10.1.0.{i}'
                    if 'subnet' not in n:
                        n['subnet'] = 'default'
                    if 'external_NAT' not in n:
                        n['external_NAT'] = False
                    nic = {
                        "network": "%s-%s" % (unit_id, n['network']),
                        "internal_IP": n['internal_IP'],
                        "subnet": "%s-%s-%s" % (unit_id, n['network'], n['subnet']),
                        "external_NAT": n['external_NAT']
                    }
                    nics.append(nic)
            else:
                nics = [
                    {
                        "network": "%s-%s" % (unit_id, student_network_name),
                        "internal_IP": f'10.1.0.{i}',
                        "subnet": "%s-%s-%s" % (unit_id, student_network_name, 'default'),
                        "external_NAT": False
                    }
                ]

            create_instance_custom_image(compute=compute, workout=unit_id, name=server_name,
                                         custom_image=server['image'], machine_type=machine_type,
                                         networkRouting=network_routing, networks=nics, tags=tags,
                                         meta_data=None, sshkey=sshkey)
            i += 1
        state_transition(entity=unit, new_state=BUILD_STATES.COMPLETED_ARENA_SERVERS)

    # STATE: BUILDING_ROUTES
    if check_ordered_arenas_state(unit, BUILD_STATES.BUILDING_ROUTES):
        state_transition(entity=unit, new_state=BUILD_STATES.BUILDING_ROUTES)
        print('Creating network routes and firewall rules')
        if 'routes' in arena and arena['routes']:
            for route in arena['routes']:
                r = {"name": "%s-%s" % (unit_id, route['name']),
                     "network": "%s-%s" % (unit_id, route['network']),
                     "destRange": route['dest_range'],
                     "nextHopInstance": "%s-%s" % (unit_id, route['next_hop_instance'])}
                create_route(route)
        state_transition(entity=unit, new_state=BUILD_STATES.COMPLETED_ROUTES)

    # STATE: BUILDING_FIREWALL
    if check_ordered_arenas_state(unit, BUILD_STATES.BUILDING_FIREWALL):
        state_transition(entity=unit, new_state=BUILD_STATES.BUILDING_FIREWALL)
        firewall_rules = []
        for rule in arena['firewall_rules']:
            if 'network' not in rule:
                rule['network'] = student_network_name
            firewall_rules.append({"name": "%s-%s" % (unit_id, rule['name']),
                                   "network": "%s-%s" % (unit_id, rule['network']),
                                   "targetTags": rule['target_tags'],
                                   "protocol": rule['protocol'],
                                   "ports": rule['ports'],
                                   "sourceRanges": rule['source_ranges']})

        # Create the default rules to allow traffic between student networks.
        firewall_rules.append({"name": "%s-%s" % (unit_id, 'allow-all-internal'),
                               "network": "%s-%s" % (unit_id, student_network_name),
                               "targetTags": [],
                               "protocol": 'tcp',
                               "ports": ['tcp/any', 'udp/any', 'icmp/any'],
                               "sourceRanges": [student_network_subnet]})

        create_firewall_rules(firewall_rules)
        state_transition(entity=unit, new_state=BUILD_STATES.COMPLETED_FIREWALL)

    state_transition(entity=unit, new_state=BUILD_STATES.READY)
def build_workout(workout_id):
    """
    Builds a workout compute environment according to the specification referenced in the datastore with key workout_id
    :param workout_id: The workout_id key in the datastore holding the build specification
    :return: None
    """
    key = ds_client.key('cybergym-workout', workout_id)
    workout = ds_client.get(key)
    # This can sometimes happen when debugging a workout ID and the Datastore record no longer exists.
    if not workout:
        cloud_log(workout_id,
                  f"The datastore record for {workout_id} no longer exists!",
                  LOG_LEVELS.ERROR)
        raise LookupError

    if 'state' not in workout or not workout['state']:
        state_transition(entity=workout, new_state=BUILD_STATES.START)

    # Create the networks and subnets
    if check_ordered_workout_state(workout, BUILD_STATES.BUILDING_NETWORKS):
        state_transition(entity=workout,
                         new_state=BUILD_STATES.BUILDING_NETWORKS)
        for network in workout['networks']:
            cloud_log(workout_id,
                      f"Building network {workout_id}-{network['name']}",
                      LOG_LEVELS.INFO)
            network_body = {
                "name": f"{workout_id}-{network['name']}",
                "autoCreateSubnetworks": False,
                "region": region
            }
            try:
                response = compute.networks().insert(
                    project=project, body=network_body).execute()
                compute.globalOperations().wait(
                    project=project, operation=response["id"]).execute()
                time.sleep(10)
            except HttpError as err:
                # If the network already exists, then this may be a rebuild and ignore the error
                if err.resp.status in [409]:
                    pass
            for subnet in network['subnets']:
                cloud_log(
                    workout_id,
                    f"Building the subnetwork {network_body['name']}-{subnet['name']}",
                    LOG_LEVELS.INFO)
                subnetwork_body = {
                    "name":
                    f"{network_body['name']}-{subnet['name']}",
                    "network":
                    "projects/%s/global/networks/%s" %
                    (project, network_body['name']),
                    "ipCidrRange":
                    subnet['ip_subnet']
                }
                try:
                    response = compute.subnetworks().insert(
                        project=project, region=region,
                        body=subnetwork_body).execute()
                    compute.regionOperations().wait(
                        project=project,
                        region=region,
                        operation=response["id"]).execute()
                except HttpError as err:
                    # If the subnetwork already exists, then this may be a rebuild and ignore the error
                    if err.resp.status in [409]:
                        pass
            state_transition(entity=workout,
                             new_state=BUILD_STATES.COMPLETED_NETWORKS)

    # Now create the server configurations
    if check_ordered_workout_state(workout, BUILD_STATES.BUILDING_SERVERS):
        state_transition(entity=workout,
                         new_state=BUILD_STATES.BUILDING_SERVERS)
        pubsub_topic = PUBSUB_TOPICS.MANAGE_SERVER
        publisher = pubsub_v1.PublisherClient()
        topic_path = publisher.topic_path(project, pubsub_topic)
        for server in workout['servers']:
            server_name = f"{workout_id}-{server['name']}"
            cloud_log(workout_id,
                      f"Sending pubsub message to build {server_name}",
                      LOG_LEVELS.INFO)
            publisher.publish(topic_path,
                              data=b'Server Build',
                              server_name=server_name,
                              action=SERVER_ACTIONS.BUILD)
        # Also build the student entry server for the workout
        publisher.publish(topic_path,
                          data=b'Server Build',
                          server_name=f"{workout_id}-student-guacamole",
                          action=SERVER_ACTIONS.BUILD)
        state_transition(entity=workout,
                         new_state=BUILD_STATES.COMPLETED_SERVERS)
    # Create all of the network routes and firewall rules
    if check_ordered_workout_state(workout, BUILD_STATES.BUILDING_ROUTES):
        state_transition(entity=workout,
                         new_state=BUILD_STATES.BUILDING_ROUTES)
        cloud_log(
            workout_id,
            f"Creating network routes and firewall rules for {workout_id}",
            LOG_LEVELS.INFO)
        if 'routes' in workout and workout['routes']:
            workout_route_setup(workout_id)
    if check_ordered_workout_state(workout, BUILD_STATES.BUILDING_FIREWALL):
        state_transition(entity=workout,
                         new_state=BUILD_STATES.BUILDING_FIREWALL)
        firewall_rules = []
        for rule in workout['firewall_rules']:
            firewall_rules.append({
                "name":
                "%s-%s" % (workout_id, rule['name']),
                "network":
                "%s-%s" % (workout_id, rule['network']),
                "targetTags":
                rule['target_tags'],
                "protocol":
                rule['protocol'],
                "ports":
                rule['ports'],
                "sourceRanges":
                rule['source_ranges']
            })
        create_firewall_rules(firewall_rules)
        state_transition(entity=workout,
                         new_state=BUILD_STATES.COMPLETED_FIREWALL)
    cloud_log(
        workout_id,
        f"Finished the build process with a final state: {workout['state']}",
        LOG_LEVELS.INFO)
Esempio n. 3
0
def build_workout(workout_id):
    """
    Builds a workout compute environment according to the specification referenced in the datastore with key workout_id
    :param workout_id: The workout_id key in the datastore holding the build specification
    :return: None
    """
    key = ds_client.key('cybergym-workout', workout_id)
    workout = ds_client.get(key)
    # This can sometimes happen when debugging a workout ID and the Datastore record no longer exists.
    if not workout:
        print('No workout for %s exists in the data store' % workout_id)
        return

    startup_scripts = None
    # Parse the assessment specification to obtain any startup scripts for the workout.
    if 'state' not in workout or not workout['state']:
        state_transition(entity=workout, new_state=BUILD_STATES.START)

    if workout['assessment']:
        startup_scripts = get_startup_scripts(workout_id=workout_id,
                                              assessment=workout['assessment'])
    # Create the networks and subnets
    if check_ordered_workout_state(workout, BUILD_STATES.BUILDING_NETWORKS):
        state_transition(entity=workout,
                         new_state=BUILD_STATES.BUILDING_NETWORKS)
        print('Creating networks')
        for network in workout['networks']:
            network_body = {
                "name": "%s-%s" % (workout_id, network['name']),
                "autoCreateSubnetworks": False,
                "region": region
            }
            response = compute.networks().insert(project=project,
                                                 body=network_body).execute()
            compute.globalOperations().wait(
                project=project, operation=response["id"]).execute()
            time.sleep(10)
            for subnet in network['subnets']:
                subnetwork_body = {
                    "name":
                    "%s-%s" % (network_body['name'], subnet['name']),
                    "network":
                    "projects/%s/global/networks/%s" %
                    (project, network_body['name']),
                    "ipCidrRange":
                    subnet['ip_subnet']
                }
                response = compute.subnetworks().insert(
                    project=project, region=region,
                    body=subnetwork_body).execute()
                compute.regionOperations().wait(
                    project=project, region=region,
                    operation=response["id"]).execute()
                state_transition(entity=workout,
                                 new_state=BUILD_STATES.COMPLETED_NETWORKS)

    # Now create the server configurations
    if check_ordered_workout_state(workout, BUILD_STATES.BUILDING_SERVERS):
        state_transition(entity=workout,
                         new_state=BUILD_STATES.BUILDING_SERVERS)
        print('Creating servers')
        for server in workout['servers']:
            server_name = "%s-%s" % (workout_id, server['name'])
            sshkey = server["sshkey"]
            tags = server['tags']
            machine_type = server["machine_type"]
            network_routing = server["network_routing"]
            min_cpu_platform = server[
                "minCpuPlatform"] if "minCpuPlatform" in server else None
            nics = []
            for n in server['nics']:
                nic = {
                    "network": f"{workout_id}-{n['network']}",
                    "internal_IP": n['internal_IP'],
                    "subnet": f"{workout_id}-{n['network']}-{n['subnet']}",
                    "external_NAT": n['external_NAT']
                }
                # Nested VMs are sometimes used for vulnerable servers. This adds those specified IP addresses as
                # aliases to the NIC
                if 'IP_aliases' in n and n['IP_aliases']:
                    alias_ip_ranges = []
                    for ipaddr in n['IP_aliases']:
                        alias_ip_ranges.append({"ipCidrRange": ipaddr})
                    nic['aliasIpRanges'] = alias_ip_ranges
                nics.append(nic)
            # Add the startup script for assessment as metadata if it exists
            meta_data = None
            if startup_scripts and server['name'] in startup_scripts:
                meta_data = startup_scripts[server['name']]

            create_instance_custom_image(compute=compute,
                                         workout=workout_id,
                                         name=server_name,
                                         custom_image=server['image'],
                                         machine_type=machine_type,
                                         networkRouting=network_routing,
                                         networks=nics,
                                         tags=tags,
                                         meta_data=meta_data,
                                         sshkey=sshkey,
                                         minCpuPlatform=min_cpu_platform)

        state_transition(entity=workout,
                         new_state=BUILD_STATES.COMPLETED_SERVERS)
    # Create the student entry guacamole server
    if check_ordered_workout_state(workout,
                                   BUILD_STATES.BUILDING_STUDENT_ENTRY):
        state_transition(entity=workout,
                         new_state=BUILD_STATES.BUILDING_STUDENT_ENTRY)
        if workout['student_entry']:
            network_name = f"{workout_id}-{workout['student_entry']['network']}"
            student_entry_username = workout['student_entry'][
                'username'] if 'username' in workout['student_entry'] else None
            security_mode = workout['student_entry'][
                'security-mode'] if 'security-mode' in workout[
                    'student_entry'] else 'nla'
            guac_connection = [{
                'workout_id': workout_id,
                'entry_type': workout['student_entry']['type'],
                'ip': workout['student_entry']['ip'],
                'username': student_entry_username,
                'password': workout['student_entry']['password'],
                'security-mode': security_mode
            }]
            build_guacamole_server(build=workout,
                                   network=network_name,
                                   guacamole_connections=guac_connection)
            # Get the workout key again or the state transition will overwrite it
            workout = ds_client.get(
                ds_client.key('cybergym-workout', workout_id))
        else:
            state_transition(entity=workout, new_state=BUILD_STATES.BROKEN)
            return
        state_transition(entity=workout,
                         new_state=BUILD_STATES.COMPLETED_STUDENT_ENTRY)
    # Create all of the network routes and firewall rules
    if check_ordered_workout_state(workout, BUILD_STATES.BUILDING_ROUTES):
        state_transition(entity=workout,
                         new_state=BUILD_STATES.BUILDING_ROUTES)
        print('Creating network routes and firewall rules')
        if 'routes' in workout and workout['routes']:
            for route in workout['routes']:
                response = compute.instances().get(
                    project=project,
                    zone=zone,
                    instance=f"{workout_id}-{route['next_hop_instance']}")
                r = {
                    "name":
                    "%s-%s" % (workout_id, route['name']),
                    "network":
                    "%s-%s" % (workout_id, route['network']),
                    "destRange":
                    route['dest_range'],
                    "nextHopInstance":
                    "%s-%s" % (workout_id, route['next_hop_instance'])
                }
                create_route(r)

    if check_ordered_workout_state(workout, BUILD_STATES.BUILDING_FIREWALL):
        state_transition(entity=workout,
                         new_state=BUILD_STATES.BUILDING_FIREWALL)
        firewall_rules = []
        for rule in workout['firewall_rules']:
            firewall_rules.append({
                "name":
                "%s-%s" % (workout_id, rule['name']),
                "network":
                "%s-%s" % (workout_id, rule['network']),
                "targetTags":
                rule['target_tags'],
                "protocol":
                rule['protocol'],
                "ports":
                rule['ports'],
                "sourceRanges":
                rule['source_ranges']
            })

        create_firewall_rules(firewall_rules)


# build_workout('isirdhzjqk')
Esempio n. 4
0
def build_guacamole_server(build, network, guacamole_connections):
    """
    Builds an image with an Apache Guacamole server and adds startup scripts to insert the
    correct users and connections into the guacamole database. This server becomes the entrypoint
    for all students in the arena.
    :param type: Either workout or arena build.
    :param build: Build Entity for the workout or arena.
    :param network: The network name for the server
    :param guacamole_connections: An array of dictionaries for each student {workoutid, ip address of their server,
        and password for their server.
    :return: Null
    """
    build_id = build.key.name
    if len(guacamole_connections) == 0:
        return None

    startup_script = workout_globals.guac_startup_begin.format(
        guacdb_password=guac_password)
    i = 0
    for connection in guacamole_connections:
        # Get a PRNG password for the workout and store it with the datastore record for display on the workout controller
        guac_user = '******' + str(i + 1)
        guac_connection_password = get_random_alphaNumeric_string()
        workout = ds_client.get(
            ds_client.key('cybergym-workout', connection['workout_id']))
        workout['workout_user'] = guac_user
        workout['workout_password'] = guac_connection_password
        ds_client.put(workout)

        safe_password = connection['password'].replace('$', '\$')
        safe_password = safe_password.replace("'", "\'")
        startup_script += workout_globals.guac_startup_user_add.format(
            user=guac_user,
            name=guac_user,
            guac_password=guac_connection_password)
        if connection['entry_type'] == 'vnc':
            startup_script += workout_globals.guac_startup_vnc.format(
                ip=connection['ip'],
                connection=connection['workout_id'],
                vnc_password=safe_password)
        else:
            startup_script += workout_globals.guac_startup_rdp.format(
                ip=connection['ip'],
                connection=connection['workout_id'],
                rdp_username=connection['username'],
                rdp_password=safe_password,
                security_mode=connection['security-mode'])
        startup_script += workout_globals.guac_startup_join_connection_user
        i += 1
    startup_script += workout_globals.guac_startup_end

    server_name = "%s-%s" % (build_id, 'student-guacamole')
    tags = {'items': ['student-entry']}
    nics = [{
        "network": network,
        "subnet": "%s-%s" % (network, 'default'),
        "external_NAT": True
    }]
    meta_data = {"key": "startup-script", "value": startup_script}
    try:
        create_instance_custom_image(compute=compute,
                                     workout=build_id,
                                     name=server_name,
                                     custom_image=student_entry_image,
                                     machine_type='n1-standard-1',
                                     networkRouting=False,
                                     networks=nics,
                                     tags=tags,
                                     meta_data=meta_data,
                                     sshkey=None,
                                     student_entry=True)

        # Create the firewall rule allowing external access to the guacamole connection
        allow_entry = [{
            "name": "%s-%s" % (build_id, 'allow-student-entry'),
            "network": network,
            "targetTags": ['student-entry'],
            'protocol': None,
            'ports': ['tcp/80,8080,443'],
            'sourceRanges': ['0.0.0.0/0']
        }]
        create_firewall_rules(allow_entry)
    except errors.HttpError as err:
        # 409 error means the server already exists.
        if err.resp.status in [409]:
            pass
        else:
            raise
    def build(self):
        key = ds_client.key('cybergym-unit', self.unit_id)
        unit = ds_client.get(key)
        # This can sometimes happen when debugging a Unit ID and the Datastore record no longer exists.
        arena = unit['arena']
        if not arena:
            cloud_log(
                self.unit_id,
                f"Build operation failed. No unit {self.unit_id} exists in the data store",
                LOG_LEVELS.ERROR)
            raise LookupError

        if 'state' not in unit or not unit['state']:
            state_transition(entity=unit, new_state=BUILD_STATES.START)

        # STATE: BUILDING_STUDENT_NETWORKS
        if check_ordered_arenas_state(
                unit, BUILD_STATES.BUILDING_ARENA_STUDENT_NETWORKS):
            cloud_log(self.unit_id,
                      f"Creating student networks for arena {self.unit_id}",
                      LOG_LEVELS.INFO)
            student_network = [{
                'name':
                self.STUDENT_NETWORK_NAME,
                'subnets': [{
                    'name':
                    f'{self.unit_id}-{self.STUDENT_NETWORK_NAME}-default',
                    'ip_subnet': self.STUDENT_NETWORK_SUBNET
                }]
            }]
            try:
                create_network(networks=student_network, build_id=self.unit_id)
            except HttpError as err:
                if err.resp.status not in [409]:
                    cloud_log(
                        self.unit_id,
                        f"Error when trying to create the student network for "
                        f"the arena {self.unit_id}", LOG_LEVELS.ERROR)
                    raise

        # STATE: BUILDING_ARENA_NETWORKS
        if check_ordered_arenas_state(unit,
                                      BUILD_STATES.BUILDING_ARENA_NETWORKS):
            state_transition(entity=unit,
                             new_state=BUILD_STATES.BUILDING_ARENA_NETWORKS)
            if arena['networks']:
                cloud_log(
                    self.unit_id,
                    f"Creating additional arena networks for arena {self.unit_id}",
                    LOG_LEVELS.INFO)
                create_network(networks=arena['networks'],
                               build_id=self.unit_id)
            state_transition(entity=unit,
                             new_state=BUILD_STATES.COMPLETED_ARENA_NETWORKS)

        # STATE: BUILDING_ARENA_SERVERS
        if check_ordered_arenas_state(unit,
                                      BUILD_STATES.BUILDING_ARENA_SERVERS):
            state_transition(entity=unit,
                             new_state=BUILD_STATES.BUILDING_ARENA_SERVERS)
            cloud_log(self.unit_id,
                      f"Creating additional servers for arena {self.unit_id}",
                      LOG_LEVELS.INFO)
            server_list = list(
                ds_client.query(kind='cybergym-server').add_filter(
                    'workout', '=', self.unit_id).fetch())
            for server in server_list:
                server_name = server['name']
                cloud_log(self.unit_id,
                          f"Sending pubsub message to build {server_name}",
                          LOG_LEVELS.INFO)
                pubsub_topic = PUBSUB_TOPICS.MANAGE_SERVER
                publisher = pubsub_v1.PublisherClient()
                topic_path = publisher.topic_path(project, pubsub_topic)
                publisher.publish(topic_path,
                                  data=b'Server Build',
                                  server_name=server_name,
                                  action=SERVER_ACTIONS.BUILD)
            state_transition(entity=unit,
                             new_state=BUILD_STATES.COMPLETED_ARENA_SERVERS)

        # STATE: BUILDING_ROUTES
        if check_ordered_arenas_state(unit, BUILD_STATES.BUILDING_ROUTES):
            state_transition(entity=unit,
                             new_state=BUILD_STATES.BUILDING_ROUTES)
            cloud_log(
                self.unit_id,
                f"Creating network routes and firewall rules for arena {self.unit_id}",
                LOG_LEVELS.INFO)
            if 'routes' in arena and arena['routes']:
                for route in arena['routes']:
                    r = {
                        "name":
                        f"{self.unit_id}-{route['name']}",
                        "network":
                        f"{self.unit_id}-{route['network']}",
                        "destRange":
                        route['dest_range'],
                        "nextHopInstance":
                        f"{self.unit_id}-{route['next_hop_instance']}"
                    }
                    create_route(route)
            state_transition(entity=unit,
                             new_state=BUILD_STATES.COMPLETED_ROUTES)

        # STATE: BUILDING_FIREWALL
        if check_ordered_arenas_state(unit, BUILD_STATES.BUILDING_FIREWALL):
            state_transition(entity=unit,
                             new_state=BUILD_STATES.BUILDING_FIREWALL)
            firewall_rules = []
            for rule in arena['firewall_rules']:
                if 'network' not in rule:
                    rule['network'] = self.STUDENT_NETWORK_NAME
                firewall_rules.append({
                    "name": f"{self.unit_id}-{rule['name']}",
                    "network": f"{self.unit_id}-{rule['network']}",
                    "targetTags": rule['target_tags'],
                    "protocol": rule['protocol'],
                    "ports": rule['ports'],
                    "sourceRanges": rule['source_ranges']
                })

            # Create the default rules to allow traffic between student networks.
            firewall_rules.append({
                "name": f"{self.unit_id}-allow-all-internal",
                "network": f"{self.unit_id}-{self.STUDENT_NETWORK_NAME}",
                "targetTags": [],
                "protocol": 'tcp',
                "ports": ['tcp/any', 'udp/any', 'icmp/any'],
                "sourceRanges": [self.STUDENT_NETWORK_SUBNET]
            })

            create_firewall_rules(firewall_rules)
            state_transition(entity=unit,
                             new_state=BUILD_STATES.COMPLETED_FIREWALL)