<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Christian Flatt]]></title><description><![CDATA[Virtualization and Self-Hosting]]></description><link>https://christianflatt.com/</link><image><url>https://christianflatt.com/favicon.png</url><title>Christian Flatt</title><link>https://christianflatt.com/</link></image><generator>Ghost 5.87</generator><lastBuildDate>Sun, 03 May 2026 14:21:40 GMT</lastBuildDate><atom:link href="https://christianflatt.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Using Ansible to Configure FortiGate VLAN Interfaces to a Basic Standard]]></title><description><![CDATA[<p>I had quite a few problems figuring out a basic configuration to assist with quickly creating new interfaces to a standard.  The below configuration is working for me perfectly, allowing quick configuration of a new interface and DHCP server to a standard for easy micro-segmentation in my home network.</p><p>This</p>]]></description><link>https://christianflatt.com/using-ansible-to-configure-fortigate-vlan-interfaces-to-a-basic-standard/</link><guid isPermaLink="false">66cb7d5a45f43801f0cb23d0</guid><category><![CDATA[Fortinet]]></category><dc:creator><![CDATA[Christian Flatt]]></dc:creator><pubDate>Sun, 25 Aug 2024 19:34:57 GMT</pubDate><content:encoded><![CDATA[<p>I had quite a few problems figuring out a basic configuration to assist with quickly creating new interfaces to a standard.  The below configuration is working for me perfectly, allowing quick configuration of a new interface and DHCP server to a standard for easy micro-segmentation in my home network.</p><p>This method assumes a few things.  All interfaces will be built the same way, on the same physical interface, fortilink in this case.  All interfaces are VLAN, all networks are /24, and all have the same DNS and search space.</p><h3 id="inventory-file-inventory">Inventory File (inventory):</h3><p>Here is my Fortigate inventory file.  This is using the HTTPS and API access method.</p><figure class="kg-card kg-code-card"><pre><code>[fortigates]
fw01 ansible_host=172.17.33.1 fortios_access_token=&lt;FORTIGATE RESTAPI TOKEN&gt;

[fortigates:vars]
ansible_network_os=fortinet.fortios.fortios

[all:vars]
ansible_httpapi_use_ssl=yes
ansible_httpapi_validate_certs=no
ansible_httpapi_port=443
</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">inventory</span></p></figcaption></figure><h3 id="interface-definitions-interfacesyml">Interface Definitions (interfaces.yml)</h3><p>This file contains two variables in a list, the name of the interface, always structered by <code>&lt;LOCATION&gt;-&lt;PURPOSE&gt;-&lt;VLAN&gt;</code>, and interface access.</p><figure class="kg-card kg-code-card"><pre><code>fortigate_interfaces:
    #access: &quot;ping, ssh, http, https, snmp&quot;
...
  - name: SER-Kasm-103
    access: &quot;ping&quot;
  - name: SER-MGMT-104
    access: &quot;ping,https,http,ssh&quot;
...</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">interfaces.yml</span></p></figcaption></figure><h3 id="playbook-fortigate-interfacesyml">Playbook (fortigate-interfaces.yml)</h3><p>This Ansible playbook file loops through the interfaces in the interfaces.yml file, defined above.  </p><figure class="kg-card kg-code-card"><pre><code>- hosts: fortigates
  gather_facts: no
  hosts: fortigates
  connection: httpapi
  collections:
  - fortinet.fortios
  vars_files:
  - interfaces.yml

  tasks:
    - name: Configure Fortigate interfaces
      fortios_system_interface:
        vdom: &quot;root&quot;
        state: &quot;present&quot;
        access_token: &quot;{{ fortios_access_token }}&quot;
        system_interface:
          name: &quot;{{ item.name }}&quot;
          vdom: &quot;root&quot;
          type: &quot;vlan&quot;
          interface: &quot;fortilink&quot;
          vlanid: &quot;{{ item.name.split(&apos;-&apos;)[-1] }}&quot;
          ip: &quot;192.168.{{ item.name.split(&apos;-&apos;)[-1] }}.1/24&quot;
          role: &quot;lan&quot;
          allowaccess: &quot;{{ item.access }}&quot;
          device_identification: &quot;enable&quot;
      loop: &quot;{{ fortigate_interfaces }}&quot;

    - name: Configure DHCP Server on Fortigate
      fortios_system_dhcp_server:
        vdom: &quot;root&quot;
        state: &quot;present&quot;
        access_token: &quot;{{ fortios_access_token }}&quot;
        system_dhcp_server:
          id: &quot;{{ item.name.split(&apos;-&apos;)[-1] | int }}&quot;
          status: &quot;enable&quot;
          server_type: &quot;regular&quot;
          interface: &quot;{{ item.name }}&quot;
          default_gateway: &quot;192.168.{{ item.name.split(&apos;-&apos;)[-1] }}.1&quot;
          netmask: &quot;255.255.255.0&quot;
          ip_range:
            - id: 1
              start_ip: &quot;192.168.{{ item.name.split(&apos;-&apos;)[-1] }}.100&quot;
              end_ip: &quot;192.168.{{ item.name.split(&apos;-&apos;)[-1] }}.254&quot;
          dns_server1: &quot;192.168.105.10&quot;
          domain: &quot;christianflatt.com&quot;
      loop: &quot;{{ fortigate_interfaces }}&quot;
</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">fortigate-interfaces.yml</span></p></figcaption></figure><h5 id="a-couple-of-notes-here">A couple of notes here:</h5><ul><li>The VLAN ID is also the third octet for ease of use of managing my home network, so I pull out the value to define the IPs.</li><li>I use the VLAN ID for the <code>id</code> of the DHCP server.  Fortinet supports auto-incrementing with 0, but the configuration will then not be idempotent on subsequent runs.  Instead, it creates a new DHCP server, assigns it to the interface, then errors out on <code>ip_range</code> portion.</li></ul>]]></content:encoded></item><item><title><![CDATA[Preventing Copying to a Mount Point via Immutability]]></title><description><![CDATA[<p>When using <code>rsync</code> for backups, there&apos;s a risk that if the target mount point is not mounted, the data could be copied to the underlying directory instead and filling up the disk. To prevent this, we can make the mount point immutable using the Change Attribute (<code>chattr</code>) command.</p>]]></description><link>https://christianflatt.com/preventing-copying-to-a-mount-point-via-immutability/</link><guid isPermaLink="false">669c5854c44abb026e800195</guid><category><![CDATA[Backups]]></category><dc:creator><![CDATA[Christian Flatt]]></dc:creator><pubDate>Sun, 21 Jul 2024 01:01:51 GMT</pubDate><content:encoded><![CDATA[<p>When using <code>rsync</code> for backups, there&apos;s a risk that if the target mount point is not mounted, the data could be copied to the underlying directory instead and filling up the disk. To prevent this, we can make the mount point immutable using the Change Attribute (<code>chattr</code>) command. All commands and examples are run on <a href="https://christianflatt.com/404/">ProxMox</a>, a Debian-based distribution.</p><h3 id="make-a-directory-immutable">Make a directory immutable.</h3><ul><li>Make sure the directory is unmounted:<br><code>sudo umount /mnt/backup</code></li><li>Set the directory to immutable:<br><code>sudo chattr +i /mnt/backup</code></li><li>Verify the directory is immutable:<br><code>lsattr -a /mnt/backup</code><br>You should see the output:<br><code>----i---------e------- /mnt/backup/.<br>--------------e------- /mnt/backup/..</code></li></ul><h3 id="trust-but-verify">Trust, but verify.</h3><ul><li>Attempt to create a test file:<br><code>sudo touch /mnt/backup/testfile</code><br>You should see an error:<br><code>touch: setting times of &apos;/mnt/backup/testfile&apos;: No such file or directory</code></li></ul><h3 id="revert-this-change">Revert this change.</h3><ul><li>To revert the change, simply change <code>+i</code> to <code>-i</code>:<br><code>sudo chattr -i /mnt/backup</code></li></ul>]]></content:encoded></item></channel></rss>