lab:
  description: |
    FortiGate Practice Lab — clean canvas for FortiGate config practice.

    Topology: EXT-USER -- INET-R1 -- FortiGate(port1/2/3) -- SW1 -- PC1/PC2,
    with DMZ-SRV on port3.

    Interfaces:
      port1 outside  203.0.113.2/28    gw 203.0.113.1
      port2 inside   192.168.1.1/24
      port3 dmz      172.16.10.1/24

    Hosts:
      PC1  192.168.1.10   PC2  192.168.1.11
      DMZ-SRV  172.16.10.10  (HTTP/HTTPS listeners)
      EXT-USER  198.51.100.10
      Public destinations: 8.8.8.8, 1.1.1.1 (INET-R1 loopbacks)
      Public VIP target: 203.0.113.10 (you create this)

    Credentials: FortiGate admin/(blank, set on first login). PCs cisco/cisco.

    See companion file fortigate-practice-exercises.md for the 10 exercises
    and stretch goals.

  notes: ''
  timestamp: 1714780800.0
  title: FortiGate-Practice-Lab
  version: 0.3.0

nodes:

  # ──────────────────────────────────────────────────────────────────
  # INET-R1 — Simulated ISP / internet edge router
  # Provides "public" destinations (Lo0=8.8.8.8, Lo1=1.1.1.1) and the
  # 203.0.113.0/28 outside link toward FortiGate. EXT-USER hangs off Gi0/1.
  # ──────────────────────────────────────────────────────────────────
  - id: n0
    label: INET-R1
    node_definition: iosv
    x: -400
    y: 0
    ram: 512
    configuration: |
      hostname INET-R1
      no ip domain lookup
      !
      interface Loopback0
       description Simulated 8.8.8.8
       ip address 8.8.8.8 255.255.255.255
      !
      interface Loopback1
       description Simulated 1.1.1.1
       ip address 1.1.1.1 255.255.255.255
      !
      interface GigabitEthernet0/0
       description To FortiGate port1
       ip address 203.0.113.1 255.255.255.240
       no shutdown
      !
      interface GigabitEthernet0/1
       description To EXT-USER LAN
       ip address 198.51.100.1 255.255.255.0
       no shutdown
      !
      ! VIP 203.0.113.10 sits in the connected /28 — FortiGate replies to ARP.
      ! No extra route needed.
      !
      line con 0
       logging synchronous
      !
      end
    interfaces:
      - id: i0
        label: Loopback0
        type: loopback
      - id: i1
        slot: 0
        label: GigabitEthernet0/0
        type: physical
      - id: i2
        slot: 1
        label: GigabitEthernet0/1
        type: physical

  # ──────────────────────────────────────────────────────────────────
  # FortiGate — uses your custom node definition (id: fortigate)
  # Boots with a near-default config. You configure everything from here.
  # ──────────────────────────────────────────────────────────────────
  - id: n1
    label: FortiGate
    node_definition: fortigate
    x: -150
    y: 0
    # No configuration block — the FortiGate boots with its default config
    # so you can practice the full setup from scratch.
    interfaces:
      - id: i0
        slot: 0
        label: p1
        type: physical
      - id: i1
        slot: 1
        label: p2
        type: physical
      - id: i2
        slot: 2
        label: p3
        type: physical
      - id: i3
        slot: 3
        label: p4
        type: physical

  # ──────────────────────────────────────────────────────────────────
  # SW1 — Inside L2 access switch
  # ──────────────────────────────────────────────────────────────────
  - id: n2
    label: SW1
    node_definition: iosvl2
    x: 100
    y: 100
    ram: 768
    configuration: |
      hostname SW1
      no ip domain lookup
      !
      vlan 10
       name USERS
      !
      interface GigabitEthernet0/0
       description To FortiGate port2 (inside)
       switchport access vlan 10
       switchport mode access
       spanning-tree portfast
      !
      interface GigabitEthernet0/1
       description PC1
       switchport access vlan 10
       switchport mode access
       spanning-tree portfast
      !
      interface GigabitEthernet0/2
       description PC2
       switchport access vlan 10
       switchport mode access
       spanning-tree portfast
      !
      interface range GigabitEthernet1/0 - 3
       shutdown
      !
      line con 0
       logging synchronous
      !
      end
    interfaces:
      - id: i0
        label: Loopback0
        type: loopback
      - id: i1
        slot: 0
        label: GigabitEthernet0/0
        type: physical
      - id: i2
        slot: 1
        label: GigabitEthernet0/1
        type: physical
      - id: i3
        slot: 2
        label: GigabitEthernet0/2
        type: physical

  # ──────────────────────────────────────────────────────────────────
  # EXT-USER — External "internet" host
  # Use to test inbound to public services (VIP at 203.0.113.10):
  #   ping 203.0.113.10
  #   nc -zv 203.0.113.10 80
  #   nc -zv 203.0.113.10 443
  # ──────────────────────────────────────────────────────────────────
  - id: n3
    label: EXT-USER
    node_definition: alpine
    x: -550
    y: 100
    configuration: |
      # Sourced as root at boot
      hostname ext-user
      USERNAME=cisco
      PASSWORD=cisco
      ip link show eth1 >/dev/null 2>&1 && IF=eth1 || IF=eth0
      ip address add 198.51.100.10/24 dev $IF
      ip link set dev $IF up
      ip route add default via 198.51.100.1
    interfaces:
      - id: i0
        slot: 0
        label: eth0
        type: physical

  # ──────────────────────────────────────────────────────────────────
  # DMZ-SRV — DMZ web/host server
  # Pre-launches HTTP listeners on 80 and 443 so VIP testing has
  # something to hit. Real IP 172.16.10.10 — you'll NAT to 203.0.113.10.
  # ──────────────────────────────────────────────────────────────────
  - id: n4
    label: DMZ-SRV
    node_definition: alpine
    x: 100
    y: -150
    configuration: |
      # Sourced as root at boot
      hostname dmz-srv
      USERNAME=cisco
      PASSWORD=cisco
      ip link show eth1 >/dev/null 2>&1 && IF=eth1 || IF=eth0
      ip address add 172.16.10.10/24 dev $IF
      ip link set dev $IF up
      ip route add default via 172.16.10.1
      # Tiny HTTP listeners — respawn after each connection so you can hit
      # them repeatedly while testing VIPs.
      ( while true; do echo -e "HTTP/1.0 200 OK\r\n\r\nDMZ-SRV port 80" | nc -l -p 80 -q 1; done ) &
      ( while true; do echo -e "HTTP/1.0 200 OK\r\n\r\nDMZ-SRV port 443" | nc -l -p 443 -q 1; done ) &
    interfaces:
      - id: i0
        slot: 0
        label: eth0
        type: physical

  # ──────────────────────────────────────────────────────────────────
  # PC1 — Inside user
  # ──────────────────────────────────────────────────────────────────
  - id: n5
    label: PC1
    node_definition: alpine
    x: 280
    y: 50
    configuration: |
      # Sourced as root at boot
      hostname pc1
      USERNAME=cisco
      PASSWORD=cisco
      ip link show eth1 >/dev/null 2>&1 && IF=eth1 || IF=eth0
      ip address add 192.168.1.10/24 dev $IF
      ip link set dev $IF up
      ip route add default via 192.168.1.1
    interfaces:
      - id: i0
        slot: 0
        label: eth0
        type: physical

  # ──────────────────────────────────────────────────────────────────
  # PC2 — Inside user
  # ──────────────────────────────────────────────────────────────────
  - id: n6
    label: PC2
    node_definition: alpine
    x: 280
    y: 150
    configuration: |
      # Sourced as root at boot
      hostname pc2
      USERNAME=cisco
      PASSWORD=cisco
      ip link show eth1 >/dev/null 2>&1 && IF=eth1 || IF=eth0
      ip address add 192.168.1.11/24 dev $IF
      ip link set dev $IF up
      ip route add default via 192.168.1.1
    interfaces:
      - id: i0
        slot: 0
        label: eth0
        type: physical

links:
  # INET-R1 Gi0/0 <-> FortiGate port1  (outside / WAN)
  - id: l0
    n1: n0
    i1: i1
    n2: n1
    i2: i0
    label: "INET-R1 to FGT-port1"
  # INET-R1 Gi0/1 <-> EXT-USER
  - id: l1
    n1: n0
    i1: i2
    n2: n3
    i2: i0
    label: "INET-R1 to EXT-USER"
  # FortiGate port2 <-> SW1 Gi0/0  (inside)
  - id: l2
    n1: n1
    i1: i1
    n2: n2
    i2: i1
    label: "FGT-port2 to SW1"
  # FortiGate port3 <-> DMZ-SRV  (dmz)
  - id: l3
    n1: n1
    i1: i2
    n2: n4
    i2: i0
    label: "FGT-port3 to DMZ-SRV"
  # SW1 Gi0/1 <-> PC1
  - id: l4
    n1: n2
    i1: i2
    n2: n5
    i2: i0
    label: "SW1 to PC1"
  # SW1 Gi0/2 <-> PC2
  - id: l5
    n1: n2
    i1: i3
    n2: n6
    i2: i0
    label: "SW1 to PC2"
