Compare commits
No commits in common. "668af8bdb6b21f3c77a0e287af35c6927c76da77" and "db397cb2b169fede4d5f99dc324e645298153f88" have entirely different histories.
668af8bdb6
...
db397cb2b1
|
@ -24,11 +24,11 @@ Create a read-only token in NetBox. Set variables required to access NetBox:
|
||||||
|
|
||||||
Run one-off tasks with (add `--key-file` or other options as necessary):
|
Run one-off tasks with (add `--key-file` or other options as necessary):
|
||||||
|
|
||||||
ansible -m ping 'spine-*'
|
ansible -i inventory.yml -m ping 'spine-*'
|
||||||
|
|
||||||
Run a playbook with:
|
Run a playbook with:
|
||||||
|
|
||||||
ansible-playbook setup.yml -l 'spine-*'
|
ansible-playbook setup.yml -i inventory.yml -l 'spine-*'
|
||||||
|
|
||||||
## NetBox data
|
## NetBox data
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
[defaults]
|
[defaults]
|
||||||
nocows = true
|
nocows = true
|
||||||
filter_plugins = filter_plugins
|
|
||||||
inventory = inventory.yml
|
|
||||||
remote_user = root
|
remote_user = root
|
||||||
vault_identity = network
|
vault_identity = network
|
||||||
|
filter_plugins = filter_plugins
|
||||||
|
|
1
group_vars/all/vars.yml
Normal file
1
group_vars/all/vars.yml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
vlans: "{{ query('netbox.netbox.nb_lookup', 'vlans', api_filter='group=new-net', raw_data=true) | sort(attribute='vid') }}"
|
|
@ -213,15 +213,17 @@ ipv6 prefix-list default permit ::/0
|
||||||
ip prefix-list fabric permit 10.34.0.0/24 ge 32
|
ip prefix-list fabric permit 10.34.0.0/24 ge 32
|
||||||
ipv6 prefix-list fabric permit 2001:1470:fffd:3400::/64 ge 128
|
ipv6 prefix-list fabric permit 2001:1470:fffd:3400::/64 ge 128
|
||||||
|
|
||||||
{% for prefix in vrf_prefixes
|
{% for vrf in inside_vrfs %}
|
||||||
| selectattr('vrf.id', 'in', inside_vrfs|map(attribute='id'))
|
{% set prefixes = query('netbox.netbox.nb_lookup', 'prefixes', api_filter='vrf_id='~vrf.id, raw_data=true)
|
||||||
| sort(attribute='family.value') | sort(attribute='vlan.vid') %}
|
| sort(attribute='family.value') %}
|
||||||
|
{% for prefix in prefixes %}
|
||||||
{% if prefix.family.value == 4 %}
|
{% if prefix.family.value == 4 %}
|
||||||
ip prefix-list office permit {{ prefix.prefix }} ge 24
|
ip prefix-list office permit {{ prefix.prefix }} ge 24
|
||||||
{% else %}
|
{% else %}
|
||||||
ipv6 prefix-list office permit {{ prefix.prefix }} ge 64
|
ipv6 prefix-list office permit {{ prefix.prefix }} ge 64
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
{% if wg_net is defined %}
|
{% if wg_net is defined %}
|
||||||
ip prefix-list vpn permit {{ wg_net | ipaddr('subnet') }}
|
ip prefix-list vpn permit {{ wg_net | ipaddr('subnet') }}
|
||||||
|
@ -235,7 +237,7 @@ ip prefix-list nat permit {{ wg_ip | ipaddr('host') }}
|
||||||
ip prefix-list nat permit {{ network }}
|
ip prefix-list nat permit {{ network }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% for prefix in bgp_prefixes | sort(attribute='family.value') %}
|
{% for prefix in query('netbox.netbox.nb_lookup', 'prefixes', raw_data=true, api_filter='role=bgp') | selectattr('tenant') %}
|
||||||
{% if prefix.family.value == 4 %}
|
{% if prefix.family.value == 4 %}
|
||||||
ip prefix-list dc permit {{ prefix.prefix }} ge 32
|
ip prefix-list dc permit {{ prefix.prefix }} ge 32
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -279,7 +281,7 @@ route-map inside-import permit 21
|
||||||
match ipv6 address prefix-list office
|
match ipv6 address prefix-list office
|
||||||
|
|
||||||
# Route maps for advertised and received routes.
|
# Route maps for advertised and received routes.
|
||||||
# Default VRF ↔ fabric.
|
# Inside ↔ fabric.
|
||||||
route-map default->fabric permit 10
|
route-map default->fabric permit 10
|
||||||
match ip address prefix-list default
|
match ip address prefix-list default
|
||||||
route-map default->fabric permit 11
|
route-map default->fabric permit 11
|
||||||
|
@ -294,7 +296,7 @@ route-map fabric->default permit 20
|
||||||
route-map fabric->default permit 21
|
route-map fabric->default permit 21
|
||||||
match ipv6 address prefix-list dc
|
match ipv6 address prefix-list dc
|
||||||
|
|
||||||
# Inside VRF ↔ firewall.
|
# Inside ↔ firewall.
|
||||||
route-map inside->firewall permit 1
|
route-map inside->firewall permit 1
|
||||||
match interface lo
|
match interface lo
|
||||||
route-map inside->firewall permit 20
|
route-map inside->firewall permit 20
|
||||||
|
@ -311,7 +313,7 @@ route-map firewall->inside permit 10
|
||||||
route-map firewall->inside permit 11
|
route-map firewall->inside permit 11
|
||||||
match ipv6 address prefix-list default
|
match ipv6 address prefix-list default
|
||||||
|
|
||||||
# Outside VRF ↔ firewall.
|
# Outside ↔ firewall.
|
||||||
route-map outside->firewall permit 10
|
route-map outside->firewall permit 10
|
||||||
match ip address prefix-list default
|
match ip address prefix-list default
|
||||||
route-map outside->firewall permit 11
|
route-map outside->firewall permit 11
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{% set dhcp_vlans = vrf_prefixes | selectattr('custom_fields.dhcp_ranges')
|
{% set dhcp_networks = query('netbox.netbox.nb_lookup', 'prefixes', api_filter='role=dhcp-pool', raw_data=true)
|
||||||
| map(attribute='vlan.vid') | sort -%}
|
| selectattr('vlan') | map(attribute='vlan.vid') | sort -%}
|
||||||
|
|
||||||
# What servers should the DHCP relay forward requests to?
|
# What servers should the DHCP relay forward requests to?
|
||||||
SERVERS="{{ dhcp }}"
|
SERVERS="{{ dhcp }}"
|
||||||
|
@ -10,7 +10,7 @@ SERVERS="{{ dhcp }}"
|
||||||
# This will be used in the actual dhcrelay command
|
# This will be used in the actual dhcrelay command
|
||||||
# For example, "-i eth0 -i eth1"
|
# For example, "-i eth0 -i eth1"
|
||||||
INTF_CMD="{{ interfaces | selectattr('parent') | selectattr('parent.name', '==', 'bridge')
|
INTF_CMD="{{ interfaces | selectattr('parent') | selectattr('parent.name', '==', 'bridge')
|
||||||
| selectattr('untagged_vlan') | selectattr('untagged_vlan.vid', 'in', dhcp_vlans)
|
| selectattr('untagged_vlan') | selectattr('untagged_vlan.vid', 'in', dhcp_networks)
|
||||||
| map(attribute='name') | sort | map('regex_replace', '^', '-id ') | join(' ') }} -iu {{ iface_uplink }} -iu peerlink.4"
|
| map(attribute='name') | sort | map('regex_replace', '^', '-id ') | join(' ') }} -iu {{ iface_uplink }} -iu peerlink.4"
|
||||||
|
|
||||||
# Additional options that are passed to the DHCP relay daemon?
|
# Additional options that are passed to the DHCP relay daemon?
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
{# interfaces that belong to this bridge #}
|
{# interfaces that belong to this bridge #}
|
||||||
{% set ports = interfaces | selectattr('enabled') | selectattr('bridge') | selectattr('bridge.name', '==', bridge.name) %}
|
{% set ports = interfaces | selectattr('enabled') | selectattr('bridge') | selectattr('bridge.name', '==', bridge.name) %}
|
||||||
{# allowed VLANs can be specified on the bridge, any of its ports, or all VLANs in NetBox #}
|
{# allowed VLANs can be specified on the bridge, any of its ports, or all VLANs in NetBox #}
|
||||||
{% set my_vlans = bridge.tagged_vlans or vlans %}
|
{% set my_vlans = bridge.tagged_vlans or (ports | iface_vlans | flatten | sort(attribute='vid') | unique) or vlans %}
|
||||||
{% set my_vlan_ids = my_vlans | map(attribute='vid') | sort -%}
|
{% set my_vlan_ids = my_vlans | map(attribute='vid') | sort -%}
|
||||||
|
|
||||||
auto {{ bridge.name }}
|
auto {{ bridge.name }}
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
# https://docs.nvidia.com/networking-ethernet-software/cumulus-linux/Layer-1-and-Switch-Ports/Interface-Configuration-and-Management/Switch-Port-Attributes/#breakout-ports
|
# https://docs.nvidia.com/networking-ethernet-software/cumulus-linux/Layer-1-and-Switch-Ports/Interface-Configuration-and-Management/Switch-Port-Attributes/#breakout-ports
|
||||||
{% for interface in interfaces | selectattr('name', 'match', '^swp[0-9]+(s0)?$') %}
|
{% for interface in interfaces | selectattr('name', 'match', '^swp[0-9]+$') %}
|
||||||
{# get '1' from 'swp1' and '2' from 'swp2s0' #}
|
{{ interface.name|regex_replace('^swp', '') }}=
|
||||||
{% set port = interface.name | regex_replace('^swp([0-9]+).*$', '\\1') %}
|
{%- if interfaces|selectattr('name', 'match', '^'+interface.name+'s[0-9]+$') %}
|
||||||
{% set count = interfaces | selectattr('name', 'match', '^swp'+port+'(s[0-9])*$') | length %}
|
4x
|
||||||
{{ port }}={% if interface.enabled or count > 1 %}{{ count }}x{% else %}disabled{% endif +%}
|
{% elif not interface.enabled %}
|
||||||
|
disabled
|
||||||
|
{% else %}
|
||||||
|
1x
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
{% set fhrp_assignments = query('netbox.netbox.nb_lookup', 'fhrp-group-assignments', raw_data=true) %}
|
|
||||||
{% set fhrp_groups = query('netbox.netbox.nb_lookup', 'fhrp-groups', raw_data=true) -%}
|
|
||||||
|
|
||||||
{% for iface in interfaces | rejectattr('name', 'in', ('lo', 'bridge')) | rejectattr('mgmt_only') | selectattr('enabled') %}
|
{% for iface in interfaces | rejectattr('name', 'in', ('lo', 'bridge')) | rejectattr('mgmt_only') | selectattr('enabled') %}
|
||||||
auto {{ iface.name }}
|
auto {{ iface.name }}
|
||||||
iface {{ iface.name }}
|
iface {{ iface.name }}
|
||||||
|
@ -40,13 +37,13 @@ iface {{ iface.name }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{#- Addresses. #}
|
{#- Addresses. #}
|
||||||
{% for addr in iface.ip_addresses %}
|
{% for addr in iface.ip_addresses | rejectattr('role') %}
|
||||||
address {{ addr.address }}
|
address {{ addr.address }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if iface.count_fhrp_groups > 0 %}
|
{% set anycast = iface.ip_addresses | selectattr('role') | selectattr('role.value', '==', 'anycast')
|
||||||
{% set fhrp_assignment = fhrp_assignments | selectattr('interface.id', '==', iface.id) | first %}
|
| map(attribute='address') %}
|
||||||
{% set fhrp_group = fhrp_groups | selectattr('id', '==', fhrp_assignment.group.id) | first %}
|
{% if anycast %}
|
||||||
address-virtual 00:00:5e:00:01:01 {{ fhrp_group.ip_addresses | sort(attribute='family') | map(attribute='address') | join(' ') }}
|
address-virtual 00:00:5e:00:01:01 {{ anycast | ipaddr(1) | join(' ') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
# Make expensive lookups to NetBox once for later reference by any host.
|
|
||||||
- name: Lookup networks and prefixes
|
|
||||||
set_fact:
|
|
||||||
vlans: '{{ query("netbox.netbox.nb_lookup", "vlans", api_filter="group=new-net", raw_data=true)
|
|
||||||
| sort(attribute="vid") }}'
|
|
||||||
prefixes: '{{ query("netbox.netbox.nb_lookup", "prefixes", raw_data=true)
|
|
||||||
| sort(attribute="prefix") | sort(attribute="family.value") }}'
|
|
||||||
|
|
||||||
- name: Select VLAN and BGP prefixes
|
|
||||||
set_fact:
|
|
||||||
vrf_prefixes: '{{ prefixes | selectattr("vrf")
|
|
||||||
| selectattr("vlan") | selectattr("vlan.id", "in", vlans|map(attribute="id"))
|
|
||||||
| sort(attribute="vlan.vid") }}'
|
|
||||||
bgp_prefixes: '{{ prefixes | selectattr("tenant")
|
|
||||||
| selectattr("role") | selectattr("role.slug", "==", "bgp")
|
|
||||||
| sort(attribute="tenant.slug") }}'
|
|
|
@ -6,10 +6,6 @@
|
||||||
command: mkinitfs
|
command: mkinitfs
|
||||||
when: "'handler' not in ansible_skip_tags"
|
when: "'handler' not in ansible_skip_tags"
|
||||||
|
|
||||||
- name: reboot
|
|
||||||
reboot:
|
|
||||||
when: "'handler' not in ansible_skip_tags"
|
|
||||||
|
|
||||||
- name: reload frr
|
- name: reload frr
|
||||||
command: /usr/lib/frr/frr-reload.py --reload /etc/frr/frr.conf
|
command: /usr/lib/frr/frr-reload.py --reload /etc/frr/frr.conf
|
||||||
when: "'handler' not in ansible_skip_tags"
|
when: "'handler' not in ansible_skip_tags"
|
||||||
|
|
|
@ -3,13 +3,23 @@
|
||||||
dest: /etc/network/interfaces.d/mgmt.intf
|
dest: /etc/network/interfaces.d/mgmt.intf
|
||||||
src: mgmt.intf.j2
|
src: mgmt.intf.j2
|
||||||
mode: 0644
|
mode: 0644
|
||||||
notify: reboot
|
register: task_mgmt_interface
|
||||||
|
|
||||||
- name: Run SSH in management VRF
|
- name: Run SSH in management VRF
|
||||||
lineinfile:
|
lineinfile:
|
||||||
path: /etc/conf.d/sshd
|
path: /etc/conf.d/sshd
|
||||||
regexp: "#* *vrf="
|
|
||||||
line: "vrf=\"mgmt\""
|
line: "vrf=\"mgmt\""
|
||||||
notify: reboot
|
register: task_ssh_vrf
|
||||||
|
|
||||||
- meta: flush_handlers
|
- name: Reboot for new VRF
|
||||||
|
reboot:
|
||||||
|
when: task_mgmt_interface.changed or task_ssh_vrf.changed
|
||||||
|
register: task_reboot
|
||||||
|
|
||||||
|
- name: Reset the connection
|
||||||
|
meta: reset_connection
|
||||||
|
|
||||||
|
- name: Wait for the network device to reload
|
||||||
|
wait_for_connection:
|
||||||
|
delay: 10
|
||||||
|
when: task_reboot.changed
|
||||||
|
|
|
@ -82,13 +82,16 @@ ipv6 prefix-list default permit ::/0
|
||||||
|
|
||||||
ip prefix-list fabric permit 10.34.0.0/24 ge 32
|
ip prefix-list fabric permit 10.34.0.0/24 ge 32
|
||||||
|
|
||||||
{% for prefix in vrf_prefixes | rejectattr('vrf.name', '==', 'outside')
|
{% for vlan in vlans %}
|
||||||
| sort(attribute='family.value') %}
|
{% for prefix in query('netbox.netbox.nb_lookup', 'prefixes', api_filter='vlan_id='~vlan.id, raw_data=true) %}
|
||||||
|
{% if prefix.vrf and prefix.vrf.name != 'outside' %}
|
||||||
{% if prefix.family.value == 4 %}
|
{% if prefix.family.value == 4 %}
|
||||||
ip prefix-list office permit {{ prefix.prefix }} ge 24
|
ip prefix-list office permit {{ prefix.prefix }} ge 24
|
||||||
{% elif prefix.family.value == 6 %}
|
{% elif prefix.family.value == 6 %}
|
||||||
ipv6 prefix-list office permit {{ prefix.prefix }} ge 64
|
ipv6 prefix-list office permit {{ prefix.prefix }} ge 64
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% if wg_net is defined %}
|
{% if wg_net is defined %}
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
{% for family, family_prefixes in vrf_prefixes | groupby('family.value') %}
|
{% for vlan in vlans %}
|
||||||
{% for vlan, vlan_prefixes in family_prefixes | groupby('vlan.vid') %}
|
{% set prefixes = query('netbox.netbox.nb_lookup', 'prefixes', api_filter='vlan_id='~vlan.id, raw_data=true) %}
|
||||||
set {{ vlan_prefixes[0].vlan.name }}{% if family == 6 %}/6{% endif %} {
|
{% set prefixes4 = prefixes | selectattr('family.value', '==', 4) | map(attribute='prefix') %}
|
||||||
type ipv{{ family }}_addr; flags interval
|
{% set prefixes6 = prefixes | selectattr('family.value', '==', 6) | map(attribute='prefix') %}
|
||||||
elements = { {{ vlan_prefixes | map(attribute='prefix') | join(',') }} }
|
set {{ vlan.name }} {
|
||||||
|
type ipv4_addr; flags interval
|
||||||
|
{% if prefixes4 %}
|
||||||
|
elements = { {{ prefixes4 | join(', ') }} }
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
set {{ vlan.name }}/6 {
|
||||||
|
type ipv6_addr; flags interval
|
||||||
|
{% if prefixes6 %}
|
||||||
|
elements = { {{ prefixes6 | join(', ') }} }
|
||||||
|
{% endif %}
|
||||||
}
|
}
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -80,6 +80,16 @@ table inet filter {
|
||||||
ct status dnat accept \
|
ct status dnat accept \
|
||||||
comment "Forward DNAT traffic for servers and suchlike"
|
comment "Forward DNAT traffic for servers and suchlike"
|
||||||
|
|
||||||
|
# Forward IPv4 to/from VPN users in the same network.
|
||||||
|
{% for vlan in vlans %}
|
||||||
|
iif @inside ip saddr @{{ vlan.name }} ip daddr @{{ vlan.name }} accept
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
# Forward IPv6 to/from VPN users in the same network.
|
||||||
|
{% for vlan in vlans %}
|
||||||
|
iif @inside ip6 saddr @{{ vlan.name }}/6 ip6 daddr @{{ vlan.name }}/6 accept
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
include "/etc/nftables.d/forward.nft*"
|
include "/etc/nftables.d/forward.nft*"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
{% set prefixes = query('netbox.netbox.nb_lookup', 'prefixes', raw_data=true) -%}
|
||||||
|
|
||||||
{
|
{
|
||||||
{% for vlan, addrs in vrf_prefixes | groupby('vlan.vid') %}
|
{% for vlan in vlans %}
|
||||||
"{{ addrs[0].vlan.name }}": {
|
{% set vlan_prefixes = prefixes | selectattr('vlan') | selectattr('vlan.id', '==', vlan.id) | map(attribute='prefix') %}
|
||||||
"ip": {{ addrs | selectattr('family.value', '==', 4) | map(attribute='prefix') | to_json }},
|
"{{ vlan.name }}": {
|
||||||
"ip6": {{ addrs | selectattr('family.value', '==', 6) | map(attribute='prefix') | to_json }}
|
"ip": {{ vlan_prefixes | ipv4 | to_json }},
|
||||||
|
"ip6": {{ vlan_prefixes | ipv6 | to_json }}
|
||||||
}{% if not loop.last %},{% endif +%}
|
}{% if not loop.last %},{% endif +%}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,13 +78,16 @@ route-map loopbacks permit 10
|
||||||
ip prefix-list default permit 0.0.0.0/0
|
ip prefix-list default permit 0.0.0.0/0
|
||||||
ipv6 prefix-list default permit ::/0
|
ipv6 prefix-list default permit ::/0
|
||||||
|
|
||||||
{% for prefix in bgp_prefixes | selectattr('tenant.slug', 'in', my_tenants) %}
|
{% for tenant in my_tenants %}
|
||||||
|
{% for prefix in query('netbox.netbox.nb_lookup', 'prefixes', raw_data=true, api_filter='tenant='~tenant)
|
||||||
|
| selectattr('role') | selectattr('role.slug', '==', 'bgp') | rejectattr('vlan') %}
|
||||||
{% if prefix.family.value == 4 %}
|
{% if prefix.family.value == 4 %}
|
||||||
ip prefix-list dc-{{ prefix.tenant.slug }} permit {{ prefix.prefix }} ge 32
|
ip prefix-list dc-{{ tenant }} permit {{ prefix.prefix }} ge 32
|
||||||
{% else %}
|
{% else %}
|
||||||
ipv6 prefix-list dc-{{ prefix.tenant.slug }} permit {{ prefix.prefix }} ge 64
|
ipv6 prefix-list dc-{{ tenant }} permit {{ prefix.prefix }} ge 64
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
# We only announce the default route to DC servers.
|
# We only announce the default route to DC servers.
|
||||||
route-map default->dc permit 10
|
route-map default->dc permit 10
|
||||||
|
|
|
@ -1,9 +1,3 @@
|
||||||
- hosts: '*'
|
|
||||||
gather_facts: false
|
|
||||||
roles:
|
|
||||||
- facts
|
|
||||||
|
|
||||||
# Set up fabric.
|
|
||||||
- hosts: spine-*
|
- hosts: spine-*
|
||||||
roles:
|
roles:
|
||||||
- spine
|
- spine
|
||||||
|
@ -16,13 +10,11 @@
|
||||||
roles:
|
roles:
|
||||||
- exit
|
- exit
|
||||||
|
|
||||||
# Set up access switches.
|
|
||||||
- hosts: access-*, sw-*
|
- hosts: access-*, sw-*
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
roles:
|
roles:
|
||||||
- access
|
- access
|
||||||
|
|
||||||
# Set up firewall.
|
|
||||||
- hosts: fw-*
|
- hosts: fw-*
|
||||||
roles:
|
roles:
|
||||||
- firewall
|
- firewall
|
||||||
|
|
Loading…
Reference in a new issue