[{"content":" With a lot of the recent nonsense going on after VMware was acquired last year, I\u0026rsquo;m seeing people start to look for ESX alternatives - especially for home labs. In fact, I also just migrated my home lab from ESX to Proxmox.\nOne of the things I needed to keep running was Cisco Modeling Labs (CML). I often use CML to build quick virtual topologies for testing things, so it was important for me that it worked on Proxmox.\nIt\u0026rsquo;s worth noting that Proxmox isn\u0026rsquo;t an officially supported platform. The docs state that only bare metal \u0026amp; VMware platforms are supported.\nThat being said, I\u0026rsquo;m happy to say that Proxmox has worked just fine for me - it just needed a few tweaks. Though just be warned that you may still run into issues and it is technically an unsupported configuration.\nIn the post below, I\u0026rsquo;ll share the steps that I used to get CML up \u0026amp; running on Proxmox.\nPrerequisites To start with, we\u0026rsquo;ll need a few things:\nA server running Proxmox I\u0026rsquo;m currently using version 8.1.3 A CML install ISO, found here We\u0026rsquo;ll be using version 2.6.1 for this guide CML\u0026rsquo;s reference platform ISO, also found at the link above Our Proxmox machine will still need enough resources to build a VM with the minimum requirements listed here.\nThe CML \u0026amp; refplat ISOs should be placed into a Proxmox ISO storage location.\nCreating the VM Next we can get to the fun part: Setting up the VM in Proxmox.\nWe\u0026rsquo;ll first locate a node in Proxmox to place our VM. Then right-click that node \u0026amp; select Create VM:\nThen, we\u0026rsquo;ll give our VM a name \u0026amp; ID. I\u0026rsquo;ve named mine CML:\nOn the next page, we can select our CML ISO from the correct storage device. We can leave the Guest OS as Linux and 6.x - 2.6 Kernel:\nNext, we\u0026rsquo;ll need to make some minor adjustments on the System tab.\nThe default BIOS will likely be set to Default (SeaBIOS). We\u0026rsquo;ll change that to OVMF (UEFI), which will also give us a few additional options.\nWe\u0026rsquo;ll keep Add EFI Disk checked, and select a storage location for that EFI disk.\nOn the Disks tab, feel free to increase the size of the VM disk. While 32GB is the minimum required, you\u0026rsquo;ll likely want more than that. I\u0026rsquo;ve increased mine to 50G for now. Keep the image format as QEMU.\nWe\u0026rsquo;ll also want to update the Async IO setting to native. This is hidden under the Advanced settings:\nOn the CPU tab, we\u0026rsquo;ll update the core count to a minimum of 4 per the requirements. Of course, you\u0026rsquo;re welcome to increase this as needed.\nMore importantly however, we\u0026rsquo;ll need to set the CPU type to host. This allows the VM to use the underlying nested virtualization features:\nNext we can assign memory to our VM.\nThe requirements state a minimum of 8GB, however this will likely only accommodate simpler labs. Some individual CML nodes require more than that to start.\nIn my case, I\u0026rsquo;ll start with 32GB - and we can always increase this later:\nLastly, on the Network tab, no changes are necessary. Of course, you can assign a VLAN if needed.\nAfter that, we can head over to the Confirm tab \u0026amp; finish up the wizard.\nAdd Refplat ISO Next, we\u0026rsquo;ll need to make sure our reference platform ISO is connected to the VM.\nWe\u0026rsquo;ll head over to the VM\u0026rsquo;s hardware tab, then click Add and select CD/DVD Drive:\nThen we\u0026rsquo;ll select the appropriate ISO storage, and pick our refplat ISO file:\nInstall CML Once that\u0026rsquo;s done, we can go ahead and power on our VM \u0026amp; pop open the console!\nIf you\u0026rsquo;ve installed CML previously on another platform, this process is just the same.\nAt boot, we\u0026rsquo;ll select Install CML.\nAfter a moment \u0026amp; a few screens, we\u0026rsquo;ll start the system configuration.\nFirst, we\u0026rsquo;ll input a system hostname:\nThen, we\u0026rsquo;ll input credentials for the system management user.\nNote: This user isn\u0026rsquo;t used to log into the CML software. Rather, it\u0026rsquo;s used for system administration tasks via the Cockpit UI, such as: Installing updates, checking logs, joining a domain, or powering off / rebooting the system.\nAfter that, we can configure our CML admin user:\nNext we have our network config. By default, CML will prompt to use DHCP, but we\u0026rsquo;ll likely want to change this to a static IP assignment:\nAfter that, CML will prompt us to confirm our settings - then it will begin the installation. Since a lot of the VM images come from the reference platform ISO, this process may take a while as each of those images are copied over to the system.\nWhen CML is ready, we\u0026rsquo;ll see something similar on the console telling us how to reach the web UI:\nAgain for reference, the main UI is reachable at https://\u0026lt;CML IP\u0026gt; and the Cockpit management UI is at https://\u0026lt;CML IP\u0026gt;:9090.\nAt this point, we can log in \u0026amp; start building labs!\nThe process for setting up CML on Proxmox isn\u0026rsquo;t super crazy, but there are just a few tweaks that need to be made during setup. I wrote this hoping that if anyone else out there is trying to set this up, maybe it would help.\nAs always, Thanks for reading!\n","permalink":"https://0x2142.com/how-to-setup-cml-on-proxmox/","summary":"In this blog post, we\u0026rsquo;ll walk through how to set up \u0026amp; install Cisco Modeling Labs (CML) on Proxmox","title":"[How To] Set up Cisco Modeling Labs (CML) on Proxmox"},{"content":"Hey there everyone, I hope you\u0026rsquo;ve had a great start to the year so far!\nIn this blog post, I wanted to spend just a few minutes going over how to solve an issue one of my peers ran into recently.\nThe Issue Let\u0026rsquo;s say we were trying to use Ansible to update the interface admin state on a Cisco Nexus switch. In this case, I\u0026rsquo;m using a virtual Nexus 9000 which is running NX-OS version 10.2(6).\nThis switch has a loopback interface (lo100), which we want to disable.\nReading the Ansible documentation for the NETCONF module, we might assume that the following would be enough to get the job done:\n- name : NX-OS Interface Updates hosts: all connection: netconf gather_facts: no tasks: - name: Update Interface State ansible.netcommon.netconf_config: content: | \u0026lt;config\u0026gt; \u0026lt;System xmlns=\u0026#34;http://cisco.com/ns/yang/cisco-nx-os-device\u0026#34;\u0026gt; \u0026lt;intf-items\u0026gt; \u0026lt;lb-items\u0026gt; \u0026lt;LbRtdIf-list\u0026gt; \u0026lt;id\u0026gt;lo100\u0026lt;/id\u0026gt; \u0026lt;adminSt\u0026gt;down\u0026lt;/adminSt\u0026gt; \u0026lt;/LbRtdIf-list\u0026gt; \u0026lt;/lb-items\u0026gt; \u0026lt;/intf-items\u0026gt; \u0026lt;/System\u0026gt; \u0026lt;/config\u0026gt; However, once we run that playbook, we\u0026rsquo;ll get an error back:\n(ansible) matt@ansible-dev:~/ansible/nxos$ ansible-playbook nx-update-interface.yaml -i inventory.yml PLAY [NX-OS Interface Updates] ********************************************************************************************************** TASK [Update Interface State] *********************************************************************************************************** fatal: [10.122.2.229]: FAILED! =\u0026gt; {\u0026#34;changed\u0026#34;: false, \u0026#34;msg\u0026#34;: \u0026#34;Request without namespace and filter is an unsupported operation\u0026#34;} PLAY RECAP ****************************************************************************************************************************** 10.122.2.229 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0 The Fix Luckily, this has an easy solution.\nAccording to the Cisco NX-OS Documentation, there was a change in version 9.3(1) that requires a filter to be sent along with any GET or GET-CONFIG request.\nBut wait - aren\u0026rsquo;t we updating the config via an EDIT-CONFIG request? We shouldn\u0026rsquo;t need to send a filter, since it\u0026rsquo;s not a GET or GET-CONFIG, right?\nWell because of the way Ansible works, we actually send two GET-CONFIG requests along with our EDIT-CONFIG. One before the change, and another one after making the change.\nThese are used to validate that we actually updated the config. Ansible uses these two GET-CONFIG requests to compare \u0026amp; see if there is actually any difference between the two.\nSo to address our error above - when we send our config change in Ansible, we also need to include the get_filter parameter. This will give Ansible a NETCONF filter use when it issues the GET-CONFIG requests to validate our changes.\nIn our case, that GET-CONFIG filter could look something like this:\n\u0026lt;System xmlns=\u0026#34;http://cisco.com/ns/yang/cisco-nx-os-device\u0026#34;\u0026gt; \u0026lt;intf-items\u0026gt; \u0026lt;lb-items\u0026gt; \u0026lt;LbRtdIf-list\u0026gt; \u0026lt;id\u0026gt;lo100\u0026lt;/id\u0026gt; \u0026lt;adminSt/\u0026gt; \u0026lt;/LbRtdIf-list\u0026gt; \u0026lt;/lb-items\u0026gt; \u0026lt;/intf-items\u0026gt; \u0026lt;/System\u0026gt; That filter would pull back just the admin state for our lo100 interface, which then Ansible could use to compare after we issue our request to disable the interface.\nFor what it\u0026rsquo;s worth, we don\u0026rsquo;t necessarily have to be that specific with our filter. We just need to ensure that whatever config returned by the filter would also include the section we intend to change.\nSo for example, we could actually just filter by System and this would still work:\n\u0026lt;System xmlns=\u0026#34;http://cisco.com/ns/yang/cisco-nx-os-device\u0026#34;\u0026gt; \u0026lt;/System\u0026gt; The main difference here is that the switch would return a much larger amount of data, which then Ansible would also need to process to detect the change. So for efficiency \u0026amp; accuracy, it\u0026rsquo;s probably still helpful to keep our get_filter scoped to a reasonably narrow amount.\nWith all that covered, here\u0026rsquo;s the example of our final playbook with the new filter:\n- name : NX-OS Interface Updates hosts: all connection: netconf gather_facts: no tasks: - name: Update Interface State ansible.netcommon.netconf_config: get_filter: | \u0026lt;System xmlns=\u0026#34;http://cisco.com/ns/yang/cisco-nx-os-device\u0026#34;\u0026gt; \u0026lt;intf-items\u0026gt; \u0026lt;lb-items\u0026gt; \u0026lt;LbRtdIf-list\u0026gt; \u0026lt;id\u0026gt;lo100\u0026lt;/id\u0026gt; \u0026lt;adminSt/\u0026gt; \u0026lt;/LbRtdIf-list\u0026gt; \u0026lt;/lb-items\u0026gt; \u0026lt;/intf-items\u0026gt; \u0026lt;/System\u0026gt; content: | \u0026lt;config\u0026gt; \u0026lt;System xmlns=\u0026#34;http://cisco.com/ns/yang/cisco-nx-os-device\u0026#34;\u0026gt; \u0026lt;intf-items\u0026gt; \u0026lt;lb-items\u0026gt; \u0026lt;LbRtdIf-list\u0026gt; \u0026lt;id\u0026gt;lo100\u0026lt;/id\u0026gt; \u0026lt;adminSt\u0026gt;down\u0026lt;/adminSt\u0026gt; \u0026lt;/LbRtdIf-list\u0026gt; \u0026lt;/lb-items\u0026gt; \u0026lt;/intf-items\u0026gt; \u0026lt;/System\u0026gt; \u0026lt;/config\u0026gt; And, if we try to run the playbook again:\n(ansible) matt@ansible-dev:~/ansible/nxos$ ansible-playbook nx-update-interface.yaml -i inventory.yml PLAY [NX-OS Interface Updates] ********************************************************************************************************** TASK [Update Interface State] *********************************************************************************************************** changed: [10.122.2.229] PLAY RECAP ****************************************************************************************************************************** 10.122.2.229 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 That looks much better!\nThanks so much for reading this blog post. I really hope it was helpful to you!\nIf you found value in this, please let me know! Feel free to leave me a comment below, or follow me on YouTube!\nOh, and for those of you on Mastodon - You can find me at @matt@0x2142.com.\nThanks again!\n","permalink":"https://0x2142.com/quick-fix-nexus-9k-ansible-netconf/","summary":"A quick blog post on how to fix an error with Ansible and Cisco Nexus 9k via NETCONF: \u0026ldquo;Request without namespace and filter is an unsupported operation\u0026rdquo;","title":"[Quick Fix] Nexus 9k \u0026 Ansible: \"Request without namespace and filter is an unsupported operation\""},{"content":" In this post, we\u0026rsquo;ll walk through how to connect an OPNsense firewall to Mullvad\u0026rsquo;s wireguard VPN. This can be used in various deployments to help protect your home network traffic.\nAs a quick note, this post is not sponsored by Mullvad - I just happen to really like their service \u0026amp; appreciate their approach to privacy and security. In fact, they currently don\u0026rsquo;t offer any incentives or paid promotions of their product. More info on their current policy can be found here.\nCreating an Account Mullvad is unique in that they don\u0026rsquo;t really require any sign up to get started with their service. Instead, when you sign up for new service, they randomly generate an account number. That\u0026rsquo;s it. They don\u0026rsquo;t require any additional information from you.\nSo first we can hit their new account page here - then click to generate a new account number.\nKeep this account number somewhere safe. Mullvad has very limited ability to help you recover the account number.\nOnce we have that, we can click the button to Add time to our account.\nMullvad doesn\u0026rsquo;t support any recurring subscriptions, so that they are able to keep less data about their customers. Instead, we just add time increments in months for how long we would like to use the service. This can be anywhere from a single month up to a year.\nIn terms of payments, they do offer a number of options, including the usual PayPal/credit cards - or if you\u0026rsquo;re truly concerned about privacy, they accept a few cryptocurrencies or you can actually mail them a cash payment as well.\nAfter a payment has been made \u0026amp; time added to the account, we can quickly jump into the configuration side of things.\nConfig: Mullvad Side First we\u0026rsquo;ll take a quick look at the configuration required on the Mullvad VPN side of things.\nOn the left side of the account page, we\u0026rsquo;ll click on WireGuard Configuration.\nFirst we\u0026rsquo;ll select Linux as our platform (In this context, what is selected here isn\u0026rsquo;t really important) - and then click the button to generate WireGuard keys:\nNote: Alternatively, we could have generated our WireGuard keys on our OPNsense firewall - then applied them here. It\u0026rsquo;s up to you on which method you prefer!\nNext, we\u0026rsquo;ll take a look at which server(s) we would like to connect to.\nWhen selecting a server, we have the option to pick our desired country \u0026amp; location - as well as picking a specific server to connect to if we choose. There is also an option to select all servers - in which case the config generator will create a WireGuard configuration file for each server.\nFor the purpose of this walk through, we\u0026rsquo;ll keep things simple \u0026amp; only select a single server. So in the screenshot above, I\u0026rsquo;ve selected us-qas-wg-004.\nWe\u0026rsquo;ll also take a quick look at the advanced options, which give us a little flexibility if we need it. For most people, these additional options will not be necessary to change or modify.\nWe can enable Multihop functionality, so long as we only selected a single server to connect to. So if you selected the All Servers option, this won\u0026rsquo;t be available. This allows us to specify an entry \u0026amp; exit server for our VPN. In other words, our device would directly connect to the entry server we select - then Mullvad would tunnel our traffic across their network to the exit server, where our traffic would be decrypted \u0026amp; forwarded out to the internet. Depending on your privacy \u0026amp; security desires, this is a really nice option to have the ability to enable.\nNext, we have options on what type of connection we would like \u0026amp; which traffic to forward.\nServer connection protocol will specify whether we are using IPv4 or IPv6 between our device \u0026amp; our VPN server. Most likely you\u0026rsquo;ll want to leave this at IPv4, unless you have an IPv6-only internet connection (or if you just would prefer to use IPv6 anyways!).\nTunnel traffic is how we can specify whether we would like IPv4 or IPv6 client-side traffic to be forwarded over the VPN. So this would depend on whether the clients on our network have IPv4 vs IPv6 connectivity, or both - and whether we prefer to only forward certain types of traffic over the VPN. I\u0026rsquo;ll be leaving this setting as the default: Both.\nNext we can specify a custom port if we would like. By default, wireguard will use UDP port 51820 - and we probably won\u0026rsquo;t need to change this unless the port is being blocked upstream.\nLastly, we also have the option to enable content blocking across the VPN. Mullvad accomplishes content filtering through DNS-level blocking - and when we finish generating a configuration file, the file will include DNS servers to use. This is okay to use if you were connecting a single client to their VPN service. However, if you\u0026rsquo;re using a router or device like OPNsense, you would need to update the DNS on all clients on your network to make this work. This is possible by updating DHCP on our router with the new DNS server address - or configuring a DNS rewrite. We won\u0026rsquo;t get into either of those in this post - so for now I will leave the content blocking options unchecked.\nOnce we\u0026rsquo;re good with our configuration - we can click the Download File button. We\u0026rsquo;ll get a standard WireGuard config file, that looks like this:\nAt this point, we\u0026rsquo;re good to move onto the next part!\nConfig: OPNsense Side Okay, so now that we have everything ready to go on the Mullvad side - we can configure our OPNsense device.\nMake sure you already have WireGuard installed on OPNsense. This can be done by navigating to System \u0026gt; Firmware \u0026gt; Plugins then searching for wireguard \u0026amp; clicking the install button.\nNext, we\u0026rsquo;ll enable Wireguard by navigating to VPN \u0026gt; Wireguard and checking the box to Enable WireGuard, then Apply.\nThen we\u0026rsquo;ll hop over to the Endpoints tab \u0026amp; configure our Mullvad VPN peer.\nFor this part of the configuration, we\u0026rsquo;ll just copy our public key, allowed IPs, endpoint address, and endpoint port from our Mullvad config file. In the screenshot below, I also named my endpoint with the specific Mullvad server I\u0026rsquo;ll be connecting to:\nThen we can click Save and Apply.\nIf you wanted multiple Mullvad servers configured, just create a new endpoint for each one. Then, make sure that you select all of the Mullvad peers on the next step below.\nAfter that, we can move over to the Local tab to define our OPNsense tunnel configuration.\nClick the button to add a new peer, then we\u0026rsquo;ll fill in our private key and tunnel address(es) from the Mullvad config file. Under Peers, we\u0026rsquo;ll also select our Mullvad VPN peer that we configured just a moment ago:\nUPDATE: Looks like with a recent OPNsense update, they now require you to enter both the WireGuard private \u0026amp; public key into the local config (shown above). In the the screenshot, I only show entering the private key - since this was all that was required at the time. Two ways to get your public key:\nLog into Mullvad \u0026amp; check the \u0026ldquo;Devices\u0026rdquo; tab under \u0026ldquo;Account Management\u0026rdquo;. This will show your device public key (They don\u0026rsquo;t keep your private key after generating it for you, only the public). If you have wireguard installed somewhere else, you can use the \u0026ldquo;wg pubkey\u0026rdquo; command to derive a public key from your private key. Command: echo \u0026lt;private_key\u0026gt; | wg pubkey Note: By default with the configuration we\u0026rsquo;ve applied so far, this VPN will forward ALL traffic on our network to Mullvad. If we would prefer to selectively choose which traffic to send over the VPN, we can check the box for Disable Routes - then use policy routing to forward specific things to Mullvad. We\u0026rsquo;ll take a look at how to do this later in the post - but for now just be aware that our current configuration will forward ALL traffic.\nOkay, with that all done we can click Apply and Save here as well.\nWith any luck, we can check the Status tab \u0026amp; see that there is data being transmitted \u0026amp; successful WireGuard handshakes:\nHowever, before our clients traffic can be forwarded over the VPN, we\u0026rsquo;ll need to create a firewall rule to permit traffic \u0026amp; a NAT rule to translate our client addresses to our Mullvad IP.\nWe\u0026rsquo;ll navigate to Firewall \u0026gt; Rules \u0026gt; WireGuard (Group). Then we\u0026rsquo;ll click to create a new rule.\nWithin this new rule, I\u0026rsquo;ll update Direction to Out and change TCP/IP Version to IPv4+IPv6. I\u0026rsquo;ll leave the source as Any:\nWe can also leave the Destination as Any, but I\u0026rsquo;ll update the rule to enable logging:\nThis rule will allow any clients behind our OPNsense firewall to reach anything on the internet.\nNext, we\u0026rsquo;ll have to create a NAT rule. This ensures that our client addresses on our network get appropriately translated to the tunnel IP address that Mullvad has assigned us.\nWe\u0026rsquo;ll navigate to Firewall \u0026gt; NAT \u0026gt; Outbound. By default, OPNsense will be set to Automatic outbound NAT rule generation. We\u0026rsquo;ll need to update this to Hybrid outbound NAT rule generation to allow custom NAT rules. Then we can click Save and Apply.\nNext, we\u0026rsquo;ll create a new Manual NAT rule, where we\u0026rsquo;ll update our Interface to WireGuard (Group):\nThen we\u0026rsquo;ll make sure our Translation / target is set to Interface Address - and again, I\u0026rsquo;ll enable logging:\nAfter we click save, we should have a NAT rule that looks like this:\nAt this point, we should be good to test our clients!\nTesting Of course, it\u0026rsquo;s easy enough to use one of our clients to check that we still have internet access - but how can we be sure that they\u0026rsquo;re using the VPN?\nThe easiest way might be to check the mullvad.net, where they do have a quick validation at the top of the page:\nSo according to Mullvad, it looks like we\u0026rsquo;re connected \u0026amp; they even show which server we\u0026rsquo;re connecting from.\nWe can also double check using a traceroute or tracepath command:\nIn this case, we can see that our traffic to Google hits our OPNsense gateway, then the Mullvad VPN gateway followed by another external address owned by Mullvad.\nSo based on some quick testing, it looks like we\u0026rsquo;re all good!\nPolicy Routing So in the above walkthrough, we configured a Mullvad VPN from our OPNsense firewall - but it is forwarding ALL of our network clients over the VPN. What about if we only wanted certain clients to use the VPN? Or all clients to use it, but only for certain destinations?\nWe can accomplish this through policy routing.\nSo the first thing we\u0026rsquo;ll do is go back to our WireGuard config, then under the Local tab. We\u0026rsquo;ll edit our configuration here, and check the box for Disable Routes.\nBy default, OPNsense / WireGuard will install routes for any IPs listed in the AllowedIPs field for each peer. In our set up, we configured 0.0.0.0/0 - which matches all traffic. By checking the box for Disable Routes, we prevent OPNsense from installing that default route - and instead we can manually specify our own.\nIf you\u0026rsquo;re curious to double check this, you can try hitting Mullvad\u0026rsquo;s website after changing this setting - and it should show that you\u0026rsquo;re no longer connected.\nThen, we\u0026rsquo;ll need to set up our WireGuard configuration to use a dedicated, named interface so that we can create a static gateway.\nWe\u0026rsquo;ll head to Interfaces \u0026gt; Assignments - and create a new interface. From the drop-down, we\u0026rsquo;ll select our WireGuard interface - in my case this was wg1. Then we can assign it a name:\nThen click the + icon to add, and Save.\nThen we can navigate to the interface name under the Interfaces menu - and enable the new interface:\nNext we can create a gateway to route traffic through. Navigate to System \u0026gt; Gateways \u0026gt; Single.\nCreate a new gateway \u0026amp; give it a name. Then we\u0026rsquo;ll enter our Mullvad VPN gateway IP address, which in my case was 10.64.0.1. How did we find this? Well earlier when we tested our VPN connectivity - we performed a tracepath. In the output of this tracepath, our second hop (the one right after our OPNsense firewall) would be the Mullvad VPN gateway. So this is the address we\u0026rsquo;ll use for our gateway here:\nThen clic Save and Apply.\nNote: You may get an error here, like \u0026ldquo;The gateway address \u0026ldquo;10.64.0.1\u0026rdquo; does not lie within one of the chosen interface\u0026rsquo;s IPv4 subnets.\u0026rdquo; To resolve this, we\u0026rsquo;ll temporarily change our interface address. Navigate to the WireGuard local config, and re-enter your tunnel address excluding the \u0026ldquo;/32\u0026rdquo;. For example, if your tunnel IP was 10.10.10.10/32, change this to just 10.10.10.10 You should be able to set the gateway address now. Be sure to change your tunnel IP back afterwards!\nNext, we\u0026rsquo;ll create a firewall rule for each set of sources or destinations we would like to manipulate.\nSo we can navigate to Firewall \u0026gt; Rules \u0026gt; Floating (or select a specific interface for clients, like LAN).\nIn here, we\u0026rsquo;ll set whichever parameters we would like to match. So for this example, let\u0026rsquo;s say I have a client PC at 10.100.100.10 and I only want traffic to 8.8.8.8 to use Mullvad. All other traffic can use the normal internet connection \u0026amp; not use the VPN.\nIn that case, I\u0026rsquo;ll set my source address to 10.100.100.10/32 and my destination to 8.8.8.8/32:\nThen all we need to do is update our Gateway to use the Mullvad gateway we just created:\nNow, if we hop over to our client PC - Mullvad\u0026rsquo;s website will say that we\u0026rsquo;re not connected to the VPN. However, we can check that traffic to 8.8.8.8 is actually being sent through Mullvad using tracepath again:\nIn the screenshot above, I also included a tracepath to 8.8.4.4, just to show that it is going through my normal internet connection \u0026amp; not Mullvad.\nThis was just one example, but you could easily create multiple firewall rules for different sources and/or destinations to control where traffic is sent. Aliases can also be used to group sources or destinations, so that multiple can be added to a single firewall rule.\nOkay - I think that\u0026rsquo;s about all I wanted to cover in this post.\nHope it is helpful! Feel free to leave a comment below - or follow me on YouTube 😊\n","permalink":"https://0x2142.com/how-to-protect-your-home-network-with-mullvad-vpn-opnsense/","summary":"In this post, we\u0026rsquo;ll walk through how to connect an OPNsense firewall to Mullvad\u0026rsquo;s VPN service. This covers both a full tunnel \u0026amp; partial tunnel configuration.","title":"[How To] Protect Your Home Network with Mullvad VPN \u0026 OPNsense"},{"content":" Back when I started diving into studying for the Cisco DevNet certifications, I thought a lot of the REST API stuff was fairly easy. But I always struggled a bit with wrapping my head around YANG models \u0026amp; how exactly NETCONF/RESTCONF worked. A lot of it seemed more abstract, and browsing through YANG models isn\u0026rsquo;t quite as intuitive as most REST API documentation.\nThese days I end up working quite a bit with NETCONF - and if there\u0026rsquo;s one tool that\u0026rsquo;s helped me more than anything, it\u0026rsquo;s certainly YANG suite. So in this post, I wanted to spend a little bit of time walking through how to get set up with this tool - and walk through a quick example of how it can be used.\nWhat\u0026rsquo;s YANG Suite? To put it simply: YANG Suite is a web-based tool that allows you to easily browse through YANG models \u0026amp; find what you need. It also has a bunch of tools to run test queries against devices, which makes developing NETCONF automation easier. It can also auto-generate code snippets - and who doesn\u0026rsquo;t like that? 😄\nYANG suite is an open source tool maintained by Cisco \u0026amp; the GitHub repo here.\nInitial Setup YANG suite is a containerized application, so we\u0026rsquo;ll need a container host to run it on. You could use something like Docker Desktop to run it on your local PC, but I\u0026rsquo;ll be using a dedicated Ubuntu machine in my lab with Docker installed.\nNote: YANG suite now supports a local Python pip-based install. Check out the GitHub repo for more info on that.\nDeploy with Docker First thing we\u0026rsquo;ll do is pull down the code from GitHub:\nmatt@yangsuite:~/yangsuite/docker$ git clone https://github.com/CiscoDevNet/yangsuite.git Next, we can execute the setup script to help provision the YANG Suite containers, which is located at yangsuite/docker/start_yang_suite.sh. The script will prompt you to set up an admin user and generate self-signed certificates:\nmatt@yangsuite:~/yangsuite/docker$ cd yangsuite/docker matt@yangsuite:~/yangsuite/docker$ ./start_yang_suite.sh Hello, please setup YANG Suite admin user. username: matt password: confirm password: email: matt@0x2142.local Setup test certificates? (y/n): y -- output omitted -- ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter \u0026#39;.\u0026#39;, the field will be left blank. ----- Country Name (2 letter code) [AU]: US State or Province Name (full name) [Some-State]: Locality Name (eg, city) []: Organization Name (eg, company) [Internet Widgits Pty Ltd]: Organizational Unit Name (eg, section) []: Common Name (e.g. server FQDN or YOUR name) []: Email Address []: Certificates generated... Building docker containers... Once all of the above info is provided, the script will build the YANG Suite contianer images \u0026amp; launch them. However, by default it will launch in the foreground - so we can kill the script with Ctrl-C \u0026amp; relaunch the containers with docker-compose up -d.\nAllow Access from Remote IPs We may need to make a quick change depending on how we\u0026rsquo;ll be accessing YANG Suite.\nBy default, YANG Suite will only permit access to the web UI from localhost - so if you\u0026rsquo;re running Docker on your local PC, then no change is necessary.\nHowever, if you\u0026rsquo;re running Docker on a dedicated VM like I am, then we\u0026rsquo;ll need to allow external IPs to access the YANG Suite UI. This is controlled by the DJANGO_ALLOWED_HOSTS environment variable that gets supplied to the container.\nAs long as we\u0026rsquo;ve already run the setup script once - our environment variables will be saved to yangsuite/docker/yangsuite/setup.env:\nDJANGO_SETTINGS_MODULE=yangsuite.settings.production MEDIA_ROOT=/ys-data/ STATIC_ROOT=/ys-static/ DJANGO_STATIC_ROOT=/ys-static/ DJANGO_ALLOWED_HOSTS=localhost YS_ADMIN_USER=\u0026lt;removed\u0026gt; YS_ADMIN_PASS=\u0026lt;removed\u0026gt; YS_ADMIN_EMAIL=\u0026lt;removed\u0026gt; We\u0026rsquo;ll need to change DJANGO_ALLOWED_HOSTS=localhost to the IP address of the Docker host. So if your Docker host is reachable at 192.168.1.1, then we\u0026rsquo;ll set: DJANGO_ALLOWED_HOSTS=192.168.1.1.\nAfter which, we can bring up the containers again with docker-compose up -d:\nmatt@yangsuite:~/yangsuite/docker$ docker-compose up -d Recreating docker-yangsuite-1 ... done Recreating docker-backup-1 ... done Recreating docker-nginx-1 ... done Once everything is running, the web UI can be reached at https://\u0026lt;ip address\u0026gt;:8443.\nLoading YANG Models After we get logged in, we\u0026rsquo;ll need to load the appropriate YANG models for whatever devices we\u0026rsquo;re using.\nFor example, I\u0026rsquo;ll be using Cisco IOS-XE devices running version 17.6.x code - so I\u0026rsquo;ll need to download the YANG models from: https://github.com/YangModels/yang/tree/main/vendor/cisco/xe/1761.\nWithin YANG Suite, we\u0026rsquo;ll navigate to Setup \u0026gt; YANG files and repositories. Then click the button to add a New repository:\nYou can name the repo whatever you like. In my case, I named it ios-xe-1761 which describes the models it will hold.\nNext we can tell YANG Suite where we want to pull our models from. There are a couple of options, including a direct upload from your local PC or querying models directly from a device - but I\u0026rsquo;ll choose Git.\nAfter which, we\u0026rsquo;ll need to fill in the following info:\nThe Repository URL will be the base Git repo that we\u0026rsquo;re pulling from. In this case, that\u0026rsquo;s https://github.com/YangModels/yang.git.\nWe\u0026rsquo;ll also need to specify which Git branch to pull from, which for the YangModels repo will be main.\nThen we\u0026rsquo;ll specify which directory to import. The YangModels repository holds models for a large number of vendors \u0026amp; devices - so we only want to pull in what is necessary. In my case, I\u0026rsquo;ve set this to vendor/cisco/xe/1761 - which will only pull in the YANG models for Cisco IOS-XE 17.6.x devices. I\u0026rsquo;ve also checked the box for Include subdirectories.\nOnce that\u0026rsquo;s all set, we can hit Import YANG files. Depending on how many models are being downloaded \u0026amp; how quick your Docker host is, this may take a few minutes.\nAfter it finishes, mine displayed a message showing that 827 models had been added:\nSelecting YANG Module Sets Once we have all of our models downloaded into YANG Suite, it\u0026rsquo;s time to set up some module sets. You can think of these as filters for the YANG models.\nFor example, later on we\u0026rsquo;ll show how to change a wireless PSK with NETCONF. So since I already know what type of information I\u0026rsquo;ll be working with, I can create a module set to only contain those models.\nYANG Suite will require at least one module set to be configured, even if you don\u0026rsquo;t want to filter anything out.\nTo do this, we\u0026rsquo;ll navigate to Setup \u0026gt; YANG model sets. Then click New YANG set:\nAgain, since we\u0026rsquo;ll be working with wireless data in a bit, I\u0026rsquo;ll name my model set wireless. We\u0026rsquo;ll also have to select which YANG repository this will pull from, which will be ios-xe-1761.\nNext, we\u0026rsquo;ll get to select which YANG models we want to include in our module set. To make things easy, I\u0026rsquo;ll filter the models by wireless and then click Add selected.\nOnce those are added, you\u0026rsquo;ll likely see an error about missing dependencies. Some of the models we selected may have sub-models or data types that are located in other models.\nIn order to quickly add these, YANG Suite provides a one-click-button to Locate and add missing dependencies:\nThat\u0026rsquo;s it - our model set is ready to use.\nAdding Devices Okay, so now that we have all of our necessary YANG models set up, let\u0026rsquo;s take a add a device that we can use for testing. I\u0026rsquo;ll be adding a Catalyst 9800 Wireless controller.\nTo do this, we\u0026rsquo;ll head over to Setup \u0026gt; Device Profiles. Then click on Create new device.\nHere we\u0026rsquo;ll fill in a name for our device profile, along with the device\u0026rsquo;s IP/FQDN and login credentials.\nScrolling down a bit, we\u0026rsquo;ll also need to make sure we enable the appropriate management protocols. So I\u0026rsquo;ll be checking the box for both NETCONF and RESTCONF, since I have both enabled. I\u0026rsquo;ll also add SSH, which isn\u0026rsquo;t shown in the screenshot below.\nPlease note, you\u0026rsquo;ll likely want to check the box for Skip SSH key validation for this device under the NETCONF settings.\nAfter that\u0026rsquo;s all done, we can click Create Profile at the bottom.\nThen, to validate everything works - we\u0026rsquo;ll select our device \u0026amp; click the button for Check selected device\u0026rsquo;s reachability.\nWith any luck, you should see a similar result as shown below:\nExample: Changing Wireless PSK Alright, now let\u0026rsquo;s take a look at how we might use YANG Suite to test NETCONF / RESTCONF calls to a wireless controller.\nIn this example, perhaps we\u0026rsquo;re developing automation to rotate the guest wireless network password on a regular basis. We need to figure out how specifically to accomplish that by finding the right YANG models \u0026amp; understanding what data is sent and received with those calls.\nWe\u0026rsquo;ll explore how to do this with both NETCONF and RESTCONF.\nVia NETCONF To start off, we\u0026rsquo;ll navigate to Protocols \u0026gt; NETCONF. On this page, we\u0026rsquo;ll have full access to explore models, build XML payloads, and test them against our configured device.\nGet-config First thing we\u0026rsquo;ll need is to select our YANG set \u0026amp; Modules. In my case, the YANG set will be wireless, and I\u0026rsquo;ll select the module: Cisco-IOS-XE-wireless-wlan-cfg.\nThen click the button to Load Modules:\nBy default, our NETCONF Operation will be set to get-config, which will work for now since we only want to retrieve configuration and not change anything yet.\nUnder the Nodes pane, we can expand the tree a bit \u0026amp; dig in to find which call we need.\nFor our example, we\u0026rsquo;ll need to examine the current configured WLAN profiles. So we\u0026rsquo;ll check the box next to wlan-cfg-entries:\nThis will retrieve all configured WLAN profiles on the controller. For now, let\u0026rsquo;s do that so we can figure out what data we\u0026rsquo;ll get back.\nWe can now click the button to Build RPC, and YANG Suite will automatically build the appropriate XML payload for us:\nYou\u0026rsquo;ll also notice that I selected the catalyst 9800 as the target device. Let\u0026rsquo;s go ahead and run this with the Run RPC(s) button, which should pop open a new window \u0026amp; establish a connection to our device.\nWe can see in the response above, that we have two WLAN profiles configured: test-lab-network and test-guest-network. For the purposes of this demo, I have opted to have the guest network PSK stored in plain text, so we can validate our changes more easily.\nOkay, so what if we wanted to refine our query XML to only retrieve data on the test-guest-network? Maybe we also want to filter our the additional information returned, and only see the current PSK?\nBack on the payload editor, we can expand wlan-cfg-entries and input our desired profile-name:\nWe\u0026rsquo;ll also find the psk item further down in the list. Now we want to pull back this field, regardless of what the PSK is currently set to. So we\u0026rsquo;ll click the value field, but leave it blank:\nOnce we click Build RPC, we can see our payload now shows the additional XML filters asking only for a single profile \u0026amp; it\u0026rsquo;s associated PSK:\nIf we run that RPC call, we\u0026rsquo;ll now see that we only get back a limited set of data:\nNow we know exactly what payload to send if we want to get back the current configured PSK for any specific WLAN profile.\nLet\u0026rsquo;s try changing it!\nEdit-config Lucky for us, we\u0026rsquo;re already nearly set up for that. We\u0026rsquo;ll need to make two quick changes. First, set the NETCONF Operation to edit-config:\nThen, under the YANG tree, we\u0026rsquo;ll set a new PSK:\nAgain, we\u0026rsquo;ll click Build RPC - and take a quick look at the proposed change:\nLook good to you? Okay, let\u0026rsquo;s send it:\nSure enough, the new configuration was sent to the wireless controller and we got a response of: ok.\nBut of course, we can check quickly by switching our operation back to get-config and re-running our previous XML payload:\nAuto-generated snippets Well if you\u0026rsquo;re new to NETCONF, you might be asking yourself \u0026ldquo;Okay great, but how do I get this into Python?!?!\u0026rdquo;\nYANG Suite has some built in tools to auto-generate Python code or Ansible playbooks, which can help us get started on our code more quickly.\nTo generate these, click the Replays button:\nFor example, I selected to auto-generate a Python script. Here\u0026rsquo;s what that looks like:\n#! /usr/bin/env python import traceback import lxml.etree as et from argparse import ArgumentParser from ncclient import manager from ncclient.operations import RPCError payload = [ \u0026#39;\u0026#39;\u0026#39; \u0026lt;get-config xmlns=\u0026#34;urn:ietf:params:xml:ns:netconf:base:1.0\u0026#34;\u0026gt; \u0026lt;source\u0026gt; \u0026lt;running/\u0026gt; \u0026lt;/source\u0026gt; \u0026lt;filter\u0026gt; \u0026lt;wlan-cfg-data xmlns=\u0026#34;http://cisco.com/ns/yang/Cisco-IOS-XE-wireless-wlan-cfg\u0026#34;\u0026gt; \u0026lt;wlan-cfg-entries\u0026gt; \u0026lt;wlan-cfg-entry\u0026gt; \u0026lt;profile-name\u0026gt;test-guest-network\u0026lt;/profile-name\u0026gt; \u0026lt;psk/\u0026gt; \u0026lt;/wlan-cfg-entry\u0026gt; \u0026lt;/wlan-cfg-entries\u0026gt; \u0026lt;/wlan-cfg-data\u0026gt; \u0026lt;/filter\u0026gt; \u0026lt;/get-config\u0026gt; \u0026#39;\u0026#39;\u0026#39;, ] if __name__ == \u0026#39;__main__\u0026#39;: parser = ArgumentParser(description=\u0026#39;Usage:\u0026#39;) # script arguments parser.add_argument(\u0026#39;-a\u0026#39;, \u0026#39;--host\u0026#39;, type=str, required=True, help=\u0026#34;Device IP address or Hostname\u0026#34;) parser.add_argument(\u0026#39;-u\u0026#39;, \u0026#39;--username\u0026#39;, type=str, required=True, help=\u0026#34;Device Username (netconf agent username)\u0026#34;) parser.add_argument(\u0026#39;-p\u0026#39;, \u0026#39;--password\u0026#39;, type=str, required=True, help=\u0026#34;Device Password (netconf agent password)\u0026#34;) parser.add_argument(\u0026#39;--port\u0026#39;, type=int, default=830, help=\u0026#34;Netconf agent port\u0026#34;) args = parser.parse_args() # connect to netconf agent with manager.connect(host=args.host, port=args.port, username=args.username, password=args.password, timeout=90, hostkey_verify=False, device_params={\u0026#39;name\u0026#39;: \u0026#39;csr\u0026#39;}) as m: # execute netconf operation for rpc in payload: try: response = m.dispatch(et.fromstring(rpc)) data = response.xml except RPCError as e: data = e.xml pass except Exception as e: traceback.print_exc() exit(1) # beautify output if et.iselement(data): data = et.tostring(data, pretty_print=True).decode() try: out = et.tostring( et.fromstring(data.encode(\u0026#39;utf-8\u0026#39;)), pretty_print=True ).decode() except Exception as e: traceback.print_exc() exit(1) print(out) Via RESTCONF What if we prefer working with REST \u0026amp; JSON instead of XML? This time we\u0026rsquo;ll navigate to Protocols \u0026gt; RESTCONF.\nHere, we\u0026rsquo;ll select our YANG set \u0026amp; modules again. In case you skipped the NETCONF section, we\u0026rsquo;re working with the wireless YANG set \u0026amp; the Cisco-IOS-XE-wireless-wlan-cfg module.\nWe\u0026rsquo;ll also select a device to use, then click Load Module(s):\nAgain, we\u0026rsquo;ll see a similar structure to the NETCONF side. But this time, we\u0026rsquo;ll utilize it quite a bit differently.\nIn the screenshot above, I\u0026rsquo;ve already selected the wlan-cfg-entries entry. Next, we\u0026rsquo;ll click the button to Generate API(s):\nWe\u0026rsquo;ll see auto-generated API documentation for all of the available calls. This list will contain every variation possible, so it can be quite long. Since we already went through the NETCONF method, we know what data we need to find / use which should make this easier.\nTo start with, I\u0026rsquo;ll expand the GET for /data/Cisco-IOS-XE-wireless-wlan-cfg:wlan-cfg-data/wlan-cfg-entries. We\u0026rsquo;ll have the ability to test the RESTCONF calls directly from this interface as well by clicking on Try it out:\nIn the screenshot above, I\u0026rsquo;ve already submitted the test request \u0026amp; gotten a response from the controller. While the screenshot is cut off a bit, we can see both of our wireless profile names.\nIf we wanted to see only the PSK, like we did with our previous example - we could scroll down quite a bit to find the GET request for /data/Cisco-IOS-XE-wireless-wlan-cfg:wlan-cfg-data/wlan-cfg-entries/wlan-cfg-entry={wlan-cfg-entry-profile-name}/psk\nIn the screenshot below, I\u0026rsquo;ve already input our test-guest-network profile name \u0026amp; executed the API call:\nSure enough, we see the PSK that we had just configured via NETCONF a few minutes ago.\nNow let\u0026rsquo;s change it. We\u0026rsquo;ll expand the PATCH API call for the same endpoint, and fill in the request body with our new PSK:\nExecuting that call, the wireless controller returned a status code of 204.\nSo let\u0026rsquo;s check again to see if that updated properly:\nNow that we have all the appropriate REST calls, it should be much easier to start developing our automation.\nOkay, I think that\u0026rsquo;s about all I wanted to cover in this post. I\u0026rsquo;ve seen a lot of people struggle with NETCONF much like I used to, and I think the real key to getting familiar is just having the right tools \u0026amp; spending enough time with it. YANG Suite has really helped me get more familiar with the various model structures and how to use them.\nHopefully this information helps others to get started. NETCONF \u0026amp; YANG are certainly big hills to climb, but the right tools can make all the difference.\n","permalink":"https://0x2142.com/getting-started-with-cisco-yang-suite/","summary":"An introduction to setting up Cisco YANG Suite for exploring and testing network device automation via NETCONF \u0026amp; RESTCONF - including an example of automating wireless pre-shared key rotation.","title":"Getting Started with Cisco YANG Suite"},{"content":" In this post - we\u0026rsquo;ll take a look at how to set up \u0026amp; configure AdGuard Home on OPNsense.\nPlease note that the AdGuard Home plugin for OPNsense is a community built plugin, and not officially supported by OPNsense.\nWhat\u0026rsquo;s AdGuard Home? Why use it? Almost every website we visit these days is loaded with additional components for advertisements, analytics, and engagement tracking. One one side, these tools can be very helpful for the company or website owner to monetize their platform and/or track \u0026amp; understand their audience\u0026rsquo;s interests.\nHowever, it\u0026rsquo;s also becoming more popular to want to avoid being tracked on every website, or reduce the amount of advertisements you see. Unfortunately, a lot of these scripts \u0026amp; code snippets are automatically embedded in websites and most don\u0026rsquo;t allow you to opt-out.\nA while back, there were a few browser extensions that became popular by automatically blocking the advertisement \u0026amp; tracking elements from loading. These were great (and still are!), but a lot of website owners have been fighting it \u0026amp; making it harder to block their content. In addition, these types of extensions operate at your web browser level - meaning that your computer has already made a few calls out to the internet before the extension even has a chance to block something.\nHere\u0026rsquo;s where we\u0026rsquo;ve started to see more ad blockers come out that operate at the network level. AdGuard Home is one of them, but you also may have seen similar packages like Pi-hole or NextDNS. These are typically packages that you install on your home network \u0026amp; run as a local Domain Name System (DNS) server.\nEach time your browser needs to load something from the web, the first step is figuring out what IP address to connect to. For this, the computer reaches out to it\u0026rsquo;s configured DNS server and provides the website name (like 0x2142.com). The DNS server looks up where that lives \u0026amp; provides the computer with the IP address (like 203.0.113.52). Then your computer can load the website by connecting to that address.\nWith a DNS-level blocker, like AdGuard Home, we can block your computer from ever trying to establish that connection. If you tried to go to a website (like 0x2142.com), and there was an embedded advertisement or tracking, AdGuard would tell your computer that the domain hosting the advertisement doesn\u0026rsquo;t exist (usually via returning a 0.0.0.0 or NXDOMAIN response). So your browser would still be able to load the main site (0x2142.com), but it would never even try to establish a connection to the advertisement or tracking components.\nSo we gain a few benefits here - the big ones being some level of privacy \u0026amp; reduced advertisement noise when browsing the web. But also since we block so much of that noise early in the process, your computer never has the opportunity to load that content - meaning that we also save on bandwidth usage \u0026amp; data costs. There may also be small performance improvements since each site has less content that needs to be loaded.\nThe other bonus worth considering is security. There are quite a handful of DNS blocklists that are constantly updated with the latest malicious or suspicious domains. The quicker we can block \u0026amp; stop clients from potentially connecting to those domains, the better off we are!\nIs there a down side? Yeah, of course there is! A lot of these DNS-level blockers pull from varying website blocklists - which are not always 100% accurate. So sometimes you may still see advertisements or get tracked. It\u0026rsquo;s not a perfect system. In addition, you may also (and sometimes often) see the reverse - parts of websites being blocked that are legitimate. And there are quite a handful of websites these days that won\u0026rsquo;t work correctly unless they can load 3rd party components. Most of the time everything will be fine, but just be aware that there may be some time spent troubleshooting \u0026amp; manually unblocking website components.\nDo I have to install this on OPNsense? Nope. AdGuard Home has a number of packages \u0026amp; ways to get running. Check out their GitHub repo.\nIf you\u0026rsquo;re already running OPNsense, it\u0026rsquo;s easy to install this as an add-on package \u0026amp; not have another system to manage. However, if you prefer to set up AdGuard (or Pi-hole, or others) elsewhere, that\u0026rsquo;s fine too. You\u0026rsquo;ll just need to update your client network\u0026rsquo;s DHCP options to use the new DNS servers. See the last section below on how to do that.\nOkay - Let\u0026rsquo;s get started with setting this up!\nTopology For the purposes of this walkthrough, we\u0026rsquo;ll be using a fairly simple \u0026amp; straightforward topology. A single OPNsense appliance connected to the internet via it\u0026rsquo;s WAN port, as well as a single client PC connected via the LAN port.\nIn this setup, the OPNsense appliance is configured to provide IP address \u0026amp; DNS information to our client PCs via DHCP.\nAdding the Community Repository to OPNsense So by default, AdGuard Home is not included in the available plugins to download/install in OPNsense. However, someone built a community plugin repository that includes a small handful of additional packages.\nBefore we can install the AdGuard Home plugin, we will need to setup \u0026amp; install that community repository.\nTo do this, we\u0026rsquo;ll need direct SSH or console access to our OPNsense appliance.\nSSH is disabled by default, but we can enable it quickly by navigating to System \u0026gt; Settings \u0026gt; Administration and then scrolling down to the Secure Shell section.\nWe\u0026rsquo;ll need to check the box for Enable Secure Shell and Permit Password Login. If you\u0026rsquo;re logging into OPNsense with the root account, you\u0026rsquo;ll also need to select Permit root user login.\nThen scroll down to the bottom of the page \u0026amp; click Save.\nNote: By default OPNsense will also have the SSH Listen Interface set to All. I would highly recommend setting this to only enable on your LAN interface Also: If you don\u0026rsquo;t need SSH access all the time, please remember to disable this service once you\u0026rsquo;re finished setting this up!\nOkay, now that\u0026rsquo;s enabled - we can connect to our OPNsense appliance using your preferred SSH client (like PuTTY).\nIf you\u0026rsquo;re using the root account, you\u0026rsquo;ll likely be dropped into the OPNsense shell - but you can select option 8 here to access the underlying FreeBSD shell.\nIn order to install the community repository, we\u0026rsquo;ll pull down the repository config file using the following command:\nfetch -o /usr/local/etc/pkg/repos/mimugmail.conf https://www.routerperformance.net/mimugmail.conf Then, we\u0026rsquo;ll need to ask OPNsense to update it\u0026rsquo;s local cache with the new repo - so it knows what packages are hosted there:\npkg update If everything is successful, you\u0026rsquo;ll see output similar to below - which lists the mimugmail repository now:\nroot@0xOPNsense:/home/matt # pkg update Updating OPNsense repository catalogue... Fetching meta.conf: 100% 163 B 0.2kB/s 00:01 Fetching packagesite.pkg: 100% 229 KiB 234.3kB/s 00:01 Processing entries: 100% OPNsense repository update completed. 822 packages processed. Updating mimugmail repository catalogue... Fetching meta.conf: 100% 163 B 0.2kB/s 00:01 Fetching packagesite.pkg: 100% 54 KiB 54.8kB/s 00:01 Processing entries: 100% mimugmail repository update completed. 177 packages processed. All repositories are up to date. Installing the AdGuard Home Package Now that the additional package repository is set up, we can download \u0026amp; install the AdGuard Home plugin via the OPNsense web interface.\nSo back in our browser, we can nagivate to: System \u0026gt; Firmware \u0026gt; Plugins. On this page we can search for adguard or scroll through the list to find it.\nThen we just click the plus icon on the right side to install (not shown in the screenshot above).\nThis should install pretty quickly:\n***GOT REQUEST TO INSTALL*** Currently running OPNsense 22.7.9 (amd64/OpenSSL) at Sun Dec 4 12:48:38 EST 2022 Updating OPNsense repository catalogue... OPNsense repository is up to date. Updating mimugmail repository catalogue... mimugmail repository is up to date. All repositories are up to date. The following 1 package(s) will be affected (of 0 checked): New packages to be INSTALLED: os-adguardhome-maxit: 1.8 [mimugmail] Number of packages to be installed: 1 The process will require 35 MiB more space. 7 MiB to be downloaded. [1/1] Fetching os-adguardhome-maxit-1.8.pkg: .......... done Checking integrity... done (0 conflicting) [1/1] Installing os-adguardhome-maxit-1.8... [1/1] Extracting os-adguardhome-maxit-1.8: .......... done Stopping configd...done Starting configd. Migrated OPNsense\\Adguardhome\\General from 0.0.0 to 0.0.1 Reloading plugin configuration Configuring system logging...done. Reloading template OPNsense/Adguardhome: OK Checking integrity... done (0 conflicting) Nothing to do. ***DONE*** Now all we have to do is enable the plugin.\nSo we\u0026rsquo;ll navigate down to Services \u0026gt; Adguardhome \u0026gt; General. Our only option here will be an Enable checkbox, so we\u0026rsquo;ll select that \u0026amp; Save.\nThe rest of the setup \u0026amp; initial configuration will be done directly from the AdGuard-specific web interface.\nInitial Setup By default, the AdGuard Home web interface will run on port 3000 \u0026amp; is not HTTPS-enabled. So if your OPNsense firewall is at https://192.168.1.1, you\u0026rsquo;ll need to connect to http://192.168.1.1:3000.\nAs long as that works - we\u0026rsquo;ll see the initial setup prompt below:\nWe\u0026rsquo;ll click on Get Started.\nNow we\u0026rsquo;ll be asked to configure the Admin Web interface (the interface we\u0026rsquo;re connected to now) and the DNS server interface (which clients will use to resolve domain names).\nBy default, AdGuard home will try to set both of these to listen on All interfaces - and set the web on port 80 \u0026amp; DNS on port 53.\nI would recommend setting the Listen Interface on both of these to only your LAN-side networks. There is no reason to enable them on your WAN, and it can be a security risk to do so.\nYou may also get warnings that port 80 \u0026amp; 53 may already be in use. For the web interface, we could change 80 to 3000 \u0026amp; just keep what we\u0026rsquo;re using now.\nHowever, if we change the default DNS port, that will cause some additional problems since client machines will query port 53. Likely if port 53 is already in use, it\u0026rsquo;s because another service on OPNsense (like Unbound DNS) is already enabled. In my case, I disabled this in favor of using AdGuard. However, if you want to use both - you can change the default DNS port in AdGuard to something like 65353, then have Unbound forward requests to AdGuard (More on this down below).\nSo here\u0026rsquo;s what my set up looks like so far, with 192.168.1.1 being my LAN side interface:\nOn the next page, we\u0026rsquo;ll be prompted to set up an administrative user \u0026amp; password for logging into AdGuard.\nNext we\u0026rsquo;ll be given instructions on how to set up client devices. In my lab network, the OPNsense firewall is providing DNS server configuration via DHCP - so we\u0026rsquo;ll get to that configuration shortly.\nFor now, we\u0026rsquo;ll just click Next.\nOn the last screen, we\u0026rsquo;ll just get a message saying that setup is complete \u0026amp; a link to open the dashboard:\nAnd now we can log in:\nAdGuard Home Configuration After logging in, the first thing we\u0026rsquo;ll see is a pretty empty dashboard. We don\u0026rsquo;t have any clients configured to use this yet, so there isn\u0026rsquo;t anything to report on.\nBlocking Domains First thing we\u0026rsquo;ll look at is our DNS blocklists. We\u0026rsquo;ll navigate to Filters \u0026gt; DNS blocklists.\nHere is where we can ask AdGuard to query lists of what domains to block. By default, AdGuard does include two - but we can add more if we want:\nIf we want to add to the configured blocklists, we can do so by clicking the Add Blocklist button. This will prompt us whether we want to choose from a pre-populated list, or supply our own custom list:\nThe easy option will be selecting from the provided lists:\nThere are a ton of different curated block lists available depending on what you\u0026rsquo;re trying to block. If we wanted to use a custom list, a lot can be found on GitHub just by searching for PiHole or Adguard blocklists.\nHow to pick a blocklist will be up to you. There are blocklists that focus on advertisements, tracking \u0026amp; analytics, parental controls, etc. So it just depends on what areas you want to focus on.\nAllowing Domains \u0026amp; Custom Filtering If we have a list of known services that we want to ensure are never blocked, we can pull those lists via Filters \u0026gt; DNS allowlists. However, it\u0026rsquo;s more likely you\u0026rsquo;ll find a handful of domains you want to unblock, rather than a whole list.\nFor that - we can go to Filters \u0026gt; Custom filtering rules. At the bottom of this page there is a tool to check filtering, where we can enter a domain name \u0026amp; instantly see what the result is.\nFor example, with the default ruleset I\u0026rsquo;ll check to see if 0x2142.com is filtered:\nSo by default that domain isn\u0026rsquo;t found anywhere, so it will be permitted. The tool also gives us a convenient button to quickly block a domain.\nWe can click that button, or add the syntax ||0x2142.com^ to the custom filtering rules at the top of the page (and saving via the Apply button). Now if we check the results again - the filter check will show the domain is blocked:\nAnd of course, we don\u0026rsquo;t want to block 0x2142.com!! So let\u0026rsquo;s add this to our allowlist instead, so that it can never be blocked 🙃. We can do that by adding @@||0x2142.com^ to the custom filtering.\nAnd now we\u0026rsquo;ll see a green box that shows that the domain is permitted via an allowlist:\nBlocking Known Services The other option worth mentioning is the ability to block certain known services, like WhatsApp, Twitter, Reddit, etc. This can be great if there are certain services you want to block, or for use as parental controls.\nThis can be found on the Filters \u0026gt; Blocked Services page.\nThis way we can select a service to block, rather than having to know all of the individual domains that service uses. For example, I\u0026rsquo;ll go ahead and select YouTube to block - and we\u0026rsquo;ll check that later on after we configure our clients.\nConfigure OPNsense DHCP to use AdGuard Now that we\u0026rsquo;ve taken a quick look at the AdGuard Home settings \u0026amp; have a few things configured - let\u0026rsquo;s look at setting up our clients to use our new DNS server.\nIn the lab environment I\u0026rsquo;m using, the OPNsense appliance is providing client IP address configuration via Dynamic Host Configuration Protocol (DHCP).\nBy default, if a specific DNS server is not configured for your client DHCP settings, then OPNsense will provide the clients with the same DNS server it uses. This could have been a DNS server that was configured when you set up OPNsense, or it also can use DNS servers that are provided by your internet service provider.\nSo to update our LAN DHCP configuration, we\u0026rsquo;ll head back to our OPNsense web interface. From there, we\u0026rsquo;ll navigate to Services \u0026gt; DHCPv4 \u0026gt; [LAN].\nIn the configuration, there is an open option for DNS Servers. We\u0026rsquo;ll set this to our OPNsense LAN IP address. In my case, that is 192.168.1.1. Then scroll to the bottom of the page \u0026amp; click Save.\nClient Testing Now we should be all set up! However, it\u0026rsquo;s important to note that because of the way DHCP works, clients may not pick up the new configuration immediately. When DHCP assigns an IP address, it also tells the client how long it can use that address for. So if a client stays powered-on \u0026amp; connected, it won\u0026rsquo;t ask for new configuration until that timer expires.\nWe can speed that up by resetting the network interface on our clients. This can be done in a number of ways including rebooting the client or simply disconnecting from wifi/ethernet \u0026amp; reconnecting.\nI\u0026rsquo;m using a Linux computer as my test system, so first I\u0026rsquo;ll check via the nslookup command - which will query our configured DNS server \u0026amp; return the resolved IP addresses.\nIf you remember, I blocked all of YouTube\u0026rsquo;s services earlier:\nAs we can see, we did get the correct IP addresses - which means our filtering isn\u0026rsquo;t working yet.\nI\u0026rsquo;ll reset the network adapter on the test PC, which will refresh the DHCP configuration - then try again:\nNow that\u0026rsquo;s the result we want! By returning the 0.0.0.0 result, our client can no longer resolve that domain. So if this was an advertisement or tracking domain, it\u0026rsquo;s now blocked from loading.\nAnd sure enough, if we now try to browse to that site via a web browser - we don\u0026rsquo;t be able to access it:\nTroubleshooting Blocked Domains Okay, so now we know our blocking works\u0026hellip;. But now someone in our home is trying to access YouTube \u0026amp; it\u0026rsquo;s not working. How can we tell if that\u0026rsquo;s our AdGuard service?\nOur first stop might be the AdGuard query log. Opening this log, we can filter by domain name or client - or show only blocked queries if we like.\nPretty quickly we can see the issue - we blocked YouTube\u0026rsquo;s services:\nNow we know how to fix the issue, which would be to unblock that service. However, if it was just a specific domain that was blocked, we would likely want to add it to our custom filtering as we showed earlier.\nReporting Last but not least, we can also check our AdGuard Home dashboard again, which should be much more interesting than before:\nHere we can quickly see how many queries have been made \u0026amp; how many were blocked for various reasons. We\u0026rsquo;ll also see what clients are using our DNS server, and which are making the most queries.\nMost interesting (at least to me), is being able to see the top domains that were queried or blocked. Here\u0026rsquo;s where you might find some interesting information. For example, on my test machine - it\u0026rsquo;s a fresh installation of Ubuntu \u0026amp; we used FireFox to test. But we can see that even during the brief time it\u0026rsquo;s been set up, almost all of the highest queried domains belong to Mozilla\u0026rsquo;s analytics services. So it may be tempting to add those to our custom blocklists.\nAdditional Info What if I have AdGuard running on a different server? Or want to keep using Unbound DNS? Sure - we can make both of those work.\nFor the first scenario, maybe we have AdGuard Home installed \u0026amp; set up on a Raspberry Pi on our network. For that, all we need to do is set that Raspberry Pi as the DNS server in our DHCP configuration on OPNsense. See above where we did that for our on-box AdGuard setup.\nFor the other situation, perhaps you want to use Unbound on OPNsense, but also AdGuard. There might be reasons for this - like even though Unbound does support DNS blocklists, AdGuard has better reporting tools. But on the other hand, Unbound has more features \u0026amp; configuration options for DNS than Unbound.\nIn this case, we would want to run AdGuard on a different DNS port (like 65353), then have Unbound forward those to AdGuard. See below if you need to change the port AdGuard uses for DNS.\nWithin OPNsense, we could go to Services \u0026gt; Unbound DNS \u0026gt; Query Forwarding. Then add a new custom forwarding entry. Here we can forward requests for specific domains if we want - or if we want to forward all DNS requests, we can leave the domain field empty. Then fill in the AdGuard information - so in my example this would be 192.168.1.1 and port 65353.\nThen click Save and Apply!\nHow do I change the interface / port for the Web UI or DNS? So perhaps we mis-typed something when configuring AdGuard. Or just wanted to change the interface IP address AdGuard listens on. No problem!\nUnfortunately, since this is a community plugin - there is no configuration for the plugin within the OPNsense interface.\nWe\u0026rsquo;ll need to reconnect to the OPNsense command line to make some additional configuration changes. This can be done via SSH or the device console.\nOnce there, we can use the command edit /usr/local/AdGuardHome/AdGuardHome.yaml.\nThat config file looks like this:\nAt the top, bind_host \u0026amp; bind_port pertains to the admin web interface. A little below there, under the dns section - you\u0026rsquo;ll see another bind_hosts and port config. Those ones are specific to the DNS server side of things.\nOnce done, save the config file by pressing Esc then selecting to quit the editor \u0026amp; save the file.\nLastly - Go back into the OPNsense web UI \u0026amp; restart the AdGuard Home service for the changes to take effect.\n","permalink":"https://0x2142.com/how-to-set-up-adguard-on-opnsense/","summary":"In this post, we\u0026rsquo;ll walk through how to install, setup, and configure AdGuard Home on OPNsense for DNS-level ad blocking.","title":"[How To] Set up AdGuard Home on OPNsense"},{"content":" Earlier this year I started trying out Golang as a new language to learn. Most of my prior experience is with Python, with a handful of other languages sprinkled in here \u0026amp; there.\nMy experience with Go so far has been good \u0026amp; I\u0026rsquo;ve been having fun learning something new. It\u0026rsquo;s been easy enough to pick up \u0026amp; there have been quite a handful of things that I really appreciate about Go vs Python.\nOne of the best ways to learn something new is to find a good project to work on. Since I\u0026rsquo;ve done quite a bit historically with Python \u0026amp; building Webex chatbots - I figured one good way to learn Go would be trying to do something similar.\nSo in this blog post, I\u0026rsquo;ll walk through how to build a simple Discord bot using a Golang module called DiscordGo. We\u0026rsquo;ll use the same project idea as my previous Webex bot, where the bot will leverage the OpenWeather APIs to retrieve weather information.\nSince I\u0026rsquo;ve been learning Go recently, I\u0026rsquo;ll do my best to try to keep this simple - so that hopefully you can follow along if you\u0026rsquo;re also new to Go 😊.\nCode repo for the examples below can be found here\nSetting up the Project So first we\u0026rsquo;ll run through some quick setup. If you\u0026rsquo;re familiar enough with Go already, you can likely skip this step \u0026amp; just set up your project in the way you prefer.\nI\u0026rsquo;ve created a new project folder called discord-weather-bot. So first we\u0026rsquo;ll initialize our Go module using the command go mod init discord-weather-bot.\nThen, I\u0026rsquo;ll create a main.go in this folder, as well as a sub-folder named bot which will contain a bot.go file.\nOnce we\u0026rsquo;re done, our initial project layout should look like this:\nWe\u0026rsquo;ll add on to this later, but this will work to start.\nNext, we\u0026rsquo;ll need to install the DiscordGo module, which we can do with one simple command: go get github.com/bwmarrin/discordgo\nGenerating API Keys Next we\u0026rsquo;ll need to get our API keys for OpenWeather \u0026amp; Discord.\nOpenWeather is going to be the easier of the two - simply sign up for a free account here. Then jump over to your account page \u0026amp; create a new API key here. Free accounts are limited to only using their current weather API, which will be more than enough for our example.\nNext, we\u0026rsquo;ll look at the Discord side of things - which will be a few more hoops to jump though.\nFirst we\u0026rsquo;ll need to head over to the Discord Applications page \u0026amp; create a new application/integration.\nWe\u0026rsquo;ll be prompted to provide a name for our app (Note, this isn\u0026rsquo;t the name of our bot - we\u0026rsquo;ll get to that shortly):\nOnce we finish that, we\u0026rsquo;ll be taken to a page to add additional information about our app - like tags, a description, an icon, or links to app resources. If you plan to publish your app publicly, you\u0026rsquo;ll want to invest some time here. However, since we\u0026rsquo;ll just be building the bot as a personal project, we don\u0026rsquo;t need to do anything here.\nNext, we\u0026rsquo;ll want to jump down to the Bot section under Settings. This is where we can set up some basics for our bot \u0026amp; get our bot API token.\nWe\u0026rsquo;ll get a quick confirmation prompt, where we\u0026rsquo;ll click on Yes, do it!\nNow we have our bot created! We can change the name of our bot here, to be separate from our app name if we want.\nScrolling down the page a bit - by default our bot is public, which means anyone could add it to their server. I\u0026rsquo;ll switch this off for now, since this is intended to be a private example bot.\nIn order for our bot to receive \u0026amp; process message content, we\u0026rsquo;ll also need to enable the toggle for message content. Note that Discord says this is only allowed for servers under a certain size, after which they would need to be verified in order to use this intent. Since this bot will only be used as an example, we don\u0026rsquo;t need to worry about that yet - but we will still need to enable the intent:\nFor now, this is all we need to set up on this page. Before we leave, we\u0026rsquo;ll want to reset our bot access token - and save the token for later:\nLast but not least, we\u0026rsquo;ll need to add our Discord bot to a server. I have a server in Discord that I use for testing, and I\u0026rsquo;ve created a private channel to experiment with this bot.\nIn order to do this, we\u0026rsquo;ll need to create an OAuth authorization link that can be used to give the bot access.\nUnder our Settings, we\u0026rsquo;ll click on OAuth2 \u0026gt; URL Generator.\nIn the screenshot above, I\u0026rsquo;ve selected the Scope as bot. And checked off permissions for Read Messages, Send Messages, and Send Messages in Threads. For what we\u0026rsquo;ll be doing in this example, that should be more than enough.\nAt the bottom of the screenshot, you can see that Discord will auto-generate a URL which we can now use to add the bot to our Discord Server.\nIf we visit that link while signed into Discord, we\u0026rsquo;ll be prompted to add it to our server:\nWe\u0026rsquo;ll also be prompted to confirm the permissions the bot will have:\nNow our bot has been successfully added to our Discord server, but will show as offline since we haven\u0026rsquo;t built it yet! Let\u0026rsquo;s get started with that.\nBuilding the Bot - Basic Interaction So we\u0026rsquo;ll start off by building out just enough of our bot code for it to send messages to our server.\nCollecting API Keys via Environment Variables For the purpose of this project, I\u0026rsquo;ll be storing both the Discord \u0026amp; OpenWeather API keys as environment variables. So well keep our main.go simple \u0026amp; just handle importing our API keys and kicking off the bot:\n// snippet from: main.go package main import ( \u0026#34;discord-weather-bot/bot\u0026#34; \u0026#34;log\u0026#34; \u0026#34;os\u0026#34; ) func main() { // Load environment variables botToken, ok := os.LookupEnv(\u0026#34;BOT_TOKEN\u0026#34;) if !ok { log.Fatal(\u0026#34;Must set Discord token as env variable: BOT_TOKEN\u0026#34;) } openWeatherToken, ok := os.LookupEnv(\u0026#34;OPENWEATHER_TOKEN\u0026#34;) if !ok { log.Fatal(\u0026#34;Must set Open Weather token as env variable: OPENWEATHER_TOKEN\u0026#34;) } // Start the bot bot.BotToken = botToken bot.OpenWeatherToken = openWeatherToken bot.Run() } In the code above, we\u0026rsquo;ll import os and log which we\u0026rsquo;ll use to handle importing our environment variables \u0026amp; generating errors if this fails. We\u0026rsquo;ll also import our bot folder with discord-weather-bot/bot.\nFor our main function, we\u0026rsquo;ll start by trying to find our environment variables. For each of the variables, we\u0026rsquo;ll use a format of \u0026lt;varname\u0026gt;, ok := os.LookupEnv(\u0026quot;\u0026lt;env name\u0026gt;\u0026quot;).\nUsing os.LookupEnv, we\u0026rsquo;ll try and search for the environment variable by name - so in the first example looking for BOT_TOKEN. If the environment variable exists, it will be assigned to botToken - and ok will be True. Otherwise, we\u0026rsquo;ll get no value for botToken, and ok will be False.\nIf either environment variable doesn\u0026rsquo;t exist, we\u0026rsquo;ll just print a log message reminding the user to set the variable. log.Fatal will also quit the program after displaying the log message.\nFinally, assuming we have both of those API keys - we\u0026rsquo;ll kick those keys over to the bot module \u0026amp; start the bot with bot.Run(). Let\u0026rsquo;s hop over \u0026amp; build that piece now.\nConnecting to Discord Next, we\u0026rsquo;ll start out bot code with enough to receive the variables being passed by main.go:\n// snippet from: bot/bot.go package bot import ( \u0026#34;fmt\u0026#34; \u0026#34;log\u0026#34; \u0026#34;os\u0026#34; \u0026#34;os/signal\u0026#34; \u0026#34;strings\u0026#34; \u0026#34;github.com/bwmarrin/discordgo\u0026#34; ) var ( OpenWeatherToken string BotToken string ) func Run() { // Not implemented yet } In the snippet above, we\u0026rsquo;ve defined two variables - OpenWeatherToken and BotToken. Note that both start with a capital letter, meaning that they have been exported \u0026amp; available to code outside this module - which is how we can update these values via main.go by setting bot.BotToken = botToken.\nOkay - Let\u0026rsquo;s get started with connecting our bot to Discord. We\u0026rsquo;ll update our Run function to the following:\n// snippet from: bot/bot.go func Run() { // Create new Discord Session discord, err := discordgo.New(\u0026#34;Bot \u0026#34; + BotToken) if err != nil { log.Fatal(err) } // Add event handler discord.AddHandler(newMessage) // Open session discord.Open() defer discord.Close() // Run until code is terminated fmt.Println(\u0026#34;Bot running...\u0026#34;) c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) \u0026lt;-c } To begin with, we create a new discord session with discordgo.New() \u0026amp; passing our BotToken. Discord requires an HTTP Authorization header that contains Bot \u0026lt;token\u0026gt; or Bearer \u0026lt;token\u0026gt; for OAuth. The discordgo.New() function is just setting this header for us.\nNext we\u0026rsquo;ll register an event handler to our Discord client. In a moment, we\u0026rsquo;ll create a new function called newMessage that will receive \u0026amp; process any new messages that are created in our channel. More to come in just a moment!\nNow we can open our websocket tunnel to Discord via discord.Open(). If you\u0026rsquo;re not familiar with websockets, I wrote a little about them when I built a Webex bot in this post. We\u0026rsquo;ll also include a defer discord.Close(), which will ensure that we gracefully close the Discord session whenever the Run() function exits.\nLastly, we\u0026rsquo;ll use Go\u0026rsquo;s os/signal package to listen for any interrupt/process kill signals. This will allow our Discord bot to run in the background until we stop it. We do this by creating a new channel of type os.Signal where we will listen for termination signals. Then using signal.Notify, we tell the signal package where to send termination signals (our c channel) - and what signals we want to listen for (os.Interrupt). Then, using \u0026lt;-c, our program will hold until something is received on the channel.\nAn operator like \u0026lt;- is typically used for concurrency, where we have one function passing data to another via a channel. The \u0026lt;- or -\u0026gt; operator indicates which direction that data is being sent. So in this example, we\u0026rsquo;re receiving data from channel c using the statement \u0026lt;-c. Now the tricky thing here, is that we\u0026rsquo;re not assigning that input to another variable (for example, incomingSignal \u0026lt;- c), since we don\u0026rsquo;t care what signal was sent - just that there was a signal received.\nAfter we receive any termination signal, the code will continue to the end of the Run() function. This will then automatically execute our defer discord.Close() from earlier, which will tear down our Discord session.\nProcessing Incoming Messages Okay, now that we have our Discord session establishment handled, let\u0026rsquo;s build out our newMessage() function to handle receiving messages.\nSo for now, our code for this function will look like this:\n// snippet from: bot/bot.go func newMessage(discord *discordgo.Session, message *discordgo.MessageCreate) { // Ignore bot messaage if message.Author.ID == discord.State.User.ID { return } // Respond to messages switch { case strings.Contains(message.Content, \u0026#34;weather\u0026#34;): discord.ChannelMessageSend(message.ChannelID, \u0026#34;I can help with that!\u0026#34;) case strings.Contains(message.Content, \u0026#34;bot\u0026#34;): discord.ChannelMessageSend(message.ChannelID, \u0026#34;Hi there!\u0026#34;) } } Let\u0026rsquo;s take a quick look at the code above.\nThe newMessage() function is our event handler that we are registering with our Discord client. It will need to receive two values - a pointer to our Discord session (*discordgo.Session), and the type of event we\u0026rsquo;re listening for. In this case we\u0026rsquo;re listening for new message creation events (*discordgo.MessageCreate). A full list of events that we could listen for are listed in the Discord Gateway Documentation.\nFirst thing we\u0026rsquo;ll do when processing the message is ensure that the bot ignores any message from itself. We wouldn\u0026rsquo;t want our bot creating a loop by responding to it\u0026rsquo;s own messages, which then generates a new message to respond to!\nWe accomplish this by checking the incoming message\u0026rsquo;s Author ID (message.Author.ID) against our current Discord session\u0026rsquo;s User ID (discord.State.User.ID). If these values match, then the incoming message was created by our bot code - so we\u0026rsquo;ll just return and stop execution of this function.\nIf the incoming message is from another user, then we can go ahead and process it. There are a number of ways to do this depending on what you want to achieve. To keep things simple, I am using a quick switch statement to evaluate the message content.\nOur bot then is listening for two keywords - \u0026ldquo;weather\u0026rdquo; and \u0026ldquo;bot\u0026rdquo;. If the incoming message content contains either of those two words, our bot will respond with a message by using discord.ChannelMessageSend - and passing the channel ID from the incoming message (message.ChannelID) and our message.\nA quick note: I used a switch/case here since it would be easier to add onto than a long list of if/else\u0026hellip; but the example code here still isn\u0026rsquo;t perfect. For instance, our second case is only looking for someone using the word bot. However this is a simple match, and would still catch on someone talking about a robot or a bottle - since both contain bot in them. If this was a production/public bot, we would want to clean that up a bit.\nTesting Okay, with that we can now give our bot a quick test.\nLet\u0026rsquo;s go ahead and start our bot with go run main.go. Don\u0026rsquo;t forget to set your BOT_TOKEN \u0026amp; OPENWEATHER_TOKEN environment variables first! (Or comment out the code for the OPENWEATHER_TOKEN, since we\u0026rsquo;re not using it just yet)\nHere\u0026rsquo;s my test run:\nAs we can see, the bot responded just as we expected!\nAnother fun thing to point out, is that since we\u0026rsquo;re using websockets for the connection (aka Discord Gateway) - our bot can also register presence events. So as long as our bot is connected, it will actually show as online:\nAdding Basic Bot Commands So far we\u0026rsquo;ve gotten our bot connected to Discord, and responding to regular chat messages. Now we\u0026rsquo;ll focus on actually adding functionality to our bot by leveraging the OpenWeather API.\nFor now, we\u0026rsquo;ll implement this with very simple command matching. In a future blog post, I\u0026rsquo;ll be showing how to accomplish this with Discord slash commands.\nSo first we\u0026rsquo;ll modify our existing switch statement in the newMessage() function. We\u0026rsquo;ll provide a little help text with our response to someone mentioning weather - and create a new command by catching messages with !zip:\n// snippet from: bot/bot.go // Respond to messages switch { case strings.Contains(message.Content, \u0026#34;weather\u0026#34;): discord.ChannelMessageSend(message.ChannelID, \u0026#34;I can help with that! Use \u0026#39;!zip \u0026lt;zip code\u0026gt;\u0026#39;\u0026#34;) case strings.Contains(message.Content, \u0026#34;bot\u0026#34;): discord.ChannelMessageSend(message.ChannelID, \u0026#34;Hi there!\u0026#34;) case strings.Contains(message.Content, \u0026#34;!zip\u0026#34;): currentWeather := getCurrentWeather(message.Content) discord.ChannelMessageSendComplex(message.ChannelID, currentWeather) } Under the !zip command, we\u0026rsquo;ll have a function called getCurrentWeather() which will return a Discord message. However, in this case we\u0026rsquo;ll change things up a little - and use an embedded message so we can include some formatting. This also means that we\u0026rsquo;ll change our response function to discord.ChannelMessageSendComplex().\nWhere discord.ChannelMessageSend() needs the channel ID \u0026amp; a simple string message, discord.ChannelMessageSendComplex() will require data of the type discordgo.MessageSend instead of a string. In our getCurrentWeather() function, we\u0026rsquo;ll see how to assemble our response using this structure.\nQuerying Weather So to start with, we\u0026rsquo;ll need to query the OpenWeather API \u0026amp; retrieve the current weather information for a given US ZIP code.\nIn our project, I\u0026rsquo;ve created a new Go file called command-weather.go in the same bot subfolder which already contains our bot.go file. This new file will also be part of package bot.\n// snippet from: bot/command-weather.go package bot import ( \u0026#34;encoding/json\u0026#34; \u0026#34;fmt\u0026#34; \u0026#34;io/ioutil\u0026#34; \u0026#34;net/http\u0026#34; \u0026#34;regexp\u0026#34; \u0026#34;strconv\u0026#34; \u0026#34;time\u0026#34; \u0026#34;github.com/bwmarrin/discordgo\u0026#34; ) I\u0026rsquo;ve included the list of imports above, in case you\u0026rsquo;re following along.\nTo start with, I\u0026rsquo;ll define two items that we\u0026rsquo;ll need for our `getCurentWeather() function:\n// snippet from: bot/command-weather.go const URL string = \u0026#34;https://api.openweathermap.org/data/2.5/weather?\u0026#34; type WeatherData struct { Weather []struct { Description string `json:\u0026#34;description\u0026#34;` } `json:\u0026#34;weather\u0026#34;` Main struct { Temp float64 `json:\u0026#34;temp\u0026#34;` Humidity int `json:\u0026#34;humidity\u0026#34;` } `json:\u0026#34;main\u0026#34;` Wind struct { Speed float64 `json:\u0026#34;speed\u0026#34;` } `json:\u0026#34;wind\u0026#34;` Name string `json:\u0026#34;name\u0026#34;` } The first item is a constant for the base URL for OpenWeather\u0026rsquo;s API. This should never need to change, which is why we use a constant here.\nIn addition, I\u0026rsquo;ve defined a new struct called WeatherData that will be used to unpack our JSON response from OpenWeather\u0026rsquo;s API. They list a current sample response payload on their documentation page, which we can use to build our data structure. Since we won\u0026rsquo;t be using all of the data provided in the response, I also don\u0026rsquo;t need to define every value in our struct.\nNote: Need a quicker way to convert a sample JSON payload to a Golang struct? There are a handful of awesome websites that will do this conversion automatically for you. I like to use this one, but that\u0026rsquo;s only one of a handful of options!\nNext, we\u0026rsquo;ll start building out our getCurrentWeather() function:\n// snippet from: bot/command-weather.go func getCurrentWeather(message string) *discordgo.MessageSend { // Match 5-digit US ZIP code r, _ := regexp.Compile(`\\d{5}`) zip := r.FindString(message) // If ZIP not found, return an error if zip == \u0026#34;\u0026#34; { return \u0026amp;discordgo.MessageSend{ Content: \u0026#34;Sorry that ZIP code doesn\u0026#39;t look right\u0026#34;, } } In our function definition, we\u0026rsquo;ll be expecting a string input containing the user\u0026rsquo;s message - for example: \u0026ldquo;!zip 12345\u0026rdquo; - and we\u0026rsquo;ll be returning a discordgo.MessageSend object like I mentioned earlier.\nNext, we\u0026rsquo;ll do a quick (and very lazy) regular expression match against the incoming message \u0026amp; attempt to pull out the ZIP code. This regex is only searching for a pattern of 5 digits. If the r.FindString() call finds a match, it will return the 5-digit ZIP code. If it doesn\u0026rsquo;t match, it will return an empty string.\nIn case we don\u0026rsquo;t match a ZIP code, we\u0026rsquo;ll check to see if the zip variable is empty. If it is, we\u0026rsquo;ll have Discord send a message back to the user saying it\u0026rsquo;s invalid.\nHere\u0026rsquo;s where we\u0026rsquo;ll see a simple example of using our discordgo.MessageSend object. This object is just a Go struct, so we can use it to store data in the format of Key: Value. In this case, we can still respond with a simple plain-text message - by using the Content key and supplying a string message. The full definition \u0026amp; possible fields for this object are in the discordgo docs.\nNext, we\u0026rsquo;ll handle making our HTTP request to the OpenWeather API:\n// snippet from: bot/command-weather.go weatherURL := fmt.Sprintf(\u0026#34;%szip=%s\u0026amp;units=imperial\u0026amp;appid=%s\u0026#34;, URL, zip, OpenWeatherToken) // Create new HTTP client \u0026amp; set timeout client := http.Client{Timeout: 5 * time.Second} // Query OpenWeather API response, err := client.Get(weatherURL) if err != nil { return \u0026amp;discordgo.MessageSend{ Content: \u0026#34;Sorry, there was an error trying to get the weather\u0026#34;, } } We\u0026rsquo;ll start by generating the complete API URL. We\u0026rsquo;ll use fmt.Sprintf to generate a formatted string \u0026amp; inject variable values. So in this case, we\u0026rsquo;re combining the base URL with the required parameters: zip, units, and appid. The zip value will contain the ZIP code we just collected from our user. appid will be our OpenWeather API key. Using fmt.Sprintf, we can inject variables using the %s placeholder for string values - then provide those values as inputs.\nSo for example, a full query URL for ZIP code 12345 may look similar to this: https://api.openweathermap.org/data/2.5/weather?zip=12345\u0026amp;units=imperial\u0026amp;appid=ABCDEF12345\nThen we create a new instance of an http.Client - just so we can modify the timeout value to 5 seconds. We then use this client to issue a HTTP GET request to the full weatherURL.\nAssuming the call succeeds, we\u0026rsquo;ll have our JSON response stored in the response variable. If not, we\u0026rsquo;ll quickly check for an error \u0026amp; send a Discord message back to the user.\nGenerating a Discord Embed Message Now we can parse our JSON response:\n// snippet from: bot/command-weather.go // Open HTTP response body body, _ := ioutil.ReadAll(response.Body) defer response.Body.Close() // Convert JSON var data WeatherData json.Unmarshal([]byte(body), \u0026amp;data) // Pull out desired weather info \u0026amp; Convert to string if necessary city := data.Name conditions := data.Weather[0].Description temperature := strconv.FormatFloat(data.Main.Temp, \u0026#39;f\u0026#39;, 2, 64) humidity := strconv.Itoa(data.Main.Humidity) wind := strconv.FormatFloat(data.Wind.Speed, \u0026#39;f\u0026#39;, 2, 64) We\u0026rsquo;ll read in the HTTP response body using ioutil.ReadAll(). We\u0026rsquo;ll need to close this object, so we\u0026rsquo;ll immediately follow that read with a defer response.Body.Close() to make sure that happens.\nNext we\u0026rsquo;ll create a new variable called data, which will be of type WeatherData - our struct that we created earlier to store the JSON data. We can then convert that JSON response to our data object using json.Unmarshal() and passing our HTTP body \u0026amp; the target variable to store the data in.\nLastly, we can start pulling out the information we need to use later. For clarity, I\u0026rsquo;ve chosen to pull out each individual value here \u0026amp; assign them to their own variable names. Since our Discord response can only be a string, we also handle type conversions here - from integer or float64 to a string.\nFinally, we can generate our Discord MessageSend object:\n// snippet from: bot/command-weather.go // Build Discord embed response embed := \u0026amp;discordgo.MessageSend{ Embeds: []*discordgo.MessageEmbed{{ Type: discordgo.EmbedTypeRich, Title: \u0026#34;Current Weather\u0026#34;, Description: \u0026#34;Weather for \u0026#34; + city, Fields: []*discordgo.MessageEmbedField{ { Name: \u0026#34;Conditions\u0026#34;, Value: conditions, Inline: true, }, { Name: \u0026#34;Temperature\u0026#34;, Value: temperature + \u0026#34;°F\u0026#34;, Inline: true, }, { Name: \u0026#34;Humidity\u0026#34;, Value: humidity + \u0026#34;%\u0026#34;, Inline: true, }, { Name: \u0026#34;Wind\u0026#34;, Value: wind + \u0026#34; mph\u0026#34;, Inline: true, }, }, }, }, } return embed } We\u0026rsquo;ll create a new variable called embed, which will store a discordgo.MessageSend struct.\nEarlier we used this to send a Content: value, but this time we\u0026rsquo;ll be leveraging the Embeds key. Embeds is of the type []*discordgo.MessageEmbed, which is again just a struct to store data. For reference, the format \u0026amp; possible fields of the MessageEmbed object are listed here\nWithin this MessageEmbed object, we can start filling in data about our message. So we fill in our message Title and Description - and tell Discord that this is going to be a richtext type of response.\nWe\u0026rsquo;ll be displaying a box in discord that has a number of key/value pairs - which we\u0026rsquo;ll use to show the measurement title \u0026amp; value. To do this, we\u0026rsquo;ll fill in the Fields: key - which takes in a list of structs of the type []*discordgo.MessageEmbedField.\nFor each of the weather measurements we want to display, we\u0026rsquo;ll provide a Name:, the Value:, and specify Inline: true. The Inline options tells Discord to try and list each key/value inline with eachother, rather than placing each pair on a new line.\nLast, we\u0026rsquo;ll return our discordgo.MessageSend object via return embed.\nTesting Let\u0026rsquo;s test this out. We can stop our currently running bot with Ctrl+C, then restart with go run main.go.\nWe should be able to ask our bot for the weather using our new !zip command:\nLook at that! The bot quickly responds with the current weather, and formatted in an embedded message. There\u0026rsquo;s a lot more we could do with embedded messages, but this is just an example to get started.\nOkay - I think that wraps up this blog post. I know it was a long one, so if you\u0026rsquo;re still with me - Thank you! I hope this was helpful.\nI am planning on a follow up post shortly, where I\u0026rsquo;ll cover how to add Discord slash commands to our example bot. Check back soon or subscribe to the Blog or YouTube if you\u0026rsquo;re interested!\n","permalink":"https://0x2142.com/how-to-discordgo-bot/","summary":"A short tutorial for building a Discord chatbot with Golang.","title":"[How To] Building a Simple Discord Bot using DiscordGo"},{"content":" So in my last post, I picked up a Qotom Mini-PC to run OPNsense on. After a few months, the device has been running well \u0026amp; I\u0026rsquo;m very happy with it.\nOne of the new things I got to try out with OPNsense was Wireguard VPN. I had previously been using something else for VPN connectivity back to my home network - but I heard good things about Wireguard \u0026amp; wanted to give it a try.\nSo far my experience has been good! I\u0026rsquo;ve been pleasantly suprised with how easy it is to configure \u0026amp; get running. In addition, the performance \u0026amp; overall experience has been very positive. The VPN connects quicker than anything I\u0026rsquo;ve used in the past, and just simply works without issue.\nAll that being said - I wanted to put together a quick guide on how to configure Wireguard on OPNsense. Specifically, this configuraion will be for remote-access VPN - where clients will connect to a VPN headend. We\u0026rsquo;ll walk through the OPNsense configuration \u0026amp; a few clients as well. So let\u0026rsquo;s dig in!\nTopology For the purpose of this blog post, we\u0026rsquo;ll be using the lab topology below:\nI will be using the reserved IP range 203.0.113.0/24 for the WAN-side addressing. I\u0026rsquo;ll have one Windows \u0026amp; one Android client that we\u0026rsquo;ll walk through \u0026amp; connect to the VPN.\nInstalling the Wireguard Plugin To get started, first thing we will want to do is install the Wireguard plugin for OPNsense. By default, OPNsense will have standard IPSec \u0026amp; OpenVPN already available - but other VPN options can be enabled easily.\nSo in OPNsense, we\u0026rsquo;ll navigate down to System \u0026gt; Firmware \u0026gt; Plugins, then search for wireguard and click the plus icon.\nThis should pull down the package \u0026amp; install pretty quickly. No reboot required here!\nOnce installed, you may have to refresh the page or navigate to a new page so that the menu bar has a chance to reload. Then we\u0026rsquo;ll have a new option under VPN:\nWireguard Tunnel Configuration Next we\u0026rsquo;ll begin configuring Wireguard on the OPNsense side.\nThere is a little bit of a chicken \u0026amp; egg scenario here since everything is based on cryptographic keys. We\u0026rsquo;ll need to generate keys on the firewall, which we need to enter on the client - but we also need the client keys to enter on the firewall. A bit of bouncing between the two - but for now we\u0026rsquo;ll try to complete as much as we can on the firewall side.\nWe\u0026rsquo;ll enable Wireguard by dropping down to VPN \u0026gt; WireGuard then clicking Enable and Apply\nNext we\u0026rsquo;ll set up the Wireguard tunnel interface on OPNsense. This will be a virtual tunnel interface that will be created as interface wg\u0026lt;instance number\u0026gt;.\nTo do this, we\u0026rsquo;ll navigate to the Local tab, and click the plus icon to add a new tunnel.\nIn the above screenshot, I\u0026rsquo;ve filled in just a few details.\nFor Name, I\u0026rsquo;ve entered our virtual interface name wg1. Since OPNsense shows the Instance as 1 - it will create a wg interface with that instance number.\nWe\u0026rsquo;ll leave Public Key \u0026amp; Private Key blank for now. OPNsense will auto-generate these keys once we save this config.\nFor Listen Port, I\u0026rsquo;ve set this to 51820 which is the default for Wireguard. It\u0026rsquo;s not stated here, but this is a UDP tunnel.\nFor Tunnel Address - this is where we add the IP address of the virtual tunnel interface. This will be the gateway for our remote clients. In my lab I\u0026rsquo;ll be using 10.50.50.1/24 here.\nWe have no peers configured yet - so we can\u0026rsquo;t select any here. We\u0026rsquo;ll leave this blank for now, but come back later.\nWe will leave Disable Routes unchecked. By default, OPNsense will add static/connected routes for any client via the tunnel interface. You might not want this behavior if you wanted to do custom routing - for example, in a site-to-site VPN connection - but we\u0026rsquo;ll leave this enabled.\nOnce we\u0026rsquo;re done, we\u0026rsquo;ll click Save.\nLike I mentioned before, OPNsense will now auto-generate our crypto keys for the tunnels. So if we edit our tunnel, we\u0026rsquo;ll now see those fields populated:\nWe\u0026rsquo;ll want to copy the Public Key \u0026amp; save it for later. This will need to be imported onto our clients, so that they can communicate securely with our firewall.\nWireguard Interface Assignment Now that we have our headend tunnel interface defined, we can map our wg1 interface to an OPNsense interface. The OPNsense documentation suggests this is optional, but I would recommend it since it will allow us to create firewall rules to permit/deny access to clients.\nWe\u0026rsquo;ll navigate to Interfaces \u0026gt; Assignments, and we should see a New interface available: our wg1 tunnel.\nWe can assign this a name, then click the plus icon \u0026amp; Save.\nNext we\u0026rsquo;ll enable the interface by navigating to Interfaces \u0026gt; WG1. Here we\u0026rsquo;ll only need to click Enable \u0026amp; save the change - nothing else is necessary.\nOf course, we\u0026rsquo;ll be prompted to apply the changes - which we will do:\nBy default, all traffic through our WG1 firewall interface will be blocked - so please make sure to configure a firewall rule to permit traffic from the Wireguard clients.\nFirewall Rules: Allow Inbound Wireguard Traffic Next we need to permit the Wireguard traffic into our firewall. By default the WAN interface will block all traffic that isn\u0026rsquo;t explicitly allowed - including our Wireguard traffic.\nFor this, we\u0026rsquo;ll navigate to Firewall \u0026gt; Rules \u0026gt; WAN. Then click the plus icon to add a new rule.\nThe screenshot above shows what our firewall rule will look like.\nHere\u0026rsquo;s the summary:\nAction: Pass Interface: WAN Direction: In TCP/IP Version: IPv4 You can enable IPv6 as well, if you have IPv6 connectivity (this is a lab box, which does not) Protocol: UDP Source: Any This will allow anyone on the internet to reach our VPN. We could restrict source IP addresses, if our clients had permanent, static IPs. Destination: WAN Address The firewall itself is the destination for this traffic Destination Port Range: (other) / 51820 I also enabled logging \u0026amp; added a quick description. After we have all this configured, we can click Save - then Apply Changes.\nClient Setup - Windows So first we\u0026rsquo;ll start with an easy configuration on a Windows client. Wireguard client software can be found on the Wireguard site here.\nFor the sake of the walkthrough, we will manually configure each client. However, this can be a difficult task if there is a large number of clients. Wireguard does support importing configurations, and there are a number of free tools available to help automate generating config files for clients - including some which will generate QR codes for easy import on mobile clients.\nSo on my lab Windows machine, we\u0026rsquo;ll open up the Wireguard client \u0026amp; click Add Empty Tunnel:\nThen we\u0026rsquo;ll be given a blank config file, with only the devices public \u0026amp; private key pair generated for us.\nWe\u0026rsquo;re going to fill in details similar to the below screenshot:\nThe configuration under [Interface] is the local, client-side configuration. I\u0026rsquo;ve added the client\u0026rsquo;s tunnel address - which will be 10.50.50.15/32. I\u0026rsquo;ve also configured DNS servers which the client can reach via the VPN.\nThen we\u0026rsquo;ll add the [Peer] section, which contains info about our VPN headend. Here\u0026rsquo;s where we\u0026rsquo;ll need the public key from our OPNsense firewall. We\u0026rsquo;ll also specify the Endpoint address, which is the IP or hostname of our VPN headend \u0026amp; the port (which by default is 51820).\nWe\u0026rsquo;ve also configured AllowedIPs as 0.0.0.0/0. This will force all client traffic over the VPN tunnel - including general internet traffic. However, we could limit this to specific subnets. For example, let\u0026rsquo;s say your network only used 172.16.90.0/24 \u0026amp; 10.1.1.0/16 subnets \u0026amp; we only wanted the user to be able to access those. We would configure the following: AllowedIPs = 172.16.90.0/24, 10.1.1.0/16. In this case, only traffic for those subnets would be routed over the VPN - any other traffic would use the devices default internet connection.\nNow, we\u0026rsquo;ll save this - and again need to copy the device\u0026rsquo;s Public Key, which we\u0026rsquo;ll need to enter on the OPNsense firewall.\nClient Setup - Adding Clients to OPNsense In order for the Windows machine to connect to OPNsense, we\u0026rsquo;ll also need to configure a client profile on the firewall.\nIn OPNsense, we\u0026rsquo;ll navigate back to VPN \u0026gt; WireGuard, then click on the Endpoints tab.\nHere we\u0026rsquo;ll configure a name for our client \u0026amp; paste in the client\u0026rsquo;s Public Key.\nWe\u0026rsquo;ll also set AllowedIPs to the client\u0026rsquo;s IP address, which we have configured as 10.50.50.15/32. This controls what IP addresses are reachable via this endpoint.\nWe do have some additional fields available, which we will leave blank. For example - Endpoint Address \u0026amp; Endpoint Port would be used to define a public IP that we expect our client to connect from. Since a remote-access client could connect from any IP, we leave those fields blank to allow this.\nOnce we\u0026rsquo;re done, we\u0026rsquo;ll click Save then Apply.\nNext we\u0026rsquo;ll jump back to the Local tab, and edit our headend tunnel configuration.\nWe should now see our windows client in the Peers dropdown. We\u0026rsquo;ll select that client, so that Wireguard will permit that client to connect via this tunnel interface.\nThen, as always, click Save \u0026amp; Apply\nLast but not least - we should restart our Wireguard server on OPNsense. This can be done by either disabling \u0026amp; re-enabling Wireguard - or by navigating back to the OPNsense dashboard \u0026amp; clicking the restart icon next to the wireguard-go service.\nTesting The Connection Okay - now that we have all that completed, it\u0026rsquo;s finally time to test connectivity from our client.\nOn my Windows client, I\u0026rsquo;ll just click the Activate button for the tunnel:\nAnd we\u0026rsquo;ll see that the Status shows Active, and we\u0026rsquo;ll start to see updates to the Last Handshake \u0026amp; Transfer fields - indicating we are connected \u0026amp; sending data.\nTo further validate, we can check the Log tab:\nThe key things to look for here, are the following messages:\nReceiving handshake response which indicates our firewall responded to our request to connect Keypair 1 created which indicates that our connection is healthy to our peer Receiving keepalive packet from peer - we should see these periodically to maintain our connection. If keepalives stop flowing, then we may have a break in connectivity between client \u0026amp; peer. We should also be able to reach out wg1 tunnel address from the Windows client:\nOn the OPNsense side of things, we can check what client is connected via the List Configuration tab, under VPN \u0026gt; Wireguard:\nOn this screen, we can see we have 1 peer connected - which matches the IP \u0026amp; public key of our Windows client. We\u0026rsquo;ll see similar output like the Windows client, where we can see the latest handshake \u0026amp; data transfer.\nAdditionally, for easier access we can add the Wireguard widget to the OPNsense dashboard.\nIf we navigate to our dashboard, then click Add widget - we can add the Wireguard widget.\nHere\u0026rsquo;s what that looks like:\nAdditional Client Setup - Android Let\u0026rsquo;s also take a quick look at a mobile client. I\u0026rsquo;ll get through this pretty quickly, since the configuration will be very similar to our other client - just a different UI.\nOn my Android device - if I open up the Wireguard app, I have a few options for creating or importing a tunnel:\nIn this case, we\u0026rsquo;ll again walk through creating a tunnel manually. Again, it\u0026rsquo;s worth mentioning that there are 3rd party apps available to auto-generate config imports or QR codes to make this easier.\nFirst we\u0026rsquo;ll have a handful of fields to fill in about the Android-side configuration. This includes a name for the tunnel, an address, and DNS servers.\nWe can click on the refresh icon in the Private Key field to auto-generate our key pairs:\nOne of the interesting parts of the mobile app is the ability to permit/exclude individual apps from traversing the VPN tunnel.\nHere\u0026rsquo;s a quick screenshot, where we can pick either to allow only certain apps to use the VPN - or permit all except a few we choose to exclude:\nIn my case, I\u0026rsquo;ll keep this set to allow all applications.\nNext we\u0026rsquo;ll work on the peer config:\nAfter that, all we need to do is save our VPN configuration - then we can toggle the tunnel on or off:\nAnd similar to the Windows client, we can click on the tunnel itself to see current status / data transfer:\nSo it looks like we should be connected \u0026amp; can try accessing VPN resources!\nWe can also use the same methods as earlier to check connectivity from the OPNsense side.\nOkay - that\u0026rsquo;s all I wanted to share today. I\u0026rsquo;ve been quite pleased with how easy to use WireGuard has been - and how well it performs! I hope this blog post was helpful if you\u0026rsquo;re interested in trying it out.\nThanks!!\n","permalink":"https://0x2142.com/how-to-set-up-wireguard-on-opnsense/","summary":"In this post, we\u0026rsquo;ll walk through a simple WireGuard remote-access VPN configuration on OPNsense - including client setup examples with Windows \u0026amp; Android.","title":"[How to] Set up Wireguard VPN on OPNsense"},{"content":" Note: I may receive commissions for purchases made through links in this post. This is to help support my blog and does not have any impact on my recommendations.\nOne day I would love to have symmetric internet speeds, but for the meantime I have to work with what I have - which is dismal upload speeds that don\u0026rsquo;t come anywhere close to the download speed.\nSo I\u0026rsquo;m currently at 200Mb/s down \u0026amp; 10Mb/s up - and I would prefer to have higher upload speeds. However, even just 20Mb/s upload requires me to purchase a 500Mb/s download plan. 🙄\nI\u0026rsquo;ve been hesitant to upgrade, since my current Meraki MX64 tops out at ~250Mb/s throughput. I would have to upgrade my firewall or half of my new download speed would be unusable.\nWhile Meraki does have faster firewalls, they\u0026rsquo;re a bit expensive \u0026amp; at the moment somewhat difficult to obtain. So instead I searched around for alternatives, and came across the Qotom Q750G5 - which is a barebones mini-PC that I could load pfSense or OPNsense onto.\nSo in this blog post - I wanted to talk about the device \u0026amp; some of the performance testing I did.\nQotom Q750G5 - Hardware First let\u0026rsquo;s take a quick look at the hardware. What got my attention quickly was the five 2.5GbE ports, but this small device offers quite a lot for how inexpensive it was.\nA quick look at the specs:\nIntel Celeron J4125 Quad Core 2.0Ghz (Burst 2.7Ghz) Five Intel I225-V 2.5Gb Ethernet ports Optional WiFi slot Optional 3G Cellular \u0026amp; SIM card slots 1 RAM slot 1 mSATA slot 1 2.5in SATA HDD slot 2x USB 2.0 \u0026amp; 3x USB 3.0 ports By default it seems this is a barebones kit, however I did opt to buy a model that included 16GB RAM \u0026amp; a 256GB mSATA SSD. It arrived with a single 16GB TeamGroup DDR4 3200Mhz module \u0026amp; 256GB Hoodisk mSATA SSD.\nAre the specs a bit overkill for an embedded firewall? Yeah definitely. But the whole kit was only around $230 USD - which seemed crazy to me.\nThe big thing that pulled me toward this model (besides price) was the 2.5GbE ports. Since a lot more places have gigabit residential internet these days, and some lucky places are starting to see 2Gb/s - I was hoping that this little box would offer me a bit of future-proofing.\nOf course, I was a little curious about what performance this device could realistically achieve - but more on that later!\nI wanted to provide a few photos of the unit as well. The casing itself is pretty minimal, but the box is definitely heavier than you would expect!\nFront Photo:\nOn the front there isn\u0026rsquo;t a whole lot to see. Power button, reset button, status LEDs, and USB ports. Oh - and there is an HDMI port as well, which comes in handy when installing an operating system (or if you\u0026rsquo;re using this as an actual PC).\nBack Photo:\nThe back shows off the five 2.5Gb Ethernet ports, as well as the power plug. While I didn\u0026rsquo;t get a WiFi-enabled unit, the back faceplate still has cutouts for wireless antenna. I could opt to add wireless to this later, but I do wish the faceplate came with plugs or something to cover the holes in the meantine.\nInside Photo:\nNow here\u0026rsquo;s where things get interesting! This box surprisingly packs a lot in it\u0026rsquo;s tiny size.\nIn the upper right, there is a PCI slot for WiFi. There\u0026rsquo;s also a WiFi/3G PCI slot in the lower left.\nJust above the 3G slot is the SSD mSATA slot. When a drive is installed, it does cover the SIM card slot - meaning that the SSD would need to be removed to change SIM cards.\nAlso on the left, mostly out of the photo, is a connector for a 2.5in HDD. While I didn\u0026rsquo;t snap a photo of the bottom of the unit, the drive would screw into the bottom plate.\nInterestingly enough, the CPU is on the other side of the motherboard. While you might think that the overall unit kinda looks like a heatsink - I was surprised to see that\u0026rsquo;s exactly what it is. The CPU is located right under the four screws with black washers and has thermal paste pre-applied. It rests up against the top of the case \u0026amp; uses the top as a heatsink.\nAs a last note about the CPU - it does get quite toasty at times. During one of my performance tests, the CPU reached ~90°C - and the top of the unit was very hot to the touch. It may not act as the world\u0026rsquo;s best heatsink, so I would avoid placing anything on top of the unit - or resting your hand there for too long 🙂.\nPerformance Testing As a quick note before we get to the good stuff: All of my tests were performed using iPerf3. This may not show us the best real-world throughput tests, but I had quite a difficult time getting dpdk drivers to compile correctly for the Intel 2.5GbE ports (so I could use something like TRex). I may attempt to go back to this later, but the iPerf data is what I\u0026rsquo;ll provide for now.\nThe Testbed For the performance testing, I used the two Intel NUC11 PCs that I purchased recently for my VMware lab. These already come with a 2.5GbE port, which I am currently using for vMotion between the two. I disconnected the vMotion port temporarily, and instead enabled PCI-passthrough to connect the ports directly to a VM.\nI built a VM on each NUC with the following specs:\nDebian 11 8x vCPU 16GB RAM PCI-passthrough to 2.5GbE adapter iPerf 3.9 was used for all tests. The iPerf server was enabled with the iperf3 -s command, and the client tests were run with iperf3 -c \u0026lt;client ip\u0026gt; -P 8 -t 600. Each test was run for 10 minutes.\nThe devices were connected \u0026amp; configured with the following topology:\nNote: In the below paragraphs, I will talk about the configuration I used for each test. If you\u0026rsquo;re interested in more detail, please check out the video at the top of this blog post. In the video, I walk through the configuration \u0026amp; setup for most of the tests below.\nTest 1: No Firewall Avg: 2.35Gb/s\nOkay, so expecting that I likely wouldn\u0026rsquo;t get the full 2.5Gb speeds once I added the firewall - I wanted to perform a baseline test with the VM\u0026rsquo;s directly connected via the 2.5GbE passthrough port.\nIn each of these tests, I was able to reach an average speed of: 2.35Gb/s\nTest 2: Routing, NAT, Simple Firewall rules Avg: 2.35Gb/s\nIn this test case, OPNsense was configured to match the diagram above. The iPerf server was located on the LAN segment, with an IP of 10.2.2.1 \u0026amp; a default route toward the LAN interface of the OPNsense box (10.2.2.2). The client is connected via the WAN interface, at 203.0.113.50.\nI created a proxy-ARP virtual address for the 203.0.113.25 IP address, then created a NAT rule to forward traffic sent to that address to 10.2.2.1.\nNext, there was a single firewall rule created to permit TCP port 5201 inbound from the WAN. This is the default port that iPerf will use.\nDuring these tests, I averaged about 2.35Gb/s and relatively low CPU usage around 10-20%.\nAs an interesting side note to this: Originally I didn\u0026rsquo;t use the default LAN \u0026amp; WAN ports (ports 1 \u0026amp; 2), but instead used ports 3 \u0026amp; 4. During testing between those ports, I could only reach ~1.7Gb/s. Once I switched to ports 1 \u0026amp; 2, I could easily hit 2.35Gb/s. I\u0026rsquo;m curious to dig in later \u0026amp; see if this is a hardware issue, or possibly OPNsense is prioritizing the LAN/WAN traffic differently\nTest 3: Large Firewall Ruleset Avg: 2.35Gb/s\nWhile my home network will likely have a somewhat minimal firewall ruleset, I was curious to see how well the device would perform with a large ruleset.\nSo I wrote a script to auto-generate ~1,200 firewall rules with random IP addresses, ports, and a mix of permit/block actions. I applied the ruleset inbound on both the LAN \u0026amp; WAN ports.\nUnder this test, I was still able to reach the 2.35Gb/s speeds - but now the CPU was creeping up to 30-40%.\nSo what next? Well I decided to see if I could stress the box a little - and increased the auto-generated ruleset to just over ~15,000 rules.\nThe increased ruleset took about 5-10 minutes to load into the system, and CPU was pegged at 100% the whole time. In fact, the CPU never really came back down. Even after the rules had loaded, the CPU was flat at 100% - and it took anywhere from 2-4 minutes to navigate between pages in the web GUI. (This is the part where my CPU temps went from \u0026lt;50°C to over 90°C 🙃)\nI ended up having to re-image the box.\nTest 4: Suricata IPS Avg: 2.35Gb/s (But sometimes much, much less)\nNext I loaded up the built-in IDS/IPS, which uses suricata. I downloaded all available free rulesets, and enabled all of them. Ideally, you wouldn\u0026rsquo;t necessarily enable everything - but I wanted to start off with a full load test.\nMy experience with these tests was highly variable. Most times, I was surprised to see that I could still push 2.35Gb/s through without any issue. During these tests, the CPU usually bounced between 50-80% usage.\nStrangely, every so often I would test and get significantly less than that. Most times when this happened, I would instead only see somewhere between 400-700Mb/s - but on one test the box slowed to just over 200Mb/s. Each time this happened, the performance tests would remain degraded until I restarted the suricata service - then I would be able to reach the 2.35Gb/s again.\nMy best guess is that something is getting stuck. I know some older IPS systems I\u0026rsquo;ve worked with in the past had issues with CPU-pinning, and you might get garbage performance if your traffic hit the wrong CPU core. I didn\u0026rsquo;t spend a lot of time digging into this, but it feels a bit similar.\nTest 5: Wireguard VPN Avg: 800Mb/s, 650-700Mb/s with IPS also enabled\nI also wanted to try VPN performance. There are a lot of options for VPN services within OPNsense, including standard IPSec and OpenVPN. However, I opted to try out wireguard - since it\u0026rsquo;s really easy to set up \u0026amp; get running.\nIn this case, I definitely hit a limitation with CPU-pinning. With my initial tests, I could only reach about ~450-500Mb/s - but the Qotom CPU was only spiking up to 70%.\nSeems like the wireguard client (at least for same source/destination/port traffic) does pin everything to a single CPU. So my client CPU, between encrypting the traffic \u0026amp; generating it, was actually maxing out long before the Qotom was.\nI ended up spinning up a second VM on the client side (which required disabling PCI passthough \u0026amp; adding both VMs to a shared vSwitch). With both of these running, I was able to max out the Qotom CPU at 100% - and hit a fairly consistent 800Mb/s VPN throughput.\nAs a final test, I enabled the suricata IPS on top of the VPN as well. Now the traffic would need to be decrypted \u0026amp; inspected before reaching the server. With a single client, I averaged ~400-450Mb/s - and with both clients it was around ~650-700Mb/s\nOverall I\u0026rsquo;m really pleased with how well this thing performs, especially for how relatively inexpensive it was. I also expected OPNsense to have a steep learning curve, but it was surprisingly easy to get up and running very quickly.\nNext I\u0026rsquo;ll be working on getting this firewall up \u0026amp; running at home. I may be tempted to write up some more on OPNsense - so if there are any questions or specific configurations you might like to see, please leave a comment!\n","permalink":"https://0x2142.com/opnsense-qotom-q750gs/","summary":"In this post, we will take a look at the Qotom Q750G5 hardware (with 2.5GbE), install OPNsense, and walk through a few performance tests.","title":"OPNsense on Qotom Q750G5 - Hardware Overview \u0026 Perf Testing"},{"content":" A few months ago, I had written a blog post on building a simple chatbot with Webex. You can find that here.\nAt the time I was already thinking about some follow-up content, where I could walk through how to use AdaptiveCards to make the bot a little fancier - but I decided to wait a bit \u0026amp; see how the first post performed first.\nWell, now it\u0026rsquo;s a few months later \u0026amp; I suppose it\u0026rsquo;s about time I write this 😄.\nSo in this post - We\u0026rsquo;ll build on the code I used in the last post. We\u0026rsquo;ll show how to add cards to both display information, as well as take user input.\nWhat are AdaptiveCards? AdaptiveCards are actually not a Webex/Cisco-specific thing! They were actually created by Microsoft with the intention of being a platform-agnostic way of displaying information. So in theory, a card schema written for a Webex bot could be taken \u0026amp; re-used on another messaging platform that implements AdaptiveCards - without any/much rework.\nHopefully this should mean a more consistent user experience for a bot that is supported across multiple platforms. The cards themselves can also enhance the experience by making your bot more dynamic \u0026amp; interactive. Maybe a developer or network engineer is okay with issuing specific syntax-based commands to a bit - but what about someone who is less computer savvy? Using cards makes it easy to provide guided input with textboxes, buttons, and toggles.\nAdaptiveCards are built on a standard JSON structure with a pre-defined schema of card properties. Let\u0026rsquo;s take a quick look at a simple example:\n{ \u0026#34;type\u0026#34;: \u0026#34;AdaptiveCard\u0026#34;, \u0026#34;$schema\u0026#34;: \u0026#34;http://adaptivecards.io/schemas/adaptive-card.json\u0026#34;, \u0026#34;version\u0026#34;: \u0026#34;1.2\u0026#34;, \u0026#34;body\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;TextBlock\u0026#34;, \u0026#34;text\u0026#34;: \u0026#34;Hello there! What\u0026#39;s your name?\u0026#34;, \u0026#34;wrap\u0026#34;: true }, { \u0026#34;type\u0026#34;: \u0026#34;Input.Text\u0026#34;, \u0026#34;placeholder\u0026#34;: \u0026#34;Your name here\u0026#34;, \u0026#34;id\u0026#34;: \u0026#34;user_name\u0026#34; }, { \u0026#34;type\u0026#34;: \u0026#34;ActionSet\u0026#34;, \u0026#34;actions\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;Action.Submit\u0026#34;, \u0026#34;title\u0026#34;: \u0026#34;Submit\u0026#34; } ] } ] } So in the JSON payload above, we\u0026rsquo;re creating a card that asks for user input. We have a block of text that asks the user \u0026ldquo;What\u0026rsquo;s your name?\u0026rdquo;, then we provide a text input field, and finally a submit button. Looks pretty straightforward, yeah?\nWe\u0026rsquo;ll get into the specifics around how to create that card JSON in a bit, but you can always use Microsoft\u0026rsquo;s AdaptiveCard Designer to play around with card elements/structure.\nSo what would that card look like when rendered in Webex? Let\u0026rsquo;s take a look:\nThat card seems a lot more user-friendly than trying to explain to your users that they need to remember some sytax like /botcommand username set \u0026lt;firstname\u0026gt; \u0026lt;lastname\u0026gt;. Instead, they can just enter the info on the card \u0026amp; click submit.\nSending a Static, Default Card from a JSON File So first we\u0026rsquo;ll start off with the easier scenario. We\u0026rsquo;ll have a pre-defined JSON card built, and use the bot to load \u0026amp; send this card response.\nOne note before we get started - I\u0026rsquo;ll be building on my simple weather chatbot code that I mentioned earlier. If you\u0026rsquo;re following along, you can pull down that code from here.\nOkay, to start off - we\u0026rsquo;ll be creating a card that allows a user to enter their desired zip code. Instead of having to remember the specific command syntax, a user will instead just issue the weather command \u0026amp; our bot will prompt for information via a card.\nI\u0026rsquo;ve used the Microsoft Adaptive Card Designer to build out the basic card structure, which I\u0026rsquo;ve kept simple for now. Here\u0026rsquo;s what that looks like:\n{ \u0026#34;type\u0026#34;: \u0026#34;AdaptiveCard\u0026#34;, \u0026#34;$schema\u0026#34;: \u0026#34;http://adaptivecards.io/schemas/adaptive-card.json\u0026#34;, \u0026#34;version\u0026#34;: \u0026#34;1.2\u0026#34;, \u0026#34;body\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;TextBlock\u0026#34;, \u0026#34;text\u0026#34;: \u0026#34;Get Current Weather\u0026#34;, \u0026#34;wrap\u0026#34;: true, \u0026#34;size\u0026#34;: \u0026#34;Medium\u0026#34;, \u0026#34;weight\u0026#34;: \u0026#34;Bolder\u0026#34; }, { \u0026#34;type\u0026#34;: \u0026#34;TextBlock\u0026#34;, \u0026#34;text\u0026#34;: \u0026#34;Enter a Zip code:\u0026#34;, \u0026#34;wrap\u0026#34;: true }, { \u0026#34;type\u0026#34;: \u0026#34;Input.Number\u0026#34;, \u0026#34;placeholder\u0026#34;: \u0026#34;00000\u0026#34;, \u0026#34;id\u0026#34;: \u0026#34;zip_code\u0026#34;, \u0026#34;min\u0026#34;: 1, \u0026#34;max\u0026#34;: 99950 }, { \u0026#34;type\u0026#34;: \u0026#34;ActionSet\u0026#34;, \u0026#34;actions\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;Action.Submit\u0026#34;, \u0026#34;title\u0026#34;: \u0026#34;Get Weather\u0026#34;, \u0026#34;data\u0026#34;: { \u0026#34;callback_keyword\u0026#34;: \u0026#34;weather\u0026#34; } } ] } ] } This card looks pretty similar to the example I used earlier. We have an initial text block that displays the title \u0026ldquo;Get Current Weather\u0026rdquo;, and we\u0026rsquo;ve added attributes for bold text \u0026amp; a medium size.\nNext, we have a normal text block prompting the user to enter their zip code. This is followed by a number input box for that data entry.\nThe input box has a \u0026ldquo;min\u0026rdquo; \u0026amp; \u0026ldquo;max\u0026rdquo; value for the zip code from 1 to 99950 - which is the full range of US zip codes. We also need to assign any input a unique \u0026ldquo;id\u0026rdquo; value - which we\u0026rsquo;ll see later is used to pull out the user input from the response.\nLast, we have our actions - which similar to earlier. This will present as a simple submit button with the text \u0026ldquo;Get Weather\u0026rdquo;. The important note here is adding a data dictionary with a callback_keyword sub-element. We set the callback_keyword to the command we want this submission to be sent to. In our case, we want the input to be sent back to the weather command.\nNow to implement. Back in our bot, we\u0026rsquo;ll edit the weather.py file to load the card \u0026amp; assign as the default command response. Let\u0026rsquo;s take a look:\n### Script snippet with open(\u0026#34;./input-card.json\u0026#34;, \u0026#34;r\u0026#34;) as card: INPUT_CARD = json.load(card) class WeatherByZIP(Command): def __init__(self): super().__init__( command_keyword=\u0026#34;weather\u0026#34;, help_message=\u0026#34;Get current weather conditions by ZIP code.\u0026#34;, card=INPUT_CARD, ) ### Script snippet In the above snippet, I\u0026rsquo;ve extracted just the pieces we\u0026rsquo;ll be editing. For full examples, please see the final example repo here.\nI\u0026rsquo;ve saved that JSON payload from earlier as input-card.json. Then we\u0026rsquo;ll open that card file \u0026amp; assign that JSON payload to a variable called INPUT_CARD.\nSince we\u0026rsquo;ll expect this card to be the primary method of collecting a zip code - we\u0026rsquo;ll make this card the default response from our bot when the command is triggered. In my original code, we initialized the WeatherByZIP class with card=None - which instead passed our input directly to the execute() function. In this case, we\u0026rsquo;ll set that to card=INPUT_CARD to send our form.\nWith all that done, let\u0026rsquo;s test it out:\nCollecting \u0026amp; Processing Card Submissions Now that we have our input card, let\u0026rsquo;s move on to processing the response.\nIn the original example, we expected the user to enter a command like weather 12345 - and it was easy enough to just strip the 5-digit zip code from the response.\nSince a card could have many input boxes, we instead receive that data back in a structured JSON format.\nIf we check out the logging in the webex_bot console, we can see the payload that gets returned to us when the card is submitted:\n2022-03-03 14:59:26 [INFO] [webex_websocket_client.root._process_incoming_websocket_message]:62 attachment_actions from message_base_64_id: Webex Teams AttachmentAction: { \u0026#34;id\u0026#34;: \u0026#34;--removed--\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;submit\u0026#34;, \u0026#34;messageId\u0026#34;: \u0026#34;--removed--\u0026#34;, \u0026#34;inputs\u0026#34;: { \u0026#34;callback_keyword\u0026#34;: \u0026#34;weather\u0026#34;, \u0026#34;zip_code\u0026#34;: \u0026#34;12345\u0026#34; }, \u0026#34;personId\u0026#34;: \u0026#34;--removed--\u0026#34;, \u0026#34;roomId\u0026#34;: \u0026#34;--removed--\u0026#34;, \u0026#34;created\u0026#34;: \u0026#34;2022-03-03T19:59:24.820Z\u0026#34; } Using this output, we can see exactly what data is passed back to our bot. Under imports, we do see the correct callback_keyword that we defined in our card JSON. But we also see the user input - which is 12345. This is listed under the key zip_code which is the id we assigned to our input field earlier.\nNow we can easily dig through this response to pull out the data we need. This JSON payload is delivered to the execute function under the attachment_actions parameter.\nSo we\u0026rsquo;ll make some minor modifications to our original code to pull that value out.\ndef execute(self, message, attachment_actions, activity): zip_code = attachment_actions.inputs[\u0026#39;zip_code\u0026#39;] As a reminder, zip_code is the variable we\u0026rsquo;re using inside the execute() function to store the requested zip code - which is then passed to the OpenWeather API.\nPreviously, we had just assigned the value of message to zip_code, which was the incoming text message from the user.\nInstead, this time we\u0026rsquo;ll fetch the zip code from the incoming card response, using attachment_actions.inputs \u0026amp; pulling out the zip_code key.\nWith that simple change completed, we should be able to ask the bot for the weather, input our zip code, then receive the same text-based output as before.\nHere\u0026rsquo;s what that looks like now:\nAs we would expect, upon hitting Get Weather - the bot returns the text-based weather report.\nNext, we\u0026rsquo;ll take a look at dynamically building a card to display weather info.\nDynamically Building Cards Now we get to the real fun part. We\u0026rsquo;ll need to dynamically build an AdaptiveCard response, depending on what values we want to present to the user.\nI will be using a Python module called adaptivecardbuilder. There are a handful of modules available that can help dynamically build cards, but this was the one I found to be most intuitive for me. While I will walk through an example here, I would also recommend reviewing their docs for additional context/examples.\nYou could also generate a JSON payload manually, or build one using the card designer \u0026amp; try to edit the JSON dynamically to change card values - but I won\u0026rsquo;t be doing that here.\nSo we\u0026rsquo;ll start off by installing the adaptive card module:\npip install adaptivecardbuilder Before we build out the full card response, let\u0026rsquo;s take a quick look at how we can use this module.\nIn our execute() function, we\u0026rsquo;ll create a simple card that shows what zip code was entered.\nFirst, we\u0026rsquo;ll need to add two new imports:\nfrom webex_bot.models.response import Response from adaptivecardbuilder import * We\u0026rsquo;ll import the Response module from webex_bot, which will be required to send any attachments / custom response to the client. We\u0026rsquo;ll also import our adaptivecardbuilder module.\nNext, we\u0026rsquo;ll modify the execute() function to look like the following:\ndef execute(self, message, attachment_actions, activity): zip_code = attachment_actions.inputs[\u0026#39;zip_code\u0026#39;] card = AdaptiveCard() card.add(TextBlock(text=f\u0026#34;Weather for {zip_code}\u0026#34;, size=\u0026#34;Medium\u0026#34;, weight=\u0026#34;Bolder\u0026#34;)) card_data = json.loads(asyncio.run(card.to_json())) card_payload = { \u0026#34;contentType\u0026#34;: \u0026#34;application/vnd.microsoft.card.adaptive\u0026#34;, \u0026#34;content\u0026#34;: card_data, } response = Response() response.text = \u0026#34;Test Card\u0026#34; response.attachments = card_payload return response First we create a new AdaptiveCard() instance. With this new object we can add different card elements.\nIn this case, we add a single card element - a TextBlock. This has multiple attributes, including the actual text content, and we\u0026rsquo;re specifying that we want a medium font size \u0026amp; bold text.\nNext we serialize the card payload to JSON using asyncio.run.\nIn order to prepare the card object to be attached to our response, we will need to set the appropriate headers. In this case we need to wrap the JSON structure with the contentType header. We could use the response.attachments object to send any number of different file types to the user, but in this case we\u0026rsquo;ll set that value to application/vnd.microsoft.card.adaptive.\nThen we can assemble the response to the client. We\u0026rsquo;ll create a new instance of the Response() object, where we\u0026rsquo;ll define some necessary attributes.\nWe\u0026rsquo;ll set our response.text first. This will be the text message sent to the client. If the client supports adaptive cards, then this message won\u0026rsquo;t be shown to the user - however, it may still appear in pop notifications. If the client does not support adaptive cards, only this message will be shown to the user.\nNext we attach our complete card payload using response.attachments.\nLast but not least, we can return that response object - which is then sent back to the user.\nIn this case, we can see the bot responds back to our request with a simple card that displays the zip code.\nLet\u0026rsquo;s move onto building out the remainder of the card. For the purposes of this example, I\u0026rsquo;ll also be pulling out a few additional items from the OpenWeather API - including sunrise, sunset, pressure, and high/low temperatures.\nI\u0026rsquo;ll go ahead and show the full card build here:\ncard = AdaptiveCard() card.add(TextBlock(text=f\u0026#34;Weather for {city}\u0026#34;, size=\u0026#34;Medium\u0026#34;, weight=\u0026#34;Bolder\u0026#34;)) card.add(ColumnSet()) card.add(Column(width=\u0026#34;stretch\u0026#34;)) card.add(FactSet()) card.add(Fact(title=\u0026#34;Temp\u0026#34;, value=f\u0026#34;{temperature}\u0026#34;)) card.add(Fact(title=\u0026#34;Conditions\u0026#34;, value=f\u0026#34;{conditions}\u0026#34;)) card.add(Fact(title=\u0026#34;Wind\u0026#34;, value=f\u0026#34;{wind}\u0026#34;)) card.up_one_level() card.up_one_level() card.add(Column(width=\u0026#34;stretch\u0026#34;)) card.add(FactSet()) card.add(Fact(title=\u0026#34;High\u0026#34;, value=f\u0026#34;{high_temp}\u0026#34;)) card.add(Fact(title=\u0026#34;Low\u0026#34;, value=f\u0026#34;{low_temp}\u0026#34;)) card.add(Fact(title=\u0026#34;Humidity\u0026#34;, value=f\u0026#34;{humidity}\u0026#34;)) card_data = json.loads(asyncio.run(card.to_json())) If this looks a little messy, it is. There is a cleaner way to do this, which I\u0026rsquo;ll show in a moment.\nThe first thing to know is that the card JSON structure is hierarchical. So individual card items need to be added under the correct hierarchy - for example, you create a ColumnSet first, then add a Column underneath, then the items within that Column last.\nSee the below visualization of this card:\nTop Level ↳ Text Block ↳ ColumnSet ↳ Column ↳ Fact Set ↳ Fact ↳ Fact ↳ Fact ↳ Column ↳ Fact Set ↳ Fact ↳ Fact ↳ Fact So on our card, we first placed the title TextBlock. Underneath that we created a ColumnSet, which will contain two Columns.\nEach one of those Columns contains a FactSet, which is a key/value pair listing of information. And of course, we add each Fact pair underneath the FactSet.\nYou\u0026rsquo;ll notice after building one Column and FactSet, we use card.up_one_level() twice - to return back to the ColumnSet level, where we then add a second Column.\nThis can certainly get a little confusing at first, but I\u0026rsquo;ll show a more intuitive method in a moment.\nLet\u0026rsquo;s take a peak at what this looks like now:\nSub-cards \u0026amp; Better Card Building In this section we\u0026rsquo;ll do two quick things. First we\u0026rsquo;ll take a look at a simpler, more visual way of building the cards. Then we\u0026rsquo;ll also expand our example with sub-cards.\nThe adaptivecardsbuilder module supports a second way of building cards, which I personally find to be much easier to use.\nIn this method, we just create a single card.add() object - and feed it a list of all the items on the card.\nFor me, this makes it easier because you can add indentation to each list item - which helps keep track of where you are at in the hierarchical structure.\nTo navigate within the structure, we use \u0026quot;\u0026lt;\u0026quot; to go up one level, or \u0026quot;^\u0026quot; to return to the top level.\nI\u0026rsquo;ve re-written the earlier example with this new structure below:\ncard = AdaptiveCard() card.add( [ TextBlock(text=f\u0026#34;Weather for {city}\u0026#34;, size=\u0026#34;Medium\u0026#34;, weight=\u0026#34;Bolder\u0026#34;), ColumnSet(), Column(width=\u0026#34;stretch\u0026#34;), FactSet(), Fact(title=\u0026#34;Temp\u0026#34;, value=f\u0026#34;{temperature}F\u0026#34;), Fact(title=\u0026#34;Conditions\u0026#34;, value=f\u0026#34;{conditions}\u0026#34;), Fact(title=\u0026#34;Wind\u0026#34;, value=f\u0026#34;{wind} mph\u0026#34;), \u0026#34;\u0026lt;\u0026#34;, \u0026#34;\u0026lt;\u0026#34;, Column(width=\u0026#34;stretch\u0026#34;), FactSet(), Fact(title=\u0026#34;High\u0026#34;, value=f\u0026#34;{high_temp}F\u0026#34;), Fact(title=\u0026#34;Low\u0026#34;, value=f\u0026#34;{low_temp}F\u0026#34;), \u0026#34;^\u0026#34;, ActionSet(), ActionShowCard(title=\u0026#34;More Details\u0026#34;), ColumnSet(), Column(width=\u0026#34;stretch\u0026#34;), FactSet(), Fact(title=\u0026#34;Humidity\u0026#34;, value=f\u0026#34;{humidity}%\u0026#34;), Fact(title=\u0026#34;Pressure\u0026#34;, value=f\u0026#34;{pressure}\u0026#34;), \u0026#34;\u0026lt;\u0026#34;, \u0026#34;\u0026lt;\u0026#34;, Column(width=\u0026#34;stretch\u0026#34;), FactSet(), Fact(title=\u0026#34;Sunrise\u0026#34;, value=f\u0026#34;{sunrise}\u0026#34;), Fact(title=\u0026#34;Sunset\u0026#34;, value=f\u0026#34;{sunset}\u0026#34;), ] ) card_data = json.loads(asyncio.run(card.to_json())) Again, you\u0026rsquo;re welcome to use whichever method suits you - but I find this method to be a bit cleaner \u0026amp; easier to read/troubleshoot.\nYou can also see that I\u0026rsquo;ve added a new ActionSet, with a single action: ActionShowCard. This adds a nice little button to our card where we can optionally display additional information.\nHere\u0026rsquo;s what that looks like now:\nAnd if we expand that sub-card:\nSub-cards can be an easy way to hide excess information to keep the response clean \u0026amp; focus on the important information.\nWell that\u0026rsquo;s it for this blog post. There\u0026rsquo;s a lot more you can do with cards, including adding images, video, tables, dropdown menus, etc. But I just wanted to show a few examples of building cards. They can be a simple way to make chatbots a little less boring!\nEnjoy!\n","permalink":"https://0x2142.com/webex-chatbot-with-adaptivecards/","summary":"In this post, we\u0026rsquo;ll expand upon the previous Webex weather bot \u0026amp; add in AdaptiveCards to improve user interaction.","title":"[How to] Webex Chatbot (Part 2): Digging in to Adaptive Cards"},{"content":" Note: I may receive commissions for purchases made through links in this post. This is to help support my blog and does not have any impact on my recommendations.\nIn the video above, I walk through some of my recent upgrades to my home lab - which include two new Intel NUC 11 mini-PCs which will run ESX 7.0.\nSince most of what I did in the video is specific to my home lab setup, I didn\u0026rsquo;t feel like it made as much sense to write out an entire step-by-step blog for the process.\nThat being said, I did still want a place to post links to everything I used in the video - including the actual hardware components \u0026amp; the VMware community drivers. So here\u0026rsquo;s where I\u0026rsquo;ll keep that info!\nVMware Community Drivers Used Community driver for USB NIC Adapters Community driver for additional NIC support Intel NUC 11 Parts Intel NUC11 Performance Barebones Kit (NUC11PAHi7) Samsung 970 Evo Plus - 1Tb Crucial Memory - 64Gb Kit (2x32Gb) UGREEN 1Gb USB Ethernet Adapter ","permalink":"https://0x2142.com/home-lab-upgrade-intel-nuc11/","summary":"In the video above, I walk through some of my recent upgrades to my home lab - which include two new Intel NUC 11 mini-PCs which will run ESX 7.0.","title":"[Video] Upgrading my Home Lab - Intel NUC11 \u0026 ESX 7.0"},{"content":" Okay, so this post is ever-so-slightly off-topic from what I usually write about here - but I\u0026rsquo;m allowed to have fun sometimes, right?\nWith the holidays coming up, I\u0026rsquo;m getting ready to take some time off \u0026amp; relax with my favorite video games.\nAnd just in time, Satisfactory just released their new Update 5 content - which finally adds support for dedicated servers. So I\u0026rsquo;ll be able to more easily play with friends \u0026amp; family who are in different time zones.\nThis will be a quick guide, mostly derived from the Satisfactory Wiki with some additional steps/context.\nDisclaimer: The dedicated server software is pretty new \u0026amp; still a work in progress, so I imagine there is a good chance some of these steps may change. I\u0026rsquo;ll do my best to keep it updated!\nA Quick Note Before getting started, just a quick note about what is used for this setup \u0026amp; what assumptions are made.\nThis guide will walk through building a new dedicated server on Debian 11. There is additional information regarding other Linux distributions \u0026amp; Windows in the Satisfactory Wiki page linked above.\nThis guide assumes that you have some fairly basic level of Linux knowledge. You should be comfortable with connecting to a Linux server via SSH and editing files. If you need additional guidance, please consider checking out the video above where I walk through the whole process!\nThis guide will not look at installing Linux, or setting up port forwarding, etc. We will only be focusing on getting the dedicated server software installed \u0026amp; running.\nSetting up Static Networking By default my Debian 11 install was configured with a dynamic / DHCP address. While this might work for other use cases, we\u0026rsquo;ll want to set a static IP address for our game server.\nSo first we\u0026rsquo;ll open up our network config file, using the following command: sudo nano /etc/network/interfaces\nThen, we\u0026rsquo;ll update our network config to look like the one below:\nauto ens192 iface ens192 inet static address 192.168.1.20 netmask 255.255.255.0 gateway 192.168.1.1 dns-nameservers 8.8.8.8 Please note, the values listed above are for example only. You\u0026rsquo;ll need to assign the correct addressing from your own local network. If you\u0026rsquo;re unsure about the interface name (mine is ens192), you can use the command ip address to list current network interfaces \u0026amp; find yours.\nOnce we\u0026rsquo;ve edited our network configuration, we\u0026rsquo;ll need to restart the network process for the changes to take effect: sudo systemctl restart networking\nNote: Assuming the static IP you configure is different than the current dynamically-assigned IP, you will lose connection with your server \u0026amp; need to reconnect using the new IP address.\nInstall Steamcmd Next, we\u0026rsquo;ll need to install the Steam commandline utility so we can download \u0026amp; manage our server.\nOn Debian, the default repositories don\u0026rsquo;t include this package. So we\u0026rsquo;ll need to add a third-party package repository.\nWe\u0026rsquo;ll edit our active/configured repositories with the following command: sudo nano /etc/apt/sources.list\nThen add the following at the end of the file:\ndeb http://mirrors.linode.com/debian bullseye main non-free deb-src http://mirrors.linode.com/debian bullseye main non-free We\u0026rsquo;ll also need to enable multiarchitecture support: sudo dpkg --add-architecture i386\nOnce that\u0026rsquo;s done, we\u0026rsquo;ll update our local cache to include the newly available list of packages: sudo apt-get update\nThen we can install steamcmd: sudo apt-get install steamcmd\nWe will likely want to create a dedicated user to run our game server. We\u0026rsquo;ll create a user called steam using the following command: sudo useradd -m steam\nThen we can switch to our steam user account: su steam\nOptionally, we can create a shortcut to the steamcmd executable within the steam user\u0026rsquo;s home directory: ln -s /usr/games/steamcmd steamcmd\nDownload / Install Satisfactory Dedicated Server Now we can pull down a copy of the server software.\nAssuming you\u0026rsquo;ve created the steamcmd shortcut as shown above, we can use the following command to pull down the server software: ./steamcmd +force_install_dir ~/satisfactory +login anonymous +app_update 1690800 validate +quit\nThis command does a few things:\n+force_install_dir - Tells steamcmd which directory to place the game files in, in this case ~/satisfactory +login anonymous - Skip logging in with a steam user account, since we just need to download an update +app_update 1690800 - This is the Satisfactory server application ID, so steamcmd knows which software to install validate - Runs a validation to ensure that the current files match what\u0026rsquo;s hosted on Steam +quit - Asks steamcmd to log off of Steam when everything is finished. If we wanted to use the experimental branch, we would also add the flag: -beta experimental\nOnce that command finishes running, the Satisfactory software should be downloaded!\nWe can try to test this out by running the software manually first. Using the following command to move into the game server folder, and execute the software: cd ~/satisfactory \u0026amp;\u0026amp; ./FactoryServer.sh\nAssuming it runs, we can press Ctrl-C at any point to stop the game server.\nRegistering a Background Service Okay, so we were able to run the server software manually for a quick test - but we\u0026rsquo;ll want to configure it to run as a background service that will start \u0026amp; stop with our Linux system. We\u0026rsquo;ll use the info located on the Satisfactory Wiki.\nSo we\u0026rsquo;ll create a new service definition using the following command: sudo nano /etc/systemd/system/satisfactory.service\nThen paste the following into that file:\n[Unit] Description=Satisfactory dedicated server Wants=network-online.target After=syslog.target network.target nss-lookup.target network-online.target [Service] Environment=\u0026#34;LD_LIBRARY_PATH=./linux64\u0026#34; ExecStartPre=/usr/games/steamcmd +force_install_dir \u0026#34;/home/steam/SatisfactoryDedicatedServer\u0026#34; +login anonymous +app_update 1690800 validate +quit ExecStart=/home/steam/SatisfactoryDedicatedServer/FactoryServer.sh User=steam Group=steam StandardOutput=journal Restart=on-failure WorkingDirectory=/home/steam/SatisfactoryDedicatedServer [Install] WantedBy=multi-user.target Please note, if you used a different user account name (instead of steam), or downloaded the game in a different directory - you will need to modify those values in the above text.\nOnce that\u0026rsquo;s done, we can enable our new service to start \u0026amp; stop with the machine: sudo systemctl enable satisfactory.service\nThen we can start the game server: sudo systemctl start satisfactory.service\nAfter a few seconds, we can check the service status here: sudo systemctl status satisfactory.service\nIf everything looks good, you should see output similar to below:\n● satisfactory.service - Satisfactory dedicated server Loaded: loaded (/etc/systemd/system/satisfactory.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2021-12-01 11:45:31 EST; 18s ago Process: 3679 ExecStartPre=/usr/games/steamcmd +force_install_dir /home/steam/SatisfactoryDedicatedServer +login anonymous +app_update 1690800 validate +quit (code=exited, status=0/SUCCESS) Main PID: 3749 (FactoryServer.s) Tasks: 21 (limit: 7087) Memory: 1.0G CPU: 18.526s CGroup: /system.slice/satisfactory.service ├─3749 /bin/sh /home/steam/SatisfactoryDedicatedServer/FactoryServer.sh └─3756 /home/steam/SatisfactoryDedicatedServer/Engine/Binaries/Linux/UE4Server-Linux-Shipping FactoryGame Dec 01 11:45:33 Satisfactory FactoryServer.sh[3756]: [2021.12.01-16.45.33:178][ 0]LogAIModule: Creating AISystem for world DedicatedserverEntry Dec 01 11:45:33 Satisfactory FactoryServer.sh[3756]: [2021.12.01-16.45.33:178][ 0]LogLoad: Game class is \u0026#39;BP_GameModeMenu_C\u0026#39; Dec 01 11:45:33 Satisfactory FactoryServer.sh[3756]: [2021.12.01-16.45.33:179][ 0]LogReplicationGraph: Display: SetActorDiscoveryBudget set to 20 kBps (5333 bits per network tick). Dec 01 11:45:33 Satisfactory FactoryServer.sh[3756]: [2021.12.01-16.45.33:205][ 0]LogNetCore: DDoS detection status: detection enabled: 0 analytics enabled: 0 Dec 01 11:45:33 Satisfactory FactoryServer.sh[3756]: [2021.12.01-16.45.33:205][ 0]LogInit: BSD IPv4/6: Socket queue. Rx: 262144 (config 131072) Tx: 262144 (config 131072) Dec 01 11:45:33 Satisfactory FactoryServer.sh[3756]: [2021.12.01-16.45.33:205][ 0]LogNet: Created socket for bind address: :: on port 7777 Dec 01 11:45:33 Satisfactory FactoryServer.sh[3756]: [2021.12.01-16.45.33:205][ 0]PacketHandlerLog: Loaded PacketHandler component: DTLSHandlerComponent () Dec 01 11:45:33 Satisfactory FactoryServer.sh[3756]: [2021.12.01-16.45.33:205][ 0]PacketHandlerLog: Loaded PacketHandler component: Engine.EngineHandlerComponentFactory (StatelessConnectHandlerComponent) Dec 01 11:45:33 Satisfactory FactoryServer.sh[3756]: [2021.12.01-16.45.33:205][ 0]LogNet: GameNetDriver EOSNetDriver_2147482561 IpNetDriver listening on port 7777 Dec 01 11:45:33 Satisfactory FactoryServer.sh[3756]: [2021.12.01-16.45.33:205][ 0]LogWorld: Bringing World /Game/FactoryGame/Map/DedicatedserverEntry.DedicatedserverEntry up for play (max tick rate 30) at 2021.12.01-11.45.33 Joining the Server We\u0026rsquo;re almost there!\nAfter the game server has been set up \u0026amp; running, we\u0026rsquo;ll need to perform the initial setup within the game itself.\nSo it\u0026rsquo;s time to launch Satisfactory \u0026amp; setup our game server!\nIn the screenshot above, the title screen now has an option for Server Manager. We\u0026rsquo;ll click that. You should then see an option to add server, which will display the following dialog:\nHere\u0026rsquo;s where we put in the IP or hostname of our local dedicated server.\nAs a reminder: If you have someone outside of your local network, they\u0026rsquo;ll need a different address to connect. Again, this post won\u0026rsquo;t dive into port forwarding or allowing access through your home router - there are plenty of good tutorials out there already!\nAfter we successfully connect to our server, we\u0026rsquo;ll be prompted to claim it. Since it\u0026rsquo;s newly created, this is where we can set our admin login \u0026amp; the server name.\nFirst we\u0026rsquo;ll be asked to provide a name to claim the server: Then an administrator password - don\u0026rsquo;t forget to save this somewhere safe!\nOkay. So now we\u0026rsquo;ll have a few options. First we\u0026rsquo;ll check out the settings page:\nHere we can modify our server name or update our admin password if we need to.\nWe can also set a Player password - This will keep people from joining your server unless they know the password.\nInterestingly enough, we can opt to have the game pause when no players are connected - or uncheck the box to let your factory continue building even when no one is playing.\nAnd lastly an option to autosave whenever someone disconnects from the server.\nIf we\u0026rsquo;re good with those settings - we can start a new game session!\nWe\u0026rsquo;ll head over to the Create Game tab:\nThis should look pretty similar to the normal game. Select the starting area \u0026amp; give the session a name.\nWe\u0026rsquo;ll also have a checkbox to automatically enter the game after it\u0026rsquo;s done being created. If you don\u0026rsquo;t check this box, you can enter the server from the Server Status page:\nHere we can get a quick view of the current server status - which tier we\u0026rsquo;re on, the current milestone, and whether or not anyone is currently connected.\nDown in the bottom left, we\u0026rsquo;ll have our Join Game button.\nTime to play!\nOkay - and that about wraps it up. I hope this post was helpful for anyone else looking to build a dedicated server. The process was surprisingly easy, and I greatly appreciate the effort that the Coffee Stain devs have put into this!!\nHappy building!\n","permalink":"https://0x2142.com/how-to-set-up-a-satisfactory-dedicated-game-server/","summary":"In a brief detour from my usual content, we\u0026rsquo;\u0026rsquo;ll take a quick look at how to build a new Satisfactory dedicated server on Debian Linux.","title":"[How To] Setup a Satisfactory Dedicated Server on Debian Linux"},{"content":" I\u0026rsquo;ve been spending some of my time lately working on a new project that involves an interactive chatbot - where you can send commands to the bot \u0026amp; ask for it to take certain actions or return information.\nI didn\u0026rsquo;t previously have a ton of experience building something like this. Mostly what I\u0026rsquo;ve done before is just using Webex or Discord APIs to send alerts or messages to a room.\nAs with anything new - I\u0026rsquo;ve stumbled my way through a couple of things, but have been enjoying the challenge of trying something different. Building out some of the more interactive parts of the bot hasn\u0026rsquo;t been quite as difficult as I originally imagined, and some of the newer modules \u0026amp; SDKs have made the process much simpler.\nWith all that being said - I wanted to throw together a quick guide on how to get started building a basic chatbot, in case anyone else is interested but worried that it may be too complicated.\nRepo here for all the sample code used below\nStep 1: Getting API Keys So the first thing we\u0026rsquo;ll worry about is getting signed up for a Webex developer account \u0026amp; generating API keys.\nSo head on over to the Webex Developer site \u0026amp; log in (or sign up for a new account).\nOnce logged in, we\u0026rsquo;ll go to the upper-right corner of the screen and click on our user icon - and select \u0026ldquo;My Webex Apps\u0026rdquo; from the drop down:\nThen we\u0026rsquo;ll be asked what type of app we\u0026rsquo;re creating. In this case, we\u0026rsquo;ll select bot:\nNext we\u0026rsquo;ll need to provide some details about our bot. This includes a display name, username, image, and some details. Once the bot is completed, we have the ability to publish it via the Webex App Hub where anyone could interact with it. If you\u0026rsquo;re looking to do this, make sure you give a good description of your bot!\nAfter we finish all that, we\u0026rsquo;ll finally get our API key!\nWebhook vs Websocket: Which to use? Next, I wanted to cover the two primary methods of sending \u0026amp; receiving messages from the Webex APIs: Webhooks and websockets.\nWebhooks Likely webhooks are the method most people are familiar with. Using this method, we need to run out bot code on a publicly reachable URL. When we run our bot, one of our first actions will be sending a request to the Webex APIs with our bot URL. This way, every time Webex receives a message from a user that wants to interact with our bot, the Webex cloud knows where to send that chat message.\nNow, the potential downside of using webhooks is that we need to 1) manage a web server and 2) expose the bot publicly to the internet. Both of these could be overcome with easy workarounds, if needed. For example, we could use something like FastAPI to quickly build our web listener \u0026amp; handle incoming POST requests. We could also use a tunneling method (like ngrok) to avoid having to directly open ports to our webserver.\nWebsockets On the other hand, we could use websockets to accomplish the same function. With a websocket, we\u0026rsquo;re instead opening a direct, persistent TCP connection to the Webex APIs. Through this persistent connection, we can send \u0026amp; receive messages very quickly and easily.\nThis method offers a few advantages over webhooks. Primarily not having to expose our app publicly to the internet. Websockets act almost similar to something like ngrok, where we create an outbound tunnel - except in this case, only Webex knows about it and has access to it (where ngrok is still providing you a public URL that anyone could hit, if they knew about it). Additionally, this method removes the need for maintaining a web server \u0026amp; having to handle all of the incoming HTTP requests in your bot\u0026rsquo;s code. There may also be a handful of API calls which are only supported via websockets today as well - like having your bot show that they\u0026rsquo;ve read a message, for example.\nI\u0026rsquo;ve also found that the websocket method seems to be much more responsive, likely because there is a persistent TCP connection open - and you no longer have to rely on a full HTTP session establishment \u0026amp; teardown for every message to the bot.\nHistorically, websockets were only officially supported via the Webex JavaScript SDK. However, it seems recently that someone has built a Python equivalent earlier this year - which we\u0026rsquo;ll be using throughout this blog post. You can find that package here\nGetting started: Registering the Bot with Webex So we\u0026rsquo;ll start with some basic functionality. Let\u0026rsquo;s grab the modules we need, apply our API key, and see if we can get a response from our bot.\nLuckily, the module we\u0026rsquo;re using makes this super easy. So let\u0026rsquo;s install via pip:\npip install webex_bot Once that\u0026rsquo;s installed, we\u0026rsquo;ll create a new Python file - I\u0026rsquo;ll call mine bot.py\nIn that file, we\u0026rsquo;ll start our script like this:\nfrom webex_bot.webex_bot import WebexBot api_token = \u0026#34;\u0026lt;TOKEN_HERE\u0026gt;\u0026#34; bot = WebexBot(api_token, approved_domains=[\u0026#34;0x2142.com\u0026#34;]) bot.run() Looks easy enough, yeah? We\u0026rsquo;ll import our webex_bot module first, as always. Then we\u0026rsquo;ll define our API key. Obviously this can be stored as an environmental variable or another more secure location, but we\u0026rsquo;ll define it here for demonstration.\nThen we create a new instance of WebexBot, by providing our API key and an optional approved_domains value. In my case, I\u0026rsquo;ve provided the 0x2142.com domain - which means the bot will reject messages from anyone who isn\u0026rsquo;t from that organization.\nLast, we start our bot with bot.run().\nLet\u0026rsquo;s go ahead and run our bot with python bot.py and see what happens!\nYou should see something similar to this:\nmatt@0x2142:~/demo-webex-bot$ python bot.py 2021-10-25 15:42:52 [INFO] [webex_bot.webex_bot.webex_bot.__init__]:39 Registering bot with Webex cloud 2021-10-25 15:42:52 [INFO] [restsession.webexteamssdk.restsession.user_agent]:167 User-Agent: webexteamssdk/0+unknown {\u0026#34;implementation\u0026#34;: {\u0026#34;name\u0026#34;: \u0026#34;CPython\u0026#34;, \u0026#34;version\u0026#34;: \u0026#34;3.8.10\u0026#34;}, \u0026#34;system\u0026#34;: {\u0026#34;name\u0026#34;: \u0026#34;Linux\u0026#34;, \u0026#34;release\u0026#34;: \u0026#34;5.10.16.3-microsoft-standard-WSL2\u0026#34;}, \u0026#34;cpu\u0026#34;: \u0026#34;x86_64\u0026#34;} 2021-10-25 15:42:52 [WARNING] [command.webex_bot.models.command.__init__]:33 no card actions data so no entry for \u0026#39;callback_keyword\u0026#39; for echo 2021-10-25 15:42:52 [INFO] [command.webex_bot.models.command.set_default_card_callback_keyword]:47 Added default action for \u0026#39;echo\u0026#39; callback_keyword=callback___echo 2021-10-25 15:42:53 [INFO] [webex_bot.webex_bot.webex_bot.get_me_info]:74 Running as bot \u0026#39;0x2142 Bot\u0026#39; with email [\u0026#39;0x2142@webex.bot\u0026#39;] 2021-10-25 15:42:54 [INFO] [webex_websocket_client.root._connect_and_listen]:151 Opening websocket connection to wss://mercury-connection-partition2-a.wbx2.com/v1/apps/wx2/registrations/06b5c858-174a-4977-a268-04530d777563/messages 2021-10-25 15:42:54 [INFO] [webex_websocket_client.root._connect_and_listen]:154 WebSocket Opened. So long as we continue to run this script in the foreground - we\u0026rsquo;ll be able to see the live log of anything that happens, including messages to our bot. So for now, we\u0026rsquo;ll leave this up \u0026amp; running.\nNow we should be able to try sending our bot a message in Webex.\nIn the Webex app, if we do a quick search for our bot (by email or name), we should see it pop up pretty quickly:\nWith our new space, we can interact with our bot. I\u0026rsquo;ll send a \u0026ldquo;Hello\u0026rdquo; message:\nAnd the webex_bot module will automatically respond back with a built-in help card.\nCurrently the bot only supports one command: echo. We\u0026rsquo;ll add our own commands shortly, but for now let\u0026rsquo;s give that a try:\nSo in the example above, I used the echo command. The bot returns a card with an input field. Once you fill out the textbox \u0026amp; hit submit, the bot will echo back whatever text was provided!\nSo now we have a running bot. But what about implementing our own commands? Let\u0026rsquo;s take a look!\nCreating custom bot commands Now we get to the fun part!! To demonstrate, let\u0026rsquo;s add a command to ask our bot for the current weather conditions. To accomplish this, we\u0026rsquo;ll use the OpenWeather APIs.\nSo to begin - We\u0026rsquo;ll probably want a separate Python file for each command we want to add. First we\u0026rsquo;ll add weather.py to our current directory.\nNext, we\u0026rsquo;ll throw in a bit of boilerplate code that will be used for each command:\nfrom webex_bot.models.command import Command import logging log = logging.getLogger(__name__) class WeatherByZIP(Command): def __init__(self): super().__init__( command_keyword=\u0026#34;weather\u0026#34;, help_message=\u0026#34;Get current weather conditions by ZIP code.\u0026#34;, card=None, ) def execute(self, message, attachment_actions, activity): return f\u0026#34;Got the message: {message}\u0026#34; So first we\u0026rsquo;ll import Command from our webex_bot module, which will be used to create our custom command class.\nNext we create a new class, in this case WeatherByZIP, and inherit the Command class from webex_bot. Within this new class, we\u0026rsquo;ll create our __init__ function \u0026amp; define some information about our command.\nSo using super().__init__(), we\u0026rsquo;ll provide what keyword should trigger this command, any help message to be provided to the user, and an optional card. We saw how the card could work earlier with the echo command - but to keep this module simple, we\u0026rsquo;ll go without a card for now.\nAfter that - we just need to create an execute function. This is what gets called by webex_bot when our command is triggered. By default, webex_bot will pass us the user\u0026rsquo;s message - and, in the case of an action (like a card submission), it will provide those details.\nTwo things to keep in mind with our execute function:\nthe message variable will contain the user\u0026rsquo;s message with the command keyword removed. So in our case, if the user\u0026rsquo;s message was \u0026ldquo;weather 12345\u0026rdquo; - then the message variable would just contain \u0026ldquo;12345\u0026rdquo;. Any text we return from our function will be sent via the bot as a chat response. If we wanted to provide extras, like cards or attachments, we would need to use the webex_bot Response object. I might cover this in a separate blog post later Okay! So right now our new command is configured to just echo back any test that is sent to the bot - similar to the echo command earlier, except without the card.\nWe\u0026rsquo;ll add our weather functionality shortly - but let\u0026rsquo;s first add our command to the bot \u0026amp; give it a quick test.\nIn our bot.py file, we\u0026rsquo;ll just need to import our command module and call bot.add_command():\nfrom webex_bot.webex_bot import WebexBot from weather import WeatherByZIP api_token = \u0026#34;\u0026lt;TOKEN_HERE\u0026gt;\u0026#34; bot = WebexBot(api_token, approved_domains=[\u0026#34;0x2142.com\u0026#34;]) bot.add_command(WeatherByZIP()) bot.run() That\u0026rsquo;s it! Now we should be able to try it out. We\u0026rsquo;ll restart our bot, and send the command weather 12345:\nSure enough, our bot gets the message, passes it to our custom command, and returns the stripped message - all as expected.\nSo let\u0026rsquo;s add our weather query \u0026amp; actually make this thing work.\nUsing the OpenWeather APIs, I\u0026rsquo;ll create a new API key - then create a variable in the command module called OPENWEATHER_KEY. Then, I\u0026rsquo;ve updated my execute function to look like this:\ndef execute(self, message, attachment_actions, activity): # Message may include spaces. Strip whitespace: zip_code = message.strip() # Define our URL, with requested ZIP code \u0026amp; API Key url = \u0026#34;https://api.openweathermap.org/data/2.5/weather?\u0026#34; url += f\u0026#34;zip={zip_code}\u0026amp;units=imperial\u0026amp;appid={OPENWEATHER_KEY}\u0026#34; # Query weather response = requests.get(url) weather = response.json() # Pull out desired info city = weather[\u0026#39;name\u0026#39;] conditions = weather[\u0026#39;weather\u0026#39;][0][\u0026#39;description\u0026#39;] temperature = weather[\u0026#39;main\u0026#39;][\u0026#39;temp\u0026#39;] humidity = weather[\u0026#39;main\u0026#39;][\u0026#39;humidity\u0026#39;] wind = weather[\u0026#39;wind\u0026#39;][\u0026#39;speed\u0026#39;] response_message = f\u0026#34;In {city}, it\u0026#39;s currently {temperature}F with {conditions}. \u0026#34; response_message += f\u0026#34;Wind speed is {wind}mph. Humidity is {humidity}%\u0026#34; return response_message So because of the way that message is passed to us, we\u0026rsquo;ll first need to strip whitespace. This is because when the user enters a command like \u0026ldquo;weather 12345\u0026rdquo;, webex_bot will only remove the \u0026ldquo;weather\u0026rdquo; portion - leaving us with \u0026quot; 12345\u0026quot;. So by stripping that value, we\u0026rsquo;re left with just the numeric ZIP Code: \u0026ldquo;12345\u0026rdquo;.\nNext we assemble our URL, supplying our parameters: zip, units, and appid. Zip is the numeric ZIP code provided by our user, units will be either imperial or metric, and appid is our API key.\nAfter that, it\u0026rsquo;s as simple as sending the GET request \u0026amp; parsing the data. In the code above, I assemble the final text response into response_message which is then returned to the user.\nAll that being done - Let\u0026rsquo;s try it out!!\nIn this example, I used the ZIP code for Richfield, OH - and our bot quickly returned that information to us.\nAnd that\u0026rsquo;s it! In just a few steps we created a new Webex bot \u0026amp; added custom commands. Once we have that process down, it\u0026rsquo;s easy enough to continue extending our bot by adding additional commands.\nThere are obviously more advanced use cases - and a whole lot of fun that comes with using Adaptive Cards\u0026hellip; But I hope this post was helpful in getting you started!\nUpdate 2022/03/12 - If you\u0026rsquo;re interested in seeing additional content around Cards, please check out the new blog post \u0026amp; video here\n","permalink":"https://0x2142.com/how-to-building-a-basic-webex-chatbot/","summary":"Let\u0026rsquo;s build a simple chatbot using Cisco Webex that can query the weather!","title":"[How To] Building a Simple Webex Chatbot with Python Websockets \u0026 OpenWeather APIs"},{"content":"I guess it\u0026rsquo;s that time of year again, where I\u0026rsquo;m starting to see the same discussion points pop up quite a bit once more:\n\u0026ldquo;As a network administrator, do I really need to learn to code?\u0026rdquo;\n\u0026ldquo;How much to I need to learn?\u0026rdquo;\n\u0026ldquo;This will NEVER be in enterprise networks\u0026rdquo;\nWell, today I\u0026rsquo;m here to give some thoughts. As with all my content, I reserve the right to be wrong or not 100% in alignment with everyone else\u0026rsquo;s view of the world.\nDo I need to learn code? Nope.\nWait really?!? Correct, no one can make you learn if you don\u0026rsquo;t have an interest in it.\nOkay, but will I benefit from it? 100% yes you will. To what degree may vary, but I think it\u0026rsquo;s worth a look.\nWell how much do I need to know? As the network architect might say, it depends.\nIs automation coming for my job? ¯\\_(ツ)_/¯\nSo what\u0026rsquo;s the right answer here? It depends on what\u0026rsquo;s right for you - what you\u0026rsquo;re interested in, what you think will help you in your job, and what you think will help you succeed.\nYes, it\u0026rsquo;s true that much of the networking industry has been shifting focus to automation \u0026amp; programmability. Open APIs have become much more important in recent years as we seek to expand our capabilities through custom code \u0026amp; integrations. I don\u0026rsquo;t see this slowing down anytime soon.\nBut will that spell the end of the network engineer as we know it? I don\u0026rsquo;t know that anyone can 100% say definitively, but I\u0026rsquo;m willing to bet that network engineers aren\u0026rsquo;t going anywhere anytime soon. The role will definitely change \u0026amp; evolve over time (as it already has been, and will continue), but truly go away? I think not.\nThe benefits of programmability I\u0026rsquo;m sure most people are familiar with a lot of this by now, but why should we care about automation? Well there are lots of reasons! For example, here\u0026rsquo;s a few:\nReducing human error Reducing time spent on repetitive tasks Accomplishing things that aren\u0026rsquo;t natively possible in a product Adding custom functionality or integrations with other systems And more! How much you should learn This one REAALLY depends. To throw out some ideas, here are some factors that could help determine how much you\u0026rsquo;ll need to know:\nHow much you enjoy it How much your company cares or has any automation initiatives The type (and age) of gear you\u0026rsquo;re working with Whether or not you have an in-house development or devops team So on one side, I think that most people who get into writing Python \u0026amp; find that they truly enjoy it will likely end up transitioning to a more developer-focused role - whether that be a network-centric devops role or pure development role. Or simply just seeking out more ways to focus on programmability within their current network engineering role. These types of people are finding that they love solving problems \u0026amp; being able to have the creative freedom to write whatever code they want to accomplish a task. They\u0026rsquo;ve found something that they enjoy - Good for them!\nOn the other side, even if a network engineer isn\u0026rsquo;t going to write code very often - just knowing the fundamentals can be hugely beneficial. For example, as a network engineer, have you ever had to learn how VMware works? Likely you didn\u0026rsquo;t feel the need to become a VMware administrator overnight - but understanding how a vSwitch works has helped you support those teams. How much easier did it become to communicate with the VMware admins?\nWith programmability, it\u0026rsquo;s much the same. If you\u0026rsquo;re not going to dive in, that\u0026rsquo;s okay. But you\u0026rsquo;ll likely benefit from a good overall understanding of how things work. For me personally, it was easy to recognize the benefit when I was a network administrator. Application team puts in a ticket saying something isn\u0026rsquo;t working right? Well guess who understands API calls, HTTP requests, and JSON/XML? It suddenly became much easier to help find root cause, and often easier to shift blame away from the network. Plus, when you speak with an application/development team \u0026amp; you can use their terms, they feel like you understand them. This helps reduce friction between teams. In my experience, what I found was that those application teams would then ask for my help more often - because I was willing to go further than \u0026ldquo;checked the firewall, it\u0026rsquo;s not the network\u0026rdquo;.\nIn my opinion, whether or not you learn to code \u0026amp; how much you learn comes down to your goals. Can you keep being a network admin \u0026amp; avoid code? Sure - just like you could continue being a network admin \u0026amp; never learn design, business goals, or what other teams in your company do. But will learning those things expand your knowledge \u0026amp; give you a better big-picture view? Yup. Would learning those things give you an advantage? Absolutely.\nWhat happens when you run into a difficult challenge where manual effort would be reckless? Maybe you pass off the job to a development team to write a utility or some custom integration. But guess what happens next? That development team doesn\u0026rsquo;t understand how your network gear operates. Again, this situation really benefits from having an individual who can speak both languages. Even if you don\u0026rsquo;t understand functions \u0026amp; classes, you might be able to read API docs for your network gear and help guide the developer through where to start.\nWhat about that in-between state? Maybe you want to learn to code a bit, but not 100% of the time. What then? Well, could you pull \u0026amp; terminate ethernet cables without a cable tester? Yes, but would you be more efficient with one? Also yes. Similarly, being able to code can just be as simple as adding another tool to your skill set. Would you automate a single port change? Nope. But what if you find yourself making that same port change several times a week? That\u0026rsquo;s when you pull out your Python skills and write a quick script.\nAnd for those who would rather just not do any of it? You\u0026rsquo;re fine. How many parts of the Internet still run legacy protocols? Someone has to support that. And there are companies out there who want commercially supported products, so they have someone to blame. Vendors know this, which is why we end up with big, centralized controllers \u0026amp; complex GUI apps to help slowly pull networking out of the CLI days. But you can\u0026rsquo;t tell me that SD-Access, ACI, or EVPN are simple technologies - companies will still benefit from employing people who understand the underlying technologies \u0026amp; protocols. Let\u0026rsquo;s be honest: automated or not, things still break - and someone will need to have the knowledge to fix it.\nSo what\u0026rsquo;s the answer here? ¯\\_(ツ)_/¯\nIt seems like these debates continue because everyone comes from different backgrounds \u0026amp; has different experiences. Some people work in companies that would never entertain the idea of custom code, where others may find their team being pushed to become more agile \u0026amp; learn Python to automate tasks. Neither approach is wrong, but of course our human instinct is that we have to assert that everyone else must be wrong, since their view doesn\u0026rsquo;t match ours. The world is never a one-size-fits-all for every situation, yet for some reason we\u0026rsquo;re all inclined to think it is.\nAll that being said, companies are going to adopt this stuff at different paces. Some find immediate value in it now, and some are scared or just set in their ways. Again, neither is wrong - but they both have their own advantages \u0026amp; disadvantages.\nAnd that\u0026rsquo;s why I say - if you don\u0026rsquo;t want to learn any of this, you have nothing to fear. There will always be organizations at different stages of the cycle.\nBut if you do learn? At worst, you gain knowledge outside of your primary domain. At best, you gain a new tool to leverage \u0026amp; find ways to improve your work.\nAt the end of the day, you have to focus on what\u0026rsquo;s right for you, your career, and your current employer. And that answer is going to be different for everyone.\n","permalink":"https://0x2142.com/do-i-really-need-to-learn-to-code/","summary":"Is learning Python really necessary for your networking career? Let\u0026rsquo;s investigate","title":"Network Engineering: Do I REALLY Need to Learn to Code?"},{"content":" I\u0026rsquo;ve been getting a handful of questions lately on the process of bringing a Cisco Catalyst 8000v or CSR 1000v into an SD-WAN environment. So I figured maybe I should put something together to share.\nOn a related note - I\u0026rsquo;ve been debating for a while doing a blog and/or video on building out a Cisco Viptela SD-WAN lab in EVE-NG. This would (tentatively) include everything from building controllers, bringing up remote sites, and template/policy configs. If this is something you might get value from, please let me know!! I\u0026rsquo;m looking for some motivation :)\nOkay - all that being said, let\u0026rsquo;s go ahead and get started.\nNote: This guide should work with CSR 1000v devices as well. But it will NOT be 100% accurate for physical ISR/IOS-XE routers, as there are some additional steps with certificates \u0026amp; the Plug and Play portal to get those running.\nTopology So to start with, figured I would share the topology that I\u0026rsquo;m working from. If you read my last post you might recognize this, but with an added location. This new location (site id 400) contains our Catalyst 8000v VM, running IOS-XE version 17.04.01a.\nController or Autonomous Mode? Back in the earlier days of IOS-XE SD-WAN, there used to be two separate software images to load on your network appliance - one for traditional IOS-XE, and one for SD-WAN code.\nWith the newer releases of IOS-XE, we\u0026rsquo;re now getting a unified image that contains both software sets. So our options now are two modes: autonomous (traditional IOS-XE) or controller (SD-WAN).\nOne way we can check this, is by running a show version and looking for Router operating mode:\nRouter# show version \u0026lt;-- Output omitted --\u0026gt; cisco C8000V (VXE) processor (revision VXE) with 2035355K/3075K bytes of memory. Processor board ID XXXXXXXXXXX Router operating mode: Autonomous 4 Gigabit Ethernet interfaces 32768K bytes of non-volatile configuration memory. 3965744K bytes of physical memory. 5234688K bytes of virtual hard disk at bootflash:. Configuration register is 0x2102 Okay, and looks like the Catalyst 8000v image I\u0026rsquo;m using booted up in autonomous mode. No big deal, we can change modes pretty easily!\nSo in normal exec-mode, we\u0026rsquo;ll use the command controller-mode enable:\nRouter# controller-mode enable Enabling controller mode will erase the nvram filesystem, remove all configuration files, and reload the box! Ensure the BOOT variable points to a valid image Continue? [confirm] As noted in the snippet above - this command will erase the config! So you will want to take a backup snapshot of your existing configuration, if this is already being used. In my case, it\u0026rsquo;s a brand new VM - so we\u0026rsquo;ll continue on.\nOnce the device is back online, we\u0026rsquo;ll log in with the default login of admin/admin. Note that you will be forced to change this upon first login.\nUser Access Verification Username: admin Password: Default admin password needs to be changed. Enter new password: Confirm password: Router# And just for fun, we\u0026rsquo;ll run a show version again to ensure we\u0026rsquo;re in controller mode:\nRouter# show version \u0026lt;-- Output omitted --\u0026gt; cisco C8000V (VXE) processor (revision VXE) with 2035355K/3075K bytes of memory. Processor board ID XXXXXXXXXXX Router operating mode: Controller-Managed 4 Gigabit Ethernet interfaces 32768K bytes of non-volatile configuration memory. 3965756K bytes of physical memory. 5234688K bytes of virtual hard disk at bootflash:. Configuration register is 0x2102 Catalyst 8000v Initial Config So since this is a lab environment, and I\u0026rsquo;m not trying to provide any Day0 provisioning files - I\u0026rsquo;ll have to complete some manual configuration to get started.\nFirst, we\u0026rsquo;ll start with some SD-WAN specific config (site id, org name, etc). I\u0026rsquo;ll be using the values that apply to my lab, so be sure to change these in yours!\nNote: If you haven\u0026rsquo;t used IOS-XE SD-WAN previously, be aware that \u0026ldquo;conf t\u0026rdquo; doesn\u0026rsquo;t work! In SD-WAN/controller mode, you\u0026rsquo;ll use \u0026ldquo;config-transaction\u0026rdquo;. In this mode, all changes will need to be committed before they\u0026rsquo;re applied to the device.\nRouter# config-transaction ! Set hostname Router(config)# hostname Cat8k-Site400 ! Required SD-WAN system configs Router(config)# system Router(config-system)# organization-name \u0026#34;SDWAN-LAB\u0026#34; Router(config-system)# site-id 400 Router(config-system)# vbond 192.168.99.241 Router(config-system)# system-ip 10.10.10.237 Router(config-system)# commit Commit complete. Cat8k-Site400# Once we have those basics configured, at a minimum we\u0026rsquo;ll also need to configure our internet-facing tunnel interface. This allows our Catalyst 8000v to communicate with our control plane for bring-up.\nCat8k-Site400#config-transaction ! Config physical interface (This is a lab, so I\u0026#39;m using a static IP) Cat8k-Site400(config)# interface GigabitEthernet1 Cat8k-Site400(config-if)# ip address 192.168.99.237 255.255.255.0 Cat8k-Site400(config-if)# no shut ! Config tunnel interface Cat8k-Site400(config-if)# interface Tunnel1 Cat8k-Site400(config-if)# no shut Cat8k-Site400(config-if)# ip unnumbered GigabitEthernet1 Cat8k-Site400(config-if)# tunnel source GigabitEthernet1 Cat8k-Site400(config-if)# tunnel mode sdwan Cat8k-Site400(config-if)# exit ! SD-WAN tunnel config Cat8k-Site400(config)# sdwan Cat8k-Site400(config-sdwan)# interface GigabitEthernet1 Cat8k-Site400(config-interface-GigabitEthernet1)# tunnel-interface Cat8k-Site400(config-tunnel-interface)# encapsulation ipsec Cat8k-Site400(config-tunnel-interface)# color biz-internet Cat8k-Site400(config-tunnel-interface)# exit ! Default route to our internet gateway Cat8k-Site400(config)# ip route 0.0.0.0 0.0.0.0 192.168.99.1 Cat8k-Site400(config)# commit Commit complete. Cat8k-Site400# A Note on Certificates Since this is a lab environment, I\u0026rsquo;m using self-signed local certificate authority to provision all of my certificate infrastructure. Because of this, I\u0026rsquo;ll need to install my local CA certificate on the Catalyst 8000v. If you\u0026rsquo;re using the default Cisco-provisioned certificate setup, you won\u0026rsquo;t need to do this.\nSince my SD-WAN lab doesn\u0026rsquo;t have direct access to my local TFTP server - I do have an out-of-band management interface connected to my Cat 8000v. We\u0026rsquo;ll start by configuring that interface:\nCat8k-Site400# config-transaction Cat8k-Site400(config)# vrf definition 512 Cat8k-Site400(config-vrf)# address-family ipv4 Cat8k-Site400(config-vrf)# exit Cat8k-Site400(config)# interface GigabitEthernet 3 Cat8k-Site400(config-if)# vrf forwarding 512 Cat8k-Site400(config-if)# ip address dhcp Cat8k-Site400(config-if)# exit Cat8k-Site400(config)# ip tftp source-interface GigabitEthernet 3 Cat8k-Site400(config)# commit The standard management VRF/VPN for SD-WAN is 512, so I kept that config to match when I configured this management interface. This will all be over-written anyways once we get connected to vManage \u0026amp; configure/push our template configs.\nOnce that\u0026rsquo;s done, we can go ahead and copy our CA certificate to bootflash.\nCat8k-Site400# copy tftp://10.0.0.2/cacert.pem bootflash: Destination filename [cacert.pem]? Accessing tftp://10.0.0.2/cacert.pem... Loading cacert.pem from 10.0.0.2 (via GigabitEthernet3): ! [OK - 1406 bytes] 1406 bytes copied in 0.112 secs (12554 bytes/sec) Cat8k-Site400# dir bootflash: | inc cacert.pem 16 -rw- 1406 May 14 2021 14:41:27 +00:00 cacert.pem ```text Then we\u0026#39;ll use the command below to install the CA certificate: ```text Cat8k-Site400# request platform software sdwan root-cert-chain install bootflash:cacert.pem Uploading root-ca-cert-chain via VPN 0 Copying ... /bootflash/cacert.pem via VPN 0 Updating the root certificate chain.. Successfully installed the root certificate chain And we can validate by using the show sdwan certificate root-ca-cert command:\nCat8k-Site400 show sdwan certificate root-ca-cert Certificate: Data: Version: 3 (0x2) Serial Number: 11:3b:0a:12:17:b0:e0:b5:4b:fa:c2:e9:2c:9c:12:84 Signature Algorithm: sha1WithRSAEncryption Issuer: DC = local, DC = 0x2142, O = SDWAN-LAB, CN = 0x2142-0XWIN1-CA-2 Validity Not Before: Nov 29 20:00:11 2018 GMT Not After : Nov 29 20:10:11 2043 GMT Subject: DC = local, DC = 0x2142, O = SDWAN-LAB, CN = 0x2142-0XWIN1-CA-2 Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: \u0026lt;-- Output omitted --\u0026gt; Activating the Catalyst 8000v Almost there! Now that we\u0026rsquo;ve finished our pre-config \u0026amp; added our root CA certificate - we\u0026rsquo;re ready to join the Catalyst 8000v to the SD-WAN fabric.\nWe\u0026rsquo;ll start over in vManage - by going to Configuration \u0026gt; Devices.\nThen we\u0026rsquo;ll find our target, unused Catalyst 8000v device. Click the ellipsis on the right side, then select Generate Bootstrap Configuration\nThis will give us a prompt to select which configuration style to generate. We\u0026rsquo;ll leave this on \u0026ldquo;Cloud-init\u0026rdquo;:\nOnce we hit okay - we\u0026rsquo;ll be presented with the info we need. We won\u0026rsquo;t necessarily need all of this information, but we\u0026rsquo;ll want to take note of our uuid and otp:\nWe\u0026rsquo;ll drag this info back over to our Catalyst 8000v, and we can now use it to activate the device \u0026amp; join to our SD-WAN fabric.\nFor the command below, chassis-number will be our uuid value - and token will be our otp.\nCat8k-Site400# request platform software sdwan vedge_cloud activate chassis-number C8K-178BXXXX-XXXX-XXXX-XXXX-XXXXXXXXBC24 token 421ecxxxxxxxxxxxxxxxxxxxxxbd53b5 Validation After a few moments, we\u0026rsquo;ll see some log messages start to appear showing our control connections coming up. You might see these on the terminal if you\u0026rsquo;re using the console port, or you can use show log:\n*May 14 15:39:06.910: %Cisco-SDWAN-Cat8k-Site400-OMPD-3-ERRO-400002: R0/0: OMPD: vSmart peer 10.10.10.242 state changed to Init *May 14 15:39:07.980: %DMI-5-AUTH_PASSED: R0/0: dmiauthd: User \u0026#39;vmanage-admin\u0026#39; authenticated successfully from 10.10.10.240:48140 and was authorized for netconf over ssh. External groups: *May 14 15:39:08.798: %Cisco-SDWAN-Cat8k-Site400-OMPD-6-INFO-400002: R0/0: OMPD: vSmart peer 10.10.10.242 state changed to Handshake *May 14 15:39:08.804: %Cisco-SDWAN-Cat8k-Site400-OMPD-5-NTCE-400002: R0/0: OMPD: vSmart peer 10.10.10.242 state changed to Up *May 14 15:39:08.808: %Cisco-SDWAN-Cat8k-Site400-OMPD-6-INFO-400005: R0/0: OMPD: Number of vSmarts connected : 1 *May 14 15:39:09.827: %Cisco-SDWAN-Cat8k-Site400-OMPD-3-ERRO-400002: R0/0: OMPD: vSmart peer 10.10.10.243 state changed to Init *May 14 15:39:10.584: %CRYPTO-6-ISAKMP_ON_OFF: ISAKMP is ON *May 14 15:39:11.756: %Cisco-SDWAN-Cat8k-Site400-OMPD-6-INFO-400002: R0/0: OMPD: vSmart peer 10.10.10.243 state changed to Handshake *May 14 15:39:11.762: %Cisco-SDWAN-Cat8k-Site400-OMPD-5-NTCE-400002: R0/0: OMPD: vSmart peer 10.10.10.243 state changed to Up *May 14 15:39:11.762: %Cisco-SDWAN-Cat8k-Site400-OMPD-6-INFO-400005: R0/0: OMPD: Number of vSmarts connected : 2 *May 14 15:39:12.738: %Cisco-SDWAN-Cat8k-Site400-OMPD-6-INFO-400007: R0/0: OMPD: Using policy from peer 10.10.10.242 *May 14 15:39:13.570: %Cisco-SDWAN-Cat8k-Site400-FTMD-6-INFO-1000020: R0/0: FTMD: SLA class added : class \u0026#39;Default\u0026#39; at index \u0026#39;1\u0026#39; loss = 25%, latency = 300ms, jitter = 100ms, app-probe-class = None *May 14 15:39:14.738: %Cisco-SDWAN-Cat8k-Site400-OMPD-6-INFO-400007: R0/0: OMPD: Using policy from peer 10.10.10.242 We can also see our control connections using the command show sdwan control connections:\nCat8k-Site400# show sdwan control connections PEER PEER CONTROLLER PEER PEER PEER SITE DOMAIN PEER PRIV PEER PUB GROUP TYPE PROT SYSTEM IP ID ID PRIVATE IP PORT PUBLIC IP PORT ORGANIZATION LOCAL COLOR PROXY STATE UPTIME ID ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- vsmart dtls 10.10.10.242 100 1 192.168.99.242 12446 192.168.99.242 12446 SDWAN-LAB biz-internet No up 0:00:00:51 0 vsmart dtls 10.10.10.243 100 1 192.168.99.243 12446 192.168.99.243 12446 SDWAN-LAB biz-internet No up 0:00:00:49 0 vmanage dtls 10.10.10.240 100 0 192.168.99.240 12646 192.168.99.240 12646 SDWAN-LAB biz-internet No up 0:00:00:52 0 Of course, we can also check to see our device status in the vManage dashboard as well.\nOver on the Monitor \u0026gt; Network page, we can see that our new Catalyst 8000v is now online:\nExtra: How do I check the routing table on an IOS-XE SD-WAN device? So - if you\u0026rsquo;ve only used the vEdge software devices, you may be used to using the show ip route or show ip route vpn 10 commands.\nIn the IOS-XE world, most SD-WAN commands are prefixed with the sdwan keyword. For example: show sdwan bfd sessions (where on vEdges, it would just be show bfd sessions)\nThis might lead you to believe that you can use show sdwan ip route or show sdwan ip route vrf 10 - but these won\u0026rsquo;t work! In fact, you\u0026rsquo;ll get the following message:\nCat8k-Site400# show sdwan ip route % Error: This command is not supported For the IOS-XE based devices, they actually just use the standard IOS-XE routing table and VRF constructs.\nSo on a vEdge, you would have VPN 0 as your transport VPN. On IOS-XE, this is just the default global routing table, shown with show ip route.\nBut what about our LAN-side service VPNs? In this case, our routes are being dumped into a VRF on the IOS-XE device.\nSo for example, I have VPN 10 in my lab which is used for LAN-side clients. We can use the show vrf command to see that this exists, and then show ip route vrf 10 to see the routes from our other SD-WAN locations:\nCat8k-Site400# show vrf Name Default RD Protocols Interfaces 10 \u0026lt;not set\u0026gt; ipv4 Gi2 512 \u0026lt;not set\u0026gt; ipv4 Gi3 65528 \u0026lt;not set\u0026gt; ipv4 Lo65528 Cat8k-Site400# show ip route vrf 10 Routing Table: 10 Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2 E1 - OSPF external type 1, E2 - OSPF external type 2, m - OMP n - NAT, Ni - NAT inside, No - NAT outside, Nd - NAT DIA i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2 ia - IS-IS inter area, * - candidate default, U - per-user static route H - NHRP, G - NHRP registered, g - NHRP registration summary o - ODR, P - periodic downloaded static route, l - LISP a - application route + - replicated route, % - next hop override, p - overrides from PfR \u0026amp; - replicated local route overrides by connected Gateway of last resort is not set 10.0.0.0/24 is subnetted, 2 subnets m 10.2.2.0 [251/0] via 10.10.10.235, 00:09:02, Sdwan-system-intf m 10.3.3.0 [251/0] via 10.10.10.236, 00:09:02, Sdwan-system-intf Okay, that\u0026rsquo;s it! Pretty quick process overall - and now we can get into applying our device/feature templates.\nHope this was helpful!!\n","permalink":"https://0x2142.com/how-to-cisco-sd-wan-onboarding-a-catalyst-8000v/","summary":"Let\u0026rsquo;s look at how to join a Cisco Catalyst 8000v to an SDWAN network","title":"[How To] Cisco SD-WAN - Onboarding a Catalyst 8000v"},{"content":" In this post, we\u0026rsquo;ll walk through configuring a Cisco Viptela SD-WAN network to integrate with Cisco Umbrella\u0026rsquo;s Secure Internet Gateway (SIG) \u0026amp; Secure Web Gateway (SWG).\nIf you\u0026rsquo;re interested in reading more about what SIG \u0026amp; SWG are, please check out my last post where I walked through this integration with a Meraki MX firewall.\nFor additional reading, there\u0026rsquo;s also a good Umbrella blog post on all the components of Cisco\u0026rsquo;s SASE architecture, which you can find here.\nOkay, with that being said - let\u0026rsquo;s get started!\nStep 1: Umbrella API Keys Okay, so we\u0026rsquo;ll start on the Umbrella side. We\u0026rsquo;ll need to generate a set of API keys, which we\u0026rsquo;ll push out to our WAN edge devices. These API keys will allow our remote devices to reach out to Umbrella \u0026amp; auto-configure their SIG tunnels.\nWe\u0026rsquo;ll log into our Umbrella dashboard at https://login.umbrella.com/\nOnce we log in, we\u0026rsquo;ll hop over to Admin \u0026gt; API Keys. Then up in the upper-right corner, click Create.\nFor this integration, we\u0026rsquo;ll need to select Umbrella Management to make sure our API keys have the access they need. Then click Create. Easy enough, right?\nOnce we do that, the Umbrella dashboard will display our API keys - which we\u0026rsquo;ll need to copy over to our Viptela SD-WAN environment.\nWe\u0026rsquo;ll also need to collect our Umbrella Organization ID. This is actually just embedded within the Umbrella Dashboard URL, and should be a seven-digit number. For example, if we look at our current URL on the API keys page:\nhttps://dashboard.umbrella.com/o/\u0026lt;ORG ID\u0026gt;/#/admin/apikeys And that\u0026rsquo;s all we need for now from the Umbrella side. So let\u0026rsquo;s hop over to Viptela.\nStep 2: Viptela Templates \u0026amp; Config For this post, I\u0026rsquo;ll be using my SD-WAN lab in EVE-NG. Currently I just upgraded the lab to run Viptela version 20.4.1, though the SIG integration has been supported since 20.1.\nJust for reference, here is the lap topology that I\u0026rsquo;m working with:\nThis lab includes a bridged connection to allow direct internet access from the lab network.\nTo configure our SIG automatic tunnels, we\u0026rsquo;ll need to create / update a few templates:\nCreate a SIG Credentials feature template Create a SIG feature template Assign SIG templates to device templates Edit Service-side VPN Template to inject a service route 2a: Creating a SIG Credentials Template First we\u0026rsquo;ll create a template which will store our Umbrella API credentials.\nIn the vManage dashboard, we\u0026rsquo;ll go to Configuration \u0026gt; Templates, then drop into the Feature tab, and click on Add Template.\nOnce we get to the next screen, we\u0026rsquo;ll select our device type. In my case, I\u0026rsquo;m using a vEdge Cloud.\nA list of templates will pop up - we\u0026rsquo;ll drop down to SIG Credentials, which will be near the bottom under the Other Templates header.\nWe\u0026rsquo;ll then be taken to the page where we create our template.\nWe\u0026rsquo;ll fill in our template name \u0026amp; description, then paste in our Umbrella details (Org ID, API Key, API Secret).\nOnce we\u0026rsquo;re done - We\u0026rsquo;ll hit Save.\nNote: At time of writing, the \u0026ldquo;Get Keys\u0026rdquo; button only functions if you\u0026rsquo;ve purchased your SD-WAN subscription with DNA Premier licensing. This license level includes Umbrella SIG, and allows this 1-click integration that pulls your Umbrella API keys automatically.\n2b: Creating a SIG Tunnel Template Okay, next we\u0026rsquo;ll need to create another feature template to specify our IPSec tunnel configuration.\nJust like before, we\u0026rsquo;ll head back over to Configuration \u0026gt; Templates \u0026gt; Feature \u0026gt; Add Template.\nThis time, after selecting our device type, we\u0026rsquo;ll choose Secure Internet Gateway (SIG) / WAN - which is located under the VPN header.\nIn the template configuration, we\u0026rsquo;ll give the template a name \u0026amp; description as always. Then we\u0026rsquo;ll jump down to the tunnel config.\nWe\u0026rsquo;ll select Umbrella for SIG Provider, then click Add Tunnel.\nWithin the tunnel config, we\u0026rsquo;ll specify an interface name - I\u0026rsquo;ll name mine ipsec1 (and I\u0026rsquo;ll create an ipsec2 shortly). We\u0026rsquo;ll also specify a Tunnel Source, which in my lab is ge0/0 for the biz-internet VPN 0 interface.\nWe\u0026rsquo;ll keep Data-Center as Primary, then click Add. Then - we\u0026rsquo;ll add a second tunnel configuration, but using Data-Center as Secondary and the interface name as ipsec2.\nWhen all that is done, we should have the following:\nIf we scroll down to the bottom of the template config, we\u0026rsquo;ll have some settings for High Availability. Here, we\u0026rsquo;ll specify what our active \u0026amp; backup IPSec tunnels are, and their weights. I\u0026rsquo;ll specify ipsec1 as active \u0026amp; ipsec2 as backup. Then click Save to finish our template.\nNote: Weight settings are only available starting with 20.4.1 \u0026amp; allow for ECMP routing to SIG. Not shown above, you can create up to four HA tunnel pairs. Depending on weighting, you can equal-cost or unequal-cost load balance between those pairs.\nWhy would you want to load balance across multiple tunnels? At time of writing, each IPSec tunnel is limited to a maximum 250Mb/s throughput. By creating multiple tunnels \u0026amp; load balancing, we can overcome this limitation if we need higher bandwidth.\n2c: Attaching SIG to Device Templates After we\u0026rsquo;ve built out our two SIG templates, we can now attach them to our device templates.\nFor me, I currently only have a single device template which is applied to all of my remote WAN edge devices.\nSo we\u0026rsquo;ll go back to Configuration \u0026gt; Templates and find whichever device template we want to use - then click the ellipsis on the far right, end select Edit.\nIn the device template, we\u0026rsquo;ll scroll down and look for our VPN 0 configuration under Transport \u0026amp; Management VPN. We\u0026rsquo;ll attach our SIG tunnel template to VPN 0, since that\u0026rsquo;s where those IPSec tunnels are being sourced from.\nOn the right side, under Additional VPN 0 Templates, we\u0026rsquo;ll click Secure Internet Gateway to add our template. Then from the drop-down, we can select the feature template we just created:\nWe\u0026rsquo;ll also include our SIG credentials template, which we can find at the bottom of the page, under Additional Templates:\nOnce we\u0026rsquo;re done, we can click Save at the bottom. This will take us through the process of pushing out these changes to our remote WAN edge devices. When this configuration is deployed, each vEdge will now reach out to Umbrella \u0026amp; auto-configure an IPSec tunnel.\nNote: While the IPSec tunnels will be established when this configuration is pushed - no traffic will flow over them yet. We\u0026rsquo;ll need to add a service route for that, which we\u0026rsquo;ll do next!\n2d: Injecting a Service Route Now we have our IPSec tunnels deployed - all we need to do is start routing traffic out to Umbrella. We\u0026rsquo;ll accomplish this using a service route on our LAN-side VPN.\nSo we\u0026rsquo;ll go over to Configuration \u0026gt; Templates \u0026gt; Feature - and we\u0026rsquo;ll scroll down \u0026amp; find whichever template we\u0026rsquo;re currently using on our LAN side. For me, I want VPN 10 to route over the tunnels, so I\u0026rsquo;ll be editing my VPN 10 template.\nWithin the template, we\u0026rsquo;ll scroll down to the Service Route section, click New Service Route, and we\u0026rsquo;ll enter our desired prefix. This will be what traffic will be routed over the IPSec tunnel to Umbrella. Since this is intended to be an internet gateway, I\u0026rsquo;ll enter 0.0.0.0/0 to inject a default route to Umbrella.\nService should be pre-selected as SIG, so we\u0026rsquo;ll click Add, then Update \u0026amp; deploy our changes to the remote edge devices.\nStep 3: Validation \u0026amp; Troubleshooting Awesome! Now we should have everything configured \u0026amp; working. So let\u0026rsquo;s jump through some initial testing and validation we can do.\nWithin vManage, we can check the status of our IPSec tunnels. We\u0026rsquo;ll go over to Monitor \u0026gt; Network then select one of our WAN edge devices.\nWe\u0026rsquo;ll click on the Interfaces tab on the left side - and we should be able to see a list of all interfaces on our device. This should include an ipsec1 \u0026amp; ipsec2 interface.\nI\u0026rsquo;ve also selected the Real Time monitor on mine, and filtered to just show traffic on the ipsec1 interface:\nYou might recall from my topology above, that I have a linux VM running behind each of the two vEdge Cloud appliances. Using these, I can also test web access \u0026amp; see what my external IP is:\nAnd sure enough, I am getting a 146.112.x.x address - which belongs to the Umbrella datacenter.\nIf we log into one of our vEdge devices, we can check the routing table with show ip route vpn 10 (or whichever VPN you\u0026rsquo;re using for the LAN-side). We should see our default 0.0.0.0/0 route via ipsec1, with a next-hop-VPN of VPN0:\nvEdge-01# show ip route vpn 10 ADDRESS PATH PROTOCOL NEXTHOP NEXTHOP NEXTHOP VPN FAMILY PREFIX ID PROTOCOL SUB TYPE METRIC IFNAME ADDR TLOC IP COLOR ENCAP VPN STATUS ------------------------------------------------------------------------------------------------------------------------ 10 ipv4 0.0.0.0/0 0 std-ipsec - 0 ipsec1 - - - - 0 F,S 10 ipv4 10.2.2.0/24 0 connected - 0 ge0/2 - - - - - F,S We can also check specifically our SIG tunnel status using the command show secure-internet-gateway tunnels:\nvEdge-01# show secure-internet-gateway tunnels TUNNEL API LAST IF HTTP SUCCESSFUL TUNNEL NAME TUNNEL ID TUNNEL NAME FSM STATE CODE REQ STATE ------------------------------------------------------------------------------------------- ipsec1 530233543 SITE200SYS10x10x10x235IFipsec1 st-tun-up 200 create-tunnel - ipsec2 530233542 SITE200SYS10x10x10x235IFipsec2 st-tun-up 200 create-tunnel - In that output above, we\u0026rsquo;ll see that we have both ipsec1 \u0026amp; ipsec2 tunnels shown. The last API queries to Umbrella have a HTTP 200, which is good. We\u0026rsquo;ll also see our tunnel names, which we can use to find our device tunnel configuration in the Umbrella dashboard.\nThe tunnel name might look like a mess at first, but it\u0026rsquo;s a unique identifier to represent each tunnel that was created. So if we break it down for this vEdge:\nThis vEdge is at Site ID 200 Shown as \u0026ldquo;SITE200\u0026rdquo; This vEdge has a system IP of 10.10.10.235 Shown as \u0026ldquo;SYS10x10x10x235\u0026rdquo; This vEdge has two IPSec interfaces, ipsec1 \u0026amp; ipsec2 Shown as \u0026ldquo;IFipec1\u0026rdquo; and \u0026ldquo;IFipsec2\u0026rdquo; If we jump over to the Umbrella dashboard, we\u0026rsquo;ll see the same. Within the Umbrella dashboard, we can jump to Deployments \u0026gt; Network Tunnels.\nOn this page, we should see a total of four tunnels listed (two from each vEdge appliance):\nAnd with everything configured \u0026amp; validated - now we can move onto configuring firewall and web filtering policies within Umbrella!\nIf you\u0026rsquo;re interested in seeing how to configure Umbrella\u0026rsquo;s cloud firewall \u0026amp; web filtering policies - please check out my YouTube Video above!\n","permalink":"https://0x2142.com/cisco-sdwan-and-umbrella-sig-integration/","summary":"A tutorial for configuring Viptela SDWAN with Cisco Umbrella Secure Internet Gateway \u0026amp; Secure Web Gateway","title":"[How To] Connect Cisco SD-WAN to Umbrella SIG/SWG"},{"content":" In today\u0026rsquo;s blog post - we\u0026rsquo;ll be checking out how to integrate a Meraki MX firewall to Cisco\u0026rsquo;s Umbrella Secure Internet Gateway (SIG) \u0026amp; Secure Web Gateway (SWG) services.\nInterested in seeing how to do this with Cisco Viptela SD-WAN? Check out this post\nNote: This integration is still rather new - so I anticipate some of the screenshots \u0026amp; steps below may differ as this post ages.\nWhat are SIG \u0026amp; SWG? Good question! A lot of us are probably familiar with Cisco Umbrella (formerly OpenDNS) for the DNS-layer security products. Using Cisco Umbrella, we can configure our end PCs or DNS forwarders to use the Cisco DNS servers. Then, we apply security policies to allow/deny DNS queries based on our policy.\nNow applying security policy at the DNS level is great! Typical web filtering only inspects HTTP/HTTPS traffic, but inspecting DNS traffic allows us to stop threats that might not use those typical ports. While there may be some applications or malware that use hard-coded IP addresses, a good majority use a domain name that requires a DNS lookup.\nBut what if we wanted to still do web filtering too? Or maybe we want a centralized, cloud-hosted firewall service? That\u0026rsquo;s where Umbrella SIG \u0026amp; SWG come in.\nSecure Web Gateway (SWG) is pretty much exactly what it sounds like. It\u0026rsquo;s a cloud-hosted web filter or web proxy, where we can set URL-based policys to permit or deny access. It supports the usual allow-lists, deny-lists, HTTPS inspection, file inspection, and policies based on user/group identities.\nSecure Internet Gateway (SIG) is a large, cloud-hosted firewall service. What we can do with SIG is tunnel all of our traffic to one centrallized location to apply firewall policies (permit/deny/etc).\nWhy would we want either of these functions to be cloud-hosted? Well we might not have the resources at every remote site to perform firewalling \u0026amp; web filtering - or we don\u0026rsquo;t want to invest in beefy hardware in our corporate datacenter, and backhaul all of our remote traffic back for inspection. The cloud-hosted piece also means a central management point, so only one place to make change for all of our remote sites - rather than having to touch dozens or hundreds of remote devices\u0026hellip;..Did someone say SASE? 🙃\nWith all that said - Let\u0026rsquo;s get into making this work!\nNote: There are many ways to use Umbrella SIG/SWG (IPSec tunnel, PAC file, Anyconnect, etc). This particular post will only cover IPSec via Meraki MX.\nStep 1: Getting our Keys First thing we\u0026rsquo;ll need to do is on the Umbrella side. We\u0026rsquo;ll need to generate tunnel keys for our Meraki MX to use for IPSec negotiation.\nWe\u0026rsquo;ll log into our Umbrella dashboard at https://login.umbrella.com/\nOnce we\u0026rsquo;re in, we\u0026rsquo;ll navigate to Deployments \u0026gt; Core Identities \u0026gt; Network Tunnels. Shown below, we currently have no tunnels configured:\nIn the upper right corner, let\u0026rsquo;s click the Add Tunnel button.\nWe\u0026rsquo;ll now see the following screen to begin creating our IPSec tunnel:\nHere, we\u0026rsquo;ll need to specify a name for our tunnel \u0026amp; which device type. The name can be anything we choose that helps us identify what locations are using this tunnel. I\u0026rsquo;ve specified MX64 as my tunnel name, since I only have one MX that I\u0026rsquo;ll be testing this with. We also have Meraki MX as an option in the Device Type drop down - so we\u0026rsquo;ll select that as well. Then, we\u0026rsquo;ll click Next.\nThen we\u0026rsquo;ll need to configure our Tunnel ID and Passphrase:\nThe Tunnel ID we\u0026rsquo;ll be sending later, as part of our IPSec local ID. The passphrase will be used as our pre-shared key for the tunnel config. Once we\u0026rsquo;re done with that, click Save.\nAs long as our ID \u0026amp; passphrase are all good, we\u0026rsquo;ll be presented with a box to easily copy these parameters into the Meraki config:\nFinally, we\u0026rsquo;ll be dropped back to our Network Tunnels page - where we can see that our tunnel is configured, but not yet established. Let\u0026rsquo;s fix that \u0026amp; hop over to the Meraki dashboard!\nStep 2: Meraki MX IPSec Configuration Okay, now onto the fun stuff.\nWe\u0026rsquo;ll log into our Meraki Dashboard at https://account.meraki.com/secure/login/dashboard_login\nOnce we\u0026rsquo;re in, you\u0026rsquo;ll need to select the network with the MX you want to connect. For me, I currently only have a single network, which contains my MX firewall.\nThen we\u0026rsquo;ll navigate to Security \u0026amp; SD-WAN \u0026gt; Configure \u0026gt; Site-to-site VPN.\nAssuming we have no other VPNs configured, you\u0026rsquo;ll have to change the VPN Type to Hub (Mesh) or Spoke. In my case, Umbrella SIG will be the only VPN I\u0026rsquo;ll have configured - so I\u0026rsquo;ll go ahead and use Hub (Mesh). Don\u0026rsquo;t mind the error regarding exit hubs, as we\u0026rsquo;ll be configuring a non-Meraki peer below.\nOkay, scrolling down the page just a bit - we\u0026rsquo;ll need to specify which VLANs/internal subnets will be routed across the VPN \u0026amp; pushed through Umbrella for SIG/SWG:\nFor testing purposes, I have a VM in a subnet labeled DMZ that I\u0026rsquo;ll be using - So that will be the only VLAN that I\u0026rsquo;ll set to VPN On.\nNext we\u0026rsquo;ll take a look at configuring Non-Meraki VPN Peers.\nAs shown in the screenshot below, we\u0026rsquo;ll need to configure the following settings:\nName - This is locally significant, I set mine to Umbrella_SIG IKE Version - Set this to IKEv2 IPSec Policies - Custom - See below Public IP - This is the peer Umbrella Datacenter Choose the DC closest to you here For this post, I\u0026rsquo;m using the New York DC Local ID - Set this to the Tunnel ID we got from Umbrella Remote ID - Leave blank Private Subnets - This is what destination IPs will be routed over the tunnel Since this is intended as an Internet gateway, we\u0026rsquo;ll use 0.0.0.0/0 Preshared Secret - Set this to the passphrase we configured in Umbrella Availability - Set this to which networks this tunnel should apply Since I only have 1 network \u0026amp; 1 MX, I set this to All Networks Okay - I mentioned above we\u0026rsquo;ll need to set some custom IPSec parameters. Here\u0026rsquo;s what we\u0026rsquo;ll configure as a custom policy:\nNote: Turns out in the drop down menu, there is also an option for \u0026ldquo;Umbrella\u0026rdquo;, so you don\u0026rsquo;t have to create a custom policy. That being said, the Umbrella preset uses DH group 5, but Umbrella\u0026rsquo;s docs ask for DH group 14.\nLastly, we\u0026rsquo;ll be able to configure whether or not we want firewall logging (I enabled this) and also whether we want to add access-lists to permit/deny any traffic over the VPN. For now I\u0026rsquo;ll be leaving this as permit any.\nFinally - we can click Save to push our configuration to the MX appliance!\nStep 3: Validation \u0026amp; Testing Hopefully once we get all that configuration done, we\u0026rsquo;ll be able to see our tunnel come up.\nWe can validate from both sides, but let\u0026rsquo;s start with Meraki. In the Dashboard menu, we\u0026rsquo;ll navigate to Security \u0026amp; SD-WAN \u0026gt; Monitor \u0026gt; VPN Status.\nThen we\u0026rsquo;ll need to click the tab for Non-Meraki peer:\nWith any luck, we should see the little green circle showing that our tunnel is online.\nIf your tunnel doesn\u0026rsquo;t come up immediately, you may have to generate some traffic from your VPN subnet to the internet. This will force the MX to try and connect to Umbrella.\nIt\u0026rsquo;s also worth noting - sometimes it may take the Meraki dashboard to show a successful connection. A better indicator is by checking the Umbrella dashboard, or checking manually with a device you are expecting to route over the VPN.\nIf for any reason we have trouble, we can also check our VPN negotiation logs by going to Network-Wide \u0026gt; Monitor \u0026gt; Event Log.\nOn this screen, make sure you\u0026rsquo;ve selected for security appliances at the top. If needed, we can also filter events by All Non-Meraki VPN / Client VPN.\nSince my tunnel came up with no issues, the output above shows a successful tunnel negotiation.\nLet\u0026rsquo;s jump over to the Umbrella side, and see what the Umbrella dashboard shows.\nBack on the Network Tunnels page - we should see that our tunnel now shows as Active.\nThis screen will also so the public IP that our MX is using to connect, as well as which Umbrella datacenter we\u0026rsquo;ve connected to.\nOkay! That\u0026rsquo;s it for now. To try and keep this blog post from getting too long, I\u0026rsquo;ve decided to split this into two parts.\nIf you\u0026rsquo;re interested in seeing how to configure Umbrella\u0026rsquo;s cloud firewall \u0026amp; web filtering policies - please check out my YouTube Video above!\n","permalink":"https://0x2142.com/meraki-mx-and-umbrella-sig-integration/","summary":"A tutorial for configuring a Meraki MX with Cisco Umbrella Secure Internet Gateway \u0026amp; Secure Web Gateway","title":"[How To] Connect Meraki MX to Umbrella SIG/SWG"},{"content":"The post below was contributed by guest author: Nicole Henry\nHey everyone. I\u0026rsquo;ve got another present for you - Free study resources to use while studying for Juniper Network\u0026rsquo;s JNCIA-Cloud cert (As a reminder, I also gave a list of Free study resources for the JNCIA-Junos exam).\n1) Juniper Open Learning If you were familiar with Junos Genius, it was discontinued, but all the Associate level material can be now found Juniper Open Learning for free. Lots of videos \u0026amp; practice exams to get your prepared for the Associate level exams!!\n2) Juniper Networks Day One Books Description: \u0026ldquo;Day One Books cover networking technologies using step-by-step instructions and practical examples written by working engineers.\u0026rdquo; Here\u0026rsquo;s the link to the Day One Books; there are numerous Day One Books, and I guarantee you\u0026rsquo;ll learn a lot. Me, personally, I like to read with physical books, so I bought the \u0026ldquo;Day One: Data Center Fundamentals\u0026rdquo; book for myself. But here\u0026rsquo;s the pdf version !!!!\n3) Documentation (aka \u0026lsquo;TechLibrary\u0026rsquo;) First: For any cert, print \u0026amp; follow the Exam Objectives.\nSecond: As you\u0026rsquo;ll see from the Exam Objectives, there are a lot of Juniper solutions on this exam. Luckily their \u0026lsquo;TechLibrary\u0026rsquo; is FILLED with all the info you need about the specific products. I really hope you like to read; you\u0026rsquo;ll be doing a lot of that. Tehehehee!\n4) Network Fun Times blog This guy Chris (who is a Juniper Ambassador!!) made a LENGTHY blog post about what he used to study \u0026amp; pass the JNCIA-Cloud. I visited this blog post countless times b/c it is just THAT GOOD. Use it. Bookmark it.\nAnd that\u0026rsquo;s it! Those are all the resources I used to pass the JNCIA-CLOUD. All FREE! I hope you found this thread useful.\nNicole ","permalink":"https://0x2142.com/jncia-cloud-study-resources-all-free/","summary":"Let\u0026rsquo;s look at some free resources for studying for the JNCIA-Cloud!","title":"JNCIA-Cloud Study Resources (All Free!)"},{"content":" Maybe it\u0026rsquo;s just me - but I feel like when we want to demonstrate product APIs to someone, we usually jump into Postman first. It\u0026rsquo;s not without good reason though. Postman is a very easy to use platform for running API calls against REST endpoints \u0026amp; see the nicely formatted output.\nThat being said, often times I\u0026rsquo;ve seen that we stop there. We might mention Python as an advanced method of using our APIs, or maybe we talk about how \u0026ldquo;if APIs are the new CLI, Postman is the new PuTTY\u0026rdquo;. I\u0026rsquo;ve seen some engineers who hear these statements and feel like Postman is being pushed on them - even when they\u0026rsquo;re perfectly happy with an SSH-based CLI.\nNetwork automation isn\u0026rsquo;t usually accomplished with just one tool - it\u0026rsquo;s a whole storage closet full of different tools \u0026amp; utilities, each with their own use cases or specializations. In this particular example - learning Python allows you to move beyond one-off API calls in Postman, and into being able to accomplish much more complex automation.\nSo the purpose of this post is to explore how to get beyond just Postman, and into using Python for REST API calls. This isn\u0026rsquo;t going to be a complete run-down of all the capabilities of both tools - but rather a few examples of simple GET requests using both methods.\nIf you don\u0026rsquo;t have Postman yet, go ahead and snag the download here.\nNote: Much of the code below is minimal and does not contain any error handling. Therefore, it should not be used in a production network. Full example code here.\nPostman: Simple GET Request So first, let\u0026rsquo;s start off with an example of using Postman for a simple GET request.\nIn this example, we\u0026rsquo;ll keep things simple \u0026amp; use a non-authenticated API endpoint. We\u0026rsquo;ll accomplish this using a free website called JSON Placeholder.\nLet\u0026rsquo;s go ahead and start up Postman, and we\u0026rsquo;ll see a blank workspace:\nWhat we\u0026rsquo;ll need to pay attention to first, is what type of HTTP request we\u0026rsquo;re sending - as well as the URL we want to send our request to. If we expand the request type dropdown, we\u0026rsquo;ll see a handful of options - but we\u0026rsquo;ll only be using a GET request for now:\nIn order to create our first request, we\u0026rsquo;ll just need to enter our API endpoint URL. We\u0026rsquo;ll use the users endpoint offered by JSON Placeholder, which is located at: https://jsonplaceholder.typicode.com/users\nIn the screenshot above, you\u0026rsquo;ll see in the highlighted box that we entered our URL. Next, we just click the big Send button on the right side.\nI already sent the GET request, so in the screenshot you\u0026rsquo;ll also notice that we have our JSON response from the API. The mock data offered by this API provides us with a list of 10 different users, along with the data that\u0026rsquo;s been entered for each user.\nWhat if we only wanted to get data for one specific user? Well, our test API site supports using query parameters to filter our response. Let\u0026rsquo;s say we want to find information for a user named Delphine. We would append ?username=Delphine to our URL:\nSo as we can see above - now our JSON response only contains the data for a single user. If we needed to find a user\u0026rsquo;s phone number, this could be an easy way to quickly filter our data \u0026amp; get to what we need.\nThis is really where Postman is great. We\u0026rsquo;re able to quickly \u0026amp; visually test out an API call. We can see what the response data looks like, and understand the structure of requests \u0026amp; responses.\nBut what about when we want to iterate through a number of users and pull only a specific few statistics? Or maybe we need a way to take a list of user information, and automatically upload or convert that data into another system.\nFor use cases like that, we\u0026rsquo;ll jump over to Python.\nPython: Simple GET Request In this section, we\u0026rsquo;ll walk through the same example from above - but this time using Python.\nFirst thing we\u0026rsquo;ll need to do, is install the Python requests library. While there are a handful of libraries that can accomplish this work, requests is fairly popular \u0026amp; easy to use.\nSo we\u0026rsquo;ll kick things off by using pip to install our library:\npip install requests Then, we\u0026rsquo;ll create a very simple Python script to perform the same initial GET request from earlier. So we\u0026rsquo;ll pull a list of ALL users from the API.\nimport requests URL = \u0026#34;https://jsonplaceholder.typicode.com/users\u0026#34; response = requests.get(URL) print(response.text) The code above should be pretty straightforward. First we\u0026rsquo;ll import our requests library. Then, just to keep the code clean, we\u0026rsquo;ll create a variable called URL to hold the URL for the API endpoint.\nNext, we send that GET request, using requests.get. Last but not least, we\u0026rsquo;ll go ahead and print out the text payload that we receive back. That output looks pretty similar:\nmatt@DESKTOP:~/postman-python$ python3 simpleGET.py [ { \u0026#34;id\u0026#34;: 1, \u0026#34;name\u0026#34;: \u0026#34;Leanne Graham\u0026#34;, \u0026#34;username\u0026#34;: \u0026#34;Bret\u0026#34;, \u0026#34;email\u0026#34;: \u0026#34;Sincere@april.biz\u0026#34;, \u0026#34;address\u0026#34;: { \u0026#34;street\u0026#34;: \u0026#34;Kulas Light\u0026#34;, \u0026#34;suite\u0026#34;: \u0026#34;Apt. 556\u0026#34;, \u0026#34;city\u0026#34;: \u0026#34;Gwenborough\u0026#34;, \u0026#34;zipcode\u0026#34;: \u0026#34;92998-3874\u0026#34;, \u0026#34;geo\u0026#34;: { \u0026#34;lat\u0026#34;: \u0026#34;-37.3159\u0026#34;, \u0026#34;lng\u0026#34;: \u0026#34;81.1496\u0026#34; } }, \u0026#34;phone\u0026#34;: \u0026#34;1-770-736-8031 x56442\u0026#34;, \u0026#34;website\u0026#34;: \u0026#34;hildegard.org\u0026#34;, \u0026#34;company\u0026#34;: { -- output truncated -- Now, let\u0026rsquo;s add a little extra logic to our next step. Rather than just specifying a static username that we want to search for, let\u0026rsquo;s ask our user to specify a username:\nimport requests URL = \u0026#34;https://jsonplaceholder.typicode.com/users\u0026#34; print(\u0026#34;Search by Username:\u0026#34;) user = input(\u0026#34;\u0026gt; \u0026#34;) queryURL = URL + f\u0026#34;?username={user}\u0026#34; response = requests.get(queryURL) print(response.text) In the code above, we made just a few changes. One is printing out a prompt to \u0026ldquo;Search by Username\u0026rdquo;. Then we use the Python input function to collect input from our user, then store that in the user variable.\nNext, we create a queryURL - which is similar to the URL we used in the Postman example. Except in this case, we\u0026rsquo;re supplying a dynamic username input based on whatever the end user wants to search for. We use a Python f-string to inject the username into our query parameters.\nLet\u0026rsquo;s see what that looks like:\nmatt@DESKTOP:~/postman-python$ python3 simpleGET.py Search by Username: \u0026gt; Delphine [ { \u0026#34;id\u0026#34;: 9, \u0026#34;name\u0026#34;: \u0026#34;Glenna Reichert\u0026#34;, \u0026#34;username\u0026#34;: \u0026#34;Delphine\u0026#34;, \u0026#34;email\u0026#34;: \u0026#34;Chaim_McDermott@dana.io\u0026#34;, \u0026#34;address\u0026#34;: { \u0026#34;street\u0026#34;: \u0026#34;Dayna Park\u0026#34;, \u0026#34;suite\u0026#34;: \u0026#34;Suite 449\u0026#34;, \u0026#34;city\u0026#34;: \u0026#34;Bartholomebury\u0026#34;, \u0026#34;zipcode\u0026#34;: \u0026#34;76495-3109\u0026#34;, \u0026#34;geo\u0026#34;: { \u0026#34;lat\u0026#34;: \u0026#34;24.6463\u0026#34;, \u0026#34;lng\u0026#34;: \u0026#34;-168.8889\u0026#34; } }, \u0026#34;phone\u0026#34;: \u0026#34;(775)976-6794 x41206\u0026#34;, \u0026#34;website\u0026#34;: \u0026#34;conrad.com\u0026#34;, \u0026#34;company\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;Yost and Sons\u0026#34;, \u0026#34;catchPhrase\u0026#34;: \u0026#34;Switchable contextually-based project\u0026#34;, \u0026#34;bs\u0026#34;: \u0026#34;aggregate real-time technologies\u0026#34; } } ] Perfect! We get the result we expected, which is the single dataset from the user that we specified.\nLet\u0026rsquo;s take this one step further! Maybe we want to build a quick utility to search for a user\u0026rsquo;s contact information. We can take the raw JSON output, pull out a few pieces of info, then apply some formatting to make it more user-friendly.\nIn the following example, we\u0026rsquo;ll update our code to search that JSON response \u0026amp; collect the user\u0026rsquo;s name, email address, and phone number.\nimport requests import json URL = \u0026#34;https://jsonplaceholder.typicode.com/users\u0026#34; print(\u0026#34;Search by Username:\u0026#34;) user = input(\u0026#34;\u0026gt; \u0026#34;) queryURL = URL + f\u0026#34;?username={user}\u0026#34; response = requests.get(queryURL) userdata = json.loads(response.text)[0] name = userdata[\u0026#34;name\u0026#34;] email = userdata[\u0026#34;email\u0026#34;] phone = userdata[\u0026#34;phone\u0026#34;] print(f\u0026#34;{name} can be reached via the following methods:\u0026#34;) print(f\u0026#34;Email: {email}\u0026#34;) print(f\u0026#34;Phone: {phone}\u0026#34;) So a few things have changed in the above example. First thing you may notice, is that we\u0026rsquo;ve added an additional import: the json library. We use this in line 11, where we convert the JSON output into a native python object using the json.loads function.\nWhat this allows us to do is easily pull individual data values from the JSON output. In the original JSON data that we received, we saw a bunch of user data organized into key-value pairs. So once we load that JSON into a Python dictionary, we can pull out individual values and assign them to variables.\nFinally - we can print all of that data out to our terminal:\nmatt@DESKTOP:~/postman-python$ python3 simpleGET.py Search by Username: \u0026gt; Delphine Glenna Reichert can be reached via the following methods: Email: Chaim_McDermott@dana.io Phone: (775)976-6794 x41206 Nothing super fancy here - but this could be the beginnings of building out a user search web page, or maybe importing certain data fields into another system. Once we get the basics of working with REST APIs out of the way, there are a ton of possibilities for what can be built.\nOkay - What About Network Automation? Many new network technologies are now API enabled. Nearly every cloud-hosted platform or management controller offers some method of interacting programmatically. In fact, most routers \u0026amp; switches even offer a REST-based API that can be used for automated configuration or monitoring.\nSo let\u0026rsquo;s take the following example: We have a couple of Meraki networks, and we\u0026rsquo;ve been tasked with providing a report of how many total clients have connected to our network in the last 30 days. In addition, it would be nice to see a breakdown of the distribution of operating systems.\nLet\u0026rsquo;s take a look at the following screenshot from Postman:\nUsing Meraki\u0026rsquo;s API docs, we can determine the exact URL endpoint we need to query. This URL requires that we know the exact network ID for the network we want to get info from, which we\u0026rsquo;ll pretend we already know. Also not shown here, we have an additional HTTP header that includes our API key - which was generated in Meraki Dashboard.\nIn addition, we have a few query parameters to help make sure we get the data we need. By default, this API endpoint will return 10 devices. In this particular network, we already know we have more than 10 - so we use the perPage query parameter to request up to 100 devices. We also wanted to query the last 30 days of devices, so we use the timespan parameter. This parameter expects the lookback to be in seconds, so we specify 43,200 seconds.\nSo we got back a list of devices - and specifically in the example shown, it looks like we have an Android device on our network. Easy enough to spot that info, right?\nWell - maybe we have more than a handful of devices. We don\u0026rsquo;t want to manually sort through all of that data \u0026amp; assemble a list. Or maybe this task needs to be run more than once. This is where we can use Python to automatically assemble that report with just a few lines of code.\nimport requests import json URL = \u0026#34;https://api.meraki.com/api/v1\u0026#34; APIKEY = {\u0026#34;X-Cisco-Meraki-API-Key\u0026#34;: \u0026#34;xxxxxxxxxxxxxxxxx\u0026#34;} --- Code omitted --- def getClients(orgID, networkList): \u0026#34;\u0026#34;\u0026#34; Query clients for each network, return client list \u0026#34;\u0026#34;\u0026#34; clientCount = {} total = 0 # Query Parameters: Return up to 100 devices seen in the past 43,200 seconds (30 days) q = {\u0026#34;perPage\u0026#34;: \u0026#34;100\u0026#34;, \u0026#34;timespan\u0026#34;: \u0026#34;43200\u0026#34;} for network in networkList: # Query clients for each network queryURL = URL + f\u0026#34;/networks/{network}/clients\u0026#34; response = requests.get(queryURL, params=q, headers=APIKEY) data = json.loads(response.text) # Grab client OS from each device \u0026amp; append to clientCount dictionary for client in data: try: clientCount[client[\u0026#34;os\u0026#34;]] += 1 except KeyError: clientCount[client[\u0026#34;os\u0026#34;]] = 1 except TypeError: continue total += 1 # Append final count of all devices \u0026amp; return dict clientCount[\u0026#34;Total Devices\u0026#34;] = total return clientCount def printReport(clientOS): \u0026#34;\u0026#34;\u0026#34; Print final output to terminal \u0026#34;\u0026#34;\u0026#34; print(\u0026#34;Count of clients by operating system:\u0026#34;) for OS in clientOS: print(f\u0026#34;{OS}: {clientOS[OS]}\u0026#34;) --- Code omitted --- To keep things simple - we\u0026rsquo;ll only look at a snippet of the code. The entire example will be posted to GitHub, and contains two additional functions that will automatically find our Organization ID \u0026amp; Network ID.\nIn our getClients function, we create an empty Python dictionary to hold our list of client operating systems - and we also create a total variable which we\u0026rsquo;ll increment for every client in the list.\nThe HTTP GET request should look fairly similar to what we used previously. The big difference is that we\u0026rsquo;re creating a dictionary called q to hold our perPage \u0026amp; timespan query parameters. Then we include that in our GET request by adding params=q to the request.\nNext - we convert the JSON response into a Python object, and walk through every device in that list. For each device in the list, we look at the \u0026ldquo;os\u0026rdquo; value to determine the operating system detected by Meraki. We then use a try/except block to see if we can increment the counter for that operating system. If we get a KeyError, then we know the OS isn\u0026rsquo;t currently on the list \u0026amp; we can just add it with a new value of 1.\nAfter all that has been completed, we return our clientCount dictionary - which our primary function passes to the printReport function. This just prints out a header, then iterates through our clientCount dictionary and prints each operating system \u0026amp; count.\nLet\u0026rsquo;s take a look at that output below:\nmatt@DESKTOP:~/postman-python$ python getOScount.py Count of clients by operating system: None: 9 Sailfish OS: 2 Nokia: 1 Windows 10: 2 Raspberry Pi: 2 Android: 4 Meraki OS: 1 Total Devices: 21 In the list above, we can see that we have a total of 21 devices in our network - and 7 different operating systems were found as well.\nThat output may not be the prettiest of reports, but it was accomplished with ~50 lines of Python \u0026amp; takes less than a few seconds to run. If we wanted to format the data in another way, or include data from a non-Meraki source, we absolutely could.\nThat\u0026rsquo;s part of what\u0026rsquo;s exciting about using Python for network automation - most anything we could think of doing is possible.\nTIP: Use Postman to Generate Python Code Okay - so I wanted to save this bit for last. Now that you\u0026rsquo;ve seen examples in both tools, I wanted to make sure we cover a feature in postman that can save some time.\nSo over in the right-hand side of your Postman window, you\u0026rsquo;ll see an icon that looks like this: \u0026lt;/\u0026gt;\nIf you click that, you\u0026rsquo;ll see a dropdown menu of various languages \u0026amp; tools. Select one of those options and Postman will auto-generate code you can use. This auto-generated code will execute the same request you have built in the Postman GUI.\nFor example, our last Postman request we used to get Meraki clients:\nI wanted to save this for last, because I feel like it\u0026rsquo;s important to understand how to write the code manually first - then take advantages of shortcuts after having a good understanding of what the code is doing.\nThat\u0026rsquo;s about all I had for this example. Again, my intent here wasn\u0026rsquo;t to create an all-inclusive resource for how to use Postman \u0026amp; Python - but rather to give some examples for each one \u0026amp; show where/why each would be used.\nI\u0026rsquo;ve met a handful of people over the years who have seen many API demos using Postman, but aren\u0026rsquo;t sold yet on the value of learning Python \u0026amp; getting into network automation.\nHopefully these examples help bridge that gap a little bit 😄\n","permalink":"https://0x2142.com/from-postman-to-python-your-first-get-request/","summary":"Let\u0026rsquo;s walk through a few simple Postman requests, and show how to do the same with Python","title":"From Postman to Python: Your First GET Request"},{"content":"The post below was contributed by guest author: Nicole Henry\nHey folks. So, we got a new Comcast xFi Gateway 3rd Generation at my parent\u0026rsquo;s house, and all the devices were able to discover our SSIDs \u0026amp; connect to the internet at both 2.4Gz \u0026amp; 5Ghz \u0026hellip;except my personal computer. My personal computer was listing networks not associated with our house, yet it wasn\u0026rsquo;t showing OUR networks. What gives?!\nFirst off, System Information about my computer\u0026hellip;\nOS: Windows 10 Home Manufacturer \u0026amp; Model: Dell Inspiron 5558 Wireless adapter: Intel(R) Dual Band Wireless-AC 3160 xFi Gateway models numbers: CGM4331COM or TG4482A Back to the goods\u0026hellip;\nI restarted my computer twice. Turned on \u0026amp; off the wi-fi on my computer. Played around with network settings on my computer. Still no luck.\nSo of course I headed to Twitter and asked for assistance. You can read the tweet and read the replies here. I got many different tips from people: update the wi-fi drivers, buy a USB Wi-fi Dongle, etc. But my friend Pat ( @Battle_Nerd_1 ) messaged me and suggested that I look into what wi-fi standards (802.11 b,a,g,n,ac) are associated with my wi-fi adapter.\nLet’s walk through how to do that, why don’t we?\n1. Look at the Advanced properties of the wifi adapter. (I have a Windows 10 device) Go to Start -\u0026gt; Device Manager -\u0026gt; Network Adapters -\u0026gt; Right click the adapter name -\u0026gt; click Properties -\u0026gt; then Advanced\nThe Advanced network properties of the computer’s wi-fi adapter is what we need to review. Notice in the “Property” field; the wi-fi standard displays 802.1b/g for my computer. This is an important detail.\n2. Compare \u0026amp; Change the settings (I use modem and router interchangeably. That’s probably not good practice, but oh well lol)\nLogin to your router (*** I’ll add instructions below how to do this) \u0026amp; start clicking around until you find the Wi-fi Mode settings. Here’s the settings for our router at 2.4GHz.\nFrom the same page, here are the Mode options of our router:\n3. Compare \u0026amp; Change the settings Remember, my wi-fi standard for my computer’s wi-fi adapter is 802.1b/g. Initially the modem’s Wi-fi Mode was set to 802.11 g/n/ax, then Pat told me to change it to 802.11 g/n because the new ax isn’t supported by a lot of wireless drivers; it’s Wi-Fi 6. Once I changed the Mode to 802.11 g/n, IMMEDIATELY my computer recognized the 2.4GHz network and connected!! Yay Pat!!\nHow to Log in to your Router/Modem (These instructions are for Windows devices )\n1. Find your Gateway/Router/Modem’s IP address Go to Start and type cmd for Command Prompt. Type ipconfig, then hit Enter Scroll down until the Wi-fi section, \u0026amp; take note of the default gateway IP address. This number will most likely look like 10.x.x.x or 192.168.x.x 2. Log into your Router/Modem Next open up a web browser and type in the aforementioned IP address, then hit Enter.\nOnce prompted for a Username and password; try Admin for username \u0026amp; Password for password. Also, It’s good practice to change the default password, so definitely do that :)\nSo now you know how to find the IP address, log into, \u0026amp; change the password of your Gateway/Router/Modem. Yay you! Now you can go back to Step 2 and learn how to view your Router/Modem’s wi-fi settings.\nSo thanks to my Twitter friend, Pat, for walking me through how to find info about my computer’s wi-fi adapter and how to change the modem/router’s wi-fi settings; also he saved me from having to spend money on a new computer! In the 5GHz Mode settings, there is no 802.11 b/g option, therefore my computer can only connect at the 2.4GHz frequency, which is not an issue for me. I can now connect to a home network \u0026amp; have internet access on my personal computer.\n","permalink":"https://0x2142.com/old-pc-wireless-failure/","summary":"A new Comcast xFi Gateway caused issues with older wireless devices. Here\u0026rsquo;s how to troubleshoot","title":"Old Computer Fails to Discover SSID nor Connect to Home Network [Solved]"},{"content":"Frequently Asked Questions Help! I need assistance / something isn\u0026rsquo;t working Leave me a comment on the blog post or video you\u0026rsquo;re using. If I can help, I\u0026rsquo;ll do what I can. That being said - any support I provide is best-effort only. I don\u0026rsquo;t work for Cisco TAC, nor am I paid by any vendor to provide support for their products.\nSince I run all of this on my own, I can\u0026rsquo;t always respond to every comment or request for help - but if it\u0026rsquo;s a publicly posted comment, sometimes other people may also be able to provide answers.\nCan I pay you to do this for me? No, sorry - I am not currently open to doing this. I produce content (blogs, videos, etc) for educational purposes \u0026amp; to help show how to do something yourself. This isn\u0026rsquo;t my job, it\u0026rsquo;s something I do for fun (and I do have a full-time job that keeps me quite busy). If you do follow one of my how to\u0026rsquo;s \u0026amp; run into an issue, I\u0026rsquo;m happy to help if I can (see above question).\nI saw someone else posted to your blog, what\u0026rsquo;s that all about? I do accept contributions / guest posts occasionally. More often than not, this is from someone who doesn\u0026rsquo;t have their own blog/platform \u0026amp; I\u0026rsquo;ve been pushing them to write something :). If you\u0026rsquo;re interested in posting something here, feel free to reach out to me at: matt at 0x2142 dot com.\nI have a product / software, will you post a review about it? At this time, likely the answer will be no - but it depends on what the product is. If it\u0026rsquo;s something I would use \u0026amp; feel that my readers would get value out of, then maybe! If it\u0026rsquo;s not a good fit, I will decline. Feel free to reach out to me at: matt at 0x2142 dot com\nDon\u0026rsquo;t you work for $Vendor? Are you just promoting their stuff? Uhh, at the time of this writing - yes, I do! However, the content I produce is on my own time, with my own opinions. None of it is sponsored, encouraged, or requested by my employer. If I ever post anything that is, I\u0026rsquo;ll be sure to add a disclaimer. That being said - I\u0026rsquo;ve worked with multiple different vendors products over the course of my career. They all have their pros, cons, and use-cases where they fit best. While I may have more access to $vendor\u0026rsquo;s products because of my employment, I try my best not to intentionally advertise or promote them.\nDo you have stickers? Why yes, I do! Want some? Hit me up at: matt at 0x2142 dot com.\nWhat\u0026rsquo;s with the Amazon affiliate links? I run this blog \u0026amp; my YouTube channel on my own time \u0026amp; money. It\u0026rsquo;s a hobby for me. In no way am I attempting to make a full-time job out of this, or turn it into a profitable side-business. However, I do invest my own money into web hosting, audio \u0026amp; video equipment, and some of the products I use in my content. Ideally, it would be nice to offset that a little bit - which is why I occasionally use Amazon affiliate links when it makes sense. This site no longer displays advertisements.\nIf you like the content that I produce, I would appreciate you using these links to support my content. And as always, sharing the content with your friends, customers, or co-workers is greatly appreciated!!\n","permalink":"https://0x2142.com/faq/","summary":"\u003ch1 id=\"frequently-asked-questions\"\u003eFrequently Asked Questions\u003c/h1\u003e\n\u003ch2 id=\"help-i-need-assistance--something-isnt-working\"\u003eHelp! I need assistance / something isn\u0026rsquo;t working\u003c/h2\u003e\n\u003cp\u003eLeave me a comment on the blog post or video you\u0026rsquo;re using. If I can help, I\u0026rsquo;ll do what I can. That being said - any support I provide is best-effort only. I don\u0026rsquo;t work for Cisco TAC, nor am I paid by any vendor to provide support for their products.\u003c/p\u003e\n\u003cp\u003eSince I run all of this on my own, I can\u0026rsquo;t always respond to every comment or request for help - but if it\u0026rsquo;s a publicly posted comment, sometimes other people may also be able to provide answers.\u003c/p\u003e","title":"FAQ"},{"content":"The post below was contributed by guest author: Nicole Henry\nHi, hello. Nicole here.\nDo you want to diversify your skillset \u0026amp; learn JunOS? Do you want to be able to add JNCIA-Junos to your resume? Here\u0026rsquo;s a thread of the free resources I used!\nLink to the original twitter thread\n1) Junos Genius Junos Genius is an amazing, stupendous, fantastic resource. USE THIS!! Create an account, scroll down to Juniper Open Learning, \u0026amp; select JNCIA-Junos (or whichever cert track in which you\u0026rsquo;re interested). At the time of this post, once you finish all the videos \u0026amp; practice test(s), there will be a voucher test. If you pass the voucher test, you\u0026rsquo;ll receive a voucher for 75% off any Associate Level exam. Again, USE JUNOS GENIUS!! Watch all the vids, take all the practice tests.\nUPDATE All the material from Junos Genius has been moved to Juniper Open Learning within the Learning Portal. The content is the same, just in a new location.\n2) \u0026ldquo;Day One: Beginner\u0026rsquo;s Guide to Learning Junos\u0026rdquo; It\u0026rsquo;s easy to read and is a very nice complement to Junos Genius. I HIGHLY recommend it. Here\u0026rsquo;s the pdf.\n## 3) \u0026ldquo;Junos for IOS Engineers\u0026rdquo; If you\u0026rsquo;re familiar with Cisco IOS, \u0026ldquo;Junos for IOS Engineers\u0026rdquo; is another option for you. (click the link \u0026amp; scroll down until you see the book). There are a bunch of Day One books, I\u0026rsquo;ve already read 2, so much information!!!\nSide note: Juniper Networks has a collection of books written by industry professionals on a variety of topics. These books are called Day One books. They can be found here. And if you\u0026rsquo;re like me and you like physical books, you have the option to buy books from the virtual bookstore !!\n4) Juniper vLabs Don\u0026rsquo;t have any physical equipment?? No problem!! Use vLabs for practice!!! Definitely watch the video on the home page before you get started; it\u0026rsquo;s a good introduction to vlabs \u0026amp; how to use it. Super highly recommend vLabs.\n5) Youtube Here\u0026rsquo;s some random youtube videos that I really liked:\n\u0026ldquo;Using Juniper for the First Time | JunOS CLI\u0026rdquo; \u0026ldquo;Interface Naming Conventions\u0026rdquo; \u0026ldquo;Juniper Device Interfaces\u0026rdquo; \u0026ldquo;Juniper Networks JNCIA-Junos Certification Practice Test\u0026rdquo; And that\u0026rsquo;s all! Those are the resources I used - all free. I knew nothing about Junos, and it took about 2 months to study for the exam using those resources and I passed on the first attempt. I hope you found this thread useful.\n(Shoutout to Matt for letting me use his site to make this post)\n","permalink":"https://0x2142.com/jncia-junos-study-resources/","summary":"Thinking about going for the JNCIA-Junos? Here\u0026rsquo;s some great free resources to get you started!","title":"JNCIA-Junos Study Resources (All Free!)"},{"content":" This post has been on my backlog for a while\u0026hellip; I\u0026rsquo;ve been intermittently trying out the IOS-XE guestshell feature for a few different projects, and never feeling like any of them were good enough to write about.\nSo why not just write about guestshell itself? It\u0026rsquo;s such a nifty tool.\nWhat is Guestshell? Now that Cisco\u0026rsquo;s Catalyst platform has standardized on IOS-XE, we can take advantage of some of the features of the Linux-based platform. One of which being Linux Containers (LXC).\nMost of the latest Catalyst routers, switches, and access points support what Cisco calls Application Hosting - which is a fancy way of just saying it can run containers on-box. Another acronym to be aware of is IOx - which is what Cisco calls their IOS-XE/Linux appliction framework.\nGuestshell is a specific feature within IOS-XE, where a dedicated pre-built container is enabled for on-box access to a Linux shell.\nWhat Hardware Supports Guestshell? Okay - so for this example, I\u0026rsquo;ll be using the new Catalyst 8000V platform (a virtual Catalyst router). I do also have a Catalyst 9200L on hand, but unfortunately it\u0026rsquo;s one of the few Catalyst platforms that does not support Guestshell (due to lower-end HW specs).\nYou can also use the Catalyst 8000V to test this in a lab, or you could use most of the Catalyst 8000 / 9000 series platforms if you had them available (or the CSRv \u0026amp; some ISR routers!).\nYou can find a list of supported platforms \u0026amp; other hardware requirements here.\nAll that being said - let\u0026rsquo;s get started!\nEnabling IOx So before we can actually use the guestshell feature, there is some pre-configuration to be done.\nFirst we\u0026rsquo;ll need to ensure that the IOx application framework is enabled \u0026amp; running. We can check this by using the show iox command:\n0x8KV01# show iox IOx Infrastructure Summary: --------------------------- IOx service (CAF) : Not Running IOx service (HA) : Not Supported IOx service (IOxman) : Not Running IOx service (Sec storage) : Not Supported Libvirtd 5.5.0 : Running And as we see above - most components are listed as \u0026ldquo;Not Running\u0026rdquo; or \u0026ldquo;Not Supported\u0026rdquo;.\nSo we\u0026rsquo;ll enter config mode, and use the iox command to enable these services:\n0x8KV01# conf t Enter configuration commands, one per line. End with CNTL/Z. 0x8KV01(config)# iox 0x8KV01(config)# 0x8KV01(config)# exit 0x8KV01# show iox IOx Infrastructure Summary: --------------------------- IOx service (CAF) : Running IOx service (HA) : Not Supported IOx service (IOxman) : Running IOx service (Sec storage) : Not Supported Libvirtd 5.5.0 : Running Configuring Guestshell Networking There\u0026rsquo;s a good chance we\u0026rsquo;ll want our container to have access to the outside world, so let\u0026rsquo;s take a quick look at the network configuration.\nNote: The networking config syntax will differ if you are using a Catalyst router or a Catalyst switch. Since I am using a Catalyst 8000V - we will look at the config for a Catalyst router first.\nIn order to allow external access to our container, we\u0026rsquo;ll need to configure a gateway interface and NAT translations. Then we can provide a full network configuration to our container:\n## Enable NAT on our external interface - in my case, gigabitEthernet1 0x8KV01(config)# interface gigabitEthernet 1 0x8KV01(config-if)# ip nat outside ## Create a virtual interface for the guestshell container 0x8KV01(config-if)# int virtualportgroup0 0x8KV01(config-if)# ip address 192.168.100.1 255.255.255.0 0x8KV01(config-if)# ip nat inside ## Create an ACL to match traffic from guestshell \u0026amp; Enable NAT 0x8KV01(config)# ip access-list standard IOX_NAT 0x8KV01(config-std-nacl)# permit 192.168.100.0 0.0.0.255 0x8KV01(config)# ip nat inside source list IOX_NAT interface gigabitEthernet 1 overload ## Assign network configuration to our guestshell container 0x8KV01(config)#app-hosting appid guestshell 0x8KV01(config-app-hosting)# app-vnic gateway0 virtualportgroup 0 guest-interface 0 0x8KV01(config-app-hosting-gateway0)# guest-ipaddress 192.168.100.5 netmask 255.255.255.0 0x8KV01(config-app-hosting-gateway0)# app-default-gateway 192.168.100.1 guest-interface 0 0x8KV01(config-app-hosting)# name-server0 8.8.8.8 Now, as I mentioned before - this config will look different if you\u0026rsquo;re using a switch vs a router.\nSo let\u0026rsquo;s take a quick look at what this would look like, if we were on a Catalyst Switch instead:\nCat9k(config)# interface AppGigabitEthernet 1/0/1 Cat9k(config-if)# switchport mode trunk Cat9k(config)# app-hosting appid name Cat9k(config-app-hosting)# app-vnic AppGigabitEthernet trunk Cat9k(config-app-hosting-trunk)# vlan 10 guest-interface 0 Cat9k(config-app-hosting-vlan-access-ip))# guest-ipaddress 192.168.100.1 netmask 255.255.255.0 Cat9k(config-app-hosting)# app-default-gateway 192.168.100.1 guest-interface 0 Cat9k(config)# name-server 8.8.8.8 Once all that config is done - Let\u0026rsquo;s move on to getting our container started!!\nManaging the Guestshell Process In order to start a container on our Catalyst device, we\u0026rsquo;ll need to go through a process of deploying the app, activating it, then starting it.\nFor guestshell, there is a shortcut command that will perform all of these steps for you - and make managing the guestshell process much easier.\nSo let\u0026rsquo;s go ahead and spin up our guestshell container with the command guestshell enable:\n0x8KV01# guestshell enable Interface will be selected if configured in app-hosting Please wait for completion guestshell installed successfully Current state is: DEPLOYED guestshell activated successfully Current state is: ACTIVATED guestshell started successfully Current state is: RUNNING Guestshell enabled successfully 0x8KV01# show app-hosting list App id State --------------------------------------------------------- guestshell RUNNING And as you can see above, everything gets automatically deployed \u0026amp; started. We can verify using the show app-hosting list command to see that our container has been started.\nIf we need to, we can also stop \u0026amp; deactivate our container using the command guestshell disable.\nWe could also use the commands below if we wanted to perform these steps manually:\n0x8KV01# app-hosting activate appid guestshell 0x8KV01# app-hosting start appid guestshell 0x8KV01# app-hosting stop appid guestshell 0x8KV01# app-hosting deactivate appid guestshell Before we move onto accessing the guestshell interface, I also wanted to show where you can get more detail about the guestshell container.\nUsing the show app-hosting detail appid guestshell command, we can see details around the container version, current MAC/IP config, and resource consumption:\n0x8KV01# show app-hosting detail appid guestshell App id : guestshell Owner : iox State : RUNNING Application Type : lxc Name : GuestShell Version : 3.2.0 Description : Cisco Systems Guest Shell XE for x86_64 Path : /guestshell/:guestshell.tar URL Path : Activated profile name : custom Resource reservation Memory : 256 MB Disk : 1 MB CPU : 800 units CPU-percent : 3 % VCPU : 1 Attached devices Type Name Alias --------------------------------------------- serial/shell iox_console_shell serial0 serial/aux iox_console_aux serial1 serial/syslog iox_syslog serial2 serial/trace iox_trace serial3 Network interfaces --------------------------------------- eth0: MAC address : 52:54:dd:d:bf:da IPv4 address : 192.168.100.5 IPv6 address : :: Network name : VPG0 Port forwarding Table-entry Service Source-port Destination-port --------------------------------------------------- Accessing the Guestshell CLI Now for the fun part! Let\u0026rsquo;s get into our guestshell container.\nWe\u0026rsquo;ll just need to use the guestshell command, and we\u0026rsquo;ll be dropped into the Linux shell:\n0x8KV01# guestshell [guestshell@guestshell ~]$ [guestshell@guestshell ~]$ uname -a Linux guestshell 5.4.40 #1 SMP Tue Oct 6 17:57:00 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux Let\u0026rsquo;s also make sure our network \u0026amp; NAT configuration worked:\n[guestshell@guestshell ~]$ ping 8.8.8.8 PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. 64 bytes from 8.8.8.8: icmp_seq=1 ttl=64 time=3.57 ms 64 bytes from 8.8.8.8: icmp_seq=2 ttl=64 time=2.64 ms 64 bytes from 8.8.8.8: icmp_seq=3 ttl=64 time=2.54 ms ^C --- 8.8.8.8 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 4ms rtt min/avg/max/mdev = 2.543/2.916/3.567/0.465 ms If needed, we can also run commands on our Catalyst device CLI without leaving the guestshell interface by using the dohost utility:\n[guestshell@guestshell ~]$ dohost \u0026#34;show app-hosting list\u0026#34; App id State --------------------------------------------------------- guestshell RUNNING [guestshell@guestshell ~]$ Just a note, the reverse is also possible. Using the guestshell run command from our Catalyst CLI, we can run commands in our container:\n0x8KV01# guestshell run uname -a Linux guestshell 5.4.40 #1 SMP Tue Oct 6 17:57:00 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux 0x8KV01# It\u0026rsquo;s also worth reiterating, that the guestshell container is a full Linux container (It runs CentOS). So if we need to install additional Linux packages, we can use our Linux package manager (YUM) to do so.\nAnyways - Let\u0026rsquo;s wrap this up by looking at the built-in Python modules \u0026amp; capabilities\nUsing Python in Guestshell One of the more appealing use cases for guestshell is the ability to run native Python scripts \u0026amp; code directly on your Catalyst device.\nA couple of possible use cases could be:\nTroubleshoot intermittent issues - Automatically collect device state (routes, MAC changes, etc) on a regular basis to log locally or send via email Automated resolution - If a certain type of state change is observed, we could run a pre-set list of commands to try and automatically fix the issue Local monitoring / troubleshooting - We could run a series of ping, web page load tests, etc from a python script to help monitor performance from the switch\u0026rsquo;s perspective Change management / notification - A script could be written to monitor the device configuration for any changes made, then send an automated email when something was detected Have a risky change that might sever your connection to a remote device? A script could execute the change for you, and if unsuccessful, revert to a working configuration (without doing a reload after) These are just a handful of ideas that came to mind while writing this blog post\u0026hellip; But there are many more possibilities!\nWithin the guestshell container, there is a special cli python module that comes pre-loaded. This module allows us to run CLI commands on our catalyst device directly from a python script \u0026amp; receive the command output for parsing.\nLet\u0026rsquo;s look at a quick example using the Python interactive interpreter to save the device configuration:\n[guestshell@guestshell ~]$ python3 Python 3.6.8 (default, Aug 24 2020, 17:57:11) [GCC 8.3.1 20191121 (Red Hat 8.3.1-5)] on linux Type \u0026#34;help\u0026#34;, \u0026#34;copyright\u0026#34;, \u0026#34;credits\u0026#34; or \u0026#34;license\u0026#34; for more information. \u0026gt;\u0026gt;\u0026gt; \u0026gt;\u0026gt;\u0026gt; import cli \u0026gt;\u0026gt;\u0026gt; status = cli.execute(\u0026#34;write memory\u0026#34;) \u0026gt;\u0026gt;\u0026gt; if \u0026#34;OK\u0026#34; in status: print(\u0026#34;Device configuration was saved!\u0026#34;) ... Device configuration was saved! The example above uses the cli.execute Python function, which will run commands in Cisco\u0026rsquo;s exec mode.\nWhat if we wanted to make configuration changes? In that case we would use the cli.configure function:\n\u0026gt;\u0026gt;\u0026gt; config_data = \u0026#34;\u0026#34;\u0026#34;interface loopback200 ... ip address 172.16.99.10 255.255.255.0 ... \u0026#34;\u0026#34;\u0026#34; \u0026gt;\u0026gt;\u0026gt; \u0026gt;\u0026gt;\u0026gt; cli.configure(config_data) \u0026#39;Line 1 SUCCESS: interface loopback200\\nLine 2 SUCCESS: ip address 172.16.99.10 255.255.255.0\\n\u0026#39; \u0026gt;\u0026gt;\u0026gt; \u0026gt;\u0026gt;\u0026gt; cli.execute(\u0026#34;show run interface loopback200\u0026#34;) \u0026#39;Building configuration...\\nCurrent configuration : 68 bytes\\n!\\ninterface Loopback200\\n ip address 172.16.99.10 255.255.255.0\\nend\\n\u0026#39; In the above example, we used a multi-line comment to provide the list of commands to enter. The cli.configure function also supports a Python list - so we could provide the commands in that format as well.\nYou\u0026rsquo;ll notice that the Python interpreter returns a status for each command that we attempted to run. This should allow us to error-check \u0026amp; ensure that all of our intended configuration was accepted.\nLastly, we used the cli.execute command again to validate that our running configuration looks correct.\nThere\u0026rsquo;s a bit more functionality built into the cli Python module, but I just wanted to cover some of the common use cases. More details can be found here\nA Few Common Questions Does this give me access to the underlying IOS-XE Linux shell?\nNope, anything running in guestshell is running in a container - which runs on-top of the IOS-XE image.\nWhat about resource conflicts?\nThe linux containers running on the IOS-XE platform are given their own dedicated resources. Any resource constraints that affect the containers will not impact normal network functions.\n","permalink":"https://0x2142.com/getting-started-with-ios-xe-guestshell/","summary":"In this post, we\u0026rsquo;ll explore how to set up Cisco IOX \u0026amp; Guestshell for on-box Python","title":"Getting Started with IOS-XE Guestshell"},{"content":" I have a local Cisco SD-WAN lab environment running at home, which was built in EVE-NG. It\u0026rsquo;s what I use whenever I need to test something for a customer, or just play around with the templates or APIs.\nI\u0026rsquo;m planning on spending some hands-on time with new features soon, along with working on some automation projects - so it\u0026rsquo;s well past time to upgrade my lab.\nCurrently I\u0026rsquo;m running on version 18.4.302, but my intent is to upgrade to 20.3.2 - which is the latest version available today.\nIn this blog post, we\u0026rsquo;ll walk through how to upgrade a Cisco SD-WAN / Viptela network, including:\nControl Plane: vManage vBond vSmart(s) Data Plane: vEdge / vEdge Cloud While my lab is using the on-prem controllers, these steps will work just the same if you\u0026rsquo;re using Cisco\u0026rsquo;s cloud-hosted controllers.\nDownloading the Software Images First thing\u0026rsquo;s first - in order to upgrade our environment, we need the correct software images!\nHead over to Cisco Software Downloads, and search for SD-WAN (or click here!).\nOnce on this page, I just want to call out that we will need to click on the SD-WAN Software Update link. This may seem simple enough, but the other links for vManage Software or vSmart Software only contain images for a new install.\nAfter that - just select the image version that you would like to upgrade to, and download both images.\nIn my case, since I\u0026rsquo;m downloading version 20.3.2, I\u0026rsquo;ll be using the following images:\nvSmart, vEdge Cloud, vEdge 5000, ISR1100 series and vBond upgrade image File name: viptela-20.3.2-x86_64.tar.gz vManage upgrade image File name: vmanage-20.3.2-x86_64.tar.gz Adding Images to the Software Repository The process for upgrading a Cisco SD-WAN environment is pretty straightforward.\nThe control plane can be upgraded independently of the edge devices, so long as everything stays within the bounds of the compatability matrix. In my case, I\u0026rsquo;ll be upgrading to 20.3.2 - and the controllers could still support edge devices as far back as 17.2. So no issues here!\nAlright - let\u0026rsquo;s get started!\nFirst, we\u0026rsquo;ll need to upload our images into our local vManage software repository. This is the file storage for all upgrade images, and it\u0026rsquo;s where the controllers \u0026amp; edge devices will go to pull their images from.\nIn the vManage dashboard, we\u0026rsquo;ll go to the Maintenance tab and select Software Repository.\nThen, click on Add New Software.\nIn here we\u0026rsquo;ll see a few options: vManage and Remote Server / Remote Server - vManage.\nWe\u0026rsquo;ll use vManage if we want to upload \u0026amp; distribute images from the local vManage server we\u0026rsquo;re logged into currently.\nAlternatively, we could use a remote file storage server by using the Remote Server option. If you choose to go this route, don\u0026rsquo;t forget to ensure that ALL controllers \u0026amp; WAN edge devices have access to this storage location.\nAfter you select an option, it\u0026rsquo;s an easy drag \u0026amp; drop to upload the software images.\nPlanning the Upgrade So before we get into actually applying our image upgrades - let\u0026rsquo;s address the questions of \u0026ldquo;What order do I upgrade things in?\u0026rdquo; and \u0026ldquo;What\u0026rsquo;s the impact?\u0026rdquo;.\nSince this is a lab environment for me, I\u0026rsquo;ll be upgrading everything all at once - since uptime / outages aren\u0026rsquo;t a factor here\nIf you\u0026rsquo;re doing this in a production environment, I highly recommend performing these upgrades in an outage / maintenance window - or at least an off-peak time.\nYes, you can upgrade the controllers at any time without causing any issue. Yes, you can upgrade a redundant pair of vEdge devices and keep a branch online. However, I would advise you to try these out off-hours first - and get your own understanding of how this works \u0026amp; what to expect before doing it in production.\nAs for the upgrade order, we\u0026rsquo;re going to start at the top of the food chain and work our way down:\nUpgrade vManage first Then vBond Upgrade ONE vSmart controller \u0026amp; wait for it to come online / re-establish control connections Then upgrade the second / redundant vSmart controller After the control plane is upgraded \u0026amp; stable - move onto the edge devices These steps can also be found under the Best Practices section of the Cisco SD-WAN Getting Started Guide. Cisco\u0026rsquo;s official recommendation is to wait 24 hours in between a few of those steps, to ensure platform stability - but for my lab that won\u0026rsquo;t be necessary\nUpgrading vManage Once we have uploaded our images \u0026amp; we have our upgrade plan - we can move forward with actually performing the image upgrades.\nStarting with vManage - we\u0026rsquo;ll go to Maintenance \u0026gt; Software Upgrades \u0026gt; vManage.\nThen we\u0026rsquo;ll click on Upgrade and select our version - in my case 20.3.2. After that, just click Upgrade\nNow, what this does in the background is just pre-stage the vManage image for an upgrade. The actual software upgrade is not occurring just yet.\nThink of this step like you might prepare an IOS/IOS-XE router: Copying the image to the device flash. The image is there and ready - but we haven\u0026rsquo;t booted to it yet.\nOnce that\u0026rsquo;s all done, we\u0026rsquo;ll go back to the vManage upgrade page and click Activate, select our image again, then click Activate.\nNow this step is where vManage will reboot, apply the upgrade, and come back online with the new image.\nAgain, back to the IOS/IOS-XE analogy: this is the equivalent of setting out boot system flash:\u0026lt;image name\u0026gt; to the new image, then rebooting our router.\nvManage may take a short while to complete \u0026amp; reinitialize. In my lab, about 10-15 minutes.\nUpgrading vBond \u0026amp; vSmart After vManage is done, it\u0026rsquo;s time to work on the real heart of our control plane: vBond \u0026amp; vSmart.\nSimilar to vManage, we\u0026rsquo;ll start by going to Maintenance \u0026gt; Software Upgrades \u0026gt; Controller.\nHere we will need to select the devices we want to upgrade. As I mentioned earlier, you may want to do these one at a time \u0026amp; in a phased approach. However, in my lab I\u0026rsquo;ll select all of them to upgrade at once.\nNow in this case, vManage will still follow the proper order (vBond, then vSmart), and even perform a rolling upgrade one device at a time. This may be suitable for you in production, but again I would urge you to test it for yourself first!\nThe other big difference here, as you can see in the screenshot above, is the presence of the Activate \u0026amp; Reboot checkbox.\nThis does exactly as you would anticipate. Instead of doing the two-step process with vManage where we staged the image, then performed the activation/reboot - this checkbox will do all of that in one step.\nIn my lab environment, I did check this box \u0026amp; allowed everything to reboot automatically.\nWhy is there a separation between uploading the image \u0026amp; rebooting / activating it? To allow better granularity over the process.\nFor example, maybe you have a poor internet connection at a branch site \u0026amp; the image upload may take a long time. This separation of tasks allows you to stage all of the images independently of applying them. If you have a short outage window, this could help you save time by pre-staging the images ahead of time.\nBack to the upgrade - just like vManage we\u0026rsquo;ll select the version we\u0026rsquo;re applying then click Upgrade\nAgain - Depending on the resources available to your controllers, the image upload / activation process may take a short while\u0026hellip;\nUpgrading the WAN Edge Appliances In my lab, I\u0026rsquo;m currently using a handful of vEdge Cloud VMs as branch office routers. The upgrade process here should apply to other edge devices as well.\nAfter we\u0026rsquo;re confident our controller upgrades have been successful \u0026amp; all control connections have been re-established - we can move to upgrading our edge devices.\nIt\u0026rsquo;s also worth mentioning that my lab currently has no redundant deployments of WAN edge appliances. All of my test \u0026lsquo;branch offices\u0026rsquo; are single-homed to one vEdge Cloud - so an outage will be required to apply the images.\nIf you\u0026rsquo;re using a redundant configuration at a remote site, ideally you would upgrade ONE edge device first. Then only upgrade the second after control connections \u0026amp; routing adjacencies had been re-established on the first device. This should allow for an upgrade with minimal downtime.\nThe process for upgrading the edge devices mirrors what we saw for vBond / vSmart.\nWe\u0026rsquo;ll go to Maintenance \u0026gt; Software Upgrade \u0026gt; WAN Edge, then select the edge devices we want to upgrade.\nClick Upgrade, select the target version from the drop-down, then click Upgrade. Again, in my case, I also selected the Activate \u0026amp; Reboot checkbox\nNOTE: It\u0026rsquo;s worth mentioning that for the WAN Edge upgrade, vManage will push the upgrade to all devices simultaneously. If you would like to perform a rolling upgrade here, you\u0026rsquo;ll have to manage it yourself. In the case of a site where a redundant vEdge is deployed \u0026amp; they share the same site ID, vManage automatically will handle upgrading only one at a time to maintain uptime (Thanks Tim McConnaughy for clarifying this!).\nOnce again, we\u0026rsquo;ll give the edge devices a few minutes to download \u0026amp; apply their software upgrades. Then we\u0026rsquo;ll check to ensure all of the control connections re-established \u0026amp; traffic is flowing.\nWrap up Once your edge devices are back online, it\u0026rsquo;s all done! The network has been upgraded to the new version.\nThere are a handful of ways to check this, but one easy way is via the device monitor page: Monitor \u0026gt; Network\nThis page will list a summary of everything in the network, including the current software version \u0026amp; number of established control connections. For me, it\u0026rsquo;s an easy way to get a one-page summary of the network.\nFrom the screenshot above - we can see that all of my lab devices are back online \u0026amp; running software version 20.3.2.\nThat\u0026rsquo;s it! Hope this post was helpful to you.\nThanks for reading!\n","permalink":"https://0x2142.com/how-to-upgrade-a-cisco-sd-wan-network/","summary":"A short tutorial on how to upgrade a Cisco/Viptela SDWAN network, including controllers \u0026amp; edge devices","title":"[How To] Upgrade a Cisco SD-WAN Network"},{"content":" In the last post, I covered a simple project that I\u0026rsquo;m working on while studying for the Cisco DevNet certifications. This is part two of a series, which will continue on while I work through this project.\nAs a brief summary - I started using a combination of Scrapli \u0026amp; Cisco Genie for a short network automation script. This script connects to a list of network switches \u0026amp; outputs a spreadsheet of interface statistics. This provides a quick snapshot view into the current port capacity within your network.\nIn this post, I\u0026rsquo;ll be showing off a bit of how to build a simple web frontend - which I\u0026rsquo;ll put together using Flask and Bootstrap.\nGetting Started with Flask Okay so I have a bad habit of making a lot of Python scripts which are essentially just command-line utilities.\nIn theory, this isn\u0026rsquo;t necessarily problem. But what if I want to share some of the tools I build? Maybe not everyone wants to compile config files \u0026amp; figure out what command-line arguments are needed. For some, they may prefer to have an easy-to-use web interface instead.\nSo for this project I opted to venture a little outside my typical comfort zone \u0026amp; build a web dashboard. I\u0026rsquo;ll admit I\u0026rsquo;ve done this once or twice before, but certainly not something I would claim proficiency in!\nThe good thing is - building a web interface doesn\u0026rsquo;t have to be complex. My intent with this post is to try \u0026amp; break it down a bit - and hopefully encourage you to try it yourself!\nInstalling Flask \u0026amp; Hello World Installing Flask is much like any other python module - we\u0026rsquo;ll use pip:\npip install flask Once installed, we can import the module into our python script:\nfrom flask import Flask Simple enough!\nNow let\u0026rsquo;s look at how easy it is to set up the web server \u0026amp; return a simple web page:\napp = Flask(__name__) @app.route(\u0026#39;/\u0026#39;) def main(): return \u0026#34;Hello there!\u0026#34; if __name__ == \u0026#39;__main__\u0026#39;: app.run() First we create a new instance of Flask, using the syntax app = Flask(name).\nNext, we just create a quick python function to return the text Hello there! You\u0026rsquo;ll notice that there is a decorator above the function - this is used by Flask to route incoming HTTP requests.\nIn this example, we use @app.route(\u0026rsquo;/\u0026rsquo;). This tells Flask to register this python function for any HTTP calls to http://\u0026lt;url\u0026gt;/. If we wanted a function for any HTTP requests to http://\u0026lt;url\u0026gt;/networking, we would modify the decorator to @app.route(\u0026rsquo;/networking\u0026rsquo;).\nLastly, we need to start the Flask web server when the script is run. This is accomplished using app.run()\nLet\u0026rsquo;s go ahead and try to run the script - which should start the Flask server:\nIn the above screenshot, you\u0026rsquo;ll see that by default Flask will start on http://127.0.0.1:5000. You may also notice that Flask will print out real-time logging of incoming HTTP requests.\nIf we visit our page, we\u0026rsquo;ll see pretty much what we might expect:\nBefore we move onto the next section, I did want to show one more example.\nLet\u0026rsquo;s say that we have a python variable (or dictionary, etc). Could we pass that as part of our web page function?\nLet\u0026rsquo;s look at this modified example:\nweb_page_text = {\u0026#34;Hello\u0026#34;: \u0026#34;There!\u0026#34;} @app.route(\u0026#39;/\u0026#39;) def main(): return web_page_text If we reload our local web page, we\u0026rsquo;ll now see the following:\nKeep this in mind - We\u0026rsquo;ll use this later!\nMaking life easier with HTML templates Now then - you could build all of your HTML in python, then return the entire HTML page as a response to the function call. That being said, just because you can doesn\u0026rsquo;t mean you should 🙃\nLet\u0026rsquo;s take a quick look at how we can use HTML templates to help build our web page.\nFirst we\u0026rsquo;ll create a new directory called templates, and create an HTML file. In this case, I named my template main.html:\nTo start with, we\u0026rsquo;ll just add some simple text into the HTML file:\n\u0026lt;body\u0026gt; Hello There - From the template! \u0026lt;/body\u0026gt; On the Flask side, we\u0026rsquo;ll have to modify our imports to include the render_template module. We\u0026rsquo;ll also update what we\u0026rsquo;re returning when an HTTP request is received:\nfrom flask import Flask, render_template app = Flask(__name__) @app.route(\u0026#39;/\u0026#39;) def main(): return render_template(\u0026#39;main.html\u0026#39;) if __name__ == \u0026#39;__main__\u0026#39;: app.run() So now instead of returning plain text, we\u0026rsquo;ll return render_template(\u0026lsquo;main.html\u0026rsquo;).\nFlask uses Jinja2 for templating, which we\u0026rsquo;ll get into shortly. The above function call tells our app to use main.html as a base template for the content that is returned to the end user.\nLet\u0026rsquo;s check out the page:\nAwesome - we receive our HTML file as a response this time.\nExtending functionality with Jinja2 Now we can start getting into the fun stuff.\nManually writing out a bunch of HTML is fun and all - but what if we could auto-generate everything programmatically?\nJinja2 allows for exactly that. We\u0026rsquo;ll be able to insert variables, if/then statements, and looping logic directly into our HTML template.\nAs an example, let\u0026rsquo;s say we had the following python dictionary - which we wanted to display on a web page:\nswitchList = [ { \u0026#34;name\u0026#34;: \u0026#34;C9200\u0026#34;, \u0026#34;serial\u0026#34;: \u0026#34;ABCD00010\u0026#34;, \u0026#34;ip\u0026#34;: \u0026#34;192.168.1.1\u0026#34;, \u0026#34;check\u0026#34;: True, \u0026#34;total\u0026#34;: 28, \u0026#34;up\u0026#34;: 10, \u0026#34;down\u0026#34;: 5, \u0026#34;disabled\u0026#34;: 13, \u0026#34;capacity\u0026#34;: 35 }, { \u0026#34;name\u0026#34;: \u0026#34;C9300\u0026#34;, \u0026#34;serial\u0026#34;: \u0026#34;ABCD00011\u0026#34;, \u0026#34;ip\u0026#34;: \u0026#34;172.168.44.2\u0026#34;, \u0026#34;check\u0026#34;: False, \u0026#34;total\u0026#34;: 48, \u0026#34;up\u0026#34;: 32, \u0026#34;down\u0026#34;: 6, \u0026#34;disabled\u0026#34;: 10, \u0026#34;capacity\u0026#34;: 67 }, { \u0026#34;name\u0026#34;: \u0026#34;C9400\u0026#34;, \u0026#34;serial\u0026#34;: \u0026#34;ABCD00012\u0026#34;, \u0026#34;ip\u0026#34;: \u0026#34;172.33.44.2\u0026#34;, \u0026#34;check\u0026#34;: True, \u0026#34;total\u0026#34;: 48, \u0026#34;up\u0026#34;: 40, \u0026#34;down\u0026#34;: 6, \u0026#34;disabled\u0026#34;: 2, \u0026#34;capacity\u0026#34;: 82 } ] This dictionary is an example from the scrapli script I\u0026rsquo;ve been working on (more detail in my last post).\nOne way for us to present this using an HTML template would be the following:\n\u0026lt;body\u0026gt; {% block content %} {% for switch in switches %} Switch Name: {{ switch.name }} - Serial: {{ switch.serial }} - Reachable at: {{ switch.ip }} {% endfor %} {% endblock %} \u0026lt;/body\u0026gt; As you can see - that\u0026rsquo;s a bit of a change from what we were using in our template before.\nThe Jinja2 syntax above uses a combination of curly braces \u0026amp; percentage signs to indicate which parts of our template are logic that should be processed before returning content to the user. Variables are represented with double curly braces.\nOnce our template receives the switchList dictionary, we can iterate through it similar to how we might in python. Using the {% for switch in switches %} syntax, we can iterate through each item - and pull out relevant data.\nWithin our for loop, we can reference the individual values of each item in the dictionary. Using the double braces, we can insert a placeholder for where a piece of content should be inserted. For example, in the above template we have a spot where we want to insert the name of the switch. We use {{ switch.name }} as a placeholder, where the value from the dictionary will be inserted once our template is processed.\nOkay - so before we test this, we have one minor modification to our python function:\n@app.route(\u0026#39;/\u0026#39;) def main(): return render_template(\u0026#39;main.html\u0026#39;, switches=switchList) We\u0026rsquo;ve changed our function render_template, but also include the python object that we\u0026rsquo;ll be passing to our template.\nLet\u0026rsquo;s test this out:\nNow there\u0026rsquo;s some magic! Using a combination of python \u0026amp; our Jinja2 templates - we can drastically reduce the amount of HTML code that we need to write.\nOkay, but that\u0026rsquo;s not pretty Yes I know. But we needed to get the basics done first!\nHere\u0026rsquo;s where Bootstrap comes in.\nI went out to Google and searched for good CSS templates. I\u0026rsquo;m not a web developer, nor am I good at design - so I\u0026rsquo;ll leave that work to someone else.\nIn my searching, I found quite a bunch of free templates\u0026hellip; but I fell in love with one called Lux by Bootswatch.\nThey provide a great sample page for the template, which shows off different variations. But the best part is that it also includes code samples!\nAfter we pick \u0026amp; download our CSS template, we\u0026rsquo;ll place it in a new directory named static:\nTo get started, we\u0026rsquo;ll need to install one additional python module:\npip install flask_bootstrap Then we\u0026rsquo;ll also need a quick modification to our base Flask app:\nfrom flask_bootstrap import Bootstrap ... code omitted ... if __name__ == \u0026#39;__main__\u0026#39;: Bootstrap(app) app.run() I removed some code to focus on just the two parts that change.\nFirst, we need to import our new module. Second, we need to inject our bootstrap plugin into the Flask app. We do this with Bootstrap(app) prior to app.run().\nLet\u0026rsquo;s move back over to our HTML template, since this is where the most of our changes will be.\nFirst, we\u0026rsquo;ll need to include a few things to make sure our CSS file is loaded:\n{%- extends \u0026#34;bootstrap/base.html\u0026#34; %} \u0026lt;head\u0026gt; {% block styles %} {{super()}} \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;{{url_for(\u0026#39;static\u0026#39;, filename=\u0026#39;bootstrap.css\u0026#39;)}}\u0026#34;\u0026gt; {% endblock %} \u0026lt;/head\u0026gt; I won\u0026rsquo;t dive in too deep on this piece, as it\u0026rsquo;s fairly straightforward. First, we\u0026rsquo;ll need to include the base/default bootstrap template. Next, we\u0026rsquo;ll include a reference to where our CSS file is located \u0026amp; it\u0026rsquo;s name - so that it gets loaded when our page is rendered.\nOkay, now for the body of the page:\n\u0026lt;body\u0026gt; {% block content %} \u0026lt;table class=\u0026#34;table table-hover\u0026#34;\u0026gt; \u0026lt;thead\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;th scope=\u0026#34;col\u0026#34;\u0026gt;Name\u0026lt;/th\u0026gt; \u0026lt;th scope=\u0026#34;col\u0026#34;\u0026gt;Current Capacity\u0026lt;/th\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;/thead\u0026gt; \u0026lt;tbody\u0026gt; {% for switch in switches %} {% if switch.capacity \u0026gt; 75 %} \u0026lt;tr class=\u0026#34;table-danger\u0026#34;\u0026gt; {% else %} \u0026lt;tr class=\u0026#34;table-success\u0026#34;\u0026gt; {% endif %} \u0026lt;th scope=\u0026#34;row\u0026#34;\u0026gt;{{ switch.name }}\u0026lt;/th\u0026gt; \u0026lt;td\u0026gt;{{ switch.capacity }}%\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; {% endfor %} \u0026lt;/tbody\u0026gt; \u0026lt;/table\u0026gt; {% endblock %} \u0026lt;/body\u0026gt; Now instead of just returning a plain-text list of all three switches, we\u0026rsquo;ll use our CSS template to generate a colorful table. For the purpose of this example, I kept the data to just showing the switch name \u0026amp; it\u0026rsquo;s current capacity.\nMost of the template is fairly simple. First we build our table header using the \u0026lt;thead\u0026gt; tags. Next we\u0026rsquo;ll build the rows of our table using \u0026lt;tbody\u0026gt; and \u0026lt;tr\u0026gt;.\nFor the Jinja2 logic - We\u0026rsquo;ll iterate through our list of switches, and add a new table row (\u0026lt;tr\u0026gt;) for each device. I also added logic to check the current capacity of the switch. If the switch capacity is greater than 75%, we create the table row using \u0026lt;tr class=\u0026ldquo;table-danger\u0026rdquo;\u0026gt; - which is a reference in our CSS stylesheet that will color the row red. If capacity is less than 75%, we use table-success for a green color.\nLet\u0026rsquo;s go ahead and check out what this looks like:\nAnd as we expect - we get our nicely formatted table \u0026amp; the correct colors on each row.\nBringing it all together Now that we\u0026rsquo;ve gotten some background on how to use Flask \u0026amp; Bootstrap, let me show you a bit of what I\u0026rsquo;ve been working on.\nI won\u0026rsquo;t cover much code in this section, but you can check out the GitHub repo if you\u0026rsquo;re interested in seeing the details.\nThe scrapli script that I wrote previously has been extended a little. The biggest change is that instead of writing its output to a spreadsheet, it will insert the data into a sqlite database.\nThe web frontend has been assembled using the same basic ideas covered in this blog (plus a lot of banging my head against the desk, trying to figure out why things don\u0026rsquo;t format or align properly 🙂). Whenever a request is made to the web dashboard, it will query the sqlite database for the requested information.\nSo, without further delay - here it is!\nI currently have one physical device running, plus a handful of virtual nexus 9k \u0026amp; CSRv devices running in Cisco Modeling Labs. So all of the data shown in this dashboard is being pulled from real(ish) equipment - no mock data.\nThis was one of those projects where I started off thinking my dashboard was going to be really simple\u0026hellip; but then I kept having new ideas and getting carried away with the design \u0026amp; functionality.\nThe table above shows each switch, it\u0026rsquo;s serial number, management IP, current software version, and the port inventory. Similar to the example earlier in this post, I have a small progress bar that shows switch capacity - and will change color depending on percentage of in-use ports.\nIf you click on any of the switch names, you\u0026rsquo;ll be taken to a page with additional detail on that particular device:\nOn this first tab, you\u0026rsquo;ll see some quick info - mostly the same that is on the main page of the dashboard. However, I added two additional tabs here - one for a detailed port breakdown \u0026amp; one for a dump of the raw CLI output.\nIf we click on the port info:\nWe have a breakdown of how many ports are currently in use vs down, as well as our breakdown of port media \u0026amp; operational speed. Since I account for switches that are anywhere from 10M up to 100G, the data for operational speeds will only show the speeds that are actually in-use on the switch.\nJust in case it\u0026rsquo;s needed, I added a tab to show the raw CLI output from the switch:\nThis could be a quick way to check port counters \u0026amp; errors, or any other information that isn\u0026rsquo;t already presented via the dashboard.\nLastly, I built a separate page to provide statistics on ALL devices in the network:\nThis page will show port count \u0026amp; availability network-wide. However, I also added some spaces to show the top 5 hardware models \u0026amp; software versions that are in-use across the network.\nI went into this project a bit worried because frontend development isn\u0026rsquo;t my strength. That being said, I really enjoyed working on this project. It was a different challenge than I\u0026rsquo;m normally used to - and it gave me a creative way to try and format \u0026amp; display the data I am collecting.\nI hope this post was useful to you. Please leave a comment below - and check out my Github repo for the full code.\n","permalink":"https://0x2142.com/web-dashboard-flask-and-bootstrap/","summary":"Building on the last post, we\u0026rsquo;ll show how easy it an be to build a Flask dashboard","title":"Automating the CLI (Part 2): Building a Web Dashboard with Flask \u0026 Bootstrap"},{"content":"I had the opportunity to attend an ARIN on the Road event last week. It was an all-day event that focused on education: who ARIN is, what they do, and some things they are working on. As a network admin I\u0026rsquo;ve had to work with ARIN a handful of times to request network resources. I figured it would be a good experience to attend one of these events and see what ARIN has to say. I actually found out about a few things I wasn\u0026rsquo;t aware of previously, so this post is going to be a brief summary of what I learned.\nAbout ARIN If you haven\u0026rsquo;t already worked with them - ARIN is the American Registry for Internet Numbers. They are a non-profit organization and their purpose is to assign/manage Internet number resources for all of North America. This includes IPv4/IPv6 addresses and BGP Autonomous System Numbers (ASNs). ARIN is one of five Regional Internet Registries (RIRs) - each managing Internet resources for it\u0026rsquo;s own individual region. All of these report back to a top-level organization, the Internet Assigned Numbers Authority (IANA).\nWhat I didn\u0026rsquo;t know: ARIN actually used to manage resources for all of South America and Africa as well. LACNIC formed and took ownership of South America in 2001, and AFRINIC took Africa in late 2004. ARIN itself has only been around since 1997, and will be celebrating it\u0026rsquo;s 20th anniversary this December.\nOutside of assigning/managing number resources - ARIN manages a huge manual of numbering policies and standards (The Number Resource Policy Manual). A good note here is that these policies are heavily influenced by the community - so if any individual or group of network operators want to change/modify or add new policies, then they can submit proposals to do so.\nIPv4 Depletion I was very interested to hear about what\u0026rsquo;s going on with IPv4/IPv6 - mostly because I\u0026rsquo;ve been trying to push for IPv6 in many of the places I have worked. The ARIN group spent a little bit of time talking about how the depletion of IPv4 addresses has affected their workload. Overall, it seems like their work has remained about the same - but it has transitioned from mostly IPv4 allocations to more IPv4 transfer requests.\nAn interesting note from this discussion was that ARIN only performs the backend registration changes for IPv4 block transfers. They play no part in the actual negotiations between two organizations. However, they do perform their own investigations during transfers to ensure that the source organization legitimately owns the IP block, and the destination organization can justify the use of the space.\nI had heard previously that ARIN kept a block of IPv4 addresses for transition to IPv6 - but I never researched it further. This was a topic ARIN touched on during the event. Essentially, they have kept ownership of a /10 block of addresses, which is split up into individual /24 blocks for assignment. Any organization can request one of the /24s when they request a block of IPv6 addresses. The organization must fill out a justification form, in which they demonstrate how the IPv4 blocks will be used to help transition to IPv6. Organizations can request one of these blocks every 6 months, provided they can still justify the need for them. This is all documented in NRPM section 4.10.\nThe somewhat surprising thing here is that ARIN was actively encouraging people to take advantage of this. Probably because they need to push IPv6 adoption in any way they can. As of the date of the event, ARIN stated that only ~60 /24 blocks had been assigned so far.\nIPv6 Adoption This part of the event wasn\u0026rsquo;t quite everything I wanted it to be. Overall ARIN touched on statistics from Google and other organizations that show the trending uptake in IPv6 network access. They also spoke briefly about how the structure of IPv6 addresses makes life easier - because the last 64 bits can always be used for host-based MAC autoconfig, then network operators only worry about subnetting above that.\nInterestingly enough, ARIN was advocating for the method of \u0026lsquo;assign way more addresses than you\u0026rsquo;ll ever need\u0026rsquo; mentality for IPv6. Another attendee asked the question \u0026lsquo;Won\u0026rsquo;t we run into the same thing as IPv4, if we just throw out v6 blocks like candy\u0026rsquo;? This actually led to hearing something I wasn\u0026rsquo;t aware of - IANA has currently only made 1/8th of IPv6 blocks public available for use. The current numbering scheme/standard will be used for this first block of addresses. If we run through them too quickly, then we can step back and re-evaluate best practices before handing out the next 1/8th block of addresses.\nDNSSec Initially I was a bit confused that DNSSec was on the topic list - but I figured maybe ARIN was just trying to push this for the betterment of the Internet. While they spoke a bit about DNSSec for forward DNS, their primary topic was how DNSSec for reverse DNS isn\u0026rsquo;t something people are normally thinking about. As it turns out, ARIN offers reverse-lookup DNSSec for any IP blocks that they assign out. This is good to know, since reverse DNS can be important for things like email security - and its certainly something I\u0026rsquo;ve never really considered in the past.\nIf you have purchased IPv4/v6 blocks directly from ARIN - I would recommend that you check this out.\nRPKI Resource Public Key Infrastructure (RPKI) is a way of cryptographically validating ownership of IP address space or routing objects. Since BGP is primarily a trust-based protocol between organizations, RPKI allows network operators to implement additional security by providing a certificate-based system of trust. The majority of this discussion was around how bad BGP security is, and that overall North America is far behind on implementing RPKI.\nARIN has a service available where they will act as your Certificate Authority (CA) for RPKI - so it only requires network operators to sign records then implement a few device changes.\nMy Thoughts Overall the event was fairly informative! It wasn\u0026rsquo;t quite everything I wanted it to be, but I did walk away with additional knowledge that I didn\u0026rsquo;t have before. I was really hoping to learn more about how other organizations are implementing IPv6, or even how other people are convincing their employers to take IPv6 adoption seriously. When I spoke with some other attendees, it seemed like not many people had IPv6 running in a production environment yet - only a few of them had even started testing. Surprisingly, even the ARIN reps were repeatedly asking people to contact them if they had an IPv6 success story to share.\nOne thing I found really interesting was surrounding DNSSec/RPKI. A few attendees asked about how many people are actually validating signed resources. It\u0026rsquo;s one thing to implement signing, but it won\u0026rsquo;t matter if no one validates the resources, right? Surprisingly, ARIN had no statistics about this - and stated the point that they cannot enforce adoption of these standards. It certainly makes sense, but it\u0026rsquo;s not something I gave much thought to previously. Since they\u0026rsquo;re just a registry, they can only make these services available - not enforce their usage. This is why they put on events such as this to raise awareness and provide education.\nARIN pushed the fact that all of their policies are community driven. There were quite a few examples throughout the event of how individual members of the community could impact changes to their policies. My primary concern is that it seemed like a majority of the individuals in attendance represented government or educational organizations - and not a lot who worked in similar network environments to what I manage. They raised their own concerns and questions, which were certainly valid for the types of infrastructure and designs that they maintain. However, a number of these things don\u0026rsquo;t really apply to my infrastructure in quite the same ways.\nIf I have to make one point here: If you\u0026rsquo;re a network operator, go subscribe to ARINs mailing lists and get involved. Maybe you don\u0026rsquo;t have any ideas for policy changes, but you never know what might come up that you could provide meaningful input on. The ARIN reps provided an example or two of when a smaller group of people suggested policy changes which drastically affected bigger companies - and almost no one opposed it until it took effect. Only you have the ability to voice your opinion and concerns about how a proposed policy could affect your network. If not, the next time you try to request a block of IP addresses or a BGP ASN, you could potentially run into roadblocks because of a policy change proposed by someone with very different needs.\nThe staff at ARIN don\u0026rsquo;t live and work in the networks that we do. They try to work with network operators to understand use cases and the possible ramifications of policy changes - but ultimately they are a small non-profit. They can\u0026rsquo;t think of everything, nor can they force network operators to contribute their opinions. Get involved and make a difference.\nAs a final note, ARIN has a Fellowship Program where you can apply to attend one of their Public Policy meetings for free. Fill out an application and if you\u0026rsquo;re chosen they\u0026rsquo;ll provide a ticket, hotel room, and travel expenses. It\u0026rsquo;s a great opportunity to experience one of these meetings, especially if you might not have the financial means to otherwise.\nThe slide deck from the event is publicly available on ARIN\u0026rsquo;s website: here.\n","permalink":"https://0x2142.com/an-afternoon-with-arin/","summary":"Attending a local ARIN event left me with some thoughts\u0026hellip;","title":"An Afternoon with ARIN"},{"content":" So I just scheduled an upcoming attempt at the Developing Applications using Cisco Core Platforms and APIs (DEVCOR 350-901) exam.\nIt\u0026rsquo;s coming up quick in two weeks here \u0026amp; I\u0026rsquo;m starting to do some review / final prep before taking the exam.\nWhy does this matter? Well - I plan on doing a few write-ups \u0026amp; videos on some of the content I\u0026rsquo;m studying. There are a lot of people diving into the DevNet exams, and I want to do what I can to help other people succeed.\nAll that being said, check back here over the next few weeks - or subscribe on YouTube - to see the additional content that will follow. While this specific blog doesn\u0026rsquo;t cover much from the exam blueprint, it\u0026rsquo;s the beginning a simple project I\u0026rsquo;ll be using in the further content.\nGetting Started with Scrapli For this project I opted to start with scrapli, and possibly migrate to using RESTCONF later on in the process. Mostly this was an excuse for me to try out scrapli, as I\u0026rsquo;ve heard a few people using it recently \u0026amp; was interested.\nScrapli is a screen scraping module for Python. If you\u0026rsquo;re not familiar with screen scraping, it\u0026rsquo;s the process of connecting to something via telnet/ssh/etc, then literally scraping or dumping the contents of the screen. There are other modules that do this as well, like paramiko, netmiko, or expect scripting.\nIn networking, too many of our devices still rely on CLI and don\u0026rsquo;t have proper API endpoints. We\u0026rsquo;re working on it, but yet still a ways from having it everywhere. So in the meantime, we still need screen scraping for automation.\nOn the whole, this isn\u0026rsquo;t necessarily a bad thing. For example, most network engineers are very familiar and competent with the CLI of any network operating system. It\u0026rsquo;s easier to jump into the world of automation if you can start with something you know, the CLI. It\u0026rsquo;s harder to force someone to start their automation journey by giving up everything they know and shoving REST APIs at them.\nOkay - enough rambling. Let\u0026rsquo;s get scrapli installed and see what it can do.\nInstalling the module Easiest part of the whole project. Install with pip:\npip install scrapli Next we\u0026rsquo;ll go ahead \u0026amp; import into our script.\nScrapli supports quite a handful of operating systems (including a few non-Cisco platforms as well!). In the case of my project though, I\u0026rsquo;m only using Cisco IOS-XE switches - so we\u0026rsquo;ll only import the scrapli driver for that.\nfrom scrapli.driver.core import IOSXEDriver Connecting to a Device Okay - now that we\u0026rsquo;re all setup with the module, it\u0026rsquo;s time to get working!\nFirst thing, we need to authenticate to our device. Building off of the example code from the scrapli page - we\u0026rsquo;ll create a short dictionary of authentication parameters first:\nswitch = { \u0026#34;host\u0026#34;: \u0026#34;10.1.1.1\u0026#34;, \u0026#34;auth_username\u0026#34;: \u0026#34;net_api\u0026#34;, \u0026#34;auth_password\u0026#34;: \u0026#34;net_api_pass\u0026#34;, \u0026#34;auth_strict_key\u0026#34;: False } cli = IOSXEDriver(**switch) cli.open() Once we build out dictionary, we\u0026rsquo;ll use **switch to unpack our key/value pairs into the IOSXEDriver object \u0026amp; assign it to a variable called cli. Then, all we need to do is call cli.open() and scrapli will open a connection to our target device.\nNow we can run any command we want by calling cli.send_command():\nsh_int = cli.send_command(\u0026#34;show interface\u0026#34;) print(sh_int.output) So in the above example, I want to issue a show interface - then print the output.\nWhat we get is shown below:\nGigabitEthernet1/0/1 is up, line protocol is up (connected) Hardware is Gigabit Ethernet, address is 78bc.1a81.e101 (bia 78bc.1a81.e101) Description: Test Port MTU 1500 bytes, BW 1000000 Kbit/sec, DLY 10 usec, reliability 255/255, txload 1/255, rxload 1/255 Encapsulation ARPA, loopback not set Keepalive set (10 sec) Full-duplex, 1000Mb/s, media type is 10/100/1000BaseTX input flow-control is off, output flow-control is unsupported ARP type: ARPA, ARP Timeout 04:00:00 Last input 00:00:11, output 00:00:03, output hang never Last clearing of \u0026#34;show interface\u0026#34; counters never Input queue: 0/2000/0/0 (size/max/drops/flushes); Total output drops: 198 Queueing strategy: fifo Output queue: 0/40 (size/max) 5 minute input rate 9000 bits/sec, 11 packets/sec 5 minute output rate 2066000 bits/sec, 221 packets/sec 2647548 packets input, 371883264 bytes, 0 no buffer Received 6985 broadcasts (6757 multicasts) 0 runts, 0 giants, 0 throttles 0 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored 0 watchdog, 6757 multicast, 0 pause input 0 input packets with dribble condition detected 50905390 packets output, 60134059101 bytes, 0 underruns 0 output errors, 0 collisions, 2 interface resets 0 unknown protocol drops 0 babbles, 0 late collision, 0 deferred 0 lost carrier, 0 no carrier, 0 pause output 0 output buffer failures, 0 output buffers swapped out [**Output Truncated**] What\u0026rsquo;s that look like? Exactly the same output we would get if we entered show interface on the command line ourselves! (Only Gig1/0/1 shown here to keep this short \u0026amp; clean)\nUsing Cisco Genie to Parse CLI Output So what if we wanted to pull a list of every interface on the switch? Maybe tally how many ports are connected vs down vs admin disabled? Or even check operational speeds \u0026amp; media types?\nWell that\u0026rsquo;s what I\u0026rsquo;m looking to do for this project.\nNow at first it may seem like we have to use regular expressions to try \u0026amp; match the content we need from the output. However, there is an easier way!\nCisco Genie is an open source project, and part of the larger PyATS automated network testing suite. If you haven\u0026rsquo;t looked at any of that yet, I would highly recommend you check it out.\nSo what\u0026rsquo;s Genie do? It\u0026rsquo;s a utility that handles all the output parsing for us. There are a ton of pre-existing parsers written, which handle all the hard work so we don\u0026rsquo;t have to.\nBest yet - scrapli has native integration into Genie. All we have to do is install one additional component:\npip install scrapli[genie] And to make use of the power of Genie, we just need to pass our raw output to it.\nSo our new code will look like this:\nsh_int = cli.send_command(\u0026#34;show interface\u0026#34;) sh_int_parsed = sh_int.genie_parse_output() print(sh_int_parsed) That\u0026rsquo;s it. And now, instead of that raw output - we get a native Python dictionary that\u0026rsquo;s ready to consume by our script:\n{ \u0026#34;GigabitEthernet1/0/1\u0026#34;:{ \u0026#34;port_channel\u0026#34;:{ \u0026#34;port_channel_member\u0026#34;:False }, \u0026#34;enabled\u0026#34;:True, \u0026#34;line_protocol\u0026#34;:\u0026#34;up\u0026#34;, \u0026#34;oper_status\u0026#34;:\u0026#34;up\u0026#34;, \u0026#34;connected\u0026#34;:True, \u0026#34;type\u0026#34;:\u0026#34;Gigabit Ethernet\u0026#34;, \u0026#34;mac_address\u0026#34;:\u0026#34;78bc.1a81.e101\u0026#34;, \u0026#34;phys_address\u0026#34;:\u0026#34;78bc.1a81.e101\u0026#34;, \u0026#34;description\u0026#34;:\u0026#34;Test Port\u0026#34;, \u0026#34;delay\u0026#34;:10, \u0026#34;mtu\u0026#34;:1500, \u0026#34;bandwidth\u0026#34;:1000000, \u0026#34;reliability\u0026#34;:\u0026#34;255/255\u0026#34;, \u0026#34;txload\u0026#34;:\u0026#34;1/255\u0026#34;, \u0026#34;rxload\u0026#34;:\u0026#34;1/255\u0026#34;, \u0026#34;encapsulations\u0026#34;:{ \u0026#34;encapsulation\u0026#34;:\u0026#34;arpa\u0026#34; }, \u0026#34;keepalive\u0026#34;:10, \u0026#34;duplex_mode\u0026#34;:\u0026#34;full\u0026#34;, \u0026#34;port_speed\u0026#34;:\u0026#34;1000mb/s\u0026#34;, \u0026#34;media_type\u0026#34;:\u0026#34;10/100/1000BaseTX\u0026#34;, \u0026#34;flow_control\u0026#34;:{ \u0026#34;receive\u0026#34;:False, \u0026#34;send\u0026#34;:False }, \u0026#34;arp_type\u0026#34;:\u0026#34;arpa\u0026#34;, \u0026#34;arp_timeout\u0026#34;:\u0026#34;04:00:00\u0026#34;, \u0026#34;last_input\u0026#34;:\u0026#34;00:00:05\u0026#34;, \u0026#34;last_output\u0026#34;:\u0026#34;00:00:08\u0026#34;, \u0026#34;output_hang\u0026#34;:\u0026#34;never\u0026#34;, \u0026#34;queues\u0026#34;:{ \u0026#34;input_queue_size\u0026#34;:0, \u0026#34;input_queue_max\u0026#34;:2000, \u0026#34;input_queue_drops\u0026#34;:0, \u0026#34;input_queue_flushes\u0026#34;:0, \u0026#34;total_output_drop\u0026#34;:198, \u0026#34;queue_strategy\u0026#34;:\u0026#34;fifo\u0026#34;, \u0026#34;output_queue_size\u0026#34;:0, \u0026#34;output_queue_max\u0026#34;:40 }, \u0026#34;counters\u0026#34;:{ \u0026#34;rate\u0026#34;:{ \u0026#34;load_interval\u0026#34;:300, \u0026#34;in_rate\u0026#34;:8000, \u0026#34;in_rate_pkts\u0026#34;:11, \u0026#34;out_rate\u0026#34;:1868000, \u0026#34;out_rate_pkts\u0026#34;:205 }, \u0026#34;last_clear\u0026#34;:\u0026#34;never\u0026#34;, \u0026#34;in_pkts\u0026#34;:2658450, \u0026#34;in_octets\u0026#34;:372811780, \u0026#34;in_no_buffer\u0026#34;:0, \u0026#34;in_multicast_pkts\u0026#34;:6783, \u0026#34;in_broadcast_pkts\u0026#34;:6783, \u0026#34;in_runts\u0026#34;:0, \u0026#34;in_giants\u0026#34;:0, \u0026#34;in_throttles\u0026#34;:0, \u0026#34;in_errors\u0026#34;:0, \u0026#34;in_crc_errors\u0026#34;:0, \u0026#34;in_frame\u0026#34;:0, \u0026#34;in_overrun\u0026#34;:0, \u0026#34;in_ignored\u0026#34;:0, \u0026#34;in_watchdog\u0026#34;:0, \u0026#34;in_mac_pause_frames\u0026#34;:0, \u0026#34;in_with_dribble\u0026#34;:0, \u0026#34;out_pkts\u0026#34;:51057453, \u0026#34;out_octets\u0026#34;:60309072263, \u0026#34;out_underruns\u0026#34;:0, \u0026#34;out_errors\u0026#34;:0, \u0026#34;out_interface_resets\u0026#34;:2, \u0026#34;out_collision\u0026#34;:0, \u0026#34;out_unknown_protocl_drops\u0026#34;:0, \u0026#34;out_babble\u0026#34;:0, \u0026#34;out_late_collision\u0026#34;:0, \u0026#34;out_deferred\u0026#34;:0, \u0026#34;out_lost_carrier\u0026#34;:0, \u0026#34;out_no_carrier\u0026#34;:0, \u0026#34;out_mac_pause_frames\u0026#34;:0, \u0026#34;out_buffer_failure\u0026#34;:0, \u0026#34;out_buffers_swapped\u0026#34;:0 } } } This format makes our lives a lot easier. For example, let\u0026rsquo;s say we wanted to determine the operational state of a port. Without the Genie integration, we would need to write some regex to comb through the raw output \u0026amp; find what we needed.\nHowever, with Genie involved - its as easy as this:\nprint(sh_int_parsed[\u0026#39;GigabitEthernet1/0/1\u0026#39;][\u0026#39;oper_status\u0026#39;]) Collecting the Useful Bits Now that we have everything in an easy-to-use format, all we need to do is write a few loops to run through our interface list \u0026amp; collect data.\nThe purpose of my script is to automate the collection of port utilization. For example, think about performing a switch refresh or some form of capacity planning scenario. You might need to inventory how many switches you have, how many ports are utilized vs how many are available, or count the number of copper vs fiber ports.\nFor this first iteration, we\u0026rsquo;ll just collect the data then dump it out to a CSV file. As I mentioned earlier, this is just the start of a small project I\u0026rsquo;ll be using to study for the DEVCOR exam - so this will be evolving into a web service later on.\nThe full script can be found here. But I\u0026rsquo;ve posted just a snippet of how we count the different interface characteristics:\n# Count all Ethernet interfaces interfaceStats[\u0026#39;total\u0026#39;] += 1 # Count admin-down interfaces if not sh_int_parsed[iface][\u0026#39;enabled\u0026#39;]: interfaceStats[\u0026#39;intdisabled\u0026#39;] += 1 # Count not connected interfaces elif sh_int_parsed[iface][\u0026#39;enabled\u0026#39;] and sh_int_parsed[iface][\u0026#39;oper_status\u0026#39;] == \u0026#39;down\u0026#39;: interfaceStats[\u0026#39;intdown\u0026#39;] += 1 # Count up / connected interfaces - Then collect current speeds elif sh_int_parsed[iface][\u0026#39;enabled\u0026#39;] and sh_int_parsed[iface][\u0026#39;connected\u0026#39;]: interfaceStats[\u0026#39;intup\u0026#39;] += 1 speed = sh_int_parsed[iface][\u0026#39;bandwidth\u0026#39;] if speed == 10_000: interfaceStats[\u0026#39;intop10m\u0026#39;] += 1 if speed == 100_000: interfaceStats[\u0026#39;intop100m\u0026#39;] += 1 if speed == 1_000_000: interfaceStats[\u0026#39;intop1g\u0026#39;] += 1 if speed == 10_000_000: interfaceStats[\u0026#39;intop10g\u0026#39;] += 1 # Count number of interfaces by media type try: media = sh_int_parsed[iface][\u0026#39;media_type\u0026#39;] if \u0026#39;1000BaseTX\u0026#39; in media: interfaceStats[\u0026#39;intmedcop\u0026#39;] += 1 else: interfaceStats[\u0026#39;intmedsfp\u0026#39;] += 1 except KeyError: interfaceStats[\u0026#39;intmedsfp\u0026#39;] += 1 Once all that runs, we create a CSV file to dump all of the data to.\nFor example, running the complete script against my lab Catalyst 9200 switch would output the following:\nHostname Model Serial Number Software Version Total Interfaces Interfaces UP (Total) Interfaces DOWN (Total) Interfaces Disabled Interface Operational Speed (10M) Interface Operational Speed (100M) Interface Operational Speed (1G) Interface Operational Speed (10G) Interface Media (Copper) Interface Media (SFP) SW-C9200 C9200L-24T-4X XX########X 16.9.4 28 7 8 13 0 1 6 0 24 4 And that\u0026rsquo;s it! Using the combination of scrapli \u0026amp; genie made this script very quick and easy to write - which means now I can focus more time on the next steps.\nAs I\u0026rsquo;ve mentioned a few times, this is hopefully the start of a short series of blog posts \u0026amp; videos as I wrap up studying for the DEVCOR exam.\nIf you found this content helpful - or you\u0026rsquo;re interested in the future content - please check back soon! Or consider subscribing to my YouTube channel, where new content will be coming shortly.\n","permalink":"https://0x2142.com/automating-the-cli-using-scrapli/","summary":"Let\u0026rsquo;s look at one way to automate routine CLI-based tasks using Scrapli \u0026amp; Cisco Genie","title":"Automating the CLI: Using Scrapli \u0026 Cisco Genie to Collect Interface Data"},{"content":" So if you\u0026rsquo;ve read some of my recent posts - you may have seen that I purchased a NetGear LB 1121 LTE Cellular modem to use for home internet backup.\nWell - I decided to upgrade!\nAfter using the NetGear modem for a while, I started having some issues where it would disconnect from the cellular connection intermittently. Since it\u0026rsquo;s not necessarily intended for the purpose I\u0026rsquo;m using it for, there wasn\u0026rsquo;t any good way to set up monitoring for it either.\nSo I opted to upgrade to a Meraki MG21. This is one of the latest additions to the Meraki family of network devices \u0026amp; is available with internal (MG21) and external (MG21E) antenna.\nI was pretty excited to get one, since Meraki tends to have decent analytics \u0026amp; configuration - and they make everything so easy!\nWe\u0026rsquo;ll walk through the setup of the MG below - but if you\u0026rsquo;re interested in seeing what the device looks like as well, definitely check out the video above!\nMG Setup - Changing the APN Okay - so after I got the SIM card inserted into the MG, the first configuration step is making sure we have the correct APN configured. As a reminder, I\u0026rsquo;m using Google Fi as my cellular provider.\nThis change will need to be done on the local web management interface - not from Meraki Dashboard.\nWhen the MG powers on, by default it will hand out DHCP addresses to any device connected to port 1. At the time of writing, these addresses were in the 192.168.5.x range.\nConnecting a PC directly to the MG port, we should be able to reach the local management web page - either by typing in the IP address into our web browser, or using mg.meraki.com:\nIn my case, I could see that the MG had auto-detected my Carrier as Google Fi. However, it still had the incorrect APN.\nUsing the Configure tab, we can change that setting. You\u0026rsquo;ll be prompted for a username \u0026amp; password. By default, the username will be the serial number of the device (including dashes) with a blank password.\nAs shown in the screenshot above, we have a handful of options - though we\u0026rsquo;ll only care about APN.\nFirst - change the Cellular Override option to Override SIM Settings. Then type in your APN. In my case, it\u0026rsquo;s h2g2 for Google Fi. Lastly, hit save at the bottom (just outside the view in the screenshot above).\nWith any luck, the modem will connect and you\u0026rsquo;ll see something like this:\nThe modem connects, gets an IP from the provider, and is able to validate connectivity to both the internet \u0026amp; Meraki Cloud.\nWhen I originally set up my MG, I did run into some issues with this. My MG connected to the internet successfully, but said it couldn\u0026rsquo;t reach the Meraki Cloud. Not sure what caused it, but it shortly resolved itself within a few minutes. Just gotta be patient sometimes, I suppose!\nConfiguring the MG in Meraki Dashboard Note: I won\u0026rsquo;t get into how to claim your device in dashboard or how to attach it to a network. If you need help, please check out the video above where I did show how to accomplish these steps\nOkay! Now that our MG is configured for the correct cell network, we can log into the Meraki Dashboard and begin configuring it.\nAfter we\u0026rsquo;ve added the MG to our network, we\u0026rsquo;ll see a new Cellular Gateway menu:\nWe\u0026rsquo;ll start first by going over to Configure \u0026gt; Settings\nFirst section we\u0026rsquo;ll see is for Addressing \u0026amp; NAT:\nAs of today, the MG doesn\u0026rsquo;t support any form of direct internet pass-through. Instead, our only option is routed mode - where the MG will hold the IP provided by our Carrier \u0026amp; NAT any requests from the devices behind it.\nWe can change the DHCP subnet configuration here, which will affect what IP addresses are handed to clients behind the MG. In my case, I\u0026rsquo;m connecting this directly to a firewall as a secondary internet uplink - so the addressing \u0026amp; subnet doesn\u0026rsquo;t matter as much. By default, the MG will always consume the first available address as it\u0026rsquo;s own.\nNext, we have a section for DHCP \u0026amp; subnets:\nHere we can change our DHCP lease time, and what DNS servers are provided to our clients. The DNS setting does have pre-defined options for Umbrella DNS, Google DNS, or using whatever the upstream carrier provides. You\u0026rsquo;re also welcome to manually specify which DNS servers to use.\nWe can also configure reserved \u0026amp; fixed IP addresses here.\nReserved IP ranges are IP addresses that we don\u0026rsquo;t want the MG to provide via DHCP. So if we had any statically configured IP addresses, we could reserve them here.\nFixed IP addresses are for any client that needs a DHCP address, but we want that IP assignment to be permanent. We\u0026rsquo;ll enter the client name \u0026amp; MAC Address here, as well as the IP we want assigned to that device. In my case, I went ahead and inserted my firewall MAC address - and I\u0026rsquo;ll just allow the firewall to get its IP via DHCP from the MG.\nBy default, the MG will block all inbound traffic from the cellular network. If we need to allow any traffic inbound, we can change the Port Forwarding settings:\nThis allows for a light configuration of an inbound NAT. Right now, I probably won\u0026rsquo;t be using this. However, I may permit VPN access into my network via the MG at a later date.\nIf I needed to add anything here, the MG allows us to translate an external / public IP \u0026amp; port to any internal IP / port combination. It appears we can even add a IP filter to permit only trusted source addresses.\nLastly - We can configure settings for Traffic Shaping:\nIn this section, we can throttle our cellular throughput \u0026amp; configure uplink monitoring.\nBy default, the cell bandwidth is set to unlimited - but we can drop this down if needed. In my case, I am not using an unlimited cell data plan - so I will throttle cellular speeds to preserve data \u0026amp; reduce charges.\nIn addition, we can configure one or more IP addresses to check uplink connectivity. These addresses will be used to collect loss \u0026amp; latency data via the cellular connection. The MG monitoring dashboard will collect \u0026amp; graph this data for easy insight into the performance metrics.\nNote: As a word of warning, these uplink monitors are constantly sending ICMP/ping requests. If you have a limited amount of cellular data, this may consume more data than you would like. In my testing, using only one IP for uplink monitoring consumed about 70-100M per day. More on this below\u0026hellip;\nMonitoring the MG Now we get to the good stuff! The primary reason I opted to buy an MG was for monitoring \u0026amp; analytics.\nBack on the dashboard, if we use the lefthand menu - we\u0026rsquo;ll go over to Cellular Gateway \u0026gt; Monitor \u0026gt; Cellular Gatways. Then select our MG out of the list.\nThe primary summary page isn\u0026rsquo;t too exciting:\nThe MG does have two gigabit ethernet ports - and we\u0026rsquo;ll see the status here.\nWe\u0026rsquo;ll also see the connectivity history to the Meraki cloud - which in my case is nearly 100%. Seems like one very minor blip just after 4am.\nWe can also see the current network utilization on the MG. This is great to have - though my current utilization is pretty low\u0026hellip; (I am using this as a backup modem, after all).\nOn the left side of the page, we\u0026rsquo;ll see some of the usual info we expect from a Meraki device. Current IP, location, Serial number, and IMEI. Just below the view of the screenshot, there is also an indicator for firmware version.\nOnto the Uplink tab! Let\u0026rsquo;s see what we have:\nFirst we\u0026rsquo;ll see the Configuration section. This just gives us a quick view into what settings the MG currently has.\nWe\u0026rsquo;ll see the current IP info provided by our carrier, and also some statistics on our cellular connection.\nJust below that info, we\u0026rsquo;ll see our cellular graphs:\nThis is what I wanted! It\u0026rsquo;s great to see a quick view into what our active uplink traffic is - as well as look back historically at what our LTE signal quality has been.\nNot pictured here - but there is also a section of graphs on this page for our uplink monitor. This is where we can see our current \u0026amp; historical loss \u0026amp; latency stats for the cellular connection. After a few days of use - I disabled the uplink monitor due to the amount of data the feature consumes.\nFinally, we also have the DHCP tab:\nThis will show our current DHCP subnet \u0026amp; any clients that have been provided an address. In my case, there isn\u0026rsquo;t any current leases here - because my firewall has a fixed IP.\nPerformance \u0026amp;\u0026amp; Considerations I\u0026rsquo;ve had the MG running for about a week now, and wanted to provide some things to think about.\nFirst - How does the modem perform? Well, the first day I had it set up - I was able to get ~150M download speeds using the MG\u0026rsquo;s built-in speed test utility:\nThat being said - I\u0026rsquo;m lucky if I get 30-50M on an average day. I might have just gotten lucky that day with some light cellular utilization in my area. Overall though, I\u0026rsquo;m pleased with the speeds I get - they\u0026rsquo;ll certainly fit my needs.\nFor the few days that I had the uplink monitoring running, I saw good results. Usually 0% packet loss, with a rare spike of 5-10%. Latency was a little less reliable, but usually bounced between 50-150ms. This was also much less than the NetGear modem I had been using, which averaged 200-250ms.\nSpeaking of uplink tests! Let\u0026rsquo;s talk about data usage\u0026hellip;.\nBy default, the MG communicates intermittently with the Meraki cloud - which consumes some data. By my measurement, this is usually less than 10Mb/day. No problem here.\nThe uplink tests, on the other hand, do consume a bit of data. I\u0026rsquo;m not sure what frequency these run on, but it\u0026rsquo;s fairly often. Even with one uplink monitor to 8.8.8.8 configured, I was seeing data usage of 70-100Mb a day.\nWhile seeing those metrics is valuable to me, it\u0026rsquo;s also not worth the data charges. If I was using a SIM card with an unlimited data plan - no doubt I would keep this feature enabled. However, since I am paying for the cell data used - I opted to disable this feature.\nThe MG does still perform it\u0026rsquo;s check-ins to the Meraki Cloud - so you\u0026rsquo;ll have availability statistics \u0026amp; monitoring\u0026hellip; But disabling the uplink monitor means you\u0026rsquo;ll lose the granular data on loss \u0026amp; latency.\nLastly, and another word of warning, when you\u0026rsquo;re actively viewing the MG monitoring page - this also consumes additional data. To demonstrate - I\u0026rsquo;ll post a snippet of the screenshot from earlier:\nIf you notice, all the way on the far left there was barely any activity. However, once I loaded the MG monitoring page - you begin to see minor spikes in data usage as the Meraki Cloud starts actively polling the MG for data.\nIn my experience so far, this isn\u0026rsquo;t a ton of data. I\u0026rsquo;ve checked in to see how the MG has been performing a few times this week, and each time has totaled around 10-15Mb of data usage.\nTo sum up - I\u0026rsquo;m cheap and want to avoid excess data usage. Just wanted to provide some of that info as something to be aware of.\nFinal Thoughts I\u0026rsquo;m only a week in, but pretty pleased with the MG\u0026rsquo;s performance. It\u0026rsquo;s maintained a very solid \u0026amp; stable connection compared to the NetGear modem it replaced. The device is intended to provide LTE connectivity or backup service for business networks, so I would certainly hope it would meet my home needs :)\nOutside of that, I do wish there was a little better documentation \u0026amp; clarity from the Meraki team on data usage. Right now their documentation only mentions the 6-8Mb of usage due to backend data to/from the Meraki Cloud:\nI would be happy to see additional settings on the uplink monitor to allow me to choose the polling frequency. I feel like throttling down the amount of requests could reduce data to a point where I would be comfortable re-enabling that feature.\nOh - and currently there are no native email alerts for the MG. So if the MG goes offline, etc\u0026hellip; there is no alerting from the Meraki Dashboard. This kinda sucks. I\u0026rsquo;m sure this is coming soon, but for the time being I\u0026rsquo;m inclined to write my own monitor using the Dashboard APIs. (see note below!)\nOverall, I\u0026rsquo;m happy with the device. Definitely looking forward to future feature \u0026amp; firmware updates to see where the Meraki team takes this platform!\nUpdate 08/28/2020 - Looks like in the week since I posted this, Meraki added alerting for the MG! Now you can be notified if the cell gateway goes offline:\n","permalink":"https://0x2142.com/meraki-mg-setting-up-merakis-new-lte-gateway/","summary":"I recently got a Meraki MG21 LTE gateway. Let\u0026rsquo;s set it up!","title":"Meraki MG - Setting up Meraki's New Cellular Gateway"},{"content":" In a recent post, I walked through setting up a Netgear LTE cellular modem with Google Fi. Might seem random - but I had a plan for this!\nIn trying to ensure I keep a stable internet connection for work, I eventually led myself down the path of buying a cellular modem. That was part one. The next step was being able to somewhat-intelligently monitor my home internet connection, and then fail over to the cell modem when connectivity was poor (think lazy SDWAN).\nAt first I figured this would be easy\u0026hellip;. but of course nothing is :)\nI currently have a Cisco FirePower 1010 appliance that I\u0026rsquo;m using for a home firewall. Unfortunately, while this thing does support IP SLA - It wouldn\u0026rsquo;t quite accomplish what I wanted to do. And to be fair, this box is intended to be a security appliance - not SDWAN.\nAnyways - I talked myself into just writing some Python automation to monitor my home internet, and dynamically inject/remove static routing entries. I needed a new project anyways\nFor those of you wanting to jump straight to the code, the GitHub repo is here\nPart 1 - Monitoring Packet Loss \u0026amp; Latency So first thing - I regularly experience both complete internet outages (always at the worst times), as well as very high latency. Packet loss seems to be less common with my ISP - but hey, it happens sometimes too.\nI opted to use the pythonping module as an easy way to collect some of the info I needed.\nAfter importing the module, it\u0026rsquo;s as easy as specifying a destination, packet size, and number of ICMP messages to send:\nfrom pythonping import ping result = ping(\u0026#39;8.8.8.8\u0026#39;, size=2, count=10, verbose=True) To simplify at least one part of the operation, pythonping has a built in function to return the average round trip:\n\u0026gt;\u0026gt;\u0026gt; print(result.rtt_avg_ms) 74.23 The actual contents of our ping results are going to be in the following format: Reply from 8.8.8.8, 10 bytes in 43.82ms\nSo a quick way for me to figure out packet loss was to simply search for the \u0026ldquo;Reply from\u0026rdquo; text in each response:\nlost = 0 for packet in result: if \u0026#34;Reply from\u0026#34; in str(packet): pass else: lost += 1 Easy enough - and we can use that to calculate the amount of packet loss against the 10 total packets sent:\nlossperc = 100 * lost / 10 Once all that is figured out, we can use a simple expression to determine whether or not the loss or latency thresholds have been exceeded:\nif response \u0026gt;= MAX_LATENCY or loss \u0026gt;= MAX_LOSS: print(\u0026#34;Loss/Latency violate thresholds.\u0026#34;) return True else: print(\u0026#34;Loss/Latency within thresholds.\u0026#34;) return False The code I wrote then takes one of two paths.\nIf the loss and latency is ABOVE our thresholds, call to the FirePower module to inject a static default route over the LTE modem If the loss and latency is BELOW our thresholds, call to the FirePower module to delete the static default route - which returns traffic to the primary internet These functions within the FirePower module were written so that each change will only be made once. For example, if the script checks and finds that the loss/latency is bad, but the static route towards the LTE modem already exists - then no additional changes are made.\nSo next - Let\u0026rsquo;s take a look at the FirePower module.\nPart 2 - Authenticating to FDM \u0026amp; Initial Checks This was my first time getting into the FirePower FDM APIs - but they ended up being fairly straightforward to use.\nTurns out that FDM has built-in API documentation, which is extremely helpful. This can be found at https://\u0026lt;FDM IP\u0026gt;/#/api-explorer\nOkay - So first thing\u0026rsquo;s first. Authenticating to the firewall:\noauth_data = { \u0026#34;grant_type\u0026#34;: \u0026#34;password\u0026#34;, \u0026#34;username\u0026#34;: \u0026#34;admin\u0026#34;, \u0026#34;password\u0026#34;: \u0026#34;Cisco1234!\u0026#34; } headers = { \u0026#34;Content-Type\u0026#34;: \u0026#34;application/json\u0026#34;, \u0026#34;Accept\u0026#34;: \u0026#34;application/json\u0026#34; } baseurl = \u0026#34;https://\u0026#34; + FDM + \u0026#34;/api/fdm/latest\u0026#34; authurl = baseurl + \u0026#34;/fdm/token\u0026#34; print(\u0026#34;Posting AUTH request to FDM\u0026#34;) # POST request to FDM with headers / oauth data resp = self.s.post(authurl, headers=headers, data=json.dumps(oauth_data), verify=False) # If success - only pull out \u0026amp; return the auth token if resp.status_code == 200: print(\u0026#34;Auth success - got token.\u0026#34;) return json.loads(resp.text)[\u0026#39;access_token\u0026#39;] else: print(\u0026#34;Authentication Failed.\u0026#34;) print(resp.text) In the code above - we take the headers and some basic authentication info (username, password, grant type) and send it in an HTTP POST request to https://\u0026lt;FDM IP\u0026gt;/api/fdm/latest/fdm/token\nThis returns an authentication token, which we\u0026rsquo;ll need to include in any further HTTP request to the firewall. This can be done by sending a new set of headers in the future:\nheaders = { \u0026#34;Content-Type\u0026#34;: \u0026#34;application/json\u0026#34;, \u0026#34;Accept\u0026#34;: \u0026#34;application/json\u0026#34;, \u0026#34;Authorization\u0026#34;: \u0026#34;Bearer \u0026#34; + self.token } Next we need to figure out which routing table to insert the route into. Since I am only using the default routing table, the script will just grab the UID for the default global routing table:\nvr_url = baseurl + \u0026#34;/devices/default/routing/virtualrouters\u0026#34; routing_table = requests.get(vr_url, headers=headers) I mentioned earlier that the script will not make any changes if the firewall is already in the desired state. So we will take time now to check what state the firewall is in, before attempting to make any changes.\nThis is accomplished by making our first primary action scanning the routing table to see if our route already exists:\nroute_url = baseurl + \u0026#34;/devices/default/routing/virtualrouters/\u0026#34; + \\ self.globalVR + \u0026#34;/staticrouteentries\u0026#34; route_data = requests.get(route_url, headers=headers) # Convert returned JSON object current_routes = json.loads(route_data)[\u0026#39;items\u0026#39;] # Run through each route returned and see if it matches the one we\u0026#39;re looking for for route in current_routes: # For each route, we need to manually go look up the uid of our gateway \u0026amp; network objects gateway = self.getNetworkObject(route[\u0026#39;gateway\u0026#39;][\u0026#39;id\u0026#39;]) dest_network = self.getNetworkObject(route[\u0026#39;networks\u0026#39;][0][\u0026#39;id\u0026#39;]).split(\u0026#39;/\u0026#39;)[0] # Match based on route prefix \u0026amp; upstream next hop gateway if gateway == GATEWAY and dest_network == ROUTE.split(\u0026#39;/\u0026#39;)[0]: print(\u0026#34;Found route to %s via %s\u0026#34; % (dest_network, gateway)) return route Depending on whether we were looking to add or remove a route, the script may proceed with doing so - or just quit if the action has already been taken previously.\nPart 3 - Failover (Add Static Route) Assuming that we need to Add a route, we need to sent a POST request to https://\u0026lt;FDM IP\u0026gt;/api/fdm/latest/devices/default/routing/virtualrouters/\u0026lt;Virtual Router ID\u0026gt;/staticrouteentries with some required information (like subnet info, next-hop, etc)\nUnfortunately, we can\u0026rsquo;t just send a POST with the target subnet \u0026amp; gateway. Those need to be created as network objects on the FirePower box beforehand. For each network object, we need to build a quick JSON object with the required info:\nhost_data = {} host_data[\u0026#39;name\u0026#39;] = name host_data[\u0026#39;description\u0026#39;] = \u0026#34;Created by ISP Failover automation\u0026#34; host_data[\u0026#39;subType\u0026#39;] = subtype host_data[\u0026#39;value\u0026#39;] = address host_data[\u0026#39;type\u0026#39;] = \u0026#34;networkobject\u0026#34; Then we can send a POST to the FDM API to create the object with our supplied parameters:\nposturl = baseurl + \u0026#34;/object/networks\u0026#34; requests.post(posturl, headers=headers, json=host_data) Once we have those objects created, we can compile all of that info into a JSON object with all the components needed to create a static route entry:\nrouteobject = {} routeobject[\u0026#39;name\u0026#39;] = \u0026#34;route_BACKUP\u0026#34; routeobject[\u0026#39;description\u0026#39;] = \u0026#34;Created by ISP Failover automation\u0026#34; routeobject[\u0026#39;iface\u0026#39;] = {} routeobject[\u0026#39;iface\u0026#39;][\u0026#39;id\u0026#39;] = iface_ID routeobject[\u0026#39;iface\u0026#39;][\u0026#39;type\u0026#39;] = \u0026#34;physicalinterface\u0026#34; routeobject[\u0026#39;iface\u0026#39;][\u0026#39;name\u0026#39;] = iface_name routeobject[\u0026#39;networks\u0026#39;] = [{}] routeobject[\u0026#39;networks\u0026#39;][0] = {} routeobject[\u0026#39;networks\u0026#39;][0][\u0026#39;id\u0026#39;] = network_ID routeobject[\u0026#39;networks\u0026#39;][0][\u0026#39;type\u0026#39;] = \u0026#34;networkobject\u0026#34; routeobject[\u0026#39;networks\u0026#39;][0][\u0026#39;name\u0026#39;] = network_name routeobject[\u0026#39;gateway\u0026#39;] = {} routeobject[\u0026#39;gateway\u0026#39;][\u0026#39;id\u0026#39;] = gateway_ID routeobject[\u0026#39;gateway\u0026#39;][\u0026#39;type\u0026#39;] = \u0026#34;networkobject\u0026#34; routeobject[\u0026#39;gateway\u0026#39;][\u0026#39;name\u0026#39;] = gateway_name routeobject[\u0026#39;metricValue\u0026#39;] = 1 routeobject[\u0026#39;ipType\u0026#39;] = \u0026#34;IPv4\u0026#34; routeobject[\u0026#39;type\u0026#39;] = \u0026#34;staticrouteentry\u0026#34; Then pass all that into a POST request to create the route object:\nadd_url = baseurl + \u0026#34;/devices/default/routing/virtualrouters/\u0026#34; + self.globalVR + \u0026#34;/staticrouteentries requests.post(add_url, headers=headers, json=routeobject) Success? Well not quite yet.\nFirePower requires all changes to be deployed (or applied) to the system before they take effect.\nSo next we need to initiate a deployment task - and periodically check on it. Deployments can take a while to complete, depending on number of changes, processing power of the appliance, etc.\n# First send a POST to the deployment endpoint: deploy_url = baseurl + \u0026#34;/operational/deploy\u0026#34; deploy_response = requests.post(deploy_url, headers=headers) # Grab the deployment task UID: deploymentID = json.loads(deploy_response.text)[\u0026#39;id\u0026#39;] # Loop while deployment is running: while deployed is False: # Sleep for a few seconds: sleep(5) # Get list of all deployment tasks: tasklist = json.loads(requests.get(deploy_url).text) for task in taskList[\u0026#39;items\u0026#39;]: if task[\u0026#39;id\u0026#39;] == deploymentID and task[\u0026#39;state\u0026#39;] == \u0026#39;DEPLOYED\u0026#39;: print(\u0026#34;Deployment status is: \u0026#34; + task[\u0026#39;state\u0026#39;]) deployed = True return True elif task[\u0026#39;id\u0026#39;] == deploymentID and task[\u0026#39;state\u0026#39;] != \u0026#39;DEPLOYED\u0026#39;: # If changes not yet deployed, check again momentarily print(\u0026#34;Deployment status is: \u0026#34; + task[\u0026#39;state\u0026#39;]) deployed = False So in the above block, we POST a request to deploy changes. The response of that POST request contains our deployment ID, which we keep to check the status later.\nThen the script waits a few seconds, and pulls a list of all deployment tasks. Search through that list for our deployment ID - and check to see if it has been completed yet. If not, wait and run the loop again.\nAnd that\u0026rsquo;s it! Once we get confirmation that our changes have been deployed - we\u0026rsquo;ve now routed traffic over the secondary connection (an LTE modem, in my case).\nPart 4 - Fail-Back (Delete Static Route) Okay - this section will be fairly quick. We\u0026rsquo;ve already looked at authenticating, checking if our route already exists, and how to add a new route. So the only thing remaining is removing our route if we wanted to migrate traffic back to the primary connection.\nSame logic applies as earlier. If our path monitoring script runs and finds that loss \u0026amp; latency are within the thresholds we set, then we make a call to the firepower module. First we check to ensure there is a route for us to delete, and if so - proceed with deleting it.\nAdding a route is a little more work, since we may need to create network objects. However, when we delete the route - we\u0026rsquo;ll just leave those objects on the FirePower box. They\u0026rsquo;ll be there for the next time we need them (which also speeds up deployment times).\nTo get started, we just need the UID for the route we want to delete.\nRemember that code I showed above, where we check to see if the static route exists? And match on our intended subnet / gateway pair? Well, I just made that into a function that will return the UID of the route if it finds it. Easy enough!\nNext, we just send an HTTP DELETE to the static routing endpoint - and reference that UID:\ndel_url = baseurl + \u0026#34;/devices/default/routing/virtualrouters/\u0026#34; + \\ self.globalVR + \u0026#34;/staticrouteentries/\u0026#34; + route[\u0026#39;id\u0026#39;] requests.delete(del_url, headers=headers) Once that succeeds - we use the same logic as earlier to deploy the changes.\nAfter that, our route is removed \u0026amp; traffic should be flowing over our primary connection again.\nIf it helps anyone - I also threw together a quick diagram to explain the flow of operations here: Well - that\u0026rsquo;s it. This was a fun side project to work on over the past week or two. I hadn\u0026rsquo;t spent much time with the FirePower/FDM APIs just yet. While there was a bit of work in creating all the necessary network objects, the overall process was fairly simple.\nIt helped tremendously that the FDM API explorer exists, and is available on-box. This utility allows you to see all the available API calls, what parameters they require, and even run test calls from the web UI. This greatly reduced the time needed to figure this out.\nHope this was interesting. If you would like to see the whole project - check out the repo on my GitHub page.\n","permalink":"https://0x2142.com/cisco-firepower-automating-cellular-failover/","summary":"How I automated internet uplink monitoring \u0026amp; route injection on a Cisco Firepower Firewall","title":"Cisco Firepower - Automating Cellular Failover"},{"content":"This week at Twitter, there was a security incident that allowed access to a number of high profile accounts. The end result was a Bitcoin scam, with every account promising to \u0026ldquo;double any BTC sent\u0026rdquo; to them.\nTwitter has come out fairly quickly to say that the incident was a result of social engineering. Someone convinced an employee at Twitter to hijack accounts via a backdoor administrative system.\nWhat I wanted to focus on today is this: Why do backdoor systems like this exist - and how do we protect ourselves?\nWe detected what we believe to be a coordinated social engineering attack by people who successfully targeted some of our employees with access to internal systems and tools.\nTwitter Support (@TwitterSupport) Everything Has a Backdoor If you think there isn\u0026rsquo;t a backdoor way into a system, you probably just haven\u0026rsquo;t found it yet.\nFrom years as working in IT - one thing is certain: Someone controls the data. And that person(s) can do with it what they wish.\nAre backdoors always a bad thing? Not inherently, no. We all want the convenience of being able to regain access to a locked account right? Then someone has to have the keys to override the system to make that change.\nWe all take our corporate email and domain logins for granted. Sure, we have a kind helpdesk employee that may help to reset a password - but how much access does that person have to our account? Even so, maybe we expect that in this case.\nFor example, there was a company I worked for a long while ago. HR/Payroll wanted to switch to using internal email to distribute paystubs. In order to do so, they required that every employee manually opt-in. None of the systems admins in the organization opted-in. We all knew how easy it was for any of us to read each others email. In fact, our internal security policy required any email containing sensitive information going in or out of the organization to be manually reviewed by someone. You know what qualifies as sensitive info? Payroll data.\nSocial media platforms (or any major website really) are no different. Someone needs to manage the backend systems. Someone has to be able to reset passwords \u0026amp; assist those who use the platform. In this particular case, someone at Twitter had the ability to hijack accounts by changing email addresses \u0026amp; resetting the passwords.\nWhat About MFA? MFA is like anything else in security - it\u0026rsquo;s another layer, but it doesn\u0026rsquo;t solve all problems.\nMFA can protect you from someone who has your login credentials (username/password). If they don\u0026rsquo;t have your token, phone, or whatever else you\u0026rsquo;re using - they can\u0026rsquo;t successfully authenticate.\nThat being said, if you already control the keys to the backend systems - bypassing or disabling MFA is just another click on your journey to taking over accounts.\nHow Do We Prevent This? That\u0026rsquo;s a good question. And a hard one to answer.\nThe general idea is to limit the amount of privilege that a single individual has. For example, if the average Twitter help desk employee is only responsible for resetting passwords - they shouldn\u0026rsquo;t even have the option to disable MFA or change email addresses.\nThat being said - that\u0026rsquo;s not always easy to accomplish. Systems need to be built around security and implement strict access control where necessary. Unfortunately we still live in a world where security is often an afterthought - or at least not designed into the solution on Day 1. That\u0026rsquo;s not even considering that often security is still under-funded, and/or under-prioritized\u0026hellip;.\nDepending on the size of the organization, this control comes in many flavors. On the more strict side, we could have an administrative panel that is only accessible by high-level, trusted employees. Even then, we could implement a multi-step process by which a single employee could not make changes to sensitive accounts without secondary approval (or more). Going a step further - proper logging \u0026amp; alerting may be triggered to alert someone that a change has happened, which prompts review to see if this was legitimate.\nMore commonly, what I see in organizations is just a simple policy. Yes, we acknowledge that you have unrestricted access to view/modify/delete customer data. But we have a policy that tells you not to.\nThe problem with that? One, it expects that most people read (and care about) the policies. I\u0026rsquo;ve seen quite a number of organizations that hand you a huge pile of policies \u0026amp; paperwork to review in your first week of employment. How many people truly take the time to read, review, and consider the content of those?\nFor two, a policy does nothing to prevent someone from taking action. Sure, fear of repercussion or losing a job may help. But if we\u0026rsquo;re talking about a potentially malicious individual, those things likely do not concern them.\nAs another example, I\u0026rsquo;ve previously worked at an organization with such policies. \u0026ldquo;Don\u0026rsquo;t look at customer data unless you have explicit approval from the customer\u0026rdquo;. Okay, but the customer opened a support ticket because of some issue they\u0026rsquo;re having. This issue sounds familiar and I can fix it real quick, if I just log into their tenant and make a change. OH. Whoops. I just saw sensitive data.\nI wish I could say things like that never happened\u0026hellip;\nWhat Now? Well, now we wait.\nTwitter has been fairly forthcoming with information so far. Yes, it\u0026rsquo;s been vague - but that\u0026rsquo;s to be expected while they pick up the pieces of what happened. The important thing is that they\u0026rsquo;ve been steadily communicating what they do know so far.\nRumors are that an internal employee may have been paid to make changes to the accounts. I suppose we\u0026rsquo;ll find out eventually - but if this is truly the case, there isn\u0026rsquo;t much to be done.\nOne would hope that access to high-profile accounts would be restricted. One would hope that the people with that access are trusted, reliable individuals. Yet here we are.\nIt will certainly be interesting to see how much data Twitter releases about the actual events \u0026amp; timeline. Preventing this from happening again will likely require further restricting access to those accounts \u0026amp; possibly some form of the multi-tiered change approval mentioned earlier.\nIn the end - it sounds very likely this was an internal job. Those can be extremely hard to prevent, and require security training, additional security controls, etc. Even then - Humans create security controls, which means we also create ways to get around them.\n","permalink":"https://0x2142.com/privileged-access-is-dangerous/","summary":"My thoughts on the recent Twitter hack, and why access-control is important","title":"Privileged Access is Dangerous"},{"content":" Note: I may receive commissions for purchases made through links in this post. This is to help support my blog and does not have any impact on my recommendations.\nIt\u0026rsquo;s been a crazy couple of months recently. With what began as a temporary lockdown \u0026amp; mandatory work-from-home has now become a question of \u0026ldquo;Will we ever return to the office\u0026rdquo;?\nLuckily I have a decent home office setup, as my job is already primarily work from home. That being said - it\u0026rsquo;s more important than ever to have a stable, reliable internet connection available. Can\u0026rsquo;t run the risk of being in an important meeting with a customer and drop from the video conference (or have audio issues, etc).\nNeedless to say, my internet provider is far from perfect. While it does perform reasonably well most days, the spikes in latency \u0026amp; packet loss have increased substantially with everyone in the neighborhood working from home.\nI\u0026rsquo;ve been debating for a while on whether or not to pick up a cellular modem. Originally I had planned on having one handy for a complete internet outage (which happens). But more recently I began to wonder if it might be worth it for routing around my primary provider when the connectivity is poor.\nTo accomplish this, I decided to use Google Fi. I\u0026rsquo;ve already used the service as my cellular provider for a number of years now and I\u0026rsquo;ve been extremely pleased with the service. One of the awesome benefits they offer for subscribers is free data-only SIM cards. So I could pay nothing for the SIM card itself (no plan/recurring costs either) - then only pay for data as I use it.\nFirst, I went ahead and tested what typical latency \u0026amp; performance was over my cell connection. Occasionally my home internet provider would see latency spikes up to 1200-1800ms. Checking the cell connection - it seemed pretty stable in the 200-300ms range. Still pretty high, but much more bearable than ~1200ms. Seemed like it should work reasonably well for what I wanted.\nThen, I had to double check compatability. Google Fi runs over multiple carriers on the backend - as long as you\u0026rsquo;re using a Fi-compatible device. If not, it seems they default to only using T-Mobile\u0026rsquo;s network. So in that case, Fi requires that our device supports LTE bands 2 \u0026amp; 4 at a minimum. The Netgear LB1120/1121 modems do, so we\u0026rsquo;re good there.\nNext, I went ahead and ordered a NetGear LB1121 LTE Cellular modem. I opted for the power over ethernet model, since I already had a Cat5 run over to where the modem would be placed. The switch I\u0026rsquo;m using is also being backed up by a UPS, so in the event of a power outage the LTE modem would stay online.\nFinally - I placed my order for the Google Fi SIM card. Easy process - $0 for the SIM, $0 for shipping, and it arrived in only a few days. As an additional note - the modem requires a micro-SIM, but Fi provides nano-SIM cards. I also had to pick up a cheap adapter kit to make this work.\nGetting the Modem Set up Once the modem \u0026amp; SIM card arrive, it\u0026rsquo;s time to get started. This will be a fairly quick and straightforward process.\nOn the bottom of the LTE modem, the device will list it\u0026rsquo;s default IP address \u0026amp; the password to log in. Take note of that, as you\u0026rsquo;ll need it later.\nAlso on the bottom is the slot to insert the SIM card. Remove the cover, insert your SIM card into the micro-SIM adapter, then insert into the cellular modem.\nNext, go ahead and power on the modem \u0026amp; get it plugged into a PC.\nOnce the modem boots up - we should be able to reach it via a web browser @ 192.168.5.1.\nLog in with the credentials from the bottom of the modem.\nUpon landing on the main page, you may see that the modem auto-connects to T-Mobile\u0026rsquo;s network. While we might be able to use this as-is, ideally we need to change our connection to specifically use Google Fi\u0026rsquo;s information.\nHead over to Settings \u0026gt; General \u0026gt; APN:\nClick Add to create a new cellular profile\nFor the network Name, enter Google Fi. For the APN enter h2g2.\nClick Save to add the profile\nThen make sure to select the correct radio button to activate the new profile:\nAt this point, I would recommend deleting the old/default T-Mobile profile. I would also suggest rebooting the device to force it to switch to Google Fi.\nIf all is well - You\u0026rsquo;ll log back in and see that the modem has now connected to the appropriate network:\nNote: I\u0026rsquo;ve had some issues where the carrier name doesn\u0026rsquo;t display correctly. It may take a few minutes to finally display \u0026ldquo;Google Fi\u0026rdquo;. However, at this point you should be connected to the correct network and be able to use it with no issues\nAdditional Settings One other thing that may be worth noting. Depending on how you\u0026rsquo;re using the modem, you may want to change the way the modem operates.\nLet\u0026rsquo;s go ahead and check out a few options on the LAN settings page. This can be found by going to Settings \u0026gt; Advanced \u0026gt; LAN.\nBy default, the modem operates in router mode. What this means is that the modem terminates the connection from Google Fi - and acts as a gateway between your devices \u0026amp; the carrier network.\nYou would want to use router mode if you have multiple devices you want to use with the modem \u0026amp; you do not have an additional router or firewall. In this case, the modem will handle providing IP addresses to the client devices. In addition, it will act as a firewall and allow client devices to the internet, while blocking any external devices from accessing the clients directly.\nThe other available mode is bridge mode. In this case, the modem operates as a pass-through device \u0026amp; assumes that there is another device behind the modem that is providing the routing/firewalling functionality.\nYou may want to use bridge mode if you already have a router or firewall you want to use - and you want to treat the modem as a second internet connection. When the modem connects to the provider, it will automatically pass the public IP address \u0026amp; configuration straight through to whatever device is plugged into it.\nIf you\u0026rsquo;re interested in how I configured the auto-failover to the cellular modem - check back in a bit. I wrote some custom automation to monitor my primary connection \u0026amp; conditionally inject a route over the LTE modem.\nI\u0026rsquo;m just wrapping up some of that, and should have something posted here shortly!\n","permalink":"https://0x2142.com/how-to-setting-up-google-fi-on-a-netgear-lte-modem/","summary":"Let\u0026rsquo;s walk though how to set up a  Netgear LTE modem to work with Google Fi\u0026rsquo;s cellular service","title":"How to: Setting up Google Fi on a Netgear LTE Modem"},{"content":" Hey there! First thing\u0026rsquo;s first - Hope all is well with everyone. The past few months have seen all sorts of craziness going on in the world. I\u0026rsquo;ve been lucky in the sense that my current job role was already well set up to make an easy transition to working from home all the time. That being said, I know it hasn\u0026rsquo;t been easy for everyone - and I know quite a few people who have lost their jobs, etc.\nSecond thing! As I\u0026rsquo;m sure you may have already seen above - I opted to spend some of my new-found free time trying out a new format. I\u0026rsquo;ve had a couple of ideas in the past for making short videos, but never forced myself to sit down and give it a shot. So here we are - after about a month of on-and-off work - I finally have something to share. Please give it a look if you\u0026rsquo;re interested, and I would appreciate any comments \u0026amp; feedback. I still have a few other ideas - so if this one goes well I may pursue producing a few more of these.\nOkay - Now onto the real content!\nA few months ago I had the chance to pick up a pair of Cisco Catalyst 9100 series access points from work. My home network has been running on Ubiquiti APs for years - but they\u0026rsquo;re getting old and I desperately needed to replace them. So along comes the Catalyst 9100 APs, which are capable of supporting 802.11ax clients - and why not take the time to upgrade \u0026amp; future proof? I needed some practice anyways, as I have a few customers at work that I think will be interested in these in the near future. I ended up with two 9120AXI APs.\nSo in the process of trying to dive on the opportunity to play with something new - I completely missed the fact that the 9100 APs have two different product SKUs. One which ships the AP pre-configured with the standard lightweight AP code that you might use if you had an external wireless LAN controller (WLC). And a second SKU that ships the AP with the Catalyst 9800 Embedded WLC (eWLC) software loaded. Of course, I grabbed the lightweight APs without checking.\nProduct SKUs (Example, using 9120AXI): - Standard Lightweight AP: C9120AXI - Pre-load with Embedded WLC: C9120AXI-EWC I wrote in a previous blog about getting MAC address filtering set up on the Catalyst 9800 WLC. When I wrote that blog I was actually using the C9800-CL, which is a virtual machine version of the Catalyst 9800 controller software. I was originally excited to run the controller as a VM and not need a hardware appliance - but then got to thinking that maybe I should try and save VM resources as well. Which led me to looking at the 9800 eWLC.\nFinding the Software Image In order to convert an existing lightweight access point to one running the embedded WLC software - we first need to grab a copy of the software images from Cisco.com. Search for the model of your 9100 access point (in my case, the 9120AXI).\nYou\u0026rsquo;ll see two options for Software Type - and it may not immediately be obvious - but we\u0026rsquo;ll need to look under IOS XE Software to find the eWLC images:\nThen we\u0026rsquo;ll grab the EWC AP image bundle. In my case, I was waiting on a specific feature set that wasn\u0026rsquo;t available until 17.x - so I downloaded the 17.2.1 software:\nOkay - after we\u0026rsquo;ve done that - let\u0026rsquo;s drop everything onto a server/laptop/etc which has console connectivity to our AP and a running TFTP server.\nOnce you un-zip the contents of that AP image bundle, you\u0026rsquo;ll notice there are quite a number of files. We\u0026rsquo;ll only need two of them - one image to load onto the AP itself, and one that will get loaded for the WLC software container. Within the image bundle, there will be a readme.txt file that will tell you which image to use with your AP:\nap1g5 : AP1815, AP154x ap1g4 : AP180x, AP183x, AP185x ap1g7 : C9115, C9120 ap1g6 : C9117 ap1g6a : C9130 ap3g3 : AP380x, AP280x, AP156x So in my case, I would use the file named ap1g7 since I had the 9120 APs. In addition, you should also see a file named C9800-AP-iosxe-wlc.binwhich we\u0026rsquo;ll need to load the controller.\nWhat\u0026rsquo;s with that second image? Well - the Catalyst 9100 APs include a feature called Application Hosting (also found on some of the Catalyst 9000 series switches). This is equivalent to running a Linux container or Docker container directly on the AP hardware. At time of writing, this is only available for the embedded WLC software. However, there will be a future software update that will allow you to provision other software containers as well (according to the data sheet).\nConverting the Access Point Now we can get started on the actual conversion process. If it\u0026rsquo;s a new AP, we can just go ahead and boot it up with a console cable connected. If it\u0026rsquo;s already running something, you\u0026rsquo;ll likely want to factory reset the AP first. You can find detailed instructions here, but a quick summary - power off the AP, hold the mode button while plugging the AP back in, and keep the mode button held for at least 20-30 seconds. If you\u0026rsquo;re logged into the console during this time - you\u0026rsquo;ll actually see the AP counting how long you\u0026rsquo;ve held the button for. Once that counter shows at least 20 seconds, release the modebutton and allow the AP to reboot.\nOnce the AP is booted. The default login is Cisco / Cisco. The enable password is also Cisco. You may also want to quickly issue a show version, and take note of the current software version running on the AP (hint: we\u0026rsquo;ll need that later).\nNext - it\u0026rsquo;s important to note that after we convert the AP - our default CLI will actually be the WLC, not the AP (even when connected via console). So we should configure a hostname and IP address for the AP now:\n0xAP# capwap ap hostname \u0026lt;hostname\u0026gt; 0xAP# capwap ap ip \u0026lt;ip-addr\u0026gt; \u0026lt;netmask\u0026gt; \u0026lt;gateway\u0026gt; Then we can load the new AP image and WLC image. Depending on which software version you\u0026rsquo;re running today, there is a different command to load the image:\n0xAP# ! If you\u0026#39;re running software 8.9 or lower, use the following: 0xAP# ap-type mobility-express \u0026lt;TFTP path to AP image\u0026gt; \u0026lt;TFTP path to WLC image\u0026gt; 0xAP# ! If you\u0026#39;re running anything above 8.9, use the following: 0xAP# ap-type ewc-ap \u0026lt;TFTP path to AP image\u0026gt; \u0026lt;TFTP path to WLC image\u0026gt; Once those commands are submitted, the AP will begin copying the software from the TFTP server. The AP will automatically reboot after the image load is completed.\nQuick note: For one of my APs, the WLC software didn\u0026rsquo;t load correctly - and when my AP rebooted I was left with an error that said \u0026ldquo;EWC-AP in Recovery Mode\u0026rdquo;. Re-copying the WLC image fixed it without much trouble. If this happens, the AP will print out the command to re-image the WLC software, but I\u0026rsquo;ll also put it here for reference:\n0xAP# ! If you end up in \u0026#34;EWC-AP Recovery Mode\u0026#34;, run the following: 0xAP# archive download-sw ewc-ap \u0026lt;TFTP path to WLC image\u0026gt; Okay - Once our AP has come back from loading up all the new software, we can get on with a bit of minimal config required to access the WLC web UI.\nFirst, we\u0026rsquo;ll configure the WLC hostname \u0026amp; our local user account. This can be switched over later to your choice of directory service:\n0xC9800eWLC# config t 0xC9800eWLC(config)# hostname \u0026lt;hostname\u0026gt; 0xC9800eWLC(config)# user-name \u0026lt;admin username\u0026gt; 0xC9800eWLC(config-user-name)# password \u0026lt;admin password\u0026gt; 0xC9800eWLC(config-user-name)# privilege 15 Next we\u0026rsquo;ll also provide the WLC with the administrative credentials used to manage the connected APs:\n0xC9800eWLC(config)# ap profile \u0026lt;profile-name\u0026gt; 0xC9800eWLC(config-ap-profile)# mgmtuser username \u0026lt;AP admin user\u0026gt; password 0 \u0026lt;AP admin password\u0026gt; secret 0 \u0026lt;AP admin secret\u0026gt; After that, we\u0026rsquo;ll go ahead and configure our basic network settings. This will be the IP address \u0026amp; gateway info that will be used to connect to the WLC web UI and SSH:\n0xC9800eWLC(config)# interface gigabit 0 0xC9800eWLC(config-if)# ip address \u0026lt;managemnt IP\u0026gt; \u0026lt;Network mask\u0026gt; 0xC9800eWLC(config-if)# no shut 0xC9800eWLC(config)# ip default-gateway \u0026lt;Gateway IP address\u0026gt; Lastly - we\u0026rsquo;ll need to actually enable the web server. In my case, I opted to not enable the standard HTTP server. I only enabled the encrypted SSL-enabled web server:\n0xC9800eWLC(config)# ! I only enabled the HTTPS server: 0xC9800eWLC(config)# ip http secure-server 0xC9800eWLC(config)# ! If you wanted to enable the plain-text / unencrypted web server: 0xC9800eWLC(config)# ip http server 0xC9800eWLC(config)# 0xC9800eWLC(config)# ! Finally - Save the config 0xC9800eWLC(config)# exit 0xC9800eWLC# wr mem When we save our config for this first time, the AP will verify that the initial configuration pieces have been completed. There is a bit of background cleanup that is performed - which removes any factory config that assisted with provisioning. Once that\u0026rsquo;s all done - we\u0026rsquo;re free to log into the web UI!\nEmbedded WLC Web Interface I won\u0026rsquo;t spend a lot of time here, as there are far too many things to cover with the new 9800 series controllers. However, I did want to point out a few things that stand out regarding the Embedded WLC.\nWhen we log into our WLC web UI - we\u0026rsquo;ll be able to use the username \u0026amp; password combo that we configured previously. Then we\u0026rsquo;ll be dropped into our WLC dashboard.\n*Note: Screenshot taken a bit after I did my initial config. This has been running a bit now\u0026hellip;\nIf we click on the number under the Access Points heading, we\u0026rsquo;ll be taken to a quick monitoring view of the current connected APs:\nHere we can see the APs we have configured, along with their IP address \u0026amp; status info. We will also see what our Current Active WLC controller is, and what our Current Standby / Preferred Active if we have either configured.\nBy default, if we boot up a second AP running the embedded WLC software, it will automatically join the WLC cluster as the secondary node. Any WLC config/settings will be copied, then it\u0026rsquo;s ready in case the primary controller fails. In the event of a failure, the secondary WLC will take over - and be accessible using the same IP \u0026amp; login info that we used on the primary.\nIn my case, I\u0026rsquo;ve also configured one of my APs as a preferred primary controller. For me, this AP is connected to a switch that has a short battery backup - so it\u0026rsquo;s less likely to experience failure. This option can be configured on the individual AP itself. In the left-hand menu, drop into the Configuration section, then click on Access Points under the Wireless header. We\u0026rsquo;ll see a very similar screen to the AP monitoring screen from above. Click on the AP that we want to make our preferred primary, then jump over to the Advanced tab. There will be a checkbox for Preferred Controller:\nWLC Image Repository Last thing - and this one is important. Normally the WLC will have dedicated storage to keep AP software images, which it distributes to new APs when they come online. Unfortunately, while we gain the flexibility and limited footprint of the embedded WLC - we also lose that dedicated storage space. As you might imagine, storage on the AP is limited.\nSo we\u0026rsquo;ll need to make sure that the WLC has an external image repository that it can use. This configuration can be done via teh web UI and CLI. I\u0026rsquo;ll cover both very quickly here.\nFrom the web UI - We\u0026rsquo;ll go to Administration \u0026gt; Software Management:\nHere we\u0026rsquo;ll see our options for where to get images, as well as an inventory of current APs \u0026amp; their images. In the Mode drop-down, we\u0026rsquo;ll have the option of tftp, sftp, CCO, and Desktop. TFTP / SFTP are what we\u0026rsquo;re used to. With Desktop, we would just upload images directly from our laptop / PC. Interestingly though, CCO allows us to provide our Cisco.com credentials - and the controller can pull images directly from Cisco. We\u0026rsquo;ll even have the option to enable automatic software downloads - and specify whether we want to auto-download the latest software release, or only the latest recommended release.\nOn the CLI side of things - we can accomplish the same using the following commands on our WLC CLI:\n0xC9800eWLC(config)# wireless profile image-download \u0026lt;profile name\u0026gt; 0xC9800eWLC(config-wireless-image-download-profile)# image-download mode tftp 0xC9800eWLC(config-wireless-image-download-profile-tftp)# tftp-image-server \u0026lt;IP address\u0026gt; 0xC9800eWLC(config-wireless-image-download-profile-tftp)# tftp-image-path \u0026lt;path to image files\u0026gt; With that, any new APs that join our WLC should be able to auto-load the software necessary.\nThanks for reading!\n","permalink":"https://0x2142.com/how-to-convert-catalyst-9100-ap-to-embedded-wlc/","summary":"A tutorial for converting a Cisco 9100 series wireless access point to host an embedded controller","title":"How To: Convert Catalyst 9100 AP to Embedded WLC"},{"content":"Things That Helped One of the big things that helped me was just the experience I had prior to starting on the CCIE. My experience going into the studying likely gave me a huge step up compared to if I tried the exam earlier in my career. If I tried the CCIE eight years ago like I originally wanted to, it would have been a lot more difficult and much more time consuming. I would have had much more to learn from scratch, and much less practical experience to help.\nAdditionally - the other huge benefit was going into the lab with a solid strategy around time and task management. There were several places through the exam that I felt like I could have easily lost 30-45 minutes on one item. It was very important for me to be able to step back and admit I couldn’t solve something. Instead, it let me focus my time on completing the tasks that I could do - and working on the unknown stuff if I had time later.\nOn the task management side - I spent time early in the study process on finding a good strategy that worked for me. Once I had this figured out - I used it on every single practice lab. I ended up using a combination of a few things other people have written about previously. My base task management was using a great blog post by Chris Miles (Read it here). In Chris’ blog, he suggests breaking up the tasks per location - then completing all the tasks for a location, one location at a time. That part didn’t work for me. Instead, I only used his method of organizing all of the tasks under individual locations - that way I could easily see what tasks were left and where I still needed to work. For example, if I needed to configure EIGRP - I could easily look at the sheet and see every location that needed some form of EIGRP config.\nFor the actual order in which I implemented tasks, I followed the guidance of a LinkedIn post by Kim Bartlett (Link here). In that article, Kim suggests a logical order of operations - like L2, IGP for MPLS, then MPLS, etc. Doing things in this way made sense to me. So I worked out what order worked for me, and decided to follow it. The big difference in my strategy, was that I found it easier to complete all tasks for a certain protocol/technology at once. For example, if I was configuring OSPF - then I would configure it at every location at the same time before moving onto the next piece. My overall order of operations was something like this: L2 -\u0026gt; all IGP -\u0026gt; VPN/MPLS -\u0026gt; MP-BGP -\u0026gt; iBGP -\u0026gt; eBGP -\u0026gt; BGP -\u0026gt; IPv6 -\u0026gt; Anything else. I found this to be a good flow for me. It allowed me to configure things like BGP only after I had already configured all of the underlying dependencies - which meant I could test immediately to see if everything was working as intended.\nAll of the above combined with constant labbing for months prior to the exam was absolutely critical to helping me pass on the first try. I had found a good strategy that worked for me and applied it to every practice lab, which meant that I walked into the actual exam feeling like I had a good way to guide myself through the onslaught of work. Had I walked in with just labbing experience and no good strategy, I don’t think I could have gotten close at all.\nOkay, Now What? I’m now getting around to posting this over three months after I passed the CCIE. I’ve spent a lot of time catching up on things around the house, reading books, running through a few video games, and overall just trying to enjoy the free time.\nThat being said - it wasn’t long for me to start feeling guilty and itching to start working on something else. My first thought was to begin working on the DevNet certifications. I\u0026rsquo;ve been doing a bit of Python \u0026amp; network scripting over the past few years, and I\u0026rsquo;m excited that Cisco is launching a certification program around it. I\u0026rsquo;ve been working on this a bit recently, which has also helped me get back into a few Python projects I hadn\u0026rsquo;t touched in a while. My current plan is to try taking some of these exams shortly after they launch.\nI’ve also kept thinking back to one of the other certifications I considered going after: the CCDE. In my current job as a Systems Engineer at Cisco, the content behind this certification applies a lot more to my job than the CCIE. That’s not saying the CCIE doesn’t help me - it absolutely does. However, my job today is more understanding the technologies and how they fit into a customer’s network, rather than performing in-depth configuration work.\nI don’t know yet whether I will fully pursue the CCDE and take the exams. But I have started reading a few of the recommended books, and I’m already finding bits of information that are valuable to me. I’m also really enjoying the content and getting much more interested in some of the topics. For now - I am planning on continuing to read through the information just to learn it and see where I can apply it. Once I get a good feel for everything, I’ll decide whether to chase the actual certification or not. For now, I think I\u0026rsquo;ll just enjoy not looking at a PuTTY window for a while 🙂\nThanks for reading - and thanks to all the people who have supported me over the past few years. It’s was a long journey, and not always an easy one - but I think it was well worth it.\nStarted here? Read the rest of my story:\nPart 1: Getting Started\nPart 2: Written Exam \u0026amp; Lab Prep\nPart 3: Lab Day\nPart 4: Lab Strategy \u0026amp; What\u0026rsquo;s Next\n","permalink":"https://0x2142.com/ccie-strategy-whats-next/","summary":"The key to the CCIE is having a good strategy. Let\u0026rsquo;s take a look at what helped me","title":"CCIE: Strategy \u0026 What's Next"},{"content":"In the weeks leading up to the lab exam - I felt very unsure of where I was at. On one side, I felt like I was doing pretty well at most of the practice labs I was working on. But on the other side, I felt like I didn’t have any true idea of what challenges the real exam would hold - so I could be missing something big and have no idea yet. I know some people will throw the exam blueprint into excel and give themselves ratings on how well they know a particular blueprint item - but I never got into using this after trying it a few times. Realistically, I should have forced myself to do this anyways. Then I would have had a more deterministic way to judge how prepared I was. Instead - I had just reached a point where I knew I just needed to take the actual exam and figure out what I didn’t know yet.\nLab day finally came - and I arrived at Cisco building 5 in Richardson, TX around 7:45am. There were already a handful of other CCIE candidates waiting outside for the building to open. Once it hit 8am, we all went in to get signed in and fill out our lunch order forms. Then it was time to wait.\nThe exam proctor showed up around 8:17 and guided us to the exam room. I figured there would be more time allotted to the proctor talking through rules, guidelines, etc… but instead he just said a few quick things and we were told to begin.\nTroubleshooting The troubleshooting section had me a bit concerned. It’s always difficult to jump into a completely unknown network and try to fix a problem - and this was no different. My first question immediately made me start panicking a little. I read the ticket, looked at the expected output - and began wondering where to start while being very aware of my short time limit. Every question felt like “I’m never going to figure this out in time” - yet after a few minutes of troubleshooting I was able to find the answers to the first few questions.\nHalfway through the section I received a few tickets that required a lot more work. Some of these I didn’t make much progress on, and some I was able to get half-way resolved. For each of these I tried very hard to keep to a reasonable time limit per question, then mark it down as something to come back to later if I had time.\nA lot of people talk about counting your points during the exam to know where you stand. I had originally assumed that this would just be a waste of time. Yet when I finished going through the remaining tickets, I knew I had to make sure I had enough points. Turned out I was barely on the edge of a passing score - assuming I had resolved all of the tickets correctly. My first two hours ran out, and I got the 30 minute warning. I was hoping to avoid using the extra 30 minutes, but I knew I needed to go back to the 3-4 questions I hadn’t completed.\nAbout 15 minutes later - I had managed to figure out one or two more of the tickets and decided to give up on the remaining items. Based on my estimated point count - I should have been in a good spot on the troubleshooting section….. But I still wasn’t confident in all of my answers. I knew I had a ticket or two that might not be resolved in the correct way. I decided to save the remaining 15 minutes and just move onto the next part of the exam.\nDiagnostics Next was the diagnostics section. My biggest complaint here (and it\u0026rsquo;s somewhat minor) is that the on-screen timer is located in a completely different place than troubleshooting \u0026amp; config. At first (probably because I was in a rush), I couldn’t find the timer - and I also had not kept track of when I began the section. That was a big mistake on my part. So I forced myself to rush through the section, knowing it could end unexpectedly at any second.\nOnce I wrapped up my diag questions - I finally found the timer… and to my surprise had just under five minutes left. Not a ton of time, but enough for me to go back and double check a few answers that I had rushed myself through. I also used the last minute or two to run for a restroom break before starting the config section.\nI honestly had no idea how well I was doing on this section. One of the questions seemed straightforward, but the answer I picked felt too simple. But maybe I was just overthinking it? The other questions made me waffle back and forth between a few answers. In the end, I just went with what my instincts told me was the most likely answer and just stuck with that.\nConfig The config section is extremely overwhelming at first. Well, I suppose it doesn’t get any less overwhelming during the exam - but you quickly get busy enough to stop caring about that 🙂\nI had about 30-45 minutes in the config section before we took lunch. That was enough time for me to get through all of the Layer 2 tasks quickly and then build out my task list on the scratch paper. During this time, I thought I was doing okay until I got to the end of one of my first tasks. I had just completed all of the items within that task when I read the last item - which made me realize I had done the entire task incorrectly. That was not a pleasant feeling. Luckily, I caught my mistake before moving on - but the time had already been wasted and now I had to go back and re-configure that entire section.\nLunch was quick. We went out, ate our food, then got back to the exam in less than 15-20 minutes. There was a bit of minor discussion - but not a whole lot.\nThe remainder of the day went by very quickly. As I had practiced during the prior weeks of practice labs, I placed my trust in strategy \u0026amp; order of operations - then just went heads down and got to work. I tried not to look at the clock and instead just focused on getting the tasks done as quickly and efficiently as possible. I’ll share a little more on my strategy in the next post.\nI ran into a few problems here and there throughout the exam, but nothing too crazy. The strategy I used allows for quick connectivity/functionality testing after completing a task, which allowed me to find and fix my errors quickly. Similar to the troubleshooting section, I hit a few tasks that I could only figure out parts of - so I marked them down to follow up later and just moved on. Since you don’t get partial credit for tasks, I knew I would need to circle back to these if I wanted a shot at passing - but there is no sense in wasting too much time on one task if I couldn\u0026rsquo;t figure it out quickly.\nBy the time I had finished every task, I finally let myself check the clock. I was shocked to see I still had almost a full hour remaining. I quickly took advantage of the time to go back to the several sections I needed more work on. A few of these I stumbled through until I was able to find my problems - and some of it I had to crack open the documentation site to figure out what I needed to do.\nRunning through a lot of the verification steps - there was still a few things not working as they should. I spent time troubleshooting, changing configs, and finally figuring out a few things. I made quite a few configuration changes here to force a few things to work, but I wasn’t sure if they were valid solutions - or if I would end up losing points for doing things I shouldn’t have.\nIn the last 10 or so minutes, I tried to very quickly add up my points while performing a quick skim through the tasks again. Being that close to the end of the exam - it made me feel a bit sick to start finding additional items I had missed. I rushed to throw in a few last-minute changes, then retest to make sure nothing broke in the process. I didn’t make it through re-reading all of the tasks, so I was left wondering what else I might have missed.\nAssuming I had not missed anything else - my count of points placed me in a fairly decent spot on config. However, since there is an overall cut score for the entire exam - I had no idea if I would have enough total points between all three sections to pass. I was already like I might have just barely scraped enough points together for troubleshooting, and diag felt like a complete wildcard.\nWhen I left the exam center, I found myself feeling much better than when I had entered. If I passed, then that would be awesome. And if I had failed, then at least I was confident in what I needed to go back and study. Rather than having to keep worrying about what tricks the exam might hold, I now had the experience of knowing what to expect. I was happy to have attempted the exam once - and knew I would be far better prepared the next time.\nThat evening I went to dinner with a few CCIE candidates who would be attempting the exam the following day. Just tried to have a good time, and not check my email too much :). When I got back to the hotel that night, I still had no results yet - so I just went to bed and tried to get some sleep.\nThe Next Day I woke up probably a dozen or more times throughout the night. Every time my first instinct was to grab my phone and see if I had gotten my results yet. Every time I forced myself to not check, and just go back to sleep. Around 5am, I finally let myself check once - but still had nothing.\nI finally got up around 6:30 - and the CCIE exam site was down. I had a bunch of text messages from people back home asking if I had anything to report - but now I couldn’t even check the site. Later I would find out that the site was broken due to an internal issue at Cisco, but for the time I couldn’t do anything. I tried a few more times throughout the morning, but mostly just gave up and decided to wait it out.\nMy flight left around 10:30 am. While waiting in the airport, I still kept checking every so often but could not get to the site.\nOnce I got onto the plane, the site finally loaded! But my results were the same: No score yet. A this point I figured I would just give up, enjoy the flight - and check when I got back home.\nBoarding took a little longer than usual for the remaining passengers. Right as it was announced that they were shutting the doors and we would be taking off shortly, I decided to try checking one more time.\nAs the site loaded - this time I was greeted with a new status: Pass.\nMy initial reaction was just absolute relief to finally be done - knowing that I didn’t have to keep worrying about trying to pass before the upcoming certification changes. I sat back for a minute before refreshing the site again to make sure the result didn’t change. Nope - the result still said pass.\nWith that - on October 9th, 2019 - I was done. I had my number. CCIE #63461.\nKeep going for the rest of my story:\nPart 1: Getting Started\nPart 2: Written Exam \u0026amp; Lab Prep\nPart 3: Lab Day\nPart 4: Lab Strategy \u0026amp; What\u0026rsquo;s Next\n","permalink":"https://0x2142.com/ccie-lab-day/","summary":"Let\u0026rsquo;s talk about my experiences with taking the CCIE Routing \u0026amp; Switching lab!","title":"CCIE: Lab Day"},{"content":"Written Exam Finally in early 2019 I gave up on trying to gauge where I was at - and figured it was time to just give the exam a shot. I had already been studying for almost a year and a half, and I was craving some definitive way of figuring out where I was at. I went ahead and scheduled an exam for Tuesday, March 12th.\nWhen I walked into the written exam, my first question immediately made me feel unprepared. It was something specific to provider WAN switching - not a topic I had spent enough time on yet. I did my best to take an educated guess, but that first question gave me a lot of doubt about how well prepared I was.\nThe written exam overall felt very\u0026hellip; all over the place. It didn’t feel like a single cohesive exam - instead it felt like 20 different banks of questions shuffled into one. Some people call the exam just random networking trivia - and in some ways that might be accurate. For example, I might have a question on very basic L2, followed immediately by a very in-depth question on MPLS. Then probably over to something completely different. I didn’t want to admit it at the time, but I probably felt far less confident in answering many of the questions I got - and gave my best effort on guessing at quite a few.\nAlready not feeling great about how well I was doing, the test finally made its way into the evolving technologies section. This section did nothing to ease my nerves :). I completely understand why this section exists, but it felt like there was almost no effort put into some of the questions. Many of the questions I got made no sense, had grammatical errors, or gave a set of possible answers that didn’t line up with what the question was asking. Even for technologies that I did have a lot of experience with, it felt like the question was just written by someone who had no understanding of it.\nAs I finished my last question, there was no doubt in my mind that I had failed. To me, it was just a matter of how badly did I miss and how can I better prepare for next time. I was already making several mental notes on what topics I desperately needed to go back and review for the next attempt.\nHowever - when I clicked through the remaining screens on the exam, I was extremely surprised to see that I had passed. It was only by a few points - but a pass is a pass!\nWalking out of the exam, I sent a message to a few people at work to let them know I had passed. Even with the score sheet in my hand, I didn’t feel comfortable saying that I had passed. At no point during the exam did I feel like I was doing well. Maybe that’s just part of the difficulty? I don’t know\u0026hellip; I\u0026rsquo;m honestly glad to see the written exam requirement is being dropped from the new exam blueprints.\nStudying for the Lab Exam Once I had gotten past the written exam, my full attention went into working toward the lab. I spent too much time initially trying to get my lab environment all sorted out. Went back and forth trying to choose between EVE-NG and GNS3, before finally settling on GNS3. Then I wasted a bunch of time trying to find the right images to use and testing them to make sure everything worked.\nFinally - I picked up a copy of “CCIE Routing and Switching v5.1 Foundations: Bridging the Gap Between CCNP and CCIE” and got started. Going through this first book was far less enjoyable than I had hoped. Each lab was a completely different topology with a lot of pre-work to get going - and in many cases completing the actual practice lab would take a fraction of the time it took to get set up. I got frustrated with this a lot - but tried to keep pushing through to at least finish the book as a starting point. This ultimately amounted to a rocky start to labbing for me. Not working on it as much as I should, and not necessarily looking forward to it.\nMy next set of materials would be the INE workbooks - which honestly are structured far better. These labs were all on a shared topology that I could easily clone in GNS3 every time I started a new section. All of the pre-config is done for you - so that you can just focus on the pieces relevant to the topic. For example, if you’re working on a BGP lab - you don’t have to start from scratch with IP addressing or L2 configs. This made the content much easier to consume, and did a lot to help me spend more time working on practice labs. I got through these labs pretty quickly and repeated quite a few for additional practice.\nAt Cisco Live US 2019 - there was a huge announcement regarding certification changes. The CCIE exam \u0026amp; content was changing (along with pretty much everything else). I wasn’t entirely surprised to hear the announcement since the existing track was several years old, and I had come across a few rumors on the internet of possible changes. Even still, I was finding myself now up against a very finite amount of time to pass the lab exam. The old test would be phased out in just eight months (in February 2020).\nAfter the announcement, I talked to my manager about what to do. We decided it would probably be in my best interests to schedule a lab date, and do whatever I can to try and pass ahead of the exam changes. So - only a few days after the new content was announced, I had scheduled a lab date for October 9th, 2019. This was less than four months away, and I still had a ton of content / practice labs to get through.\nHaving the looming deadline did great things for my motivation :). On the good side of things - It helped me to spend more and more time studying for the lab exam. I was able to focus more than before, and I was finding it much easier to push myself to practice even when I wasn\u0026rsquo;t necessarily excited to. Over the summer I nearly doubled the amount of time I had spent labbing compared to before the announcement. On the not-so-good side - I had also put together a week-by-week plan of what I still needed to accomplish between now and October. It was a tighter timeline than I was originally looking at, and now it felt like I didn’t have enough time to accomplish everything. I pushed through it anyways, knowing that October was just my first attempt. If I couldn’t finish everything in time, then I would still have time before the second try.\nRemember back when I mentioned that six year gap between getting the CCNP and starting on the CCIE? This is the big part where that helped me a ton. Going through a lot of the workbooks - I didn’t necessarily feel like anything was too crazy. Over the past 10+ years I’ve worked at a number of different companies and had the opportunity to play with a lot of networking gear. I had a great base of experience with most L2/L3 technologies, including quite a bit of practice with all the fun that BGP has to offer.\nOne of the other big things that I think helped was that not all of my prior experience was on Cisco equipment. Having to learn how to configure BGP, VRFs, or switching on multiple vendors forces you to think beyond the syntax. Every vendor implements things in their own unique way - and this helps you to get beyond just memorizing what commands to enter. Instead, you begin having to learn much more about the underlying technologies and how they operate - and understanding what you’re actually trying to accomplish. Then it’s just a matter of researching whatever syntax that specific vendor uses to implement that function.\nHaving that good base of knowledge and experience helped me burn through the practice labs fairly quickly. A lot of content felt very familiar, with maybe a few new variations of commands - or maybe a new option that I hadn’t previously used. Even some of the pieces that I hadn’t used much of before, like DMVPN or multicast, still seemed easy enough to grasp how it worked and learn the necessary syntax.\nThat being said - In a lot of ways it also gave me a false sense of security. Feeling like maybe I knew more than I realized and therefore maybe I was better prepared. Yet at the same time, knowing how difficult the lab is supposed to be - and constantly wondering what I could be missing.\nKeep going for the rest of my story:\nPart 1: Getting Started\nPart 2: Written Exam \u0026amp; Lab Prep\nPart 3: Lab Day\nPart 4: Lab Strategy \u0026amp; What\u0026rsquo;s Next\n","permalink":"https://0x2142.com/ccie-written-exam-lab-prep/","summary":"A short look at my experiences studying \u0026amp; taking the Cisco CCIE written exam","title":"CCIE: Written Exam \u0026 Lab Prep"},{"content":"Now that we\u0026rsquo;re firmly into 2020 - I finally decided it was about time to get this posted. I actually wrote most of this shortly after passing the exam, but it just sat unedited and collecting dust since them.\nIn about a month, most of the exams will be changing over to the new blueprints so I\u0026rsquo;m not sure how relevant any of this will be - but it\u0026rsquo;s still worth throwing out there, right?\nWhy CCIE? Why now? The two years I spent working on the CCIE dragged on for what seems like forever. Back in late 2017, I had hit a point where I felt like I wasn’t being challenged enough technically - and I missed the old days of excitement when I was studying/labbing for certifications exams. I had always wanted to go after the CCIE for a number of reasons, but it never made sense before. I had decided that maybe it was finally time to give it a shot.\nTo step back for just a moment - I originally began my career in networking by taking advantage of the Cisco Networking Academy program, which had been offered at my high school. It’s hard to believe I started that over 14 years ago - but it was likely the single most influential thing in getting me where I’m at in my career today. After two years of classes, I walked out in late 2007 with my CCNA and eager to begin working in networking.\nOver the next few years - I worked on a number of additional certifications. I always had fun going after certifications because they gave me a path to follow and a goal to achieve. They helped to make the process of learning a bit more fun. On the Cisco side of things, I worked on the CCDA, CCNA Voice (now retired), and my CCNA Security. Finally in 2011 I finished up my CCNP and had to figure out what was next. I was super interested in the CCIE - but there was no way my company would pay for it. For the time I shelved the idea - but I didn’t give up on it as a goal. Instead, I just continued to maintain \u0026amp; recertify my existing certs, and picked up the CCDP along the way.\nFast forward to late 2017. I had officially passed my 10 year anniversary on my CCNA. I was also feeling like I was hitting a wall in my technical abilities. I wanted to do something different and fun - and my first thought went back to pursuing a new certification because of how much I used to enjoy the process. I debated between a handful of certs, including CISSP, CCNP Security, CCDE, and CCIE R\u0026amp;S. After giving it some thought and talking to a few people, I decided it was finally time to tackle the CCIE and work toward one of my long-standing goals. That six year gap between CCNP and starting on the CCIE would come back to cause me a lot of problems, but also help me in a few ways I hadn’t expected - both of which I’ll talk about later.\nTime to Study On October 4th, 2017 - I ordered by first set of books and began studying for the CCIE Routing \u0026amp; Switching written exam.\nTo be absolutely honest, I had no plan going into this. Historically when I took certification exams my process was usually watching a set of training videos (usually CBT Nuggets), reading through the official cert guides a few times, picking up maybe another book or two, taking a bunch of notes, then a lot of labbing. It was never enough for me to just watch/read about the stuff - I needed to get hands on and break it to really learn. Usually by the time I had finished all of that, I would be feeling confident enough to go give the test a shot. I went into the CCIE written assuming this strategy would still probably work - and I was absolutely wrong.\nWhen I began working through the books and videos I had - I found that I wasn’t getting as excited about it as I had hoped. In fact, it just felt like so much of the content was just review of things I had learned years ago during CCNP studies. That long gap since my CCNP also left me reluctant to want to memorize all of the little details again. How many things had I studied for the CCNP that I never used in my actual job? I certainly didn’t want to waste the time trying to re-learn/re-memorize those things now\u0026hellip; But I knew I would need to if I wanted to pass the exam. This kinda killed my motivation in some ways - because I would end up having to force myself to try and retain information that I didn’t want to.\nStudying for the written was hard for me - and probably more than it should have been. Between the mixed motivation, I was also working through a lot of stress and nonsense in both my personal and work life. I would eventually work through these issues - but sometimes it would mean having to take a few weeks off from studying.Every time I took a break, I knew I needed to - yet it was still very demoralizing.\nI got some help toward my goal in June 2018: I had the opportunity to take a job working at Cisco as a Systems Engineer. In terms of working toward the CCIE, this was an absolute key step in getting there. I was finally working for a company that was willing to encourage and help me toward my goal. I was also surrounded by a ton of engineers and enthusiastic networking professionals who were there to support me. I got to spend time with other people who were working on certifications, and even network engineers at my customers who always wanted to ask how my studies were going. This helped a lot to get me back into being excited about the content - and brought a bit of motivation back.\nEven though I was spending a lot of time studying for the written exam - I never really felt like I was making true progress. I believe this was likely caused by the fact that the exam blueprint is so large and diverse. I never settled on a good method to reliably track how far I had progressed on all of the content. While I felt like I had learned a lot, I also perpetually felt like I was nowhere close to where I needed to be. I also have an old habit of waiting to schedule the exam until after I already already feel confident I have a good shot at passing. With the CCIE written, I felt like that level of confidence was never going to happen.\nKeep going for the rest of my story:\nPart 1: Getting Started\nPart 2: Written Exam \u0026amp; Lab Prep\nPart 3: Lab Day\nPart 4: Lab Strategy \u0026amp; What\u0026rsquo;s Next\n","permalink":"https://0x2142.com/story-time-how-i-started-working-toward-the-ccie/","summary":"Why \u0026amp; how I started studying for the CCIE a few years ago","title":"Story Time! How I Started Working Toward the CCIE"},{"content":" Update 2020 / 05 / 19 - I\u0026rsquo;ve added a video above that walks through the steps detailed in this blog post.\nIf you\u0026rsquo;re using the \u0026lsquo;Basic\u0026rsquo; Wireless setup, you may see an error when trying to apply the policy: \u0026ldquo;switch 1 dbm wireless Use of default ACL preauth v4 is not permitted\u0026rdquo;\nIf you come across this error, it\u0026rsquo;s a known bug (CSCvt18875) specific to only the \u0026lsquo;Basic\u0026rsquo; setup wizard (which is what I used in this post below). If so, check out the video above which walks through the \u0026lsquo;Advanced\u0026rsquo; setup and bypasses this error.\nI\u0026rsquo;ve been spending a bit of time over the past few weeks building up a wireless lab. Trying to get a good understanding of how the new Catalyst 9800 wireless controller works, and how it differs from some of the previous iterations.\nIn order to play around with the new controller, I decided to try to build a new configuration that mimics my current home wireless. Today I am using Ubiquiti APs, which come with their own free controller software. Most of my current config is fairly straightforward - a few SSIDs, two APs, and a guest network with captive portal. One of my SSIDs is dedicated to any IoT devices and is more restrictive than the other networks. This network uses both a pre-shared key for authentication as well as MAC-based filtering.\nIn this post - we\u0026rsquo;ll walk through how to set up a new SSID with client MAC filtering.\nNote: This was written using Catalyst 9800-CL version 16.12.1s. APs are configured in flexconnect with local authentication (no AAA, ISE, etc)\nWhile this post is not focused on in-depth WLAN config, we will start by quickly setting up a new network.\nOnce you get logged into the controller - we\u0026rsquo;ll click on the Wireless Setup icon in the upper right-hand side. Then drop down to the Basic option:\nThis takes us through a pretty quick and easy wizard to set up our new location \u0026amp; wireless networks. At first, we will have no locations configured - so we will click Add:\nWe\u0026rsquo;ll start off building our location by giving it a name and description. These are used for some naming of policy objects within the WLC, so make sure to use a name that makes sense.\nIn my case, I\u0026rsquo;m also going with a flexconnect deployment - so we\u0026rsquo;ll select that option and also provide the AP native VLAN.\nNext, we\u0026rsquo;ll click over to the Wireless Networks tab. This is where we will create our WLAN and apply the initial configuration. We don\u0026rsquo;t have any networks yet, so click Add\nThe two primary things we need to address are highlighted in red below. We need to create a WLAN and assign it to a VLAN. Let\u0026rsquo;s start with the WLAN by clicking Define new.\nOn the General tab, we\u0026rsquo;ll give this WLAN a profile name and a SSID.\nNext, we\u0026rsquo;ll hop on over to the Security tab, and focus on the Layer 2 sub-tab. The first thing I want to point out - is that at this point, we will not be enabling the MAC Filtering checkbox. We\u0026rsquo;ll need some additional config first, then come back to this later.\nScroll down to the bottom of the window and there will be some settings for your authentication. By default, 802.1x will be enabled. This post won\u0026rsquo;t cover how to setup/configure that. Instead, we\u0026rsquo;ll be deselecting 802.1x and checking the box for PSK. Then a text field will appear for us to enter the Pre-Shared Key.\nOnce we click Apply to Device, we\u0026rsquo;ll finish up by assigning our VLAN or VLAN Group. In this case, I have already created a VLAN named IoT. If you haven\u0026rsquo;t created a VLAN yet, you can do so by going to Configuration \u0026gt; Layer 2 \u0026gt; VLAN. Then add a new VLAN under the VLANtab.\nClick Add, then we\u0026rsquo;ll be back to the wizard and see the new WLAN we just created.\nIn order to finish up with the wizard, we just need to assign our Access Points. Click on the AP Provisioning tab. If you have already configured APs to join to this controller, you will see them on the left side under Available APs. Check which ones to apply this WLAN to, then click the arrow to move them to APs on this location.\nClick Apply, and that will finalize all of the configuration we just did - then drop us back to the Wireless Setup page.\nOkay - Now that we have that completed, we can move onto creating our MAC filtering policies.\nBack in the menu - Let\u0026rsquo;s go to Configuration \u0026gt; Security \u0026gt; AAA\nIn this section - we first need to create an Authorization policy. Select the AAA Method List tab, then Authorization, then Add to create the new policy.\nIn here we\u0026rsquo;ll specify a name, then select Type: network, and Group Type: local. Then go ahead and Apply to Device\nOnce we have that, let\u0026rsquo;s go over to the AAA Advanced tab, and click Add in the Attribute List Name section.\nHere we need to provide the SSID we want our MAC policy to apply to. Under Attribute Type, select SSID. Then under Attribute Value, select the target SSID that our policy will be tied to.\nDon\u0026rsquo;t forget to click Save on the attribute before clicking Apply to Device!\nTime to input our list of device MAC addresses! Drop into the Device Authentication section, and click Add - or upload a CSV file if you have one.\nInput the device MAC Addrees and select the Attribute List Name that we configured just a minute ago. Then Apply to Device\nAfter that - we should have our completed list of MAC addresses which will be permitted to join our wireless network. All we need to do is go back to our WLAN and enable MAC filtering.\nLet\u0026rsquo;s go to Configuration \u0026gt; Tags \u0026amp; Profiles \u0026gt; WLANs\nThis should give us the list of any configured WLANS - including the one we created earlier. Go ahead and click on it to edit.\nNext, we\u0026rsquo;ll jump straight to the Layer 2 section under the Security tab. Check the box for MAC Filtering and select the Authorization List we created from the drop down.\nAnd we\u0026rsquo;re done! Clients that want to join our newly created SSID will need the pre-shared key we configured, but they will also need to be manually added to our MAC address filter as well. While this isn\u0026rsquo;t a perfect security measure since MAC addresses can be easily spoofed - it does add an extra layer of protection to keep unauthorized devices from inadvertently being able to join this specific WLAN.\nIn the event that a client is NOT on the authorized list, you may see the following logs in a client debug:\n2019/10/25 22:26:12.327 {wncd_x_R0-0}{1}: \u0026amp;#91;client-orch-sm] \u0026amp;#91;22573]: (note): MAC: aaaa.aaaa.aaaa Association received. BSSID aaaa.aaaa.b34d, old BSSID 0000.0000.0000, WLAN SuperSecret, Slot 1 AP aaaa.aaaa.b340, AP_3802-1F01 2019/10/25 22:26:12.327 {wncd_x_R0-0}{1}: \u0026amp;#91;client-orch-state] \u0026amp;#91;22573]: (note): MAC: aaaa.aaaa.aaaa Client state transition: S_CO_INIT -\u0026gt; S_CO_ASSOCIATING 2019/10/25 22:26:12.328 {wncd_x_R0-0}{1}: \u0026amp;#91;client-orch-state] \u0026amp;#91;22573]: (note): MAC: aaaa.aaaa.aaaa Client state transition: S_CO_ASSOCIATING -\u0026gt; S_CO_MACAUTH_IN_PROGRESS 2019/10/25 22:26:12.328 {wncd_x_R0-0}{1}: \u0026amp;#91;client-auth] \u0026amp;#91;22573]: (note): MAC: aaaa.aaaa.aaaa MAB Authentication initiated. Policy VLAN 0, AAA override = 0, NAC = 0 2019/10/25 22:26:12.329 {wncd_x_R0-0}{1}: \u0026amp;#91;ewlc-infra-evq] \u0026amp;#91;22573]: (note): Authentication Success. Resolved Policy bitmap:11 for client aaaa.aaaa.aaaa 2019/10/25 22:26:12.329 {wncd_x_R0-0}{1}: \u0026amp;#91;ewlc-infra-evq] \u0026amp;#91;22573]: (ERR): SANET_AUTHC_FAILURE - AAA Server Down username ac37434a673a, audit session id 000000000000006B3E9D2A55, 2019/10/25 22:26:12.331 {wncd_x_R0-0}{1}: \u0026amp;#91;client-orch-state] \u0026amp;#91;22573]: (note): MAC: aaaa.aaaa.aaaa Client state transition: S_CO_MACAUTH_IN_PROGRESS -\u0026gt; S_CO_ASSOCIATING 2019/10/25 22:26:12.331 {wncd_x_R0-0}{1}: \u0026amp;#91;dot11] \u0026amp;#91;22573]: (ERR): MAC: aaaa.aaaa.aaaa Failed to assoc failure tr state entry. Incorrect validation status value :1 2019/10/25 22:26:12.331 {wncd_x_R0-0}{1}: \u0026amp;#91;dot11] \u0026amp;#91;22573]: (ERR): MAC: aaaa.aaaa.aaaa Dot11 update co assoc fail. Sent assoc failure to CO. delete reason: 9, CO_CLIENT_DELETE_REASON_MAB_FAILED 2019/10/25 22:26:12.331 {wncd_x_R0-0}{1}: \u0026amp;#91;client-orch-sm] \u0026amp;#91;22573]: (note): MAC: aaaa.aaaa.aaaa Client delete initiated. Reason: CO_CLIENT_DELETE_REASON_MAB_FAILED, fsm-state transition On the opposite side, when a client is successfully able to pass MAC authentication - the logs will show the following:\n2019/10/25 21:25:53.148 {wncd_x_R0-0}{1}: \u0026amp;#91;client-auth] \u0026amp;#91;22573]: (note): MAC: aaaa.aaaa.aaaa MAB Authentication initiated. Policy VLAN 0, AAA override = 0, NAC = 0 2019/10/25 21:25:53.150 {wncd_x_R0-0}{1}: \u0026amp;#91;ewlc-infra-evq] \u0026amp;#91;22573]: (note): Authentication Success. Resolved Policy bitmap:11 for client aaaa.aaaa.aaaa 2019/10/25 21:25:53.151 {wncd_x_R0-0}{1}: \u0026amp;#91;client-auth] \u0026amp;#91;22573]: (note): MAC: aaaa.aaaa.aaaa MAB Authentication success. 2019/10/25 21:25:53.151 {wncd_x_R0-0}{1}: \u0026amp;#91;client-orch-state] \u0026amp;#91;22573]: (note): MAC: aaaa.aaaa.aaaa Client state transition: S_CO_MACAUTH_IN_PROGRESS -\u0026gt; S_CO_ASSOCIATING 2019/10/25 21:30:08.626 {wncd_x_R0-0}{1}: \u0026amp;#91;client-orch-sm] \u0026amp;#91;22573]: (debug): MAC: aaaa.aaaa.aaaa Received Dot11 association request. Processing started,SSID: SuperSecret2, Policy profile: Home_WLANID_3, AP Name: AP_3802-3F01, Ap Mac Address: aaaa.aaaa.c9a0 BSSID MAC aaaa.aaaa.b34d wlan ID: 3RSSI: 0, SNR: 32 2019/10/25 21:30:08.626 {wncd_x_R0-0}{1}: \u0026amp;#91;client-orch-state] \u0026amp;#91;22573]: (note): MAC: aaaa.aaaa.aaaa Client state transition: S_CO_L2_AUTH_IN_PROGRESS -\u0026gt; S_CO_L2_AUTH_IN_PROGRESS 2019/10/25 21:30:08.626 {wncd_x_R0-0}{1}: \u0026amp;#91;dot11] \u0026amp;#91;22573]: (info): MAC: aaaa.aaaa.aaaa DOT11 state transition: S_DOT11_ASSOCIATED -\u0026gt; S_DOT11_MAB_PENDING 2019/10/25 21:30:08.626 {wncd_x_R0-0}{1}: \u0026amp;#91;client-orch-state] \u0026amp;#91;22573]: (note): MAC: aaaa.aaaa.aaaa Client state transition: S_CO_L2_AUTH_IN_PROGRESS -\u0026gt; S_CO_MACAUTH_IN_PROGRESS 2019/10/25 21:30:08.626 {wncd_x_R0-0}{1}: \u0026amp;#91;client-auth] \u0026amp;#91;22573]: (info): MAC: aaaa.aaaa.aaaa Client auth-interface state transition: S_AUTHIF_ADD_MOBILE_ACK_WAIT_KM -\u0026gt; S_AUTHIF_MAB_AUTH_DONE 2019/10/25 21:30:08.626 {wncd_x_R0-0}{1}: \u0026amp;#91;client-orch-sm] \u0026amp;#91;22573]: (debug): MAC: aaaa.aaaa.aaaa Processing MAB authentication result status: 0, CO_AUTH_STATUS_SUCCESS 2019/10/25 21:30:08.626 {wncd_x_R0-0}{1}: \u0026amp;#91;client-orch-state] \u0026amp;#91;22573]: (note): MAC: aaaa.aaaa.aaaa Client state transition: S_CO_MACAUTH_IN_PROGRESS -\u0026gt; S_CO_ASSOCIATING 2019/10/25 21:30:08.626 {wncd_x_R0-0}{1}: \u0026amp;#91;dot11] \u0026amp;#91;22573]: (debug): MAC: aaaa.aaaa.aaaa dot11 send association response. Sending association response with resp_status_code: 0 2019/10/25 21:30:08.627 {wncd_x_R0-0}{1}: \u0026amp;#91;dot11] \u0026amp;#91;22573]: (info): MAC: aaaa.aaaa.aaaa dot11 send association response. Sending assoc response of length: 137 with resp_status_code: 0, DOT11_STATUS: DOT11_STATUS_SUCCESS 2019/10/25 21:30:08.627 {wncd_x_R0-0}{1}: \u0026amp;#91;dot11] \u0026amp;#91;22573]: (note): MAC: aaaa.aaaa.aaaa Association success. AID 1, Roaming = True, WGB = False, 11r = False, 11w = False 2019/10/25 21:30:08.627 {wncd_x_R0-0}{1}: \u0026amp;#91;dot11] \u0026amp;#91;22573]: (info): MAC: aaaa.aaaa.aaaa DOT11 state transition: S_DOT11_MAB_PENDING -\u0026gt; S_DOT11_ASSOCIATED ","permalink":"https://0x2142.com/how-to-catalyst-9800-mac-filtering/","summary":"A tutorial on configuring MAC address filtering on a Cisco 9800 WLC","title":"How to: Catalyst 9800 MAC Filtering"},{"content":"Note: I may receive commissions for purchases made through links in this post. This is to help support my blog and does not have any impact on my recommendations.\nOver the past year and a half (and still ongoing), I\u0026rsquo;ve burned through a handful of books and other resources while working toward the CCIE. I know quite a few people have already dumped out their recommended reading lists - but I figure it\u0026rsquo;s still worth writing out what has been working for me 🙂\nI went ahead and studied for the written exam independently of the lab - so I\u0026rsquo;ll be providing the resources that I used for each separately.\nAlso note: This page will be updated as I continue working toward the certification. I\u0026rsquo;m not done yet!\nWritten Exam Resources Books:\nCCIE Routing and Switching v5.0 Official Cert Guide Routing TCP/IP, Volume I \u0026amp; Volume II CCIE/CCDE Evolving Technologies Study Guide Videos:\nCBT Nuggets - CCIE Routing \u0026amp; Switching Lab Exam Resources Books:\nYour CCIE Lab Success Strategy: The Non-Technical Guidebook IPv6 Fundamentals: A Straightforward Approach to Understanding IPv6 Cisco QOS Exam Certification Guide (IP Telephony Self Study) IP Multicast, Volume I: Cisco IP Multicast Networking MPLS and VPN Architectures More soon! Videos:\nINE CCIE R\u0026amp;S playlist Workbooks:\nCCIE Routing and Switching 5.1 Foundations: Bridging the Gap Between CCNP and CCIE INE CCIE Routing \u0026amp; Switching v5 workbook More coming soon 🙂 Hope these links help! What has worked for you guys? Add on in the comments below.\n","permalink":"https://0x2142.com/ccie-rs-study-resources/","summary":"A short list of some of the CCIE study resources I\u0026rsquo;ve been using","title":"CCIE R\u0026S Study Resources"},{"content":"A few weeks ago I got my hands on a Cisco UCS C220 M4 server - which I\u0026rsquo;ve set up in a lab to install and test Cisco\u0026rsquo;s Network Function Virtualization Infrastructure Software (NFVIS). I really wanted to get this running on an Enterprise Network Compute System (ENCS) box, but you can\u0026rsquo;t always get everything what you want :). The UCS machine is also on the list of supported platforms, so we\u0026rsquo;ll use that - but everything here should apply similarly to the ENCS platform.\nWhat is NFVIS? NFVIS is an operating system developed by Cisco which is intended to be deployed at branch office locations - and allow for quick deployment of network services in lightweight VMs. For example, we might want to reduce cost and hardware footprint by deploying a single ENCS machine, then deploy our typical branch services on top of that (DNS, Firewalls, SDWAN, etc). Under the hood, NFVIS is built on top of CentOS and KVM.\nIn the image below, we have an ENCS unit that is running ISRv, FTDv, and a vEdge Cloud. NFVIS has the ability to build out traffic flows for service chaining. In this particular setup, we could have all branch traffic receive a default route up to our ISRv. The ISRv forwards traffic to a Firepower VM (FTDv) which performs some traffic inspection before passing everything up to the vEdge Cloud.\nWe\u0026rsquo;ll be coming back to this diagram later to see how we can build out this flow of services. For now, let\u0026rsquo;s dive into how we can get NFVIS up and running.\nInstalling NFVIS Lucky for us - the installation of NFVIS is fairly straightforward!\nCreate a bootable USB - or mount the installation ISO via CIMC Upon boot, select \u0026ldquo;Install Cisco NFV Infrastructure Software\u0026rdquo;: Wait. A while. Install time can vary depending on your hardware. Once completed, log into the CLI: Default login = admin/Admin123# You\u0026rsquo;ll be prompted to change the default admin password immediately: Install completed! Now let\u0026rsquo;s look at some of our base configuration..\nInitial Configuration By default the NFVIS install will have a LAN and WAN bridge (lan-br and wan-br, respectively). The LAN config will be set up with a static IP of 192.168.1.1/24, and the WAN will be set for DHCP. We can check the current network settings by running the show system settingscommand:\nIn this case, my WAN interface is able to get an IP via DHCP. We\u0026rsquo;re likely going to want to change this to a static IP address - which we can do from the CLI or web interface. Let\u0026rsquo;s start by trying this from the CLI:\nconfig t system settings wan ip address \u0026lt;ip addr\u0026gt; \u0026lt;netmask\u0026gt; system settings default-gw \u0026lt;gateway addr\u0026gt; system settings hostname \u0026lt;hostname\u0026gt; Changes can then be applied using the commit command\nWe can verify these settings by repeating the show system settings command we used earlier.\nLet\u0026rsquo;s go ahead and log into the web interface to see what the network configuration looks like there:\nIf we know our WAN or LAN IP from earlier, we can just pop that in our web browser. Go ahead and log in using the new admin credentials we just created: We\u0026rsquo;ll be taken to the primary NFVIS dashboard, which will currently show no active VMs deployed: In the left-hand menu, expand Hostthen click on Settings: Here we can see that we already configured our IP Addressing/hostname - but we could use the Edit button at the bottom to change any of these values. For example - I\u0026rsquo;m going to go ahead and select Static for both the Management (LAN) and WAN IP addresses.\nAnother quick tip - if we need to modify which physical network adapters are tied to an internal network bridge, we can find that under VM Life Cycle \u0026gt; Networking:\nThat\u0026rsquo;s all for this time. In the next post, we\u0026rsquo;ll take a look at how to package VM images and deploy our service chain.\n","permalink":"https://0x2142.com/getting-started-with-cisco-nfvis/","summary":"A quick look at how to spin up NFVIS on a Cisco ENCS","title":"Getting Started with Cisco NFVIS"},{"content":"Just over a year ago I posted that I was starting work toward the CCIE R\u0026amp;S exam. My original goal was to take the written exam by June of this year - so what progress have I made, and where am I at now?\nWell I ended up missing the original goal I had set. It\u0026rsquo;s now October and I haven\u0026rsquo;t even scheduled the written exam yet. I haven\u0026rsquo;t given up though - and my current plan is to shoot for attempting the written before the end of 2018.\nThat being said - The past year has been an interesting experience, and I want to talk about some of the things I\u0026rsquo;ve dealt with that caused me to completely miss my goal.\nHaving the right support is important This was one of the biggest things that has impacted my progress. My first IT job was working for a local consulting company who was a Cisco Partner - so they highly valued certifications and continued education. They offered a ton of free training options to employees, and occasionally offered incentives to achieve certain levels of certification. This is where I passed the vast majority of certifications that I still hold today and I felt like I had a lot of fun working toward them. I was constantly surrounded by other people who were also working on certifications or just general training. Getting time to study or lab new stuff never felt like work - but instead it was very exciting and I really enjoyed it (More about this later).\nAfter I left that job, I worked for a couple of companies who didn\u0026rsquo;t place much value on certifications. Unfortunately this shift made those environments counter-productive toward certification studies. There wasn\u0026rsquo;t much interest/support for what I was working on, and it was very difficult to get study time or training money. Once in a while I was able to get reimbursement for a passed exam, but even that wasn\u0026rsquo;t the easiest thing to come by.\nAll of this meant that getting started in the CCIE studies ended up being more difficult than I wanted it to be. It might seem stupid - but it was hard to get myself motivated when it seemed like I had no support behind me. I tried pushing through anyways, but eventually it became clear that I wasn\u0026rsquo;t enjoying it. I still kept making progress, but extremely slow progress - maybe reading only a few pages every week or two when I thought about it. This caused me to start doubting myself a bit, and begin wondering if maybe I just lost that early-career excitement.\nLucky for me, this changed in a big way in June of this year. I left my prior job to begin a new adventure working for Cisco. I was expecting a bit more of a supportive environment, since it\u0026rsquo;s a Cisco certification - but I was very surprised with what I actually got. A number of people I met asked if I had a CCIE cert or if I had any intention of getting one - and they encouraged me to go for it. Once I said I was working on it, most people were more than willing to talk through their experiences and offer support or advice. My manager has also offered to help me with time and materials that I might need. Needless to say, I\u0026rsquo;m getting excited about getting back into learning and labbing.\nThere is a lot of content The amount of content to be consumed for the exam is enormous. As I\u0026rsquo;ve talked to a few people recently, I keep comparing the CCIE against what I used to do for a CCNA/CCNP level exam. For example, the CCNP certification is so cleanly broken into three parts - so each exam has a somewhat smaller focus area to study. The SWITCH exam only focuses on switching and layer 2 technologies - and the overall exam blueprint is a manageable list. When I studied for the SWITCH test years ago, the amount of time investment was comparatively small. I spent time watching training videos, reading through the cert guide once or twice, and then some amount of labbing along the way - and that was enough to do well on the exam. At the time I was also allotted time within work hours to study, since I was still working for the consulting company who valued certifications. This all made it feel significantly quicker and easier to work toward than what I\u0026rsquo;m facing today with the CCIE.\nComparing that to what the CCIE Routing \u0026amp; Switching exam covers in a single test - it feels a bit crazy. There is such a wide base of topics covered that it\u0026rsquo;s easy to sit back and wonder how you\u0026rsquo;ll ever manage it. And not only do you need to know fundamental routing and switching concepts/protocols/etc, but you need to have a much deeper understanding of them than before. Now while that\u0026rsquo;s the part that I really enjoy, it does become a bit overwhelming to keep track of. I\u0026rsquo;m finding it easy to feel discouraged by the lack of progress I\u0026rsquo;m making - especially given how much easier prior exam study was. I am actually reading a lot - but it\u0026rsquo;s still just a small percentage of the overall material. I guess it just takes a bit of a mental refocusing to account for the scale of things. After all, this isn\u0026rsquo;t something you can accomplish overnight - so why expect that you can?\nAnother thing I\u0026rsquo;m still struggling with is separating which content is new vs review. It\u0026rsquo;s been easy for me to gloss over sections on some routing protocols or pretty much anything layer 2 - because I feel too comfortable with them. There is a lot of content that feels like just reviewing things I already know or I\u0026rsquo;ve studied before\u0026hellip; but hidden away are still bits of information that are relevant. I know I need to get better at forcing myself to read through everything, even if it feels like the 500th time I\u0026rsquo;ve read about how to make BGP work. Even if I do feel like I know something fairly well, it\u0026rsquo;s worth reviewing to further solidify knowledge of those concepts.\nSometimes life happens Going for a CCIE-level certification isn\u0026rsquo;t necessarily something that you can just work on once in a while. It takes a fairly decent amount of dedication, time, and motivation. It consumes quite a bit of your free time - which can make things difficult when suddenly life decides to get in your way.\nTo start with, I probably shouldn\u0026rsquo;t have started studying when I did. I might say it was a bad time, but is there ever really a good time? Probably not - but I think I started chasing the CCIE for bad reasons. Don\u0026rsquo;t get me wrong, getting my numbers has been a goal of mine for a long time. However, at this time last year I was having a bit of a rough patch - finding myself feeling demotivated and struggling to keep interested in technology. I knew I used to love studying new stuff for certification tests, and I knew I wanted the CCIE eventually - so I figured it might be a way to kick-start myself back into getting excited again.\nHow did that go? Well months of making myself miserable for not making enough progress - and constantly feeling guilty for not studying enough. Unfortunately, I wasn\u0026rsquo;t fixing the root causes of my problems - I was just trying to distract myself with something else. Not my best idea, and something I can look back on now and easily identify - but at the time it was much more difficult. There were a lot of things playing against me. I was working too much, which meant that every night I came home and wanted to do absolutely nothing. I was on-call often enough that my sleep schedule suffered and some weeks I was only averaging 4-5 hours a night. I felt restricted in my ability to change or improve the situation. These and other things amounted to me no longer feeling interested in what I was doing every day, and even harder to get excited about studying something new. A few people I was talking to at the time suggested that I might be dealing with complete burnout - but I was stupid enough to think \u0026rsquo;nah, not me'.\nWhile this whole thing is written in the context of studying for the CCIE exam, that\u0026rsquo;s certainly not the only thing that suffered as a result of my lack of motivation. I stopped writing much here, for one. In fact I pretty much quit all of my personal hobbies and interests. Any time I had an idea for something, I couldn\u0026rsquo;t find the effort to even try.\nDid everything magically change when I got the new job this year? Not at all - but it was a start in the right direction. Unfortunately, recovering from that state of life takes time. So I did exactly that: I gave myself a while to get settled into the new job and start working on getting my life and sleep back to a good point. This included forcing myself to give up on the CCIE studies for a few months. I still spent a long time feeling like I wasn\u0026rsquo;t getting better, or maybe I was going to be permanently disinterested in technology. It was sometimes a bit terrifying, because when I started in IT I felt so excited about everything - and I just wanted to get back to feeling like that.\nAfter a few months of giving myself time, I can finally say that I think I\u0026rsquo;m making some decent progress. I\u0026rsquo;m starting to get excited when I\u0026rsquo;m out talking to my customers about their networks. I\u0026rsquo;m able to reliably sit down several times a week and read some of my CCIE books. It\u0026rsquo;s not quite back to the way things used to be, but I\u0026rsquo;m at least finally hopeful that I\u0026rsquo;m headed in the right direction.\nSometimes life is going to get in your way, whether for CCIE studies or otherwise. I guess at some point you need to know when to give up and come back to it when the time is right. Sometimes you just need time.\nThis post ended up going in a direction that I wasn\u0026rsquo;t entirely intending on - but I\u0026rsquo;m going to leave it. Hopefully this might help out someone else out there who has been struggling with similar problems. And if you are - feel free to reach out. I would love to talk to you about it.\nAs I said in the intro - I am still serious about going for the CCIE. I still need some time to catch up, but I feel that I\u0026rsquo;m making good progress now. I\u0026rsquo;m currently looking at trying to schedule an attempt at the written within the next few months. I\u0026rsquo;ll keep you guys updated!\n","permalink":"https://0x2142.com/ccie-progress-update/","summary":"It\u0026rsquo;s been a while - let\u0026rsquo;s talk about where I\u0026rsquo;m at with my studies","title":"CCIE Progress Update"},{"content":"With the release of Android 9.0 recently, Google enabled a big change for how user\u0026rsquo;s can protect their privacy: DNS over TLS. While the concept isn\u0026rsquo;t brand new, it also hasn\u0026rsquo;t exactly exploded in usage either. This is going to start changing as Google rolls out the new version of their operating system that not only comes with the feature, but also enables it by default.\nHow does this actually protect user privacy? Best described in the intro to RFC 7858, DNS over TLS \u0026ldquo;eliminates opportunities for eavesdropping and on-path tampering with DNS queries\u0026rdquo;. Using the new standard, DNS queries of a client can be encrypted using TLS and tunneled directly to the DNS server. The intent is that monitoring of user activity becomes more difficult, since their DNS requests are no longer sent across the network in plain text.\nIt seems that DNS security has become a point of focus recently. Over the past year or so, we have seen two new big companies join the public DNS space: Quad9 (backed by IBM) and CloudFlare. Both companies advertise that they are focused on security and privacy of the individual user. They offer features like: blocking of malicious domains, DNS over TLS support, and limited historical logging of user requests. CloudFlare has even already posted steps for enabling their DNS over TLS service with the new version of Android. For the end user, this is great - free services that offer better security and increased privacy. However, this can impact how businesses protect their users.\nA lot of organizations use web filtering as an important step toward securing client traffic - which can be accomplished through DNS filtering and/or web proxies. These methods might be used for compliance reasons, malware/threat detection, or even just managers who want to monitor productivity. However, there are a few big changes happening in user security/privacy right now that begin to make that more difficult: Increased focus on DNS privacy and the recent release of TLS 1.3.\nYears ago I used to work for a local government organization where security was a high priority for IT operations. Web access was extremely limited and configured to whitelist only for known sites that were business related. If you wanted general internet access, you had to log into one of two segmented computers that were dedicated to that purpose. Even still, our IT security manager had concerns on the potential for threats and/or data exfiltration via HTTPS-enabled websites. One of my projects was to tackle this problem by implementing SSL decryption on our web filtering platform. At the time, implementing a web proxy as a man-in-the-middle wasn\u0026rsquo;t very complicated. Generate a CA cert for the proxy, distribute that cert to clients, then enable SSL decryption. Sure, there were bumps and hurdles - but the overall process achieved the result we were looking for.\nThe problem today is that newer standards and features (like TLS1.3, HSTS, or certificate pinning) are adding enhanced privacy features - which make that user data more difficult to reliably decrypt. That\u0026rsquo;s good right? Well, maybe - but only for the user. As an organization trying to secure your systems and private information, these measures are making life a lot more difficult. Even as a network admin, it\u0026rsquo;s become more difficult to troubleshoot web applications via packet captures. The use of ephemeral keys for TLS connections means that you can\u0026rsquo;t decrypt the session even if you have the server-side private keys.\nNow the addition of DNS over TLS give us more of the same problem. Even if you couldn\u0026rsquo;t look into an encrypted web session, maybe you could gather what the destination is by reading the DNS requests. Instead, users now have the ability to encrypt their DNS queries, encrypt their web sessions, and make the life of a security admin a nightmare.\nFrom the perspective of a corporate security team, the future is going to be more challenging (and probably for a lot more reasons than just this). However, I think that at some point we will have to reach a better balance of user privacy while still providing adequate security coverage. For some that might mean installing feature-heavy agents on every client device to inspect everything before it leaves the device. Or it might mean trying to rely on statistical, analytical, and behavioral data to detect patterns and anomalies (something like what ETA is trying to accomplish) rather than just tearing apart the individual user sessions.\nWhatever the solution ends up being, it should be an interesting area to watch develop. In the meantime, let\u0026rsquo;s just agree not downgrade user security or privacy just to shove our security tools in. We\u0026rsquo;re all users of some service or another, and we all have some expectation of privacy. Is it worth reducing security and increasing risk just to see if your employees are using their web access responsibly? Probably not. Instead it\u0026rsquo;s time to look for new ways to solve this problem, and find ways to stay secure while still ensuring user privacy.\n","permalink":"https://0x2142.com/the-future-web-privacy-or-security-you-only-get-one/","summary":"Sometimes it feels like technology advancement conflicts with trying to encourage security","title":"The Future Web: Privacy or Security (You Only Get One)"},{"content":"Note: I may receive commissions for purchases made through links in this post. This is to help support my blog and does not have any impact on my recommendations.\nThe future is APIs! SD-EVERYTHING! Automation! Orchestration! Artificial Intelligence and Machine Learning! Sound familiar? It\u0026rsquo;s all part of the messaging going around in just about everything IT-related. With as much as you keep hearing about it, you might think that it\u0026rsquo;s all anyone is doing anymore. Yet it still just seems like not a whole lot of people are really getting into it in my area. Every vendor event I\u0026rsquo;ve gone to this year has asked attendees the same questions: \u0026ldquo;How many of you are leveraging the APIs in your network hardware/software?\u0026rdquo;. And every time the same answer - maybe two or three people in a room of 40 raise their hands.\nSo where is the problem? Is all of this just marketing fluff or am I just talking to the wrong people?\nLet\u0026rsquo;s think about this from a typical network admin\u0026rsquo;s perspective. Shifting from traditional CLI to automation and APIs can seem difficult or overwhelming. Let\u0026rsquo;s say I want to automate a new VLAN deployment. Oh, you\u0026rsquo;re telling me I need to stop and learn vendor APIs… but before that I need to understand how to write scripts. But I\u0026rsquo;ve never even programmed something before. There are dozens of languages - how do I pick one? How much fundamental programming knowledge do I really need to have before starting? I don\u0026rsquo;t want to be a developer!\nOkay, okay - just stop there for a second. No one is asking you to drop networking and write code for a living. The end goal of all this programmability stuff isn\u0026rsquo;t to turn networkers into developers - It\u0026rsquo;s to enable network/systems admins to be more efficient at their jobs. Why copy/paste the same config change to 100+ devices, if you can mass-deploy the change via an API? That\u0026rsquo;s a lot of time savings that could be used toward educating yourself on new products, planning other projects, or thinking about your ideal network design.\nI\u0026rsquo;ve heard a lot of the same things over the past few years:\n\u0026ldquo;Programming is difficult\u0026rdquo; or \u0026ldquo;I don\u0026rsquo;t know where to start\u0026rdquo;\nTry learning Python. It\u0026rsquo;s simple to get started and you can build from there.\n\u0026ldquo;I don\u0026rsquo;t know what an API is or how to use it\u0026rdquo;\nDon\u0026rsquo;t worry about that yet - start with learning the basics and APIs will make sense later.\n\u0026ldquo;I\u0026rsquo;m not a developer\u0026rdquo;\nNo one is asking you to be one! But learning the basics of scripting and automation gives you a whole new toolset to solve problems.\nFor me personally - I would never want to be a developer. I can\u0026rsquo;t stand the thought of coming into work every day and just writing code. Some people might enjoy that, but for me it doesn\u0026rsquo;t sound like fun. However - I enjoy writing scripts to solve problems, especially when it ends up making my job easier. I think that\u0026rsquo;s the part where some people tend to get stuck though. A lot of automation sounds like I need to be able to develop a huge 10,000+ line application to pull data from 15 sources and aggregate it to make intelligent network changes. Ehhh\u0026hellip; Nope, not really. But what about just a quick script that runs every 5 minutes to check an interface statistic, and email you when a particular threshold is exceeded? Realistically that could be done in less than 50-100 lines of a script and maybe 30 minutes worth of work.\nStill not interested? That\u0026rsquo;s okay too. Traditional networking isn\u0026rsquo;t going away any time soon, and over time the vendors will write all of that automation for you. They will package it up in a pretty GUI and sell it off to companies that want it. In fact, this has already happening and has been for quite some time. This isn\u0026rsquo;t a bad thing - vendors need to make money, and not all companies will have the time or skilled resources to automate all the things. However, a network admin who can write their own scripts/automation won\u0026rsquo;t be exclusively tied to a vendor to help them - and instead they will be empowered to solve more problems themselves.\nWhere do you get started? I already wrote a bit earlier this year on a few resources for learning Python - which you can find here. I also wanted to point out some other great resources that are a bit more specific to using those skills for network automation:\nPython For Network Engineers - Don\u0026rsquo;t know anything about Python yet? Start here! This is a free course provided by Kirk Byers for anyone who is interested in using Python for network automation. Once a week you\u0026rsquo;ll get an email with all the great free content, but it will be up to you to spend time going through it. Go sign up, and set aside an hour or two each week to practice.\nCisco DevNet - There is a ton of great content here. While DevNet does offer some tutorials on basic Python fundamentals, the real value here is examples on how to use some network APIs (NX-OS, Meraki, UCS, etc). Also - one of the best parts about DevNet is the sandboxes they offer. Want to write scripts against the FirePower Management Center, but you don\u0026rsquo;t have one to test with? Well with DevNet you can get access to one! Get familiar with your Python basics, then come here to see where you can start using those skills with your existing infrastructure.\nNetwork Programmability and Automation - This is a fantastic book. Not free, but it is well worth the ~$30. Once you have a good handle on how to write some basic network automation with Python, I highly recommend picking this up. While Python is covered here, the book does a great job of introducing you to all of the other toolsets available. Curious about how Linux or Ansible fit into network automation? You can find out here - and learn about APIs and source control systems too!\nSo - What are you waiting for? Go get started, and see what you can accomplish. Learn the basics - and keep an open mind for opportunities to use those skills.\nHave suggestions on where else to learn? Comment below!\n","permalink":"https://0x2142.com/where-is-all-the-automation/","summary":"Learning Python for network automation doesn\u0026rsquo;t have to be scary. Let\u0026rsquo;s look at how to get started","title":"Where is all the Automation?"},{"content":"Who we are Our website address is: https://0x2142.com/.\nWhat personal data we collect and why we collect it Comments This website uses a lightweight, self-hosted commenting service where all data is stored locally. No comment data is collected by third parties.\nThe following data is optionally collected (should you provide it):\nName Email address Website Comments may be completely anonymous, with none of the above information provided. Source IP subnet information (/24 - example: 10.10.10.x) is collected only for spam-prevention.\nComments are manually approved before appearing on the website, and therefore are not sent to any third party services.\nCookies If you leave a comment on our site you may opt-in to saving your name, email address and website in cookies. These are for your convenience so that you do not have to fill in your details again when you leave another comment.\nEmbedded content from other websites Articles on this site may include embedded content (e.g. videos, images, articles, etc.). Embedded content from other websites behaves in the exact same way as if the visitor has visited the other website.\nThese websites may collect data about you, use cookies, embed additional third-party tracking, and monitor your interaction with that embedded content, including tracing your interaction with the embedded content if you have an account and are logged in to that website.\nAnalytics This website makes use of Goat Counter for analytics to track visitors and visitor interaction. Goat Counter is a privacy-focused, open source analytics service which does not use cookies. For additional information about what data is collected, please see the Goat Counter Privacy Policy. Analytics data from 0x2142.com are also publicly viewable here.\nAdvertisements This website no longer displays advertisements.\nWho we share your data with Analytics data, comment data, and email subscription data is only used internally to improve this website. This data is not shared outside of the services used to collect the data.\nHow long we retain your data If you leave a comment, the comment and its metadata are retained indefinitely. This is so we can recognize and approve any follow-up comments automatically instead of holding them in a moderation queue.\nWhat rights you have over your data If you have left comments on this website, you can request to receive an exported file of the personal data we hold about you, including any data you have provided to us. You can also request that we erase any personal data we hold about you. This does not include any data we are obliged to keep for administrative, legal, or security purposes.\nInformation Requests For any inquiries regarding this privacy policy, or for requests to export/delete data – please contact privacy (at) 0x2142 (dot) com.\n","permalink":"https://0x2142.com/privacy-policy/","summary":"\u003ch2 id=\"who-we-are\"\u003eWho we are\u003c/h2\u003e\n\u003cp\u003eOur website address is: \u003ca href=\"https://0x2142.com/\"\u003ehttps://0x2142.com/\u003c/a\u003e.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"what-personal-data-we-collect-and-why-we-collect-it\"\u003eWhat personal data we collect and why we collect it\u003c/h2\u003e\n\u003ch3 id=\"comments\"\u003eComments\u003c/h3\u003e\n\u003cp\u003eThis website uses a lightweight, self-hosted commenting service where all data is stored locally. No comment data is collected by third parties.\u003c/p\u003e\n\u003cp\u003eThe following data is optionally collected (should you provide it):\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eName\u003c/li\u003e\n\u003cli\u003eEmail address\u003c/li\u003e\n\u003cli\u003eWebsite\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eComments may be completely anonymous, with none of the above information provided. Source IP subnet information (/24 - example: 10.10.10.x) is collected only for spam-prevention.\u003c/p\u003e","title":"Privacy Policy"},{"content":"Note: I may receive commissions for purchases made through links in this post. This is to help support my blog and does not have any impact on my recommendations.\nBack when I bought my Synology NAS in 2012, I picked up 4 Seagate 3TB drives. Yes, they\u0026rsquo;re the same model (ST3000DM001) that BackBlaze reported about in 2015 since these drives have an unusually high failure rate. I even managed to have one drive fail within the first few weeks, but I didn\u0026rsquo;t think much of it - just submitted an RMA. Since then, I haven\u0026rsquo;t worried too much about it and those drives have been running for over six years straight with no issues. Well, until recently anyways…\nHmm. Two drives in my NAS are starting to complain... They\u0026rsquo;re also 6 1/2 years old\u0026hellip;\n\u0026hellip; But replacing them costs money.\n— Matt (@0x2142) April 27, 2018\nAlmost a month ago, I got a couple of email alerts from my Synology complaining about bad sectors. I hopped onto the device to check it out, and decided to run some of the extended SMART tests against the array. I already have these tests scheduled to run regularly, but I figured it can\u0026rsquo;t hurt to run them just in case. As luck would have it, Disk 1 of my array failed it\u0026rsquo;s SMART test within minutes.\nDear user, Disk 1 on DS918+ has degraded severely and is in failing status. Please make sure that your data has been backed up and then replace this disk. Additional disk information: Brand: Seagate Model: ST3000DM001-9YN166 Capacity: 2.7 TB Serial number: XXXXXXXX Firmware: CC9C S.M.A.R.T. status: Failing IronWolf Health Management: - Bad sector count: 128 Disk reconnection count: 0 Disk re-identification count: 0 SSD estimated lifespan: - The other three disks completed their tests with no reported issues. Disk 3 did worry me a bit though, because it had a rather high bad sector count (~12,000). I figured I would worry about Disk 1 for now, and once it was re-built I would start slowly working my way through the array. The drives were six years old anyways and I didn\u0026rsquo;t expect them to last forever. At the same time, I really didn\u0026rsquo;t want to dump the money to buy all new drives just yet. After doing my own research and talking to a few other people I know, I opted to try out the Western Digital Red drives. These drives are made for NAS systems and include some features that standard consumer drives don\u0026rsquo;t. My array was also around 65% full, so I figured now would be the best time to start expanding the array. I bought one of the 4TB WD40EFRX drives and had it shipped as quickly as possible. Unlike my previous Synology DS411, the 918+ supports the ability to hot-swap drives (the DS411 required disassembling the chassis). Once my new drive showed up, I pulled out disk 1 and put in the new drive. Logged into the Synology DSM interface and told it to start rebuilding the new drive. Simple enough, right?\nWell, as luck would have it, I only got a few hours into the rebuild before I got this wonderful email:\nDear user, Volume 1 has crashed. The system may not boot up. At this point I was at work and wondering what the state of my NAS was. Did another drive die during rebuild? Have I lost data? Will I be able to recover any of my stuff? I threw in a ticket to Synology support immediately so I could get their input on this. I got back both good and bad news. The bad news? The volume was not going be repairable, and any attempt to repair disks would fail. However, the good news was that my data was fine, but accessible in a read-only state.\nI still had my DS411 lying around, so I decided to make the investment in all new drives. I picked up three additional Red drives and loaded all four them into my DS411. Soon enough I had created a new volume/RAID array and I was ready to copy my data over from the DS918+.\nThe big question was how exactly to replicate the data. I knew Synology had a few options for NAS-to-NAS syncing/backups, but I hadn\u0026rsquo;t used them before.\nThe first option was to use Synology\u0026rsquo;s HyperBackup application. Unfortunately, this required two things I don\u0026rsquo;t have. First, it would require me to install Hyperbackup, which wouldn\u0026rsquo;t work since the volume was read-only (a state which also appeared to kill all non-critical packages). Second, it would require me to have at least double the space on my backup device - Enough to store a full-NAS backup, along with enough to restore that entire backup.\nThe second option for copying the data was using the Shared Folder Sync - which is the method I ended up using. This allowed me to set up a Synology-managed rsync job between the two NAS devices. I did need to make a configuration change to create the rsync tasks, which I couldn\u0026rsquo;t do on the crashed volume. However, I took a chance and found out that when I reboot the crashed NAS, the volume would be writable until the volume crashed again. This gave me about ten minutes after startup to quickly create the replication job.\nUnfortunately my older DS411 could barely handle the CPU requirements needed to copy the data. It did work, but it took a full week to copy ~5.3TB over a gigabit network. Once that copy was finished, I did a quick double-check to make sure I had everything. Then I went ahead and swapped the new drives into my DS918+ following the same steps I used during my original migration.\nOverall I think I got extremely lucky here. The only thing I ended up losing was an iSCSI LUN with a few VMs, but I was able to retain all of my critical data. While I do have all of my NAS data backed up to a cloud service, I wasn\u0026rsquo;t really happy with the idea of having to download 5.3TB over the Internet to restore my data. Since this event, I\u0026rsquo;ve configured some additional alerting/monitoring of the drives in my DS918+. Next time I see signs of drive trouble I\u0026rsquo;ll probably just replace it immediately without thinking twice about it. Hopefully this helps out anyone else who runs into this issue! Let me know in the comments if you have any questions\n","permalink":"https://0x2142.com/synology-volume-crash/","summary":"What happens when you delay replacing a bad drive \u0026amp; experience multiple RAID failures?","title":"Synology Volume Crash"},{"content":"This post has been on my mind for a while now. I\u0026rsquo;ve worked as a network admin for long enough, and opened more technical support cases with vendors than I want to think about. Over the years I\u0026rsquo;ve developed my own process for how I handle those support cases in an effort to get a quick and efficient resolution. Some of this stems from starting off in a NOC, where calling vendor support was practically step 1 of any troubleshooting procedure. A lot of this is based on my own experiences or things I\u0026rsquo;ve been taught by former co-workers.\nOn the other side of things, I\u0026rsquo;ve worked with people over the years who haven\u0026rsquo;t had quite the same experiences as I have. Some of them don\u0026rsquo;t typically call vendors for support - or maybe they\u0026rsquo;ve just never been lucky enough to be the first responder on a Severity-1/Production-down case. This has occasionally resulted in a number of vendor support cases being closed with highly unsatisfactory results. In fact, there have been times where this has been bad enough that it gives management the impression that we received bad support from the vendor - but what if it was just our own inability to work effectively with that vendor? Maybe we didn\u0026rsquo;t push hard enough on an issue or stress the importance.\nI feel that in a majority of cases, it shouldn\u0026rsquo;t be difficult to get the results you want out of a vendor support case. So I\u0026rsquo;ve put together a list of my own personal tips and guidelines for working with technical support.\nThe Vendor is your partner - Not the enemy I\u0026rsquo;ve seen a lot of people call vendor support for help, then treat the support engineer as the bad guy. If you want to get a quick resolution, then you need to look at them as your partner. They\u0026rsquo;re here to help you figure out your issue and get everything fixed. When you open a new case, give them a concise summary of your issue with any relevant details you think are important. Don\u0026rsquo;t hide information, as this will just prolong finding a resolution - Give them everything they need. If you know your vendor always asks for the same diagnostics information every time you open a case, then have that information ready before you contact them.\nAlways remember how difficult the job of a technical support rep can be. They\u0026rsquo;re likely sitting in a call center, just waiting for the next case. They may have in-depth knowledge of their product, but they\u0026rsquo;re walking into your network completely blind. They won\u0026rsquo;t know your traffic flows, or that some systems are redirected through a proxy, or that one band-aid fix that Joe put in a year ago and never documented. Your support rep is going to do the best job they can to get a handle on what your network looks like, but be prepared to guide them. How would you like to troubleshoot a completely different and unknown network every time your phone rings?\nHave realistic expectations Especially when you\u0026rsquo;re new to a career in IT, it can be hard to gauge what you can and can\u0026rsquo;t ask of your tech support rep. One of my former jobs had a policy that for every ticket with AT\u0026amp;T we opened, we would be required to call back every hour for a status update. If there wasn\u0026rsquo;t one, then we were supposed to demand escalation to a manager. That policy might make sense if it\u0026rsquo;s a critical issue, but what about something that\u0026rsquo;s not? Have realistic expectations about what your vendor can do.\nTroubleshooting takes time. If your support engineer grabs a bunch of logs and says they\u0026rsquo;ll need to get back to you - then you\u0026rsquo;ll need to give them the time they need. Feel free to ask for a time estimate - but if they say they\u0026rsquo;ll have something to you by the following day, don\u0026rsquo;t start bugging them every hour.\nRemember that your support engineer\u0026rsquo;s job is to help you. If you don\u0026rsquo;t feel like that\u0026rsquo;s happening, you have a few options. You can ask for a case escalation or ask for the case to be reassigned. You never know the skillset of the person receiving your case, and you might get someone who isn\u0026rsquo;t super familiar with the problem you\u0026rsquo;ve raised. As long as there is no urgency, I will usually give the person time to work the issue - but be prepared to request a case transfer if it becomes apparent that they\u0026rsquo;re not getting anywhere. For example, I once had a case for a web-based firewall management system. The engineer I got was very good with the GUI side of things, but wasn\u0026rsquo;t very knowledgeable when troubleshooting took a turn toward the underlying linux system. A quick request to transfer the case to someone more experienced in this side of the system and we had the case solved within an hour. If an escalation or case transfer doesn\u0026rsquo;t help, you can also usually reach out to your local account representative and ask them to help push the issue for you.\nIt\u0026rsquo;s also very helpful to have an idea of your vendor\u0026rsquo;s support policies. Have a question about how to set up a new feature? Some vendors don\u0026rsquo;t permit you to open a case for a new configuration, and will refer you to their professional services team. On the other hand, some vendors are perfectly okay with helping you figure out how to set up something. Even better, some support teams are willing to stand by during migrations and upgrades, just in case you need their help. In my experience, if you\u0026rsquo;re not 100% confident in your changes, then it\u0026rsquo;s better to open a proactive case beforehand.\nBe clear about the impact If your entire datacenter is offline because of an issue, make sure that you immediately stress the importance. Again, your support engineer is jumping into your environment blind. Does this firewall performance issue impact twenty people, where it is just a minor inconvenience? Or is this issue prohibiting 50,000 customers from using the services you provide? The last thing you want is a misunderstanding of impact when it\u0026rsquo;s a high priority issue for your business.\nUsually when I open a high severity case, I\u0026rsquo;ll let the engineer know: \u0026ldquo;Just so we\u0026rsquo;re on the same page, this issue impacts a large datacenter impacting 600+ customers. We need to get this back into a stable state as soon as possible\u0026rdquo;. High severity cases can be stressful for both sides, and I try to be clear about the impact without making that worse.\nOn the other side of things, if there is a low severity issue - don\u0026rsquo;t blow it out of proportions. I\u0026rsquo;ve worked with too many engineers who open up a Sev 1/Prod-Down case for every issue, even if the issue is just a minor inconvenience. Categorize your issues appropriately when you open them - and do your best to be realistic. A slow download for three users probably doesn\u0026rsquo;t warrant getting half a dozen TAC engineers on a conference bridge.\nIn case of emergency Emergency situations are a completely different subject - so I want to spend a bit of time covering them separately. It\u0026rsquo;s really important to know what constitutes a true emergency in your environment. Is it when an office (or datacenter) goes offline? Or maybe even a single extremely critical business application? Things break - so have a plan and be prepared.\nStep one - Always call into your vendors support line. You don\u0026rsquo;t want to open a web/email case and wait around for a technician. This might seem obvious, but I\u0026rsquo;ve known a lot of people who complain about the vendor\u0026rsquo;s response on a critical issue when they opened a case via a support portal. Find the vendor\u0026rsquo;s support number (or have it saved somewhere) and call them.\nStep two - Ask for a warm handoff. In most cases, the person answering the support line is just creating a case and routing it to the appropriate team/ticket queue. They may just give you a ticket number and tell you to expect a call back shorty. If the issue is truly critical, ask them for a warm handoff to a technician. Most vendors I have worked with have had no problem doing this, and it helps you get to troubleshooting faster.\nStep three - Clarify your issue and set expectations. You may be in a rush to get the issue fixed, but take a minute to explain your issue thoroughly and clearly. The more information you give to your support technician, the more easily they can dive into troubleshooting. And as I had mentioned earlier, be sure to set expectations and be extremely clear about the impact of your issue.\nStep four - Keep troubleshooting on track. As I\u0026rsquo;ve stated before, you know your environment/network better than your vendor does. If they start looking at something you don\u0026rsquo;t believe is related, you need to guide them back to the main problem.\nIn addition, if you feel after a bit that the troubleshooting isn\u0026rsquo;t making progress - then request an escalation or a second set of eyes. There is no harm in asking for more eyes on the problem. I\u0026rsquo;ve even had situations before where the technician said \u0026ldquo;Well, I think we need to do X to fix it\u0026rdquo;, and I\u0026rsquo;ve just asked them to see what their peers think. You would rather be sure about a change, than make the issue worse.\nStep five - See the issue through to resolution. Make sure you get your environment back to a stable state before ending the call. If the technician wants to drop off and call you back after reviewing logs, let them know you\u0026rsquo;re willing to just wait on hold. Once they\u0026rsquo;re off the phone, it\u0026rsquo;s easy for your technician to get dragged into another issue.\nIf the call ends with everything in a temporary state - then take follow ups on your next steps and make sure you accomplish them! Maybe you were able to restore connectivity, but need to wait for a maintenance window to make a change that is more service impacting than the original issue. Or maybe your support technician needs you to gather additional logs that they can forward to development. Whatever it ends up being, make sure you take note of it and follow through.\nThese are just some of my own personal tips that have worked for me. Support calls with vendors don\u0026rsquo;t always need to be a massive pain to deal with. Sure, sometimes you might have bad luck and get an inexperienced technician - but I find most issues with vendors can be solved easily enough once you know how much you can push them, and what you can and cannot ask for.\nI hope these are useful - Let me know in the comments if you have any additional tips!\n","permalink":"https://0x2142.com/tips-for-working-with-vendor-support/","summary":"The key to a successful support case is to work with your vendor, not against them\u0026hellip;","title":"Tips for Working with Vendor Support"},{"content":"We have some new F5 load balancers in our environment, which means I need a method of grabbing regular configuration backups. There are a number of methods out there, but I\u0026rsquo;ve opted to use SolarWind\u0026rsquo;s CatTools software since we already own it.\nThe config I used is based on this blog post. It\u0026rsquo;s a great write-up on how to back up F5 configurations using CatTools. I don\u0026rsquo;t want to replicate what was written over there - but I did hit some issues that were specific to my use-case that I wanted to share.\nWhile I was happy to find the article linked above, the immediate results didn\u0026rsquo;t work so smooth for me. This may be due to some key configuration differences that I face in my network:\nAll the F5\u0026rsquo;s are LDAP integrated - so there isn\u0026rsquo;t an easy way to provide LDAP users with direct bash access All of my F5\u0026rsquo;s are remote appliances, where the backup configuration is being copied across the WAN Getting around the first problem was my biggest challenge. CatTools is a very command/response-oriented application. Any remote LDAP authenticated users are immediately dropped into F5\u0026rsquo;s shell: tmsh. To get from there to their \u0026lsquo;advanced shell\u0026rsquo; is as simple as typing bash. However, When the terminal prompt changes, it often throws CatTools into a state of \u0026ldquo;I didn\u0026rsquo;t receive the response prompt I expected, therefore kill the job - something went wrong\u0026rdquo;. I spent a bit more time on this than I wanted to - but the underlying problem was that the \u0026ldquo;F5.BigIP\u0026rdquo; device type was specifically looking for the tmsh shell and couldn\u0026rsquo;t handle the prompt change. The fix? Switch the device type to \u0026ldquo;Linux.RedHat.Bash\u0026rdquo;, then add the bash command to the first line of your backup script.\nThe next problem was using TFTP to copy the backup archives over the WAN. Even some of the new F5\u0026rsquo;s with minimal configuration still generate a 10Mb file. Doesn\u0026rsquo;t seem like much, but when you\u0026rsquo;re copying that over a WAN between two datacenters, that turns into a ~5 minute file transfer. CatTools by default will only wait 30 seconds after executing a command before it expects a response. So every time I tried to run the job, CatTools would kill it only 30-seconds into the file transfer. Luckily enough, they support a utility command that can alter the normal timeout:\n%ctUM: Timeout 600 tftp -m binary 192.168.1.10 -c put $filename %ctUM: Timeout 0 The command %ctUM: Timeout 600 changes the timeout value to 600 seconds, or 10 minutes. The TFTP file transfer command is next, which is now permitted up to 10 minutes to finish. The last command resets the timeout back to the default (30 seconds).\nI also realized that the original script doesn\u0026rsquo;t purge the backup archive afterwards. For my use case, I would much rather automatically clean up the backup files once they\u0026rsquo;ve been transferred to a central location.\nSo after all that, here is the version of that script that I\u0026rsquo;m using:\nexport date=`date +\u0026#34;%y%m%d\u0026#34;​` export filename=$HOSTNAME.$date.ucs tmsh save /sys ucs /var/local/ucs/$filename cd /var/local/ucs %ctUM: Timeout 600 tftp -m binary 192.168.1.10 -c put $filename %ctUM: Timeout 0 rm -f $filename Thanks again to the original blog post for getting me on the right track with this! I hope that my ramblings here are helpful to anyone with a similar deployment scenario.\n","permalink":"https://0x2142.com/automated-f5-backups-with-cattools/","summary":"How to configure SolarWinds Kiwi CatTools to monitor \u0026amp; back up F5 load balancer configurations","title":"Automated F5 Backups with CatTools"},{"content":"Note: I may receive commissions for purchases made through links in this post. This is to help support my blog and does not have any impact on my recommendations.\nI have always said that I\u0026rsquo;m not sure I could write code for a living, but I do really enjoy writing scripts that make my life easier. Today\u0026rsquo;s post is a great example of that. The ease of use offered by the Juniper SRX firewalls and JunOS is something that I wish I had in all of my networking infrastructure. Even better, a brand new SRX 300 can be purchased on Amazon for less than $300 - which made a great addition to my home lab. Now I have a place to develop and test automation without breaking production 🙂\nAnyways - I had a requirement to monitor my SRX clusters for route changes. Specifically I\u0026rsquo;m using this with devices where I have implemented customer peering via BGP. The SRXs don\u0026rsquo;t natively offer any form of monitoring and alerting for this, and the current monitoring applications at my disposal don\u0026rsquo;t either. So I decided to write something myself, which took significantly less time than I had assumed.\nCode has been posted up to my GitHub - but I\u0026rsquo;m going to walk through some of it here. This script is intended to run as a cron job on a 5 or 10 minute interval.\n# Imports from jnpr.junos import Device from jnpr.junos.op.routes import RouteTable On my initial research to see how easy this would be to pull off, I found a nifty thing in the JunOS PyEZ package. Turns out they already offer a module literally called RouteTable that can pull the information I need.\nOriginally, I spent a bit of time trying to figure out how to use the standard device API to pull this info, but this module made everything 10x easier.\n################# # Required values ################# deviceName = \u0026#39;SRXFirewall\u0026#39; deviceIP = \u0026#39;0.0.0.0\u0026#39; apiuser = \u0026#39;username\u0026#39; apipassword = \u0026#39;password\u0026#39; SMTP = \u0026#39;smtp-alias\u0026#39; fromName = \u0026#39;BGP Monitor\u0026#39; fromAddr = \u0026#39;bgpmonitor@domain.com\u0026#39; toName = \u0026#39;contactName\u0026#39; toAddr = \u0026#39;contact@domain.com\u0026#39; prefixDict = { \u0026#34;FriendlyName\u0026#34;: \u0026#34;Prefix\u0026#34;, \u0026#34;Route-to-Inet\u0026#34;: \u0026#34;0.0.0.0/0\u0026#34; } ################ The section above contains a list of the required variables for this script to function. A lot of them are going to be self-explanatory - but I wanted to take a moment to look at the prefixDict. This is a Python dictionary that maps a friendly name to a route prefix, which is used when we check our routing table. For example, if you wanted to monitor the SRX device for a route for 10.10.10.0/24 which belongs to Customer1, then we would just add and entry in this dictionary for \u0026ldquo;Customer1\u0026rdquo;: \u0026ldquo;10.10.10.0/24\u0026rdquo;\nAlright - now let\u0026rsquo;s skip to the good stuff:\n# Function to check received BGP routes def checkBGP(): try: # Open SRX session dev.open() except: sys.exit(0) # Pull device routing table, then keep only BGP originated routes allroutes = RouteTable(dev) bgp = allroutes.get(protocol=\u0026#34;bgp\u0026#34;).keys() # Close SRX session dev.close() The section above is the beginning of the checkBGP function. Simple enough - just open an API session to the SRX and grab the entire routing table. That module I was talking about earlier makes it super easy! Next, we pull only the routes originating from BGP and assign them to a variable called bgp.\nSo in order to run this script repeatedly as a cron, I needed a place to persistently store the last retrieved routing info. For the time being, this is done by simply writing a temp file with the information:\nif not os.path.isfile(tempfile): with open(tempfile, \u0026#39;w+b\u0026#39;) as a: a.write(str(bgp)) sys.exit(0) # Local file used to keep track of BGP learned routes with open(tempfile, \u0026#39;ab\u0026#39;) as a: lastroutes = a.readlines() # Compare if routes are different if str(bgp) == str(lastroutes[0]): sys.exit(0) if str(bgp) != str(lastroutes[0]): pass # Delete file, then re-create with new route list #os.remove(tempfile) with open(tempfile, \u0026#39;w+b\u0026#39;) as a: a.write(str(bgp)) The logic in the comparison is simple enough. If the current string of routes pulled from the SRX equals what is in the temp file (from the last run), then we assume no changes have occurred - and the script ends. Otherwise, if they don\u0026rsquo;t match then something has changed.\nOnce we know something has changed, we\u0026rsquo;ll go ahead and find out exactly which route entry is missing:\n# Create Status list, by checking received routes in bgp object status = [] for name,prefix in prefixDict.items(): if prefix in bgp: status.append(\u0026#34;%s - RECEIVED\u0026#34; % name) if not prefix in bgp: status.append(\u0026#34;%s - MISSING\u0026#34; % name) This is where our prefixDict from earlier comes into play. We\u0026rsquo;ll look at every BGP prefix that we defined in that dictionary, and see if it exists in the current SRX routing table. All of this information (both routes received and missing) get added to an alert email.\nLastly, we\u0026rsquo;ll go ahead and compose our alert email to send out:\n# Send alert message sendMail(str(lastroutes[0]), str(bgp), status) I\u0026rsquo;m not going to cover the email function here, since it\u0026rsquo;s pretty straightforward.\nThat\u0026rsquo;s it! The existence of the JunOS RouteTables module made creating this script a piece of cake. I\u0026rsquo;m considering adding onto this later with some additional functionality, so be sure to check my GitHub repo if you\u0026rsquo;re interested.\nHope this is useful to someone else out there - Let me know in the comments!\n","permalink":"https://0x2142.com/juniper-srx-automated-route-monitoring/","summary":"How I automated BGP routing table monitoring for Juniper SRX firewalls","title":"Juniper SRX - Automated Route Monitoring"},{"content":"When I started in networking, I never would have thought that security would be such an important part of my job. However, it has become something that I\u0026rsquo;m involved with almost every day - tasks like applying security configurations, participating in audits, or spending a day chasing down the latest vulnerabilities. It\u0026rsquo;s already become second nature to watch for what\u0026rsquo;s new in the security realm, so that I\u0026rsquo;ll be more prepared when someone asks about it.\nEarlier today, Cisco released their 2018 Annual Cyber Security Report. I\u0026rsquo;ve spent some time digging through the report and thinking about what they\u0026rsquo;ve written. It\u0026rsquo;s interesting to read through the trends and survey results, and try to get an idea of where security efforts should be focused for the coming year. This post is going to cover just a subset of what\u0026rsquo;s in the complete report. I\u0026rsquo;ll be covering the topics that I found particularly interesting, and give my own thoughts/views on them.\nThe Encrypted Web is Great\u0026hellip; For Attackers Unsurprisingly, Cisco reports a growing trend in attacks and exploits that are taking advantage of encrypted transport. As a lot of large companies and Internet bodies are pushing for a 100% encrypted web, should we be surprised? Nah, it\u0026rsquo;s the logical next step. Users want encryption because it means privacy - but that privacy also brings a method of concealing attacks.\nNew exploits and malware are heavily leveraging encrypted transport to bypass all of the security we put in place to detect them. Typical defense technologies like intrusion prevention systems (IPS) are fantastic, but only when they can actually read the data. If a user download\u0026rsquo;s malware through an HTTPS call, IPS won\u0026rsquo;t usually catch it. And when that malware can now take advantage of SSL to reach back out to a command \u0026amp; control server? Yeah, IPS might not help us there either.\nThere are technologies out there that allow enterprises to see this traffic - but maybe not enough of us are adopting it yet. A forward proxy for outbound web filtering is great. One that implements SSL decryption and inspection is even better. If your company isn\u0026rsquo;t already decrypting outbound web traffic, then this needs to be a priority.\nInbound web traffic can be just as dangerous. New IIS vulnerability? Sure, let\u0026rsquo;s grab the latest IPS signatures and \u0026hellip; Oh wait, our IPS runs on our edge firewall, which sits in front of those web servers - which are all using SSL for encryption. That means any malicious traffic is going to slide right through our IPS undetected and land on our unpatched web servers. Get a Web Application Firewall (WAF), and let it front-end your SSL traffic. These things can be expensive and a nightmare to configure and tune properly, but right now they are one of your best options for inspecting web traffic.\nOld Attacks Aren\u0026rsquo;t Going Away - They\u0026rsquo;re Just Getting an Upgrade This year\u0026rsquo;s report highlighted that much of the older attacks are still here, and they\u0026rsquo;re not giving up yet. Attacks via email are still present and doing more damage than you would hope. We\u0026rsquo;re certainly getting better about implementing spam filters with reputation filtering, but attackers aren\u0026rsquo;t giving up yet.\nEmail attacks are relying more on social engineering and targeted phishing. These messages are also utilizing SSL to encrypt the malicious links within the emails. Infected attachments are surprisingly still a big issue, with Microsoft Office and PDF files still being the worst offenders. Just because attackers are finding new and exciting ways to hit us doesn\u0026rsquo;t mean they\u0026rsquo;re giving up on the tried and true methods. We still need to focus on all the standard attack vectors, like email. Implementing intelligent email/spam filters and providing user awareness training are the primary methods we have to combat this.\nThe Cloud is Secure! \u0026hellip;We Think This one I found particularly fun. Out of all the companies surveyed by Cisco for this report, 57% of them said they believe the cloud offers better security. Wait - Did I misread that? More than halfof respondents think that the cloud offers better security than their own infrastructure! This makes me wonder\u0026hellip;\nFrom my perspective, a cloud service provider is just another company. In most cases, they run just another network and hit a lot of the same challenges that non-cloud companies are facing. And we can only assume that cloud providers are prioritizing security and not just trying to turn a quick profit. Cloud companies have the advantage of being able to hire a dedicated security team that their customers can leverage. However, enterprises are complaining about lack of skilled security engineers, and I\u0026rsquo;ll bet it\u0026rsquo;s not because cloud providers are picking them all up.\nCloud definitely offers benefits - but this needs to be a well-calculated risk. For a smaller company without dedicated IT staff, a cloud solution would most likely offer security improvements over their own infrastructure. As companies scale, however, their security requirements do too. We need to make sure that the cloud providers we choose are also capable of adhering to those standards. Before you move to the cloud: ask questions about their security practices, get answers, and demand more information on the parts that are important to your business.\nAnother fun note from Cisco - some of the damage done by cloud providers is a simple mis-understanding of ownership. If you subscribe to a complete Software as a Service (SaaS) provider, chances are good that the provider worries about all of the critical security configurations. However, if you\u0026rsquo;re going to a cloud provider just for infrastructure (like AWS), then you are likely responsible. In the case of AWS, you\u0026rsquo;re being provided a server - and that\u0026rsquo;s where Amazon\u0026rsquo;s responsibilities end. It\u0026rsquo;s up to the enterprise to still make sure that those servers are patched, hardened, and audited. Treat the cloud as an extension of your own infrastructure and polices, not a separate entity.\nDiversifying Risks? Maybe not It used to be a somewhat well-established security practice to use multiple vendors. Have a need for two sets of firewalls? Make sure you use two vendors, so that a vulnerability in one doesn\u0026rsquo;t affect the other. Seems like sounds logic - until you have to train staff to be experts on multiple platforms, and keep up to date on all the latest patches from each vendor.\nCisco is finding that the more vendors a company has in their environment, the more problem we have maintaining everything. From my own experiences, I can say this is certainly a problem. In environments where I\u0026rsquo;ve had up to four vendors for firewalls and switching, it becomes difficult to work with. It\u0026rsquo;s hard on IT staff to maintain knowledge of configuration and best practices for each different vendor - and when a new vulnerability comes out, we end up spending way more time trying to track down each vendor\u0026rsquo;s responses and patches.\nIt makes sense that companies who have a more tightly integrated infrastructure might have an easier time managing it. Cisco might want you to buy 100% into their ecosystem (of course), but I do think there is value in consolidating your infrastructure. One or two vendors will be much easier to establish relationships with than half a dozen of them. Your IT staff can dedicate their focus to mastering only a couple of technologies, rather than spreading themselves over a dozen different platforms. And when that new vulnerability is released? It should be much more straightforward to patch all of your systems quickly.\nThere\u0026rsquo;s That Automation Thing Again I think we\u0026rsquo;re finally beginning to reach a point where automation is really showing it\u0026rsquo;s value in the security realm. A typical company is going to have so many different systems and alerts that it doesn\u0026rsquo;t make sense for someone to manually review and act upon every one. This is where automation really begins to shine.\nCisco\u0026rsquo;s report shows that more companies are relying heavily on automation. This can be used for alert response, reporting, and behavioral analytics. Especially when I keep hearing that there is a skills shortage in security, we need to take advantage of what automation can offer. This doesn\u0026rsquo;t always have to be home-grown scripts either - there are a number of offerings already available.\nTake a second look this year. Try to see where automation can fit into your infrastructure to help improve both operations and security.\nThanks for reading! Just as a friendly reminder - All of the opinions stated in this post (and all others here) are 100% my own, and do not represent any vendor or employer. Since security has become more of an important part of my job, reports like this are always very interesting to read. I\u0026rsquo;ve only covered a handful of what was in the report - just what was particularly interesting to me. If you\u0026rsquo;re interested in reading more, check out the full report here.\n","permalink":"https://0x2142.com/thoughts-on-ciscos-2018-annual-cybersecurity-report/","summary":"My thoughts on Cisco\u0026rsquo;s Annual CyberSecurity Report","title":"Thoughts on Cisco's 2018 Annual CyberSecurity Report"},{"content":"Over the past two years we have made a ton of progress shifting datacenter infrastructure from 1G to 10G+. A majority of this has been through a vendor migration back to Cisco for switching - and specifically using the Nexus 9372 line. These boxes come with 48 ports of 1G/10G SFP+ and another 6 QSFP ports that hit 40G.\nLate last year we placed an order to expand our 10G+ coverage in one of our larger datacenters. After meeting with our local Cisco reps and talking through options, we settled on a pair of Nexus 93180YC-EX switches. The new toys offer additional flexibility, by providing 48 SFP+ ports capable of 1G/10G/25G and the 6 QSFP ports are 40/100G.\nA week or two ago we worked during a planned maintenance window to try and bring the new 93180s online. The new switches are just directly connected back to the 9372s using four QSFP-40G-CR4 cables. The time comes, we turn up the ports - and they don\u0026rsquo;t come up. We know the cable types definitely work, since we\u0026rsquo;re using them for all of our current interconnects between the 9372s. Unfortunately, due to tight timelines on maintenance windows - we have to turn down the ports and move on to other task.\nSo we go down the normal line of troubleshooting. Reseat cables - still nothing. Remove port-channel/VPC configurations - nothing. Test the QSFP cables by cabling in between just the new 93180s - yeah, ports come up and the cables are good. One of my teammates, who is running with this task, is almost at the point of opening up a support case with TAC. I double checked the switch port configurations - but everything looks good. My first thought was that maybe there is a speed/autonegotiation issue - since the QSFP ports on the 9372s are fixed 40G, while the 93180s are 40/100G.\nWe scheduled another quick no-downtime maintenance window to test out the theory. Each of the ports on both sides of the connection gets the following configuration changes:\nSwitch(config)# interface x/x Switch(config-if)# no negotiate auto Switch(config-if)# duplex full Switch(config-if)# speed 40000 The time comes - and sure enough the ports come online.\nJust wanted to throw this out there in case anyone else runs across the same problem. The fix is surely easy enough, but you don\u0026rsquo;t always think of autonegotiation issues - especially in such a simplistic configuration as this.\nI also wanted to say thanks to the great people in the #CiscoChampions DataCenter group. I was able to run the problem through them, and they suggested the same potential root cause. It\u0026rsquo;s always great to have a second opinion to provide some confidence, especially when there are strict time constraints for maintenance.\n","permalink":"https://0x2142.com/autonegotiation-issues-on-nexus-qsfp-ports/","summary":"Interoperability issues between Nexus QSFP modules can cause autonegotiation to fail","title":"Autonegotiation issues on Nexus QSFP Ports"},{"content":"You can\u0026rsquo;t always get what you want. As an engineer though, it\u0026rsquo;s your job to determine what\u0026rsquo;s best for the company and recommend it to management. What happens if your suggestion gets turned down? Well certainly your proposal must have been mis-understood, right? Maybe the decision-makers don\u0026rsquo;t truly understand the risk involved in not following your recommendation, whether that be financial, security, or otherwise. But maybe they do understand - and not only do they understand but they\u0026rsquo;re willing to accept that risk.\nRisk acceptance is something that I\u0026rsquo;ve seen misunderstood by engineers over and over again. Especially when you\u0026rsquo;re earlier in your career, you get the feeling that you know what you\u0026rsquo;re doing - and therefore management should listen to you. I get it - I\u0026rsquo;ve been there too. It can be hard to propose an idea, then have that idea immediately shot down by your peers or management. That\u0026rsquo;s not to say that they aren\u0026rsquo;t listening, but maybe you might not have a great view of everything behind their decision.\nLet\u0026rsquo;s take an example. As an engineer (server/network/security/etc), you\u0026rsquo;ve been alerted by your vendor that there is a huge vulnerability in a critical business application. That vendor has been extremely proactive, and has promptly provided you with detailed KB articles and download links to the upgrade package. The vulnerability that\u0026rsquo;s been disclosed certainly seems bad enough - it allows for remote code execution by an unauthenticated user. What do we do with this? Well we immediately meet with management to tell them that we need to patch this application immediately.\nTheir answer?\nNo.\nWell that seems just impractical, doesn\u0026rsquo;t it? They don\u0026rsquo;t really want to leave the company at a massive risk to attack, do they? No - they certainly don\u0026rsquo;t. However, it may be that management has chosen to accept the risk for one reason or another. Let\u0026rsquo;s dig into a few reasons why they might:\nExpense (Money) - It could be that the cost to remediate the issue is significant. Maybe the software is a bit out-dated and the company hasn\u0026rsquo;t paid for support. Renewing that support contract might be tens of thousands of dollars or more. If there are enough mitigating factors, that cost might not be worth it.\nExpense (Time) - People also cost money, and time spent on fixing this issue also means lost productivity on other tasks. For some enterprises, an upgrade of a critical business application could take months of work. What happens when this new version breaks compatibility with another business application? Well now we\u0026rsquo;ve added on extra time so we can upgrade both. If there is no other value to the upgrade, then the fix might be held until there is.\nMitigating Factors - There may be enough other security controls in the network to reduce risk. Maybe in this case only the core servers for this application are affected - and they reside on their own network segment behind a firewall. Maybe that firewall also runs an intrusion prevention system, which has already been updated with signatures to stop this particular attack. There could be a number of things going on which have given management the comfort that the system is still safe.\nAny of these might lead to the final decision: we\u0026rsquo;re going to live with this risk. By accepting it, we\u0026rsquo;ve evaluated it, we\u0026rsquo;re aware of it, and we are consciously choosing not to fix it. We may have limited the scope of this risk via other mitigations - but ultimately we are not completely ridding ourselves of this risk.\nDoes risk acceptance only apply to security? Nope. It applies to just about anything really. What if I recommend that we need to replace older hardware or risk network instability? That risk could be acceptable if the hardware isn\u0026rsquo;t critical. Same thing goes if we decide to forego renewing support on a pair of firewalls. Maybe it\u0026rsquo;s even suggesting an implementation of a new technology. Proposing to a big cloud provider that they should prepare for IPv6? There is a good chance they may still accept the risk of IPv4 depletion 🙂\nAs an IT engineer, it\u0026rsquo;s not just our job to keep things running and get projects done - it\u0026rsquo;s also our responsibility to keep the best interests of the business in mind when it comes to design, implementation, and maintenance. We need to evaluate all our options and always recommend what we think the best path is. However, we also need to respect that our recommendations may not always be followed. There may always be something else going on that we\u0026rsquo;re not aware of.\nOne last piece of advice I can give - If there is ever something you feel very strong about, get the final decision in writing. If you truly feel that the risk is too great, and you\u0026rsquo;ve made your pitch that has still been rejected - get the decision in writing (whether email, chat log, etc). This may seem silly at the time, but it\u0026rsquo;s better to be safe. People forget, whether intentionally or not - and if the risk becomes a reality, you don\u0026rsquo;t want to give them any reason to put the blame on you.\n","permalink":"https://0x2142.com/what-is-risk-acceptance/","summary":"Are all risks bad? If not, how do we determine what is acceptable?","title":"What is Risk Acceptance?"},{"content":"Last week I came across a thread on Reddit that asked the question: \u0026ldquo;What is your company\u0026rsquo;s policy on maintenance windows?\u0026rdquo;. This got me thinking about how maintenance windows have been handled at the various companies I\u0026rsquo;ve worked at, and how those schedules/restrictions impact project timelines, network design, etc.\nMany of the places that I have worked at in the past have been typical 8a-5p/M-F shops. Outside of normal business hours, no one really cared if the network was available. Sure, we might have people who worked late - but a few hours notice via email was always enough. However, the company I work for currently has much tighter restrictions on when work can be performed. We have worldwide customers in over a dozen datacenters and some fairly strict uptime SLAs. What this comes down to is a once-a-month allowance for scheduled maintenance - where the timeframe is limited anywhere from 15 minutes to 4 hours.\nSome of the immediate impacts of these differing maintenance window schedules are somewhat obvious. Network maintenance can be practically open to all nights and weekends with a lot of typical 8-5 businesses. This means changes can happen much more frequently - especially changes involving a full network outage. For example, at one of my previous jobs I needed to upgrade each floor of the building from individual Cisco Catalyst 3548 switches to new 2960X stacks. This required moving the cables for up to 200 ports per floor (while also trying to clean up cable management). I was able to complete the work by just coming into the office earlier every day to move the connections before anyone else arrived.\nOn the other hand, a cloud service provider can\u0026rsquo;t just decide one day to take a few hour outage to swap out network equipment. Instead, changes have to be carefully planned, scheduled, then executed within a short window. Customers have come to expect 100% uptime - and rightfully so. However, we still need some amount of time dedicated to performing upgrades, changes, or other maintenance activities. The simple switch migration from the last example suddenly becomes a multi-month ordeal in an environment such as this. You might be ready to jump on the work, but you need to wait for the next regularly scheduled window - and even then you may have only a handful of time to complete your task. If you don\u0026rsquo;t complete all of it in the time allocated? Well now your project gets pushed back another month.\nSo you might ask - as a business scales, does it always end up creating this maintenance monolith? It might - but it certainly doesn\u0026rsquo;t have to end that way. The effects of higher uptime requirements and shorter maintenance periods might seem like nothing but bad news. However, the change in mindset that comes along with that does bring some unique benefits.\nThe first major benefit comes in the form of planning. When you have 15 or 30 minutes to complete an entire migration or upgrade, it becomes extremely beneficial to plan out a complete play-by-play of every activity. The limited window means that simple mistakes can cost you valuable time. Of course, the tendency for maintenance windows to be scheduled for late nights also compounds the problem since you may be tired or less alert. For critical maintenance tasks that I need to accomplish, I take the time to create a step-by-step checklist of every command that must be run, every system that must be tested, and every step needed to roll back. Sufficient planning means less mistakes, which in turn increases chances of success during a tight work period.\nAutomation and efficiency start to become a necessity when you have only a few minutes to perform a task. Sure, I might create a very detailed checklist of what must be accomplished - but what happens if it\u0026rsquo;s simply too much for the time allocated? You can\u0026rsquo;t complete a 20-minute task in a 15-minute outage window, right? Sometimes we can schedule extended maintenance periods, but this certainly isn\u0026rsquo;t feasible every month. This is where we begin to try and identify inefficiencies and tasks that would benefit from automation. Over the years I have written a handful of scripts and utilities that allow for normal maintenance tasks to be completed quickly. These are things that might have otherwise continued to be done manually (and error-prone) without the timing restrictions.\nA short maintenance period also encourages more careful network design. If you\u0026rsquo;re only permitted a half-hour of downtime, then you start looking for ways to minimize the impact. Could the network be designed in a way that allows for a no-downtime switch upgrade or replacement? If not, then how do we get there? In many smaller business networks you might plan for redundancy but never test it - but in a high-uptime environment you begin to relyon it. If you want to get to a point where work can be accomplished with minimal downtime (or even during normal hours), then you must be confident that your network can seamlessly absorb the impact.\nI certainly wish some days that I could go back to a life where downtime is acceptable any time during off-hours. However, I\u0026rsquo;m sure that the desire for higher uptime and greater reliability are likely here to stay - and I believe that I\u0026rsquo;ve learned some valuable lessons in trying to meet those requirements. An extremely short maintenance period certainly complicates things, but it also forces us to look for process and design improvements. I believe that the end result is a better network for both the business and it\u0026rsquo;s customers.\nWhat are your maintenance practices like? Do you have hours or minutes? Comment below!\n","permalink":"https://0x2142.com/how-does-maintenance-scheduling-affect-your-network/","summary":"My thoughts on years of off-hours maintenance windows","title":"How Does Maintenance Scheduling Affect Your Network?"},{"content":"Today we\u0026rsquo;re going to take a look at how to configure an etherchannel between two Cisco Switches.\nWhat is an etherchannel? It\u0026rsquo;s a way of taking multiple independent links and bundling them together, so that they appear as one logical connection between two devices. Etherchannels are commonly used between two switches, or between a switch and a host - which allows for both additional bandwidth and fault tolerance/redundancy. In the example today, we\u0026rsquo;ll be using an etherchannel protocol called Link Aggregation Control Protocol (LACP). LACP is an IEEE standard (802.3ad).\nYou might be thinking \u0026ldquo;Wait, wouldn\u0026rsquo;t multiple links cause a loop? Or trigger Spanning-tree to block ports?\u0026rdquo;. Not in this case! Etherchannel technologies work around those problems by creating a single logical interface for spanning-tree to worry about. The etherchannel protocol itself worries about loop prevention in between the two devices, so we get multiple ports of non-blocking bandwidth.\nFor everything we cover in this example, we\u0026rsquo;ll be using the following topology:\nSo we have two switches, which are connected together via Eth0/0 and Eth0/1. Each switch has three VLANs configured - 10, 20, and 30.\nConfiguring an Etherchannel I\u0026rsquo;ll only be showing the configuration from the perspective of 0x2142-SW1 - but all configuration is replicated on 0x2142-SW2.\n! We\u0026#39;ll use the interface range command to apply the etherchannel configuration to ! both Eth0/0 and Eth0/1 at the same time: 0x2142-SW1(config)#int range Eth0/0 - 1 ! We specify which etherchannel protocol to use by configuring \u0026#39;channel-protocol\u0026#39; ! PAgP is a Cisco Proprietary protocol, but we\u0026#39;ll be using LACP for this example: 0x2142-SW1(config-if-range)#channel-protocol ? lacp Prepare interface for LACP protocol pagp Prepare interface for PAgP protocol 0x2142-SW1(config-if-range)#channel-protocol lacp ! Next we need to specify a channel-group and mode: 0x2142-SW1(config-if-range)#channel-group 1 mode ? active Enable LACP unconditionally auto Enable PAgP only if a PAgP device is detected desirable Enable PAgP unconditionally on Enable Etherchannel only passive Enable LACP only if a LACP device is detected 0x2142-SW1(config-if-range)#channel-group 1 mode active Creating a port-channel interface Port-channel 1 0x2142-SW1(config-if-range)# *Jan 26 01:03:04.532: %LINEPROTO-5-UPDOWN: Line protocol on Interface Port-channel1, changed state to up Let\u0026rsquo;s talk through a few notes about the above configuration. In order to enable etherchannel, we only need to configure two commands: channel-protocol and channel-group. The channel-protocol command tells the switch which etherchannel protocol to use for negotiation (LACP in this case). The channel-group command provides two necessary components: the group number and mode. The group number is just a device-local identifier for which group to add these ports to. When we specified group 1, the switch adds both Eth0/0 and Eth0/1 into the new logical interface Port-Channel 1.\nThe etherchannel mode is also important. The two primary options we want to look at for LACP are active and passive. Active tells the switch to preemptively send out LACP negotiation packets. In this case, the switch really wants the ports to become a bundle and will ask it\u0026rsquo;s partner device for an etherchannel to be formed. Passive mode tells our switch to only negotiate if another device wants to. In this mode, our switch won\u0026rsquo;t send out any etherchannel negotiation packets unless its partner device does so first.\nGenerally speaking, the most common configuration is to set the mode on both devices to active. This ensures that both devices actively participate in trying to establish an etherchannel. Placing one device in active and one in passive will work as well. However, if both devices are placed into passive mode, an etherchannel will never form.\nValidation So how do we validate that the etherchannel has formed correctly? One way is using the show etherchannel summary command:\n0x2142-SW1#show etherchannel summary Flags: D - down P - bundled in port-channel I - stand-alone s - suspended H - Hot-standby (LACP only) R - Layer3 S - Layer2 U - in use N - not in use, no aggregation f - failed to allocate aggregator M - not in use, minimum links not met m - not in use, port not aggregated due to minimum links not met u - unsuitable for bundling w - waiting to be aggregated d - default port A - formed by Auto LAG Number of channel-groups in use: 1 Number of aggregators: 1 Group Port-channel Protocol Ports ------+-------------+-----------+----------------------------------------------- 1 Po1(SU) LACP Et0/0(P) Et0/1(P) From the output above, we see that there is one group configured with the group ID of 1. It shows that both Eth0/0 and Eth0/1 have been added into the Port-channel 1 interface. The (SU) next to the Port-channel interface indicate that the etherchannel is up (U) and configured for layer 2 (S). I mentioned earlier that spanning-tree only worries about the port-channel interface, not the individual member ports. We can also check that out by using the show spanning-tree command:\n0x2142-SW1#sh spanning-tree vlan 20 VLAN0020 Spanning tree enabled protocol rstp Root ID Priority 32788 Address aabb.cc00.1000 This bridge is the root Hello Time 2 sec Max Age 20 sec Forward Delay 15 sec Bridge ID Priority 32788 (priority 32768 sys-id-ext 20) Address aabb.cc00.1000 Hello Time 2 sec Max Age 20 sec Forward Delay 15 sec Aging Time 300 sec Interface Role Sts Cost Prio.Nbr Type ------------------- ---- --- --------- -------- -------------------------------- Et0/2 Desg FWD 100 128.3 Shr Et0/3 Desg FWD 100 128.4 Shr \u0026lt;-- Output omitted --\u0026gt; Po1 Desg FWD 56 128.65 Shr Making Configuration Changes to an Etherchannel Now that we have a working etherchannel - We have a few things that need special attention. The individual port configurations, Eth0/0 and Eth0/1 in this case, need to match at all times! Port configuration mis-matches are going to be an easy way to inadvertently bring down the port-channel. The good thing is that we now have a convenient Port-Channel interface which we can use for configuration. This logical port will replicate any configuration changes to all member ports.\n! Let\u0026#39;s jump into our Port-Channel 1 interface and configure a trunk for VLAN 20 0x2142-SW1(config)#int po1 0x2142-SW1(config-if)#switchport mode trunk 0x2142-SW1(config-if)#switchport trunk allowed vlan 20 ! Now we can check the individual port configs: 0x2142-SW1(config-if)#do sh run int e0/0 Building configuration... Current configuration : 176 bytes ! interface Ethernet0/0 switchport trunk allowed vlan 20 switchport mode trunk channel-protocol lacp channel-group 1 mode active end 0x2142-SW1(config-if)#do sh run int e0/1 Building configuration... Current configuration : 176 bytes ! interface Ethernet0/1 switchport trunk allowed vlan 20 switchport mode trunk channel-protocol lacp channel-group 1 mode active end Easy enough, right? The configuration changes for the trunk are now on both Eth0/0 and Eth0/1.\nTroubleshooting Etherchannels There is always a possibility that something goes wrong - so let\u0026rsquo;s take a quick look at some common problems and how to fix them.\nRemember how I said that the member port configurations had to match? Here\u0026rsquo;s what happens if we make a configuration change on only one of the two member ports:\n0x2142-SW1(config)#int eth0/1 0x2142-SW1(config-if)#switchport trunk allowed vlan 30 0x2142-SW1(config-if)# *Jan 28 20:43:55.458: %EC-5-CANNOT_BUNDLE2: Et0/1 is not compatible with Et0/0 and will be suspended (vlan mask is different) Eth0/1 immediately gets put into a suspended state, and is no longer active in the port-channel interface. In this case the switch gives us a good hint as to what\u0026rsquo;s wrong - vlan mask is different. Error messages will vary slightly, but a suspended port is easy to fix by comparing individual port configurations and fixing the mismatch.\nHere\u0026rsquo;s another one:\n*Jan 28 21:06:07.346: %EC-5-L3DONTBNDL2: Et0/0 suspended: LACP currently not enabled on the remote port. *Jan 28 21:06:08.009: %EC-5-L3DONTBNDL2: Et0/1 suspended: LACP currently not enabled on the remote port. This error message can mean a few things - the common one being exactly what it states! Check both sides of the connection, and ensure that LACP is configured on each device. This error message can also occur on certain mismatches - like if one side is running as a Layer 2 etherchannel, but the other side is running as Layer 3.\nOne more:\nJan 28 20:83:55.458 %ETHPORT-5-IF_DOWN_PORT_CHANNEL_MEMBERS_DOWN: Interface port-channel1 is down (No operational members) The above message is also somewhat self-explanatory. In this case, the switch is unable to bring up the port-channel interface, because none of the underlying member ports are coming online. Troubleshoot what might be wrong with those ports first, then the port-channel should come up.\nHope this was useful! In a later post, we\u0026rsquo;ll dig into more configuration and considerations - like packet hashing, layer 3 etherchannels, and how packets are weighted between interfaces.\nQuestions? Drop them in the comments below!\n","permalink":"https://0x2142.com/l2-basics-configuring-an-etherchannel/","summary":"How to configure a basic etherchannel on Cisco devices","title":"L2 Basics: Configuring an EtherChannel"},{"content":"I received an email late yesterday afternoon - to my surprise I was invited to join the Cisco Champions program for 2018! I applied back in November, but I never would have thought I would actually get selected.\nI\u0026rsquo;m very excited to join the wonderful group of existing Cisco Champions, a few of which who I\u0026rsquo;ve bugged on Twitter already. I\u0026rsquo;ve heard a lot of great things about the program, but since it\u0026rsquo;s my first year I\u0026rsquo;m interested to experience it for myself. Overall I\u0026rsquo;m happy for the opportunity and I\u0026rsquo;m looking forward to what it brings!\nThanks for being here - It\u0026rsquo;s going to be an exciting 2018!\n","permalink":"https://0x2142.com/2018-cisco-champions/","summary":"#CiscoChampion","title":"2018 Cisco Champions"},{"content":"Maybe 2018 isn\u0026rsquo;t off to quite the best start. Recent processor vulnerabilities have people scrambling to patch and update systems. Stuff like this ends up being a fairly large sink of time for any systems/network administrator. The worst part is that we have practically no control of when this stuff happens or how much time it\u0026rsquo;s going to take to resolve. What we dohave control over, however, is our ability to make our own lives easier through automation.\nA lot of people take the beginning of the year to make new resolutions and goals for the coming months. So this year, I\u0026rsquo;m urging you to add one more to your list: Try and automate something that will make your life easier.\nWhere to Start What you choose to automate doesn\u0026rsquo;t need to be extremely complex or elaborate, just anything that will save you a little bit of time. Never used a scripting language? I can\u0026rsquo;t recommend enough using Learn Python The Hard Way to start learning. This site is what I used about five years ago to get into scripting. Another great resource is CodeAcademy, where they have web-based interactive tutorials (also check out their specific module on Python and APIs).\nOnce you get a good handle on the basics, start thinking about repeatable tasks that are great candidates for automation. Start with something simple - maybe a script that prompts the user for information, then generates the command-line entries to configure new switch ports. Then someone can easily copy and paste the commands from the script output to achieve their desired configuration. Something like this might not immediately seem like a huge time savings, but it gives you a place to start and get familiar with what is possible. Once you get something like that working, it\u0026rsquo;s not too difficult to extend the script later and actually include calls to the switch APIs to automate the changes.\nIs Python/scripting your only option? Not at all. There are also automation toolsets like Ansible, which can abstract the code layer a bit. For quite a number of systems that I deploy to an average datacenter, I already have Ansible playbooks written to handle that work. My actual time involved in deploying standard network monitoring applications and tools to a new datacenter went from hours to less than five minutes. For the purposes of this post, I\u0026rsquo;ll be speaking more to the Python/Scripting side. However, the important point is not necessarily which toolset you choose - it\u0026rsquo;s the fact that you try to use any one of these tools or others to automate something.\nStick with it Learning a scripting language at first might seem like a very unnecessary and time consuming task. However, this is something that will pay off in the long run. When I started learning Python, all I wanted to do was parse data from several CSV files and combine the necessary data into one large file. Stupid simple script, but it saved me half an hour each day for a previously manual task.\nI\u0026rsquo;m not at all a fantastic developer by any means, nor would I want to write code for a profession. I just really enjoy problem solving, and sometimes the best way to solve a problem is with a bit of custom scripting. What gets me excited is the process of finding something that wouldn\u0026rsquo;t normally be possible and knowing that I have the skills and ability to make it happen. Over the past five years, my basic level Python abilities have enabled me to work my way through a number of problems - or write various scripts to make my job easier.\nYou\u0026rsquo;ll need to dedicate some time and put in the effort up front to learn a new skill, but trust me it will be worth it.\nLook for New Opportunities Once you have learned the basics, start looking for ways to use your new skills. It\u0026rsquo;s a different way of thinking in some cases, and will likely take a bit of adjustment. Whats that? Your load balancer doesn\u0026rsquo;t have built-in reporting functionality to tell you how many server pools you have (and how many are actually fully functioning vs degraded)? Yep, they probably have an API which would be easy enough to pull that data from.\nOver the years, I\u0026rsquo;ve built scripts to automate load balancer configurations, generate reports, alert on BGP peering changes, auto-remediate IPSec VPN disconnects, and even a full IPSec VPN dashboard (since the vendor doesn\u0026rsquo;t supply one). As a network administrator, having the automation skills in Python has allowed me to accomplish many tasks that my co-workers have stated aren\u0026rsquo;t possible (solely based on the functionality not being native to a product). Sure, I spend a bit of time up front writing and testing out scripts - but it not only saves me time/effort, but also my peers who I share the scripts with. For example, my team used to have a maintenance task that would take a full hour to complete on a monthly basis. About a week worth of my own effort to write a script, and all of that work is now automated into a 30 second process.\nThink about how automation can help not just you, but your whole team.\nThe Future of Networking If you follow practically any news source for computer networking, I\u0026rsquo;m sure you\u0026rsquo;ve heard this already. Over the next few years the role of a traditional network administrator can and will change. Businesses are evolving more rapidly to meet customer demands, and we need to ensure that our networks can keep up. The only way this is going to be possible is through automation, or hiring an ridiculous number of people.\nPractically all major networking vendors are integrating APIs into the new iterations of their device platforms. Some are fantastic, and some are less than ideal - but they\u0026rsquo;re all working on it. In some way or another these new APIs will become a part of your job - whether you\u0026rsquo;re writing the code to perform tasks or just using a script written by someone else. Does this mean we have an end to our careers in the future? No - the CLI will take a while to completely disappear. Even if it does go away completely, the role of a network admin will not be replaced, but evolve into something a bit different from what we know today. Even today, having the skills to automate tasks out of your daily job can allow you to spend your time on more important things (like a new network design, or that big project you haven\u0026rsquo;t had time to look at yet).\nYou can probably get away with not learning scripting and automation for quite some time yet - but don\u0026rsquo;t you want to make your life easier and be prepared for the future of your career? I know I do.\n","permalink":"https://0x2142.com/you-should-automate-something-this-year/","summary":"Need a push to start learning network automation?","title":"You Should Automate Something This Year"},{"content":"2017 is over! Now we\u0026rsquo;re on to whatever 2018 may bring. The past year has been very interesting for me. For one thing, it was the first full year of this blog which started in December of 2016. While I didn\u0026rsquo;t quite accomplish everything here that I had hoped for, I still managed to do a lot more than I realistically expected.\nOne of the things I\u0026rsquo;ve had problems with in the past is keeping a blog updated. Usually I would start, write an entry or two, then completely forget about it. I never thought I had good enough content to warrant sharing, or I was trying to keep to too narrow a topic. So when I started this blog, I said that I was going to focus on networking but leave it a bit more open-ended. I also wanted to try sharing some more generalized IT experience and career advice. I started off with a list of topics that I wanted to write about, and even began pre-writing a few of them so that I had a bit of content lined up ahead of time.\nEven though I told myself originally that I was only going to post something whenever I had something good to share, I still ended up setting myself a goal of writing one thing a week. For a while this actually worked out, because I was forcing myself to think about it more often - but eventually I ran out of immediate ideas. I had to remind myself that it was more important for me to write/post content that was actually worth reading, not just having something available on a weekly basis. Even so, I\u0026rsquo;ve managed to post 44 items since I started, 40 of which were in 2017 - Much better than I had actually anticipated.\nSo here is to 2018 - I\u0026rsquo;m not going to try and set any strict goals for myself in terms of posting content (or at least I\u0026rsquo;ll tell myself that now). However, I\u0026rsquo;m also going to try and work on getting better at putting up content. I spend too much time waiting for that \u0026lsquo;great thing\u0026rsquo; to write about, and not enough time on just writing something that might not be particularly fantastic - even though it might still benefit someone. I feel like I have a lot to share, and not everyone is an expert. Continuing to think that much of my content \u0026lsquo;isn\u0026rsquo;t good enough to post\u0026rsquo; is just holding me back. I\u0026rsquo;m going to try and be better this year about this - and not keep waiting for only the \u0026lsquo;great things\u0026rsquo; to share.\nThe other big thing I\u0026rsquo;ll be focusing on this year is studying for the CCIE R\u0026amp;S, which I wrote about in October. I bought a few books and found some training videos, which I\u0026rsquo;ve been slowly working though\u0026hellip; and when I say slowly, I mean probably much slower than I should be. Now that the holidays are over and it\u0026rsquo;s a new year, I\u0026rsquo;ll be pushing myself a more to actually make progress. My current tentative goal for attempting the written exam is June - so I\u0026rsquo;m hopeful that I\u0026rsquo;ll be able to make it work.\nThe blog has been fun so far, and I\u0026rsquo;ve done a bit more than I thought I would with it. However, there was one thing over the past year that I wasn\u0026rsquo;t really expecting at all - getting to talk with a bunch of other people who are interested in networking/IT. I\u0026rsquo;ve mostly been on Twitter, and more recently on Reddit\u0026rsquo;s /r/networking and /r/cisco. There have been a ton of people I\u0026rsquo;ve gotten to talk to, get opinions from, or even a few people that I\u0026rsquo;ve been able to help out with some of their problems. A large portion of my career has been limited to working with just a small team of people, few which actually have much interest in networking. I\u0026rsquo;ve really enjoyed the experiences over the past year, and I\u0026rsquo;m really looking forward to what else might come. If you\u0026rsquo;re one of the people I\u0026rsquo;ve interacted with over the past year, thank you!\nA new year comes with new challenges, problems, and complaints - but it also comes with new accomplishments and new things to look forward to. I hope that all of you reading this are able to set new goals for the year and pass your expectations!\n","permalink":"https://0x2142.com/one-year-later/","summary":"Some thoughts on the past year, and goals for the year to come","title":"One Year Later"},{"content":"Earlier this year I was involved in a string of interviews for an open network engineer position. The questions and scenarios provided during the interviews were aimed for someone mid-level. One of the more basic-ish scenario questions I like to ask is the following:\nGiven a brand new switch, can you provide me the commands you would use to configure the first four ports for hosts in VLAN 15?\nThis question is always interesting because I get such a wide variety of responses. You can certainly filter out people quickly who have never touched a switch. Some people will start with conf t, while others just jump straight into setting the VLAN tag. Some people specify that they\u0026rsquo;ll use an interface range command, while others get confused and want to configure the ports as a trunk. In fact, during this year\u0026rsquo;s interviews I had quite a significant number of people who completed the scenario by providing the commands to configure a trunk port, instead of static access. One thing that struck me as noteworthy is that the people who did this also provided the commands they needed to change the default native VLAN.\nFor a vast majority of networking devices (maybe all of them), the default/native VLAN for a trunk port is VLAN 1. This is not the best configuration for reasons we\u0026rsquo;ll get into a bit later - but unfortunately it needs to be manually changed. In every interview where the candidate suggested this be changed, I followed up with asking why. I asked so that I can find out two things: How well the individual is at explaining concepts, and whether or not this is something they just do because they were taught or if they actually understand the logic behind it. Surprisingly, a vast majority of the candidates could only provide the reasoning that \u0026ldquo;Well, VLAN 1 is bad\u0026rdquo; - but they couldn\u0026rsquo;t elaborate on why.\nSo why is VLAN 1 bad to use? Technically, VLAN 1 itself isn\u0026rsquo;t the problem. The concept of a default VLAN allows for someone to attack a network by taking advantage of how switches use a default VLAN. Since VLAN 1 is typically set as the default for most vendors, then it becomes a well-known configuration for attackers to abuse. If every vendor had set the default native VLAN to 52, then we would still run into the exact same problem except the \u0026lsquo;bad\u0026rsquo; VLAN would be 52. So let\u0026rsquo;s back up just a little here and explain a bit of background. The concept of a VLAN is a segmented logical network. Rather than requiring a different piece of physical hardware to keep hosts separate, we can assign them into different virtual LAN segments. This is accomplished by \u0026rsquo;tagging\u0026rsquo; each Ethernet packet with a VLAN ID. Internally, the switch code does not allow packets that contain one VLAN ID tag to be sent to hosts configured with a different VLAN ID tag.\nLet\u0026rsquo;s look at a few practical examples of how this works. When you configure a host port, you would configure the switch with the appropriate VLAN ID tag that the host should be assigned to. The actual server may know nothing about the VLAN it is in, but the switch knows to inject that VLAN tag into the Ethernet headers of every packet received from that server. For the rest of that packet\u0026rsquo;s life on the network, every switch reads that VLAN tag to assist in forwarding decisions. Trunk ports are commonly used between switches, and these ports are enabled to carry multiple VLAN ID tags. The problem here is that each switch is expecting that the packets it receives already contain a VLAN tag in the Ethernet header.\nSo whats the problem with the default native VLAN? This is a special type of VLAN in which the switch never attaches a VLAN tag to the headers. Whenever we connect two switches via a trunk port, we are likely configuring multiple VLANs that need to cross that link. However, the switches themselves still need to communicate directly with eachother (for protocol negotiations/spanning-tree), and each switch isn\u0026rsquo;t necessarily going to know what VLAN the other wants to use for this management traffic. This is where the concept of a native VLAN comes in. For every configured trunk link, each switch has a default native VLAN for which it expects to receive packets with no VLAN tag headers. Even though normal network traffic crossing a trunk link is going to require a VLAN tag in the headers, the switch-to-switch control-plane communication is sent with no header present.\nThis is where VLAN 1 becomes a problem because of the native VLANs are processed/interpreted by the switch. Whenever a switch receives a packet which contains the VLAN ID set to the native VLAN, it knows that packet doesn\u0026rsquo;t need to contain a VLAN header - so it removes the VLAN ID header.\nHow could this be exploited? Well for example, let\u0026rsquo;s say that we had a network where every developer PC was using a trunk port and permitted to use VLAN tags. This might be so they could run local VMs for testing that connect to both the DEV and QA networks. If we leave the default native VLAN as 1, then a malicious developer could exploit this to gain access to another segment. This is accomplished by using a software package to double-tag an Ethernet packet with two separate VLAN ID headers. The first VLAN tag header will be set to the native VLAN (VLAN 1), and the second header will be set to the target VLAN - let\u0026rsquo;s say we use VLAN 20 for the accounting network. When the switch receives the packet across the trunk link, it will read the Ethernet headers. When it processes the first VLAN tag and sees that it matches the default native VLAN, the switch strips this header - which then leaves the second VLAN tag header for VLAN 20. When the switch goes to forward this traffic to it\u0026rsquo;s destination, the remaining VLAN header will allow the packet to bypass any security measures and be directly forwarded on VLAN 20.\nWell what if we don\u0026rsquo;t provide end users with a trunk port? Could they still execute this type of attack? Remember that the default switchport configuration allows for dynamic negotiation - where the connecting computer can tell the switch whether or not it needs an access port or a trunk port.\nIf VLAN 1 is well-known as the default, what should the native VLAN be set to? Anything you like! The key thing is that it should be a VLAN which has no access to any network resources - so it should be a VLAN with no hosts and gateway. I usually don\u0026rsquo;t even create the VLAN on the switch itself, therefore immediately black-holing all traffic sent to that VLAN.\nHow else can this be prevented? Always statically set your end user ports to switchport mode access, and enable switchport nonegotiate. This will prevent the switch from allowing port negotiations, which prevents a user from tricking the switch into assigning a trunk port.\nNewer Cisco switches also support a global configuration command: vlan dot1q tag native. This command will force the switch to require VLAN tags to be present on packets in the native VLAN. See more detail here. This will need to be configured on all switches in your network to be effective.\nDo you change the native VLAN in your network? Or is it not a big security concern for you? Comment below!\n","permalink":"https://0x2142.com/whats-wrong-with-vlan-1/","summary":"\u0026ldquo;VLAN 1 is bad!\u0026rdquo; - I\u0026rsquo;m sure you\u0026rsquo;ve heard this before. Ever wonder why?","title":"What's wrong with VLAN 1?"},{"content":"There is a key to being successful at just about any IT job: Stop just doing work, and start understanding what you\u0026rsquo;re doing. Might seem like an odd thing to say right? But this is something that I have seen confuse engineers at earlier points in their careers.\nIn a lot of jobs, the initial training you receive is fairly straightforward. You are usually taught how to respond to a task by following a series of steps to get an intended result. Training like this is great - It helps to achieve consistency and efficiency. You bring in any new person and give them the exact same troubleshooting steps, implementation steps, and/or validation steps - and you\u0026rsquo;re likely to get a similar result every time.\nThis is the point where I have seen far too many people stop though. They are happy with doing their job, and don\u0026rsquo;t necessarily want to progress their career or maybe they don\u0026rsquo;t know how. These engineers will continue to produce decent work at the quality that they were taught at. Even for those who try and progress further (maybe through certifications or otherwise), there is a difference between learning new technologies/concepts and truly understanding them. For some people out there, having this basic level of skill is all they really want - and if that\u0026rsquo;s their goal in life, then this type task-based knowledge is perfect. But if you really want to master the domain of technology that you are interested in, then you need to put forth the time and effort into gaining that understanding.\nFor me, a true understanding of a technology means that you\u0026rsquo;re able to speak confidently about how something works, abstract concepts to apply to similar products, and mentally walk though how the technology might handle a given situation.\nLet me provide an example or two that might help to frame this a bit better. Given a particular network, an engineer might know that for traffic to get from point A to point B, it travels through two firewalls. Every time there is a new request to permit a new traffic flow, that engineer knows that they must make a configuration change to one or both of those firewalls to allow that traffic through. However, to this engineer, the inner workings of that firewall are a complete mystery. The firewalls are complete black boxes which take in traffic through one interface and spit it out another. So when there is a technical issue within the firewall appliance, they may be extremely limited in their troubleshooting abilities - and they may have no choice but to call the vendor for support.\nAnother engineer who has a deeper understanding of how firewalls work might see the problem differently. This engineer knows that for every packet received, the firewall follows a specific flow of processing. That flow could include any number of things, including NAT, routing, firewalling, IPS, VPN, etc. This engineer knows which order those things get processed and what effects those processes can have on the traffic. So when we have a technical issue with this firewall appliance, this engineer may be able to mentally walk though the packet flow/processing and determine where the problem may be - sometimes without even looking further than general log files.\nAnother example is something that I see quite often. An engineer is asked to implement something - let\u0026rsquo;s say a new port configuration for a server. They follow their known process for implementing this change, but something doesn\u0026rsquo;t quite work right. So they change settings or maybe delete the entire port configuration and start over - but eventually they get it working. They don\u0026rsquo;t know why it didn\u0026rsquo;t work the first time, or what caused it to work the second time - but it works now, so they aren\u0026rsquo;t concerned with it. However, it\u0026rsquo;s possible this engineer ends up running into this same problem more than once. The ideal step here would be to step back and look at what was different between the original configuration and the working configuration. Maybe there is an additional command in the original configuration which seems suspect - a quick search of the internet could turn up an explanation behind why that command was preventing the port from working as expected. After that research, that engineer would not only know why their configuration didn\u0026rsquo;t work - but now they know what that command actually does, which could be beneficial in a future scenario.\nAs I briefly mentioned earlier, not all IT admins or engineers are concerned with gaining a significant level of understanding. There are those who want to come to work, get their job done, then go home to their families - and there is absolutely nothing wrong with that. For me personally, I can\u0026rsquo;t handle running into a problem and not knowing exactly what the cause was. An issue that \u0026ldquo;fixes itself\u0026rdquo; is never an acceptable answer to me, because if something caused the problem once then it can certainly happen again. I don\u0026rsquo;t enjoy having to blindly configure an option on a system without knowing what\u0026rsquo;s going on in the background. Some people might call me crazy, but this seems to be a skill/trait shared by many higher-level engineers I have worked with. So how do you get to a point where you really understand a system? For me, it\u0026rsquo;s been a lot of playing in labs, reading vendor documentation, and not settling until I feel like I can speak confidently to how something works. I never feel truly comfortable in a new company until I can mentally walk through every device a packet touches from source to destination - and know which devices configs/routes may have an impact on that flow. Any time there is a problem with something, I spend time digging into it until I know what caused it - even if the problem is only momentary and goes away. Not only do I then understand why the problem happened, but I also learn how to quickly identify similar issues again.\nEspecially if you\u0026rsquo;re still in the beginning stages of your career, I can\u0026rsquo;t stress enough how important it is to understand the technologies you\u0026rsquo;re responsible for. Take the extra time and study it, play with it, break it and fix it again. Know how things work and what their behaviors are under different conditions. Don\u0026rsquo;t settle for \u0026ldquo;It just works because it does\u0026rdquo;. One of the key skills I\u0026rsquo;ve seen in engineers who truly understand their domain of technology, is the ability to abstract concepts to apply to other systems. Someone who has a deep understanding of routing and switching technologies might prefer to work with a certain vendor, but given any router/switch they can make it work.\nHave you worked with anyone who you think has a great understanding of what they do? What other skills or traits do they display that makes them successful? Comment below!\n","permalink":"https://0x2142.com/how-to-improve-stop-doing-start-understanding/","summary":"The best way to truly understand a technology is to dig deeper than surface-level configurations","title":"How to Improve: Stop Doing, Start Understanding"},{"content":"Over this past weekend I purchased a few upgrades to my home network/lab. One of which was upgrading my older Ubiquiti 802.11n wireless access point to the newer 802.11ac model they have out. The other purchase was a new external firewall. I had previously been running on a Cisco ASA5505, but the device is older and doesn\u0026rsquo;t support some of the newer features I would like to play with. In addition, in my current job I no longer support Cisco firewalls. So I bought a Juniper SRX300 - which should allow me to play with some new features I want, plus it can be a playground for testing things I want to do at work.\nAnyways - after I cut over to my new firewall, I\u0026rsquo;ve been digging through logs to make sure that I didn\u0026rsquo;t miss anything. I have all of my device/lab logs going into an instance of Splunk Light (their free product). It makes it easy to collect and search through logs, and it\u0026rsquo;s extremely easy to set up and use. A few quick queries and I came across one or two minor things that needed to be tweaked on my firewall - but I also saw some traffic that I wasn\u0026rsquo;t sure about.\nSo that brings me to my question of the day: Do you know what\u0026rsquo;s going out of your network?\nA lot of people I know only use firewalls to block inbound access, both in homes and businesses. For homes it\u0026rsquo;s more understandable since most average people aren\u0026rsquo;t network admins. However, it still surprises me how many businesses are willing to add a \u0026lsquo;permit any any\u0026rsquo; out to the internet. Yes, I block all traffic by default through my home firewall, both inbound and outbound. Yes, it\u0026rsquo;s a bit of a pain sometimes when something isn\u0026rsquo;t quite working right - but it\u0026rsquo;s usually a quick ACL change, and overall I would rather take the minor inconvenience for the security gains.\nWhen I originally built the firewall policy for my network, I started off simple. I know we need DNS, HTTP, and HTTPS outbound - easy enough, right? Then I started watching logs for blocked traffic and trying to decipher what else was trying to communicate outbound using another port. Some things were very easy to determine - TCP 5228 out to a Google owned IP? Yep that\u0026rsquo;s actually a known thing - a lot of Google services, like Chrome, will use this. Some other things were harder to figure out - like game consoles which use a very wide range of non-standard ports. Many of these weren\u0026rsquo;t really documented well by the console manufacturer, and meant that I spent a while between browsing forums and some trial and error.\nThis really gets interesting when you start digging past the stuff you know about. What about a PC in my home network that is trying (and getting blocked) to reach a few random IPs in Korea and Russia over a bunch of non-standard TCP ports? Yeah that doesn\u0026rsquo;t make me feel comfortable. Could it be a legitimate application, or is it malware? A few quick searches on the internet don\u0026rsquo;t turn up anything immediately helpful. For the time being, I\u0026rsquo;ll keep stuff like this blocked until I have time to spin up some packet captures to see what this traffic is actually doing.\nFor a business I feel like this type of thing is even more important than just what I\u0026rsquo;m doing at home. You certainly don\u0026rsquo;t want end users (or servers) possibly running strange applications, which might be transferring data to some unknown external party. It seems like larger companies seem to have a better handle on restricting outbound access than most smaller companies, who likely don\u0026rsquo;t have the time or see the value. However, I\u0026rsquo;ve also worked with a few larger organizations who still permit all user and server traffic out to the internet with no filtering in place.\nIf you\u0026rsquo;re not already blocking outbound traffic - Get some good logging in place. Use something like Splunk Light and start collecting firewall logs for everything going out of your environment. Start with the basics - create a list of the software/ports you know you\u0026rsquo;ll need to open. After a few weeks, start digging through the logs to figure out what else might need to be added to your list. Once you feel comfortable that you\u0026rsquo;ve compiled a sufficient base ruleset, schedule a time to make the change and put it in place. Start blocking the unknown traffic - and only permit when necessary.\nHow do you have your firewalls configured today? Do you permit everything or are you very restrictive? Comment below - I\u0026rsquo;m curious to see what other people are doing.\n","permalink":"https://0x2142.com/whats-going-out-of-your-network/","summary":"Ever consider enabling firewall filtering for outgoing traffic from your network? Let\u0026rsquo;s look at why that could be interesting\u0026hellip;","title":"What's Going Out of Your Network?"},{"content":"Spanning-tree protocol (STP) is one of those network technologies that is easy to forget about. It exists in the background of almost every network, and for the most part it does it\u0026rsquo;s job without any issues. However, there is still a huge benefit to understanding what STP does and how it works - because it\u0026rsquo;s default behaviors might not the the best for every network.\nI\u0026rsquo;ve been making progress going through my CCIE books, and the earlier sections are focusing on layer 1 and layer 2 technologies. A lot of this is review from CCNP studies, but with STP the book starts to get into additional detail on the inner workings of the protocol - which I\u0026rsquo;m finding to be quite fascinating. It seems like in many of the companies that I\u0026rsquo;ve worked I\u0026rsquo;ve found that a majority of the IT staff (whether sysadmins or network admins) don\u0026rsquo;t really have a good handle on how STP works and why it needs to be tuned. So this post is meant to cover spanning-tree at a very high level, and I\u0026rsquo;ll include some examples from issues I\u0026rsquo;ve seen in the past.\nSo what is spanning-tree protocol anyways? At it\u0026rsquo;s very basic level, STP is a communications protocol used between switches to allow them to identify redundant paths in the network. The goal of STP is to figure out what is the most efficient L2 path through the network, then block all other paths to prevent loops. The best way I\u0026rsquo;ve heard STP explained is that it\u0026rsquo;s essentially a routing protocol for layer 2. Rather than routers communicating and exchanging routes to determine the best path through a network, all of the switches will talk to determine the best (loop-free) layer 2 path.\nSTP determines the best layer 2 path - but the best path to what? When configuring a standard routing protocol (like EIGRP or OSPF), you might have a node that advertises a route for 10.10.10.0/24. All other routers in the network are going to select a best path to the router who originates this advertisement - but how does something like this work when we\u0026rsquo;re talking about layer 2?\nSpanning-tree relies on the concept of having a single root bridge of each network. At the beginning of a spanning-tree process, all switches will hold a quick election to determine who the root bridge is - then each switch will figure out what it\u0026rsquo;s own best path is to that device. The switch that ultimately becomes the root bridge will be based on the priority set by the administrator - but by default all switches are pre-configured with the same priority. In a tie, the switch with the lowest MAC address will win and become the root bridge.\nWhat does that actually mean? More or less, one switch gets put in charge of defining the best path through the network. All other switches examine all of their redundant paths to the primary switch, then figure out which of those paths are more preferable than the others. An important note here, is that the \u0026ldquo;best path\u0026rdquo; selected is all from the specific viewpoint of whichever switch takes charge.\nFor an example, let\u0026rsquo;s use the following topology:\nIn this example, we have five switches and a firewall - which are used to provide connectivity to two network segments (NET1 and NET2). For each of the two network segments, there are a number of different paths that traffic could take to reach the firewall. Without spanning tree, NET1 might send traffic to SW4, which in turn would forward it to both SW2 and SW3. This sounds like a good thing, since we would use all available paths to try and reach the firewall - but in reality this can cause other problems like the firewall receiving packets out of order.\nSo for the example above, let\u0026rsquo;s assume that SW1 becomes our root bridge. SW1 is now in charge of determining what the best path through the network is. It does this by sending out messages on all ports connected to other switches, called Bridge Protocol Data Units (BPDU). In this message, SW1 asserts it\u0026rsquo;s role as the root bridge - and provides some information for other switches to use for path selection. Each switch will examine the message from SW1 to determine which of it\u0026rsquo;s uplinks is the most efficient path to SW1. Once each switch does this, it will forward on the message to downstream switches - this time adding in some of it\u0026rsquo;s own information (or path cost).\nAfter all that is complete, we might be left with the following path below:\nThe green lines above show the final path that was selected. For NET1 to reach the firewall, it would use SW4, then SW2, then up to SW1. For NET2, it would use SW5 \u0026gt; SW2 \u0026gt; SW1. This leaves the orange links unused. In fact, spanning-tree will place these links into a blocking state. The switches might still listen on those links, just in case their neighbor starts advertising a better path - but they will not forward any data traffic on these connections. In the case of SW2 suddenly failing, SW4 and SW5 would still be aware of their connections through SW3 - and after a brief period would begin using those links to reach the firewall.\nThis is a very simplistic explanation, and there is a lot more in the background that actually happens during spanning-tree operation. There are a number of different STP standards that a switch can run, each with their own options for configuration and tuning. There are also methods of providing a loop-free path while still utilizing some redundant paths. I plan to cover some more detail on these topics in later posts.\nSo why should I care about STP? Remember that part earlier when I said that if STP priority is not configured, then switch with the lowest MAC becomes the root bridge? Well as it turns out, MAC addresses are the hardware addresses configured by the manufacturer - and these addresses increment as they produce new devices. So the lower MAC addresses are typically going to be the oldest equipment in your network. Unfortunately, this can have a dramatic effect on your network traffic if you\u0026rsquo;re not paying attention to STP.\nFrom the earlier example, what happened if SW4 became the root bridge? Maybe this was an old Cisco 2950 that someone forgot to replace and it\u0026rsquo;s just been left in the network. If the STP configuration went unmodified, then this switch would likely become the root bridge of our network. Let\u0026rsquo;s look at what that path might look like:\nSo in this case, SW4\u0026rsquo;s path to the firewall hasn\u0026rsquo;t changed. However, it\u0026rsquo;s best path to SW5 and NET2 is through SW3 - which means any traffic from NET2 to the firewall has to follow the path of SW5 \u0026gt; SW3 \u0026gt; SW4 \u0026gt; SW2 \u0026gt; SW1. Not only does that add more layer 2 hops that NET2 has to pass through, but it also adds more (unnecessary) load onto SW4. What happened if SW4 was so old that it still had 100M ports? It might get overwhelmed pretty quickly.\nNow you might be thinking, \u0026ldquo;How often does this really happen\u0026rdquo;? Well, when I started at my last job they were experiencing a similar issue. The primary building had three floors, each with two Cisco 3548 switches to service users. Each of these switches linked back to a pair of Cisco 4500 core switches. All of the 3548 switches were purchased at the same time (far prior to the 4500s), and it turned out that one of them on the third floor had the lowest MAC address in the network. The entire layer 2 topology was then based on this switch as the central point of the network. This caused the interconnects between the core switches to be put into blocking mode - meaning that if a switch on the second floor needed to connect to the alternate core switch, then it would have to pass traffic through the third floor. A quick change to the spanning-tree priority (during a maintenance period) was all that was needed to put the core switches back in charge.\nThis doesn\u0026rsquo;t immediately make spanning-tree a bad technology. As with just about anything in IT, it\u0026rsquo;s something you need to understand and tune to fit your needs - otherwise you\u0026rsquo;ll just get less-than-ideal results. At another employer, I actually found out that the previous network administrator had manually disabled all of the redundant paths in the network - because he didn\u0026rsquo;t understand STP, and therefore thought that any redundant paths would cause a loop. Spanning-tree isn\u0026rsquo;t something we need to be afraid of - it just needs a little attention.\nSo next time you\u0026rsquo;re logged into one of the switches in your network, just run show spanning-tree and double-check that the switch you assume is your root bridge actually is.\nWell I hope that this was helpful. As I mentioned earlier, I meant this as a fairly basic overview - but I intend on diving a bit deeper in later posts. The most fascinating part of networking to me is all the details on how things like spanning-tree actually work behind the scenes. Have any spanning-tree stories? Leave a comment below\n","permalink":"https://0x2142.com/l2-basics-spanning-tree-protocol/","summary":"The fundamentals of how Spanning-Tree Protocol works","title":"L2 Basics: Spanning-Tree Protocol"},{"content":"Note: I may receive commissions for purchases made through links in this post. This is to help support my blog and does not have any impact on my recommendations.\nBack in 2011 I picked up a Synology DS411 NAS, which has proved to be one of the most beneficial parts of my home lab. When I purchased it, I filled it with 4x 3TB drives for a total of 12TB of storage (~8TB usable with RAID5). I use the NAS as an iSCSI datastore for my home ESX hosts, which has helped me to run many more test virtual machines than I could have otherwise. I\u0026rsquo;ve also been using the NAS as a media server, file server, and a general backup location for everything I do.\nThe only problem with the DS411 is that the device is now over six years old - which means its processing power just doesn\u0026rsquo;t keep up with what I need it for today. The device is also reaching its end of life state, so I needed to replace it anyways. For reference, the device only came with a single-core 1.6Ghz processor and 512Mb of RAM.\nSynology just recently released their new 2018 models, so I opted to pick up the DS918+. I could have upgraded to an equivalent model(DS418) of what I already have, but I was really interested in some of the additional features offered by the plus-series model. The DS918+ supports docker containers and Synology\u0026rsquo;s own virtualization hypervisor - along with the ability to add extra RAM modules later if I need them.As sad as I am to see my DS411 go, it was definitely time for an upgrade!\nAnyways, I just completed the migration from my old DS411 NAS to a new DS918+. The whole process was much easier than I had anticipated, but I figured I would write up a quick summary of what I did:\nFirst thing - Update the current NAS to the latest version of Synology DSM\nControl Panel \u0026gt; Update \u0026amp; Restore \u0026gt; Click Update if one is available Take a backup of the DSM configuration\nControl Panel \u0026gt; Update \u0026amp; Restore \u0026gt; Configuration Backup \u0026gt; Click Backup Configuration Power off the old NAS - in my case, my DS411\nUnplug the old NAS and remove the drives\nFor the DS411, this requires disassembling the chassis MAKE SURE YOU KEEP THE DRIVES IN ORDER (I actually printed labels to put on mine) For the DS411, the drive numbers start top to bottom (Disk 1 is top, Disk 4 is bottom) Install the drives into the new NAS - in my case, a DS918+\nAgain, these drives must be replaced in the same order! The DS918+ numbers left to right (Disk 1 is the first slot on the left, Disk 4 is last slot on the right) Once all of the drives have been inserted - Plug in the new NAS and power it on\nDownload the Synology Assistant application\nThis is necessary because the new NAS will not retain the previous IP configuration of the old NAS Once the new NAS is booted up - Open Synology Assistant and click Search\nIt should locate the NAS, and the status should be Migratable In my case, it shows both network adapters for the NAS Select the NAS and click Connect - This will launch the Web UI of the NAS\nThe WebUI should show something similar to the screenshot below, with the button to Migrate\nIf the WebUI does not show you a migrate option, DO NOT CONTINUE. You may need to double-check that the drives have been inserted in the correct order. Click Migrate, then you will be prompted to select whether you would like to migrate all settings or perform a clean install of DSM\nPerforming a clean install will still retain all of the data on the NAS, but all DSM settings will be lost I selected the option to retain all of my settings Next, Click through the prompts to install the newest version of DSM Wait for the system to download and install DSM\nOnce complete - you will be brought to your DSM login screen and the migration is complete!\nIf you selected to keep the DSM settings, everything should still be there - with the exception of your IP/network configuration.\nAfter all that is complete - you\u0026rsquo;re ready to enjoy your new Synology NAS! The migration was significantly easier than I had expected it to be. The longest part for me was just removing the drives from the DS411 - since it requires disassembling the chassis and removing multiple screws to free the drives from the drive sleds. So far the DS918+ is fantastic - and I would highly recommend purchasing one to anyone who is interested.\nHope this quick tutorial helps out - Let me know in the comments!\n","permalink":"https://0x2142.com/how-to-migrating-to-a-new-synology-nas/","summary":"Just bought an upgraded Synology NAS? Here\u0026rsquo;s how to migrate to the new unit","title":"How to: Migrating to a New Synology NAS"},{"content":"Disclaimer: I\u0026rsquo;m not at all sponsored by anything I review on here. If there ever comes a time where someone is crazy enough to sponsor a review, I\u0026rsquo;ll definitely let you all know\nI\u0026rsquo;ve been considering the idea of posting some short reviews of products or services I use. Not at all meant to make this a review site, but just a bit of \u0026ldquo;here is what I use and what I think of it\u0026rdquo;. This is going to be the first shot at that idea - so let me know what you think!\nWhen I was first looking to start up my own personal blog again, I did quite a bit of digging around to try and find where to host it. I considered hosting it myself at home, but overall I didn\u0026rsquo;t really want to manage that overhead. I also looked at a number of hosting providers, but I never really found anything that I was quite happy with. So many of them would offer you an instance of a web platform like WordPress or Drupal, but not much control beyond that. I really wanted something where I could have full control over my own VM, like a VPS, but most of these services cost more than I wanted to pay.\nRight in the middle of my research for a hosting provider last year, Amazon announced a new AWS service offering: LightSail. I had already briefly considered picking up a small AWS VM for what I wanted to do, but I honestly didn\u0026rsquo;t want to try and manage usage when every single little thing is an additional cost (storage, bandwidth, etc). However, LightSail really caught my attention because of the stupid-simple pricing structure - the smallest allocation is only $5 a month and includes 512MB of RAM, 1 core CPU, 20GB SSD, and 1TB of free data transfer. This was probably more than enough for me to get started with a site, and the pricing was very tempting. As if they wanted to provoke me to try it even further, Amazon actually offers your first month free (for the $5/mo instance). Additionally, Amazon offers up to three free public IP addresses and free DNS management along with every plan - which I thought made this a fantastic offer.\nWith all that LightSail seemed to offer, I decided to jump on the $5/mo plan and use the free trial period to give it a shot. The setup of my first VM was extremely simple and only a few clicks: Pick a size, give it a name, and select which applications you want pre-deployed (optional). I went ahead and selected my options, and within seconds my VM was already powered on, pre-configured, and ready to use. Unfortunately, it seemed like my VM deployment didn\u0026rsquo;t quite go as smoothly as I would have liked. I wasn\u0026rsquo;t able to log into the admin console for my pre-deployed web application, and the deployment guide didn\u0026rsquo;t seem to match up with what I was seeing on my VM. I spent about ten minutes or so trying to troubleshoot this before it hit me: I could just delete and re-deploy this VM in seconds. So I did exactly that, and within 2 minutes I already had another VM instance deployed - and this one worked perfectly.\nNext I went ahead and applied a static IP to my instance. You don\u0026rsquo;t necessarily have to do this, and if you host your DNS within LightSail then it will automatically update your DNS any time your instance IP changes. However, since they offer up to three static IP addresses for free, I don\u0026rsquo;t really see a reason why you wouldn\u0026rsquo;t use it - unless the VM was temporary or not being published publicly. This process is also extremely straightforward: Click the button to create a new static IP, give it a name, and select which VM to assign it to. Easy enough, right? I also tried out the DNS hosting for only a very brief period of time. I ultimately ended up opting to move my DNS hosting out to CloudFlare, which I would like to cover in another post.\nOnce the LightSail VM is up and running, it\u0026rsquo;s easy enough to connect to it. They offer a web-based SSH console, and by default allow access to ports 443 and 22 for remote connections. Remote SSH connections are handled by public/private key pairs only - no username/passwords permitted for login. I actually prefer this, and there is an extremely simplistic interface for generating new or additional SSH keys and automatically configuring them within your VM. The management console also offers an easy-to-use firewall system, where you can open ports for common services via a drop-down menu. You\u0026rsquo;re also able to enter custom port numbers, or remove any/all open ports entirely. As a quick note: LightSail offers no traditional console access to your VM - so if you close port 22, then you won\u0026rsquo;t even be able to manage the VM from the web console since it uses SSH. For me, I would rather take the additional security step to only enable that port when I absolutely need to access the VM via SSH.\nSo it\u0026rsquo;s been almost a year since I started using Lightsail and overall I am extremely pleased. I\u0026rsquo;ve run one primary VM since I opened my account, and I\u0026rsquo;ve spun up a few additional VMs here and there for different testing. It\u0026rsquo;s easy enough to just turn up a new VM, try it out, and then just purge it if/when you don\u0026rsquo;t need it any more. The billing reports are very straightforward too - and I\u0026rsquo;ve so far never come close to using all of my 1TB free data transfer. The VM itself is extremely snappy for only a single core with 512MB of RAM.\nOverall I would highly recommend giving LightSail a try, even if you only use it for the 1 month free trial. I\u0026rsquo;ve been very happy with the service so far, and I\u0026rsquo;m looking forward to any new features/functionality that might be added in the future.\nAs a quick summary:\nBenefits:\nExtremely easy to use (Simplistic interface) Three free static IPs Free DNS management Up to 1TB of free data tranfer First month of a $5 instance is free Pre-deployment/configuration of multiple different web applications Drawbacks:\nYou can\u0026rsquo;t move which zone your VM is deployed in You can\u0026rsquo;t rename your VM instance In order to upgrade plans, you have to take a snapshot and restore it to a new VM - and this is currently only available via the AWS APIs, so no way to do this in the web portal Snapshots take forever, and you get charged for how long they are stored for (I\u0026rsquo;ve had a snapshot of a 20G VM take \u0026gt;30 minutes in the past) My wishlist:\nAn integrated load balancer - even one with very basic options available Native support for IPv6 addresses - Amazon already offers a free IPv4 address, so why not v6 too? More storage options - I am careful about how many images i upload to my site, because I only have 20GB of space available. I don\u0026rsquo;t want to pay for S3 if I don\u0026rsquo;t have to. UPDATE 12/19/2017- As of the end of November, Amazon has added both Load Balancing and additional storage to LightSail. Currently the load balancing functionality costs $18 per month, and additional disk space is $0.10 per GB per month.\nHave you used LightSail before? What are your thoughts? Comment below!\n","permalink":"https://0x2142.com/review-amazon-lightsail/","summary":"My experience with hosting this blog on Amazon Lightsail","title":"Review: Amazon LightSail"},{"content":"Note: I may receive commissions for purchases made through links in this post. This is to help support my blog and does not have any impact on my recommendations.\nI first completed my CCNA certification back in August of 2007. After that I started working on certifications pretty heavily, because I wanted to learn as much as I could about networking. I used the certifications as both motivation to learn and a measurable goal of my knowledge. Over the next few years I obtained a number of Cisco\u0026rsquo;s associate-level certifications, and by April of 2011 I had finally obtained the CCNP.\nLater in 2011 I had changed jobs to a company where certifications were not valued as much. Instead, they urged me to return to school and obtain a college degree. This obviously took up enough of my free time that I really couldn\u0026rsquo;t spend as much time on studying certifications as I wanted to. In 2014 when I needed to re-certify my CCNP, I was just barely able to squeeze together enough time to study for the CCDP ARCH exam. This allowed me to re-certify what I already had, plus gain an additional certification.\nFast forward to early 2017 - I needed to re-certify again. I spent a bit of time trying to figure out what new tests I could study for. If I was going to re-certify then I would rather spend that time learning something new than just re-take a test for something I\u0026rsquo;ve already done. Unfortunately, I was nearing the end of my college degree program, and I just couldn\u0026rsquo;t find the time to dedicate to a new certification - so I ended up re-taking the CCNP TSHOOT exam to re-certify.\nAfter I finished the degree program, I opted to finally take a break for a bit. Even just two months later, and I was already considering what to do next in terms of certification studies. I wanted to look at Juniper\u0026rsquo;s certification line, since I\u0026rsquo;m more heavily involved in their equipment now - but I also wanted to look at what\u0026rsquo;s next in terms of Cisco certifications.\nWell, I\u0026rsquo;ve finally made up my mind, and purchased my first set of books to begin studying for the CCIE R\u0026amp;S. I\u0026rsquo;ve been itching for the past few months to start working on something, but I wasn\u0026rsquo;t really having much luck making a final decision. However, I was talking recently with our new manager at work about the potential of going to Cisco Live in 2018. This is something I\u0026rsquo;ve inquired about multiple times before and had no luck in getting approval to go. Since we have a new manager, the answer has changed to a \u0026ldquo;Sure, why not?\u0026rdquo;. Since I found out that Cisco Live offers free certification testing (and the CCIE tests are quite expensive), I decided to use that as my motivation to begin studying.\nSo here goes nothing! My current goal date is June 10th of 2018. By that date I want to be 100% confident in my ability to take and pass the CCIE R\u0026amp;S written exam. I half had the notion of trying to shoot for being prepared for the lab by then, but eight months might be a little too tight of a timeline - at least given what I\u0026rsquo;ve read from other people\u0026rsquo;s experiences. So I\u0026rsquo;ll shoot for the written test by then, with the intent of scheduling the lab soon after.\nMy current plan is to read through the CCIE R\u0026amp;S Official Cert Library first, then use that as a gauge to see what I know I\u0026rsquo;ll need a refresh on some of the content from that. I also know that IS-IS is included in the CCIE, which is something that was removed from the CCNP right before I started studying for it. However, it was actually still part of the CCNA content when I took that - so I have a very basic level of understanding. Outside of that - most of my current and recent jobs have focused heavily on switching technologies and less on routing. I\u0026rsquo;ve been working quite substantially with BGP, but not much with internal routing protocols - so that\u0026rsquo;s another point where I\u0026rsquo;ll likely spend additional time.\nI know I definitely have a lot to learn, and it\u0026rsquo;s going to be a long several months of study. Obtaining the CCIE certification has been one of my goals since nearly the beginning of my networking career. I\u0026rsquo;m really excited by actually getting the chance to work towards that goal. I\u0026rsquo;m sure I\u0026rsquo;ll be writing a bit here and there as I go through my studies, so look forward to that!\nIf you have any insight you wish to share, please leave a comment below.\nWish me luck!\n","permalink":"https://0x2142.com/my-2018-goal-ccie-rs/","summary":"I\u0026rsquo;m finally starting to work toward one of my long-standing goals: The Cisco CCIE Certification","title":"My 2018 Goal: CCIE R\u0026S"},{"content":"Default settings are the worst. Every systems has them, and they\u0026rsquo;re great until they\u0026rsquo;re not. For whatever reasons in the past, my predecessors decided to purchase a bunch of bare-bones HP servers and install Check Point\u0026rsquo;s firewall software on them. The HP servers were significantly cheaper than buying Check Point\u0026rsquo;s branded appliances, but unfortunately they come with a different set of risks. For example, you have to work on estimating max throughput yourself, rather than knowing exactly what the appliance is rated for.\nOver the past few weeks, we have been lightly troubleshooting an issue between a VMware vCenter server and the ESX hosts that it manages. ESX hosts were randomly showing up as disconnected for a brief moment, then would reconnect. It was nothing extremely impacting, but a mild annoyance for the server guys. A couple of people on my team had taken a quick look on the network side, and turned up empty handed. Due to some upcoming maintenance work the server team needed to perform, I was asked to spend some time trying to isolate the root cause of this issue.\nFirst thing was digging through the logs from the two different sets of firewalls between these systems. The first firewall set showed that traffic was passing normally as I would expect. However, I started seeing some unexpected logs for the second firewall set, a CheckPoint cluster. The logs showed that vCenter was opening connections out to the ESX hosts for a short while, then the CheckPoint would log a \u0026ldquo;TCP Packet out of state\u0026rdquo; error. The details of this log would show that vCenter sent a non-SYN packet to the ESX host (usually a PSH ACK).\nSeeing an error like that indicates that something is killing the TCP connection before vCenter is finished using it. vCenter still believes that the connection is open, which is why it sends packets with incorrect flags. Since we were already aware that this particular CheckPoint cluster has some issues, we began examining this cluster first. Sure enough, the IPS logs on the device showed that the cluster was often reaching \u0026gt;80% of it\u0026rsquo;s maximum concurrent connections and then enabling the \u0026ldquo;Aggressive Aging\u0026rdquo; feature.\nAggressive Aging is a CheckPoint protection which prevents the cluster from running out of memory and potentially crashing. By default, this is set to take effect whenever the cluster exceeds 80% of it\u0026rsquo;s available memory or concurrent connections. This protection will continue to be enabled until the cluster drops below another threshold, which is below 78% by default. Seems like a helpful feature to have, right? Yeah - but there are some considerations with how this protection works. When Aggressive Aging is activated, the cluster significantly reduces all of the normal TCP timeout values. For example, CheckPoint\u0026rsquo;s documentation shows that new TCP sessions are given only 5 seconds to establish, instead of the normal 25 seconds. This also changes how long a TCP session can be open from 1 hour to 10 minutes. In order to help drop below the 78% threshold, Aggressive Aging will evaluate and terminate 10 connections for every individual new connection that is established.\nAs I stated previously, this cluster was already pretty busy - often hitting CPU limits mostly. However, through the brief research I completed, it looks like increasing the concurrent connections table mostly affects RAM utilization more than anything else. This system has over 20G of RAM and is typically only using around 4GB. I was still concerned that an increase in total concurrent connections could mean more CPU usage, because that means more connections for the IPS to process. Unfortunately, CheckPoint has no publicly available utilities to help calculate what to set your max concurrent connection limit to. In fact, when I opened a support ticket with them, I was told to \u0026ldquo;just keep increasing it, until you hit a point where the cluster is no longer triggering Aggressive Aging. Then add about 10-20k above that to set the new maximum concurrent connection limit\u0026rdquo;. That\u0026rsquo;s not really an acceptable answer to me, but I wasn\u0026rsquo;t able to get anything more out of them.\nSo in order to change the maximum concurrent connections (Using R77.xx), you need to open SmartDashboard and open the cluster object. Then find Optimizations in the left-hand menu. Here you can set a new manually-defined limit, or allow the cluster to automatically scale the maximum connections. If this cluster was significantly less busy, I might be tempted to enable the automatic limit for a bit and try to get a baseline. However, I would rather not open myself up to the chance of crashing the cluster - so I manually increased the limit from 25,000 to 50,000. Install the policy for the configuration to take effect. You can see the current concurrent connections by either looking at the Overview page in SmartDashboard, or logging into the cluster CLI and using the cpview utility.\nIn my case - the new connections almost immediately started ramping up to ~35,000. Within a day we started encountering the Aggressive Aging protection again, but it was happening significantly less often than before. This also resolved our ESX host disconnection problem, which proved my theory that the Aggressive Aging feature was causing our problem. I\u0026rsquo;ve been slowly monitoring and increasing the concurrent connections limit since, and I think we have finally stabilized around 90,000. Just think of how many connections were denied or terminated early because this limit was in place!\nMoral of the story here: Understand the systems that you own. This firewall cluster had been in place years before I was hired, and all of the settings were left at their defaults. Default settings probably work for most cases, but they also come with their own problems. This setting had likely been the cause of multiple problems in the past, however no one truly understood they system enough to find out what was happening. Ever have a scenario where a default setting caused problems? Share it in the comments!\n","permalink":"https://0x2142.com/devil-in-the-defaults/","summary":"It\u0026rsquo;s always the simple details that come back to haunt us\u0026hellip;","title":"Devil in the Defaults"},{"content":"Over the past few years of my Juniper SRX adventures, I\u0026rsquo;ve run into a few cases where the Routing Engine (RE) CPU is pegged at 100%. From what I\u0026rsquo;ve seen so far, this is typically one of three causes: high traffic (spike in IPS inspection), logging using event mode, or a stuck web management session.\nIn a few occasional cases, the CPU issue doesn\u0026rsquo;t resolve itself and someone needs to manually investigate the cause. Luckily, the httpd issue is pretty easy to spot and fix - so I wanted to cover that briefly today. This issue can crop up randomly after someone uses the JWeb GUI to administer an SRX firewall. You could avoid this issue entirely by disabling the web interface entirely - but that\u0026rsquo;s not always possible.\nSo the first thing we want to do is log into our SRX firewall and check the current CPU utilization for our RE processor:\n{primary:node0} root@test-srx\u0026gt; show chassis routing-engine node 0 node0: -------------------------------------------------------------------------- Routing Engine status: Temperature 41 degrees C / 105 degrees F CPU temperature 70 degrees C / 158 degrees F Total memory 4096 MB Max 1556 MB used ( 38 percent) Control plane memory 2976 MB Max 804 MB used ( 27 percent) Data plane memory 1120 MB Max 773 MB used ( 69 percent) 5 sec CPU utilization: User 41 percent Background 0 percent Kernel 59 percent Interrupt 0 percent Idle 0 percent Model RE-SRX345 Serial ID XX1000XX0002 Start time 2016-09-01 02:49:50 UTC Uptime 351 days, 13 hours, 28 minutes, 47 seconds Last reboot reason 0x1:power cycle/failure Load averages: 1 minute 5 minute 15 minute 1.29 1.27 1.10 So we can see that over the past 5 seconds, there is 0% idle CPU - It\u0026rsquo;s all split between User and Kernel. Some higher-end SRX models will also show utilization for 1 minute, 5 minutes, and 15 minutes.\nNext, we want to confirm which process is consuming that CPU:\n{primary:node0} root@test-srx\u0026gt; show system processes extensive node 0 node0: -------------------------------------------------------------------------- last pid: 25330; load averages: 1.16, 1.24, 1.10 up 351+13:29:51 16:19:11 165 processes: 21 running, 132 sleeping, 12 waiting Mem: 354M Active, 191M Inact, 1253M Wired, 585M Cache, 112M Buf, 1595M Free Swap: PID USERNAME THR PRI NICE SIZE RES STATE C TIME WCPU COMMAND 1635 root 7 76 0 1192M 113M RUN 0 ??? 281.93% flowd_octeon_hm 14607 nobody 3 76 0 14848K 6308K ucondt 0 25:03 83.45% httpd 21 root 1 171 52 0K 16K RUN 0 6952.9 0.00% idle: cpu0 1679 root 1 76 0 48580K 24476K select 0 90.2H 0.00% mib2d 1715 root 1 76 0 35264K 19520K select 0 49.0H 0.00% snmpd 23 root 1 -20 -139 0K 16K RUN 0 29.9H 0.00% swi7: clock 1681 root 1 4 0 101M 68284K kqread 0 28.0H 0.00% rpd 22 root 1 -40 -159 0K 16K WAIT 0 26.0H 0.00% swi2: netisr 0 \u0026lt;-- Output Truncated --\u0026gt; In this case it\u0026rsquo;s pretty clear that httpd is the top offender for CPU usage. You might also notice the process named \u0026lsquo;flowd_octeon_hm\u0026rsquo;. This is part of the firewall processes, so don\u0026rsquo;t be surprised if this process is also one of the top. It\u0026rsquo;s pretty normal for this process to show \u0026gt;100% CPU, so this is safe to ignore. If you see eventd as a top consumer, then you might have your logging configured to use event mode rather than stream mode - which I\u0026rsquo;ll cover in another post.\nSo how do we fix the httpd problem? Reboot the SRX? Well, yeah that would probably fix it - but there is an easier way:\n{primary:node0} root@test-srx\u0026gt; restart web-management Web management gatekeeper process started, pid 25343 One quick command and we\u0026rsquo;ve restarted all of the web management processes, including httpd. So now you\u0026rsquo;ll want to give the SRX a few seconds to recover itself - then run the show system processes extensivecommand again:\n{primary:node0} root@test-srx\u0026gt; show chassis routing-engine node 0 node0: -------------------------------------------------------------------------- Routing Engine status: Temperature 41 degrees C / 105 degrees F CPU temperature 69 degrees C / 156 degrees F Total memory 4096 MB Max 1556 MB used ( 38 percent) Control plane memory 2976 MB Max 804 MB used ( 27 percent) Data plane memory 1120 MB Max 773 MB used ( 69 percent) 5 sec CPU utilization: User 6 percent Background 0 percent Kernel 3 percent Interrupt 0 percent Idle 91 percent Model RE-SRX345 Serial ID XX1000XX0002 Start time 2016-09-01 02:49:50 UTC Uptime 351 days, 13 hours, 32 minutes, 52 seconds Last reboot reason 0x1:power cycle/failure Load averages: 1 minute 5 minute 15 minute 0.35 0.99 1.04 Looks much better, with 91% idle CPU!\nEven though this issue can be annoying, its a quick fix - I recommend that you perform some sort of CPU monitoring/alerting on your SRX clusters (I use Observium for this). This will help to identify the issue quickly and then get it resolved quickly. If this issue is left unchecked, it can sometimes cause some latency and performance issues.\nHope this helps!\n","permalink":"https://0x2142.com/srx-httpd-high-cpu/","summary":"How to manage stuck httpd sessions that are causing 100% CPU utilization","title":"SRX High CPU: httpd"},{"content":"It\u0026rsquo;s August of 2017 - which means it\u0026rsquo;s been a long ten years since I originally obtained by CCNA certification in 2007. I figured it might be a good time to take a minute and look at what that has meant for me, and how the last ten years of my career have gone.\nWhen I got my CCNA certification, I was only two months out of high school. I had just finished two years of the Cisco Networking Academy coursework, and I had no idea what that would actually mean for me. I went and took the certification exam mostly because I felt that like that was the only way to validate what I had learned during the two year class. I failed it once or twice, which nearly discouraged me enough to not try again. However, I ended up passing the test and becoming Cisco certified on August 27th, 2007.\nObtaining that certification didn\u0026rsquo;t immediately make me valuable to anyone. However, it definitely helped to get my resume in front of a number of people who probably wouldn\u0026rsquo;t have taken a look otherwise. At the time, I had no college degree and absolutely no real-world networking experience. I owe that CCNA cert for helping me get my first job - but after that it was up to me to prove my worth.\nIt\u0026rsquo;s amazing to sit back and realize that ten years has passed already. So much has happened, so much has changed. I spent the first three or four years of my career studying hard to additional Cisco certs, which I used as motivation to learn more about networking. Certifications can be great for validating what you know, but it\u0026rsquo;s the real-world skill that really pays off in the end. Even with my original intent to become a network admin, I\u0026rsquo;ve ended up wearing a lot of hats and picking up more of a variety of skills than I ever thought I would. It\u0026rsquo;s definitely been a good thing though, since it has allowed me to get a better understanding of other systems - which in turn helps me to better support them as a network admin.\nEven though today I don\u0026rsquo;t really manage much in the way of Cisco equipment, the original skills that I learned in the Cisco Networking Academy program have given me a great base knowledge to work with. All of the fundamental networking skills I learned have translated quite well to other vendors and products. I\u0026rsquo;ve spent the past few years working with Brocade, Juniper, Check Point, and a number of other vendors - and I feel like I have had a much easier time picking up the new skills than I might have had otherwise.\nToday I still hold and maintain my Cisco certifications - and I plan to continue doing so for the foreseeable future. Someday I would like to achieve a CCIE/CCDE-level certification, but for now I am happy with what I have and what these certifications have helped me to achieve in my career.\nThanks for reading - here is to hoping for the next ten years to be just as good as the last.\n","permalink":"https://0x2142.com/ten-years-of-cisco-certification/","summary":"It\u0026rsquo;s hard to belive I started my networking career ten years ago.. How quickly time flies!","title":"Ten years of Cisco Certification"},{"content":"I ran into an interesting issue recently, which was caused by use of the switchport protected command. So I use a pair of Cisco 2960-8TC-L switches at home, for both my home network and lab. A few months back I ran a bunch of ethernet cabling within my house, which all terminated in a patch panel in the basement. I was able to migrate/consolidate enough of my ports so that I could dedicate one of the 2960s to the patch panel. I had eight ports on my switch, and eight ethernet drops in my house - one of which ran back to my lab network for internet.\nUsually when I configure something like this, I want to try and take security into consideration as much as possible. I have a Synology NAS on my home network, which contains enough of my personal backups that I would want to keep this inaccessible from a typical house-guest. So by default, I made the following configuration standards on the ports connected to my patch panel:\nAny unconnected ports were added to my guest VLAN (which only has internet access) Any ports that needed to be in my home VLAN were configured with port security, sticky MAC, and maximum 1 MAC allowed All ports were configured as switchport protected (except the uplink) The concept of protected switchports should be fairly simple: Any port configured with switchport protected is not permitted to communicate with any other port configured with switchport protected. A protected switchport is only permitted to communicate with a non-protected port (in this case, my uplink/trunk to my other 2960). I added this mostly as a safeguard against a potentially malicious house-guest.\nHowever, once I actually began to use my patch panel ports, I began to experience a very interesting issue. For example, I purchased a home security camera which by default used a wireless connection. The location of the camera unfortunately made the wifi connection a bit more unreliable than I would like for a security camera. So I went ahead and ran a cable to the nearest ethernet drop.\nThe IP camera uses my Synology NAS as a backend storage for any recordings. It was able to connect and stream video on the wireless connection, but the video was choppy. Once I plugged in the ethernet cable the connection actually got worse than it already was. From my Synology - the camera would become unresponsive for a while, then you could reach it again for a few moments, then back to unresponsive (about 60-70% packet loss). If I disabled the wireless NIC entirely, the camera would be completely unresponsive. However, this whole time I was able to reach the camera with no issues from my laptop which was connected via wireless (my AP uses the same switch as the Synology).\nThe NAS is connected to the 2960 in my lab, which is connected to the patch-panel-2960 via a single trunk port. From the Synology, I could still see ARP entries for my wired camera - I just couldn\u0026rsquo;t reach it via ping or http. I spent a good hour or two trying a number of things: clearing ARP entries, double checking my trunk port configs, and I also upgraded the firmware on the IP camera. Nothing seemed to work. It made even less sense that anything else connected to the Synology-side switch could hit the camera with no problems.\nIt\u0026rsquo;s worth noting that no ports on the Synology-side 2960 were configured with switchport protected - only the ports on the patch panel side. So I finally tried removing the switchport protected command off of the IP camera port - and magically it all started working.\nThe protected switchport config worked exactly as I would have expected for traffic between ports on the same switch - however, it seemed to act against what I would have expected once it crossed a trunk to another switch. It was especially odd that it only seemed to crop up between the Synology and the IP camera. Oh well, I guess the only way you really learn something is by breaking it, right? I hope this might help someone who finds themselves in a similar situation.\n","permalink":"https://0x2142.com/odd-behavior-of-protected-switchports/","summary":"Some thoughts on an interesting issue I ran into with older Cisco switches \u0026amp; protected port configuration","title":"Odd Behavior of Protected Switchports"},{"content":"In last weeks post, we took a look at how to set up a chassis cluster on a Juniper SRX Firewall. So now that we have a basic cluster setup - Let\u0026rsquo;s explore some of the additional options and configuration items.\nRedundant Ethernet Interfaces So first thing is first - Once you have a cluster configured, you\u0026rsquo;ll probably want to configure a few sets of redundant ethernet interfaces. These interfaces are also often referred to as reth interfaces. This will create a shared interface between your SRX pair, where you can configure IP address and VLAN information to be shared between the two. Let\u0026rsquo;s say that we have a Juniper SRX 1500 cluster, and we want to create a redundant interface for one of our 10Gb ports. Here is how we would do that:\nroot@testsrx# set interfaces xe-0/0/16 gigether-options redundant-parent reth1 root@testsrx# set interfaces xe-7/0/16 gigether-options redundant-parent reth1 root@testsrx# set interfaces reth1 redundant-ether-options redundancy-group 1 In the config above, we first take both of our interfaces (xe-0/0/16 on node0, and xe-7/0/16 on node1) and tell them that they now belong to a redundant interface group (reth1). Next, we enter into the reth1 config, and associate it to a redundancy group.\nYou\u0026rsquo;re also going to need to keep in mind that the SRX requires you to specify how many redundant ethernet interfaces will be configured. This is likely a memory thing, since each SRX also has a different maximum number of reth interfaces that can be configured. For example, if you tell the SRX that you need 5 reth interfaces, then the SRX will allocate system resources to manage those interfaces. In order to set the number of available reth interfaces, we\u0026rsquo;ll use the following command:\nroot@testsrx# set chassis cluster reth-count 5 Redundancy Groups A redundancy group, or RG, is used as a container for logically grouping redundant interfaces/virtual routers which must fail over together. A single RG can be configured as primary on one of the two active SRX firewalls is a cluster - with the ability to fail over to the other node. For example, we might want be planning on only using one virtual routing instance on our SRX - so we would create RG1 and assign out interfaces to belong to it.\nA quick note - all interfaces in a single virtual router must belong to the same RG. This way the virtual routing instance and all of it\u0026rsquo;s associated interfaces will always run on the same SRX node. In order to achieve an active/active firewall configuration, you would need to create two separate virtual routers, each with their own reth interfaces and different RGs. Then you would make RG1 primary on node0, and RG2 primary on node1.\nIn most configurations, dumping all of your reth interfaces into RG1 will be sufficient. You\u0026rsquo;re likely going to want to set up a priority for each RG - and maybe even preemptive fail-over. In order to do that - you\u0026rsquo;ll have to configure each cluster member with a priority:\nroot@testsrx# set chassis cluster redundancy-group 0 node 0 priority 200 root@testsrx# set chassis cluster redundancy-group 0 node 1 priority 50 root@testsrx# set chassis cluster redundancy-group 1 node 0 priority 200 root@testsrx# set chassis cluster redundancy-group 1 node 1 priority 50 root@testsrx# set chassis cluster redundancy-group 1 preempt The higher priority wins here - so if you set node0 to a higher priority and preempt is enabled, then node0 will actively try to take ownership of RG1. I would rather not set preempt on RG0 for a few reasons - which we\u0026rsquo;ll cover in the next section. Priorities can also be modified using interface monitoring, so if a particular interface goes offline we can decrement the priority of that node (also covered below).\nA Note About RG0 You might notice from the last post, that you\u0026rsquo;re output of show chassis cluster status already showed two redundancy groups: RG0 and RG1. RG0 is only used for management traffic and manages the routing engine for your SRX. Unfortunately, this can lead to some weird behaviors that you might not be expecting.\nFor example, whichever node is primary for RG0 is the only node that collects interface and monitoring statistics. If you\u0026rsquo;re using a monitoring tool that polls data from both of your SRXs, then the secondary for RG0 will report nothing about it\u0026rsquo;s interfaces, CPU, etc. This is also true if you log into the actual SRX itself - a show interfaces will actually return a bunch of default values, including showing that your ports are half-duplex. Don\u0026rsquo;t panic though, this is just an oddity of RG0. If you log back into the primary node for RG0, then it will show all of the proper statistics for both SRX firewalls.\nDue to these weird things about RG0 - I prefer to always leave it on node0. Therefore I know which one to log into whenever I need to look at something, or which SRX to check in our monitoring tools. It\u0026rsquo;s also worth noting that whichever SRX is primary for RG0 is also the node you\u0026rsquo;re going to need to log into for configuration changes - even if all of your other redundancy groups are the other SRX.\nWeird, right?\nOh, and be warned that since RG0 controls the routing engine, a failover of this RG can cause brief outages. This is primarily because the routing table and firewall state information will be lost. The secondary node has to spin up new processes for the routing engine, and at least currently there isn\u0026rsquo;t a graceful sync of all of that data.\nInterface Monitoring I mentioned setting device priorities a bit earlier. Setting interface weights is going to be the primary method for dynamically affecting those priorities, and therefore possibly causing a preemptive failover. One example might be that you\u0026rsquo;re using an SRX cluster for your edge firewall, and you want it to automatically fail over if the primary loses it\u0026rsquo;s internet uplink.\nNote that you must configure the physical interfaces here, not the redundant ethernet interfaces:\nroot@testsrx# set chassis cluster redundancy-group 1 interface-monitor xe-0/0/16 weight 160 Remember when we set the priorities of our firewalls earlier? Node0 was set to 200, and node1 at 50. So here we are saying that xe-0/0/16 on node0 is worth 160 points. So if xe-0/0/16 goes down, then node0 will decrement it\u0026rsquo;s priority by 160 - which will be 40. This will trigger a preemtive failover by node1. The reverse is also true - when xe-0/0/16 comes back up, then node0\u0026rsquo;s priority will go back up to 200. Then node0 will take back ownership of RG1.\nManual Failover There is a pretty good chance at some point you might need to perform a manual failover of your SRX redundancy groups. Maybe you need to do some maintenance or upgrades, or you just want to make sure failover works as you expect. In either case, the commands to do this are pretty straightforward:\nroot@testsrx\u0026gt; show chassis cluster status Cluster ID: 5 Node Priority Status Preempt Manual failover Redundancy group: 0 , Failover count: 0 node0 200 primary no no node1 50 secondary no no Redundancy group: 1 , Failover count: 0 node0 200 primary yes no node1 50 secondary yes no root@testsrx\u0026gt; request chassis cluster failover redundancy-group 1 node 1 root@testsrx\u0026gt; show chassis cluster status Cluster ID: 5 Node Priority Status Preempt Manual failover Redundancy group: 0 , Failover count: 0 node0 200 primary no no node1 50 secondary no no Redundancy group: 1 , Failover count: 1 node0 200 secondary yes yes node1 255 primary yes yes Okay - so let\u0026rsquo;s talk about a few things that have happened here. I always recommend that you run a show chassis cluster status first, so you know where things already stand. Then we can proceed by requesting a failover. To do this, you have to specify which redundancy group you want to fail over, and which node you want to become the new primary. So in this case, we made node1 the new primary of RG1.\nYou might also notice that the priorities have changed, and the devices are marked as being in a manual failover state. This is important, because you cannot manually fail back until you reset this state. That\u0026rsquo;s right - if you tried to run the failover command again to move RG1 back to node0, it will not work. An automatic failover due to hardware failure or interface monitoring will still be permitted. In order to perform a manual fail-back to node0, we have to run the following reset command:\nroot@testsrx\u0026gt; request chassis cluster failover reset redundancy-group 1 Hopefully between last weeks post and this one, you should have a good handle on the basics of configuring a chassis cluster on your new pair of Juniper SRX firewalls. Let me know in the comments below if this helped you!\n","permalink":"https://0x2142.com/srx-basics-redunancy-groups-and-failover/","summary":"Some background \u0026amp; configuration examples for Juniper SRX redundancy groups","title":"SRX Basics: Redunancy Groups and Failover"},{"content":"So you just unboxed a brand new pair of Juniper SRX firewalls - now what? Well, the first thing you\u0026rsquo;re likely going to want to do is get the two devices hooked up and clustered together. That should be pretty simple, right? Yeah, mostly - though there are a few variations between device models, and there are a few fine-print steps that might keep you from getting everything working the first time.\nSo let\u0026rsquo;s take a look at what we need to do!\nPhysical configuration First thing we need to do is get both devices unboxed and cabled appropriately. In order to get a successful cluster configured, we will need to get two critical ports connected: the HA control port and the cluster fabric port. Technically only the HA control port is required to get a cluster working, but you\u0026rsquo;ll want to get the fabric port working as well - here is what both ports are used for:\nHA Control Port - This is used for communication between the cluster members. This connection is used just for control-plane stuff - like keepalives/heartbeats and config sync between the two nodes.\nFabric Port - This port is used for data sync between the cluster members. All routing/firewall state information is synced using this port, and any cross-cluster traffic is also transferred using this port (for example, if one SRX was primary for a redundancy group, but the secondary was the active BGP speaker for your upstream connection - then the traffic would come in through the secondary and cross this link to reach the primary RG)\nThe fabric port is the easiest to connect - because you can use any port you like, then specify which port to use in the CLI. The control port, however, must be the assigned port that Juniper allocates for this use. Unfortunately, this port varies between device models. The two most common SRXs that I\u0026rsquo;ve deployed are the 345 and 1500. The SRX 1500 has a dedicated 10G HA control port, but the SRX 345 actually uses ge-0/0/1 on both nodes for this. Juniper lists what all those port assignments are over on this page.\nOnce those ports are connected, go ahead and power on both devices!\nJunOS Config Okay - Once the physical configuration has been completed, there are a few things that need to be configured on both devices before you can establish a cluster.\nWhen you first boot each device, you\u0026rsquo;ll log in with root and no password. Then you\u0026rsquo;ll be dropped into the JunOS shell, and you\u0026rsquo;ll need to type cli to start the JunOS command-line interface. Then type configure to get into the configuration mode.\nroot@testsrx% cli root@testsrx\u0026gt; configure root@testsrx# In the config mode, we\u0026rsquo;ll need to set a root password before we can enable the clustering. This password must match on both devices!\nroot@testsrx# set system root-authentication plain-text-password New Password: \u0026lt;type your root password here\u0026gt; Retype new password: \u0026lt;and again...\u0026gt; For the SRX 1500 series, where there is a dedicated HA Control port, this is enough to get the cluster working. But for some of the branch SRXs, like the 300 series, you\u0026rsquo;ll need to make a few additional changes. These devices come with a default config, which includes IP addresses on certain interfaces. Unfortunately, this will conflict with your cluster config and will not allow your cluster to reach a healthy state.\nIn my config, I already plan on re-configuring all of the interfaces and security-zones to fit my needs - so I will just delete those entire config sections:\nroot@testsrx# delete interfaces root@testsrx# delete security After all that is done, we need to commit our changes: ```text root@testsrx# commit Finally we can go ahead and set up the cluster! This config is actually done outside of configure mode, so you will need to exit that.\nSo one thing to note here - each cluster will be configured with a cluster-id. This MUSTbe unique across any layer 2 subnet. So if we had multiple SRX clusters within a single broadcast domain, we would need to assign each one a different cluster ID. I\u0026rsquo;ll use cluster-id 5 in this example.\nOn whichever SRX you want to be the primary node:\nroot@testsrx# exit root@testsrx\u0026gt; set chassis cluster cluster-id 5 node 0 reboot I personally like to give the primary a minute or to into the boot process before I configure the secondary, but we\u0026rsquo;ll do so with a similar command (just specifying node 1 instead of 0):\nroot@testsrx\u0026gt; set chassis cluster cluster-id 5 node 1 reboot After both nodes come back online, log into node 0 and run the following command:\nroot@testsrx\u0026gt; show chassis cluster status Cluster ID: 5 Node Priority Status Preempt Manual failover Redundancy group: 0 , Failover count: 0 node0 100 primary no no node1 100 secondary no no Redundancy group: 1 , Failover count: 0 node0 100 primary yes no node1 100 secondary yes no Perfect! Now let\u0026rsquo;s go configure our fabric ports! Interface fab0 will be configured as the fabric port on node0, and interface fab1 will be configured as the fabric port on node1.\nroot@testsrx\u0026gt; configure root@testsrx# set interface fab0 fabric-options member-interfaces ge-0/0/10 root@testsrx# set interface fab1 fabric-options member-interfaces ge-5/0/10 root@testsrx# commit Now that we\u0026rsquo;re in a cluster, all of this configuration can be done on node0 - but note that in this case the secondary device\u0026rsquo;s ports all start with ge-5/x/x. This is another oddity of JunOS - that numbering scheme isn\u0026rsquo;t always the case. In the SRX1500s, the node1 ports all start with ge-7/x/x - so this will vary depending on what devices you\u0026rsquo;re working with. If you ever need to check this - you can run show interface terse to list all interfaces in the cluster.\nAs a final verification that all our ports are up, drop out of config mode and run show chassis cluster interfaces:\nroot@testsrx\u0026gt; show chassis cluster interfaces Control link 0 name: ge-0/0/1 Control link status: Up Fabric interfaces: Name Child-interface Status fab0 ge-0/0/10 up fab0 fab1 ge-5/0/10 up fab1 Fabric link status: up Hooray! We now have a functioning SRX cluster!\nSometimes if this doesn\u0026rsquo;t work, the output of show chassis cluster status will show the secondary node as disabled or lost. I\u0026rsquo;ve found that lost usually indicates a conflicting configuration on the cluster interfaces (like leaving the default IPs configured). If you see disabled, try rebooting the secondary node again - and if that doesn\u0026rsquo;t work, then you may need to disable clustering on both nodes and re-configure. This can be done using the set chassis cluster disable reboot command.\nNext week, we\u0026rsquo;ll look at redundancy-groups, performing manual failovers, and setting up interface monitoring for automatic failovers. Hope this was helpful!\n","permalink":"https://0x2142.com/srx-basics-clustering/","summary":"A short tutorial on how to configure Juniper SRX clustering","title":"SRX Basics: Clustering"},{"content":"This is a multi-part series - If you just hit this page, please check out the prior posts first!\nPart 1 - Initial Thoughts Part 2 - Leaning Django Part 3 - Creating the Dashboard Alright - We\u0026rsquo;re finally at the last step to this project. We already have Django running with a somewhat decent-looking web dashboard. Now the only thing that remains is polling our SRX firewalls for VPN connections, then populating this information into the database.\nFor this to happen, I built a custom management command for Django - the cool thing here is that we can easily use this within a cron job to automatically schedule updates at regular intervals. So within our application (the vpn folder), we\u0026rsquo;re going to create a management folder then a commands folder within that. In here you can name your script whatever you want, so I went with cronpoller.py.\nThe script that I wrote is extremely simplistic and could probably use quite a number of improvements - which I will slowly make over time. But for now, it is what it is - and it does exactly what I need it to.\nFirst thing we need to do is import a few things we will need:\nfrom django.core.management.base import BaseCommand, CommandError from vpn.models import Firewall, Datacenter from jnpr.junos import Device from lxml import etree We have some Django modules for treating this as a management command - and we\u0026rsquo;re also importing our existing models into this script. This part is really cool to me, because it means that this cron script can just reference the existing objects that we created to get their attributes. Next, we import the JunOS stuff from their pyEZ packages (don\u0026rsquo;t forget to install pyEZ!). The last one is going to be used to parse the responses from our SRX into something we can use.\nAlright - so in order to build a management command, we have to create a class called Command, then define our functions within that. Within the Command class, Django will look for a function called handle to execute. In my final script, I have that function plus one called getVPNStatus. Let\u0026rsquo;s start with putting together getVPNStatus:\n# This function will poll the device for status def getVPNStatus(self, fw, dc): connectedlist = [] # So we generate a JunOS pyEZ device connection using the information about the # firewall that we gather from the database object device = Device(fw.firewall_manageip, user=fw.firewall_user, password=fw.firewall_pass) # Try to open a connection out to the target device try: dev.open() except: # If for some reason this doesn\u0026#39;t work, just return UNREACHABLE - which # we\u0026#39;ll assume means the device is down return \u0026#34;UNREACHABLE\u0026#34; # Here is where we poll the SRX for a list of all IPSec Security Associations. # The equivalent of the \u0026#39;show security ipsec sa\u0026#39; command response = etree.tostring(dev.rpc.get_security_associations_information()) # The SRX returns a response in XML, which we\u0026#39;ll need to dig through # Credit to the guys over at \u0026lt;a href=\u0026#34;http://packetpushers.net/parsing-junos-xml-python/\u0026#34; target=\u0026#34;_blank\u0026#34; rel=\u0026#34;noopener\u0026#34;\u0026gt;Packet Pushers\u0026lt;/a\u0026gt; for a great post explaining how to # parse these responses with open(response) as a: xmldoc = etree.parse(a) docroot = xmldoc.getroot() rootchildren = docroot.iter() for child in rootchildren: # For each IPSec SA returned, we need to find the remote gateway IP, which # we use to tie the connection back to the connected datacenter if child.tag == \u0026#34;sa-remote-gateway\u0026#34;: connectedlist.append(child.text) # Once we\u0026#39;ve built our list, send it back! return connectedlist That already is major part of our script. It will connect out to each device, grab a list of every connected IPSec tunnel, then return back a list of connected gateways. Now we just need to write our handle function, which will do all of the remaining work.\ndef handle(self, *args, **options): # Let\u0026#39;s quickly create two lists - based on our firewall and datacenter models # This actually polls the database for anything that\u0026#39;s been created dclist = Datacenter.objects.order_by(\u0026#39;datacenter_code\u0026#39;) fwlist = Firewall.objects.order_by(\u0026#39;firewall_name\u0026#39;) # This will loop through each firewall, then call the getVPNStatus function for fw in Firewall.objects.order_by(\u0026#39;firewall_name\u0026#39;): statuslist = self.getVPNStatus(fw, dclist) # Once we have our response, we\u0026#39;re going to check the returned list of connected # gateways against our list of datacenters from the database for dc in dclist: for remoteFW in fwlist: # If we find another datacenter in the connected gateway list, add # that datacenter to the vpnstatus list as \u0026#34;VPNUP\u0026#34;, otherwise assume it\u0026#39;s down if remoteFW.firewall_vpnip in statuslist: vpnstatus[dc.datacenter_code] = \u0026#34;VPNUP\u0026#34; break else: vpnstatus[dc.datacenter_code] = \u0026#34;VPNDOWN\u0026#34; # After all that is done - we just save the new vpnstatus list to the firewall_vpnstatus # field in the database fw.firewall_vpnstatus = vpnstatus fw.save(update_fields=[\u0026#34;firewall_vpnstatus\u0026#34;]) I\u0026rsquo;ve said this before, but I think it\u0026rsquo;s worth noting again - This probably isn\u0026rsquo;t the best or most efficient/reliable way of doing this. I think that over time I would like to revisit this and refine it a bit - but this is what I\u0026rsquo;ve put together so far. In fact, I think it\u0026rsquo;s worth stating that this will probably break in a fantastically horrific manner. This isn\u0026rsquo;t a finished product, but pretty much just version 0.1 - the base functionality works, but it\u0026rsquo;s in desperate need of refinement.\nAlright - so now we just have to add a cron job in our system to call this script and run it. Depending on how many firewalls you have and how the latency is, you might need a different interval than I am using - but I went ahead and set mine to run once every 10 minutes. I would like to get this down to 5 minutes or less, but I might need to figure out a way to potentially multi-thread the cronpoller.py script. So here is what I did in /etc/cron.d/vpnstatuspoller:\n*/10 * * * * * python /root/junos-dashboard/manage.py cronpoller After letting the script poll my firewalls - I ended up with a dashboard that looks like this:\nFuture improvements Of course, I still have a few things I would like to add or improve on. I\u0026rsquo;ve been keeping a list of some ideas that I\u0026rsquo;ve had, which I\u0026rsquo;ll share here:\nAdd a timestamp to each firewall that contains the last poll time (in case something gets missed or hasn\u0026rsquo;t updated yet) Possibly add ability to click a \u0026lsquo;down\u0026rsquo; VPN cell to force clear SA Set up email alerts from the cronpoller to automatically notify me of a failure Ability to click on any VPN cell and get additional info - like VPN uptime, subnets routed across it, or maybe the IKE/IPSec negotiation parameters Ability to click on any firewall name and get additional info - like uptime, JunOS version, CPU/Memory I finally got around to getting myself a GitHub account, so I might put the final code up there once I\u0026rsquo;m done. I also have a number of other JunOS scripts that are probably worth posting up there as well.\nWell, I hope you enjoyed reading about this project - because I certainly enjoyed working on it. This was one of my first real projects using the JunOS pyEZ libraries, and I got to pick up and learn how to use Django as well. The experience of building this has given me ideas for other JunOS automation projects - so look out for some of those in the future!\n","permalink":"https://0x2142.com/building-a-vpn-dashboard-using-django-and-junos-pyez-part-4-polling-the-srx/","summary":"This post focuses on the Juniper SRX side of my VPN dashboard - writing the Python scripting to query VPN status from each device","title":"Building a VPN Dashboard using Django and JunOS pyEZ (Part 4 – Polling the SRX)"},{"content":"This is a multi-part series - If you just hit this page, please check out the prior posts first!\nPart 1 - Initial Thoughts Part 2 - Leaning Django In the last post, we got Django working well enough to start serving up web requests. That\u0026rsquo;s a great start - So now it\u0026rsquo;s on to actually building the project. We\u0026rsquo;re going to cover this in two parts: the front-end dashboard, and the back-end scripts. In this post, I\u0026rsquo;m going to show you how I began putting everything together to assemble the front-end HTML dashboard.\nAs a quick refresher note, we created a project in the last post called junos-dashboard. Within that project, we created our app named vpn. This folder is primarily where we\u0026rsquo;ll be spending our time today. There are two files in our app folder that will need a few edits:\nviews.py - This is where we originally created our basic index page, which currently just spits out a message. So we\u0026rsquo;ll need to add code in here to render our dashboard instead.\nmodels.py - In here we will define our objects and the characteristics about those objects that we want to store/retrieve in the sqlite database.\nLet\u0026rsquo;s go ahead and open up our models.py file. For my dashboard, I have two objects I want to track - Firewalls (the SRX hosting the VPN) and Datacenter (as a location tracking). So in order to create those objects, we just have to add two classes into the models.py file:\nfrom __future__ import unicode_literals from django.db import models class Datacenter(models.Model): def __str__(self): return self.datacenter_code class Firewall(models.Model): def __str__(self): return self.firewall_name Easy enough, right? So here is the really cool thing about Django (or probably any web framework really) - These model objects are now something that we can reference in our code to change or query attributes about them. Even cooler is that Django automatically builds an admin page to our site, where we can create new instances of each model right there (which I\u0026rsquo;ll cover later). So this means that all I have to do is define these two models and their attributes, then anyone on my team can log into the dashboard admin page and add new firewalls or datacenters.\nOkay, so in order to actually make these models useful, we need to add our attributes. For the datacenter object, I only care about two things - What is the datacenter code (identifier), and is the datacenter an active location. So here is where we add those options under our datacenter class:\nclass Datacenter(models.Model): datacenter_code = models.CharField(\u0026#39;Datacenter Code\u0026#39;, max_length=10) datacenter_active = models.BooleanField(\u0026#39;Datacenter Active?\u0026#39;, default=True) The firewall object gets a little more complicated because I want to be able to define quite a few things. The obvious attributes are: the firewall name, which datacenter it belongs to, and whether or not it is an active firewall. I\u0026rsquo;ll also need to collect this firewall\u0026rsquo;s VPN peer IP, so that I can compare it against other devices to check the connection. But wait - how am I actually checking that VPN status? Oh, I guess I\u0026rsquo;ll also need to collect a username/password for the SRX API, as well as a valid management IP to reach the API.\nLet\u0026rsquo;s take a moment though - because I stopped here and realized that I\u0026rsquo;ll need to collect user/password data in a form, then it\u0026rsquo;s going to be stored in a sqlite database. Oh, and of course it will be unencrypted. I didn\u0026rsquo;t really like that idea - so I did some research on how to encrypt one of the object attributes. Turns out, it\u0026rsquo;s actually pretty easy since someone has already created a module to do exactly that.\nSo let\u0026rsquo;s grab that module real quick:\n[root@0x2142-Centos vpn]# pip install django-encrypted-fields Once we have the module, it requires us to generate some keys which will be used for encrypting our data. The following is based on the steps listed on the GitHub page for django-encrypted-fields:\n[root@0x2142-Centos junos-dashboard]# mkdir fieldkeys [root@0x2142-Centos junos-dashboard]# keyczart create --location=fieldkeys --purpose=crypt [root@0x2142-Centos junos-dashboard]# keyczart addkey --location=fieldkeys --status=primary --size=256 Next, we just need to add this keyfile into our settings.py:\nENCRYPTED_FIELDS_KEYDIR = \u0026#39;/root/junos-dashboard/fieldkeys\u0026#39; Alright - back to actually creating the attributes we need for our firewalls. Since we\u0026rsquo;re using this new module, we\u0026rsquo;ll need to add an additional import statement - otherwise, we are just adding the fields I covered earlier:\nfrom encrypted_fields import EncryptedCharField class Firewall(models.Model): firewall_name = models.CharField(\u0026#39;Firewall Name\u0026#39;, max_length=50) firewall_location = models.ForeignKey(\u0026#39;Datacenter\u0026#39;, on_delete=models.CASCADE) firewall_active = models.BooleanField(\u0026#39;Firewall Active?\u0026#39;, default=True) firewall_manageip = models.GenericIPAddressField(\u0026#39;Management IP\u0026#39;) firewall_vpnip = models.GenericIPAddressField(\u0026#39;VPN Interface IP\u0026#39;) firewall_user = models.CharField(\u0026#39;API User\u0026#39;, max_length=50, blank=True) firewall_pass = EncryptedCharField(\u0026#39;API Pass\u0026#39;, max_length=50, blank=True) firewall_vpnstatus = models.TextField(editable=False, blank=True) A few things to note here - The firewall_location attribute will actually query the table of datacenter objects - so we\u0026rsquo;re not creating any data twice. You might also notice that I added a firewall_vpnstatus text field, which is going to be hidden in the administrative console. Because I\u0026rsquo;m a terrible programmer, I\u0026rsquo;ll be storing all of the VPN status info in this text field in the database. Yes, there is probably a much more elegant way of handling this - but again, I\u0026rsquo;m a network admin not a professional coder. For what I need, this gets the job done. If you have a better way of accomplishing this - let me know! I would be very interested in another method.\nI also found out later that just because I encrypted the field doesn\u0026rsquo;t mean that Django knows the field is a password. I wanted the field to be treated like a password input, so the data wasn\u0026rsquo;t visible to anyone who just logged in. I found out that you can create a forms.py file within the vpn directory, and pretty much override the field type to account for this.\nSo here is what I threw into the forms.py file to handle password input:\nfrom django.forms import ModelForm, PasswordInput from .models import Firewall class FirewallForm(ModelForm): firewall_pass = CharField(widget=forms.PasswordInput) class Meta: model = Firewall fields = [\u0026#39;firewall_pass\u0026#39;] That was actually way easier than I had anticipated.\nAwesome, now our work on the models is completely done - why don\u0026rsquo;t we log into the administrative web interface and take a look at how it all turned out?\nHere is a view of the datacenter page - where we can add a new location:\nAnd now for the firewall objects - with all the fields we need to get the job done:\nLooks pretty good, huh? And we didn\u0026rsquo;t even need to do any work to get the free admin functionality! This is honestly my favorite part of Django. Without posting a ton of additional screenshots, the admin page also provides audit logs - so we can see who changed what objects. Very helpful.\nAlright - now let\u0026rsquo;s start work on our views.py file, so we can actually start putting all this together in our dashboard!\nWithin our views.py file, we already had our index function, which just returned a string of text. We\u0026rsquo;ll be adding to that in a moment - but first I created a separate function called buildrows, which does exactly what you think. This function will pull each firewall and it\u0026rsquo;s status, and build our little HTML table. I\u0026rsquo;m not going to go into great detail on this - but check out the comments for more information on what\u0026rsquo;s going on here:\n# This is our function to build the HTML table - We\u0026#39;re going to be expecting this function # to be fed a list of firewalls and datacenters def buildrows(firewall_list, datacenter_list): firewallstatus = [] # We will iterate through each firewall and compile each row for our table for fw in firewall_list: # The first cell in our row is going to be the firewall name and it\u0026#39;s own VPN peer IP onerow = \u0026#34;\u0026lt;td\u0026gt;\u0026lt;b\u0026gt;%s\u0026lt;/b\u0026gt;\u0026lt;i\u0026gt;%s\u0026lt;/i\u0026gt;\u0026lt;/td\u0026gt;\u0026#34; % (fw.firewall_name, fw.firewall_vpnip) # Now we iterate through every datacenter, in order to see which ones we have a # connection to from this firewall for dc in datacenter_list: # First thing is first - If this firewall contains the name of the datacenter # then we\u0026#39;ll print N/A, since we wouldn\u0026#39;t expect a VPN to itself if str(dc.datacenter_code) in str(fw.firewall_name): onerow += (\u0026#34;\u0026lt;td\u0026gt;N/A\u0026lt;/td\u0026gt;\u0026#34;) continue # Also, if there is no VPN status in the database, then just print \u0026#39;No Status\u0026#39; if not fw.firewall_vpnstatus: onerow += (\u0026#34;\u0026lt;td\u0026gt;No Status\u0026lt;/td\u0026gt;\u0026#34;) continue # This portion is pretty self-explanatory - We parse the vpnstatus field in the # database. If the state of the connection is \u0026#39;VPNUP\u0026#39;, then we print a cell for # that datacenter with the text \u0026#39;UP\u0026#39;. If the state is \u0026#39;VPNDOWN\u0026#39;, then we print # \u0026#39;DOWN\u0026#39;. And if nothing matches, print \u0026#39;No Status\u0026#39; statuslist = ast.literal_eval(fw.firewall_vpnstatus) try: status = statuslist[dc.datacenter_code] if status == \u0026#34;VPNUP\u0026#34;: onerow += (\u0026#34;\u0026lt;td class=\\\u0026#34;vpnup\\\u0026#34;\u0026gt;\u0026#34;) onerow += (\u0026#34;UP\u0026#34;) elif status == \u0026#34;VPNDOWN\u0026#34;: onerow += (\u0026#34;\u0026lt;td class=\\\u0026#34;vpndown\\\u0026#34;\u0026gt;\u0026#34;) onerow += (\u0026#34;DOWN\u0026#34;) else: onerow += (\u0026#34;\u0026lt;td\u0026gt;\u0026#34;) onerow += (\u0026#34;No Status\u0026#34;) onerow += (\u0026#34;\u0026lt;/td\u0026gt;\u0026#34;) except KeyError: onerow += (\u0026#34;\u0026lt;td\u0026gt;\u0026#34;) onerow += (\u0026#34;No Status\u0026#34;) onerow += (\u0026#34;\u0026lt;/td\u0026gt;\u0026#34;) # Once complete, we append this row to the status list firewallstatus.append(onerow) # All done! Return our list of statuses! return firewallstatus That block might not make a ton of sense at the moment - but it will shortly. Essentially I have a script that is connecting to each firewall via the SRX API and polling the VPN status for each peer. The script is then writing all of this information to a field in the database called vpnstatus in one long string, which kinda looks like this:\n{u\u0026#39;US-1\u0026#39;: \u0026#39;VPNUP\u0026#39;, u\u0026#39;US-2\u0026#39;: \u0026#39;VPNUP\u0026#39;, u\u0026#39;EU-1\u0026#39;: \u0026#39;VPNUP\u0026#39;, u\u0026#39;EU-2\u0026#39;: \u0026#39;VPNUP\u0026#39;, u\u0026#39;CN-1\u0026#39;: \u0026#39;VPNDOWN\u0026#39;} The buildrows function is then just grabbing each firewall and then parsing this data to figure out what it has connections to - then generates a row in our table.\nWith that done, our index function within views.py needs a little work to start displaying our table. You\u0026rsquo;ll also notice we\u0026rsquo;re importing the models we created, as well as a template loader - which I\u0026rsquo;ll get to in a moment. So here is what that function will look like when we\u0026rsquo;re done:\nfrom .models import Firewall, Datacenter from django.template import loader import sys, ast def index(request): firewall_list = [] datacenter_list = [] firewall_status = [] # This will grab each datacenter object (if they\u0026#39;re marked active) from the # database and add them to a list for each in Datacenter.objects.order_by(\u0026#39;datacenter_code\u0026#39;): if each.datacenter_active == True: datacenter_list.append(each) # Same thing here - grab our firewalls and append to a list for each in Firewall.objects.order_by(\u0026#39;firewall_name\u0026#39;): if each.firewall_active == True: firewall_list.append(each) # Now we pass those lists to the buildrows function to assemble our HTML table firewall_status = buildrows(firewall_list, datacenter_list) # We\u0026#39;re also going to apply a template to make the page look fancy template = loader.get_template(\u0026#39;web/index.html\u0026#39;) # This is taking all of our lists, and passing them into our HTML template # so that it can build the page semi-automatically context = { \u0026#39;datacenter_list\u0026#39;:datacenter_list, \u0026#39;firewall_list\u0026#39;: firewall_list, \u0026#39;firewall_status\u0026#39;:firewall_status, } return HttpResponse(template.render(context,request)) Alright - our views.py file is now complete. I wanted to keep the index function kind of simple, so we just have it pull data from the database, pass it to another function for processing, then return it to the browser for display.\nSo in the code above, you might have noticed that I was using an HTML template to make the page look fancier. I did this by going out to Google and finding a free CSS template for HTML tables. I won\u0026rsquo;t post the CSS here, but there are plenty of free templates out there - just find whatever suits your taste. Once you have that CSS file, create a directory (within our app folder) called static and place the CSS file in there.\nThen, it\u0026rsquo;s one simple template HTML file to load our CSS and render our table. Within the app directory, I created a templates folder, then a folder called web within that. I added a new file called index.html - which looks like this:\n\u0026lt;title\u0026gt;SRX VPN Dashboard\u0026lt;/title\u0026gt; \u0026lt;div align=\u0026#34;center\u0026#34;\u0026gt; {% load static %} \u0026lt;!-- Load the CSS template here --\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; type=\u0026#34;text/css\u0026#34; href=\u0026#34;{% static \u0026#39;style.css\u0026#39; %}\u0026#34; /\u0026gt; \u0026lt;!-- Add a header to the table --\u0026gt; \u0026lt;div class=\u0026#34;table-title\u0026#34;\u0026gt; \u0026lt;h3\u0026gt;SRX VPN Dashboard\u0026lt;/h3\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Create our first row, which will be a list of each data center location --\u0026gt; {% if firewall_list %} \u0026lt;table class=\u0026#34;container\u0026#34;\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;th\u0026gt;\u0026lt;/th\u0026gt; {% for datacenter in datacenter_list %} \u0026lt;th\u0026gt;{{ datacenter.datacenter_code }}\u0026lt;/th\u0026gt; {% endfor %} \u0026lt;/tr\u0026gt; \u0026lt;!-- Then we add a row for each firewall, followed by its status for each datacenter --\u0026gt; {% for firewall in firewall_status %} \u0026lt;tr\u0026gt; {{firewall|safe}} \u0026lt;/tr\u0026gt; {%endfor%} \u0026lt;/table\u0026gt; \u0026lt;!-- If something goes wrong, we just print an error --\u0026gt; {% else %} No firewalls were found. {% endif %} \u0026lt;/div\u0026gt; Guess what? The dashboard is complete! Now we can run our Django server using \u0026lsquo;./manage.py runserver\u0026rsquo; and take a look at what we have created.\nPretty simplistic - but we\u0026rsquo;re getting very close to what I wanted to accomplish. The dashboard loads and generates the table, but it doesn\u0026rsquo;t have any data yet.\nIn the next post - We\u0026rsquo;ll take a look at building the backend script to poll all of our SRX firewalls for VPN connections. I hope you enjoyed reading this - let me know what you think in the comments below!\nThis is a multi-part series - Check out the other posts:\nPart 1 - Initial Thoughts Part 2 - Leaning Django Part 4 - Polling the SRX ","permalink":"https://0x2142.com/building-a-vpn-dashboard-using-django-and-junos-pyez-part-3-creating-the-dashboard/","summary":"This post focuses on the web GUI side of the VPN dashboard - storing \u0026amp; displaying the information that gets collected","title":"Building a VPN Dashboard using Django and JunOS pyEZ (Part 3 – Creating the Dashboard)"},{"content":"I\u0026rsquo;ve really been meaning to write this out for a while - but since I\u0026rsquo;ve been doing a lot of interviewing recently, I figured it\u0026rsquo;s about time. I\u0026rsquo;ll be completely honest here, the first few iterations of my resume absolutely sucked. I had a family member help me put one together, and I slowly modified it from there - but it wasn\u0026rsquo;t spectacular.\nA few years into my first IT job, and I worked with a great guy who took the time to teach me some critical resume skills. The things he taught me seem like common sense now, but at the time they seemed to do magical things for me. I actually had a few recruiters comment on how much they liked my resume - which seemed pretty amazing.\nSo what I want to share today are some of those tips that I was given, including some things I personally look for when I\u0026rsquo;m interviewing people.\n1. Formatting, spelling, and grammar I have to put this first because it is absolutely critical. For me personally, I am very disappointed when I see a resume with bad formatting, misspelled words, or cringe-worthy grammar. Your resume doesn\u0026rsquo;t have to look like a work of art, but don\u0026rsquo;t just include massive blobs of text. Break it up and create a nice simple layout. Don\u0026rsquo;t forget to use spell check - it\u0026rsquo;s already included in most word processing applications.\nGrammar can be more difficult - again, I don\u0026rsquo;t expect perfection, but have someone else read through to ensure everything makes sense. I\u0026rsquo;ve literally received resumes before where they didn\u0026rsquo;t use any punctuation. Everything on your resume needs to maintain the same tense, and I prefer it to be past tense. Every point on your resume should reflect work that you have done - so you want to speak about it as a past experience.\n2. Use clear, concise bullet points to describe your work history I\u0026rsquo;ve come across a lot of people who type an entire paragraph under each job. It\u0026rsquo;s harder to read through quickly, and it just doesn\u0026rsquo;t look good. Remember when I mentioned formatting? Use bullet points.\nEach bullet point should be kept to a maximum of one line. The occasional two-lined bullets are acceptable, but try to keep them to a minimum. Usually, if you need more than one line, then your point may be too wordy and you may need to re-phrase your statement. Each bullet should be a contained statement of your work experience. For example: \u0026ldquo;Managed Cisco ASA firewalls across five business locations\u0026rdquo;. From that single statement, I should be able to get an idea of what that means.\n3. Keep it under two pages - but keep the relevant details Maybe this is again personal preference, but I don\u0026rsquo;t really want to look over a resume that\u0026rsquo;s longer than two pages. When you only have a single job to put on your resume, it\u0026rsquo;s important that you put a lot of detail about what you\u0026rsquo;ve done. In this case, it\u0026rsquo;s acceptable to fill most or all of a page with your sole point of job experience. However, as you start adding more job history, the detail listed for your older positions should be stripped down to the most important points.\nAlways keep your most recent experience first!\n4. Don\u0026rsquo;t re-write your job description - focus on your individual achievements I\u0026rsquo;ve read too many resumes where the job history read like a list of job postings. Mentioning some of your job responsibilities is fine, but it shouldn\u0026rsquo;t be the entire thing. As someone reading your resume, I don\u0026rsquo;t want to read about what you\u0026rsquo;re supposed to do in your job role - I want to read about the things you\u0026rsquo;ve accomplished.\nAs an example to this, someone might put on their resume \u0026ldquo;Monitored security logs for anomalous events\u0026rdquo;. That\u0026rsquo;s great - but I\u0026rsquo;m not getting a sense of this person being self-motivated. Their job was monitoring logs for events, so they did exactly that. But what if this person was someone who really made something of that job duty? Then it would be better if they listed an actual individual accomplishment, like \u0026ldquo;Mitigated major threat to the company by identifying early indicators of security breach\u0026rdquo; or even something like \u0026ldquo;Refined security log review processes and mentored new employees on performing thorough review\u0026rdquo;. These statements give talking points - both something for me to ask about, and something for you to show your skills and pride of your work.\n5. Tailor your resume to the job you want When I started my career, I worked for an IT consulting company. As a result, I had an extremely wide range of experiences. Everything including help desk, network admin, storage admin, Windows sysadmin, antivirus admin, VoIP admin, etc. My resume from my first job was a mess - mostly because of the amount of roles I had to fill. If anyone looked at that resume, it would be impossible for them to tell that I really wanted to become a full-time network engineer.\nMy point is that you should revise your resume to fit what you\u0026rsquo;re looking for. If you have a strong drive to specialize in server hardware and Windows administration, then your resume shouldn\u0026rsquo;t have a ton of detail about your experiences with database administration. You don\u0026rsquo;t have to remove that experience completely from your resume, but your resume should carry a theme surrounding the job you want.\nIf you want to take this a step further - revise your resume for the job you\u0026rsquo;re applying for. If they\u0026rsquo;re looking for significant experience in vulnerability scanning and analysis, then make it a point to highlight your experiences that match that. Maybe you have a bit of experience with Nessus, but it\u0026rsquo;s not typically something you call out much on your resume. If the job posts that the position requires a expert knowledge of Nessus, then you definitely want to make sure your knowledge is immediately visible to them. Remove some less important bullet points, and add in a few more that pertain to the job role.\n6. If you put something on your resume, be prepared to defend it I love this from both the standpoint of an interviewer and an interviewee. Resumes are essentially a quick summary of your experiences, and you should be expected to be called out on anything listed there. Never put something on your resume that you can\u0026rsquo;t speak to.\nAs an interviewer, I like to find things on someones resume that they\u0026rsquo;ve claimed experience or expertise with and ask them about it. Especially people who put a list of technologies they\u0026rsquo;re familiar with. I don\u0026rsquo;t expect you to be able to answer extremely in-depth questions about every single thing on your resume. But more often than not, I ask \u0026ldquo;Okay, so I see you\u0026rsquo;ve listed experience with BGP and MPLS on your resume\u0026rdquo; and the response I get is either \u0026ldquo;Yeah, I know what those are\u0026rdquo; or \u0026ldquo;We used it at my last job (but I had no direct experience with it)\u0026rdquo;.. You don\u0026rsquo;t want to find yourself in this situation.\nAs an interviewee, I\u0026rsquo;ve had this same thing happen to me multiple times - except that these turn into ways for me to speak about things I\u0026rsquo;ve done with those technologies. It makes a good impression if you\u0026rsquo;re able to quickly recount knowledge or experiences of any random thing listed on your resume.\n7. Keep it updated IT is fast paced. Things change - and you would be surprised how much you accomplish in six months. You probably won\u0026rsquo;t be surprised at how quickly you forget everything that you did in that six months though. For that reason, you need to make it a point to keep your resume updated - even if you\u0026rsquo;re not looking for a new job in the immediate future. That way, you make sure that you remember your recent accomplishments to add onto your resume - and you can also take a moment to review and remove older/less relevant items. I would recommend doing this every six to eight months.\nYou never know when you\u0026rsquo;re going to see a job you want, or get contacted by a recruiter for a perfect opportunity. Don\u0026rsquo;t miss your opportunity because you need time to update your resume. Keep it updated. I\u0026rsquo;ve known a lot of people to say \u0026ldquo;Oh yeah I\u0026rsquo;ll apply for that, I just need to update my resume first\u0026rdquo; - but then they never do.\n8. About objectives or mission statements Alright, one last thing. Some people put objectives or personal mission statements on the top of their resumes. Personally, I don\u0026rsquo;t like them at all. I\u0026rsquo;m okay with them if you are a young professional who is looking for their first or second job. After that - I don\u0026rsquo;t want to see it.\nAlso - If you feel the need to put an objective at the top of your resume, please do me a favor: do not put \u0026ldquo;I\u0026rsquo;m working to become a \u0026rdquo;. I would say that about 80% of the resumes I see with objectives look exactly like that. I understand that you want this job, otherwise you wouldn\u0026rsquo;t have applied for it, right? So if you absolutely have to put an objective, make use of the space - tell me where you see yourself in five or ten years, or what your ideal position is. It means a lot more to me if you put something like \u0026ldquo;Working to gain knowledge and expertise as a network admin, with the goal of becoming a senior network architect\u0026rdquo;. Then I see that you have goals, and you\u0026rsquo;re working toward them.\nOkay! That\u0026rsquo;s all I\u0026rsquo;ve got for now on this - but I hope that it helps you if you\u0026rsquo;re reading through this. I owe a lot of my resume-writing knowledge to the guy who originally helped me seven years ago, but I make it a point to pass along the help whenever I can.\nIf you have any other tips, feel free to share them in the comments below!\n","permalink":"https://0x2142.com/how-to-write-a-decent-it-resume/","summary":"I\u0026rsquo;ve been given a lot of resume tips over the years - so I wanted to share the ones I\u0026rsquo;ve found most helpful","title":"How to Write a Better IT Resume"},{"content":"This is a multi-part series - If you just hit this page, please check out the prior post first!\nPart 1 - Initial Thoughts Once I figured out the overall idea behind what I wanted to do - the next big part was figuring out where to start. As I mentioned in part 1, I had never used Django before starting this project. So I figured that learning how to to use a new web framework might be the best place to begin.\nThe great thing, is that Django already has some great tutorials on how to get the packages installed and begin working on your first project. Their tutorials can be found here:\nInstalling Django Writing your first app - Credit to this page for some parts of the examples below I used both of these tutorials when I wrote my dashboard, and pretty much just modified what they were doing to fit my needs. After I got a baseline down, it was pretty easy to keep going and complete what I wanted to do.\nNote: Just as a pure warning - I\u0026rsquo;m a network admin, not a professional programmer. Python, Django, and the Junos PyEZ libraries make it easy enough for me to make my ideas into a reality. However, that doesn\u0026rsquo;t mean I write perfect or even great code. (I learned Python from Learn Python The Hard Way - I highly recommend this site, as it was a great resource!)\nAlright, with that out of the way - Let\u0026rsquo;s get started!\nStep One - Installing Django First thing is first, I learned Python on the 2.7.x chain, and I\u0026rsquo;ve been terrible about forcing myself to switch to 3.x. So for the purposes of this tutorial, everything I\u0026rsquo;ve written was intended for Python 2.7.5. Installing the actual Django package is a super simple task (if you use pip):\n[root@0x2142-Centos ~]# pip install django Collecting django Downloading Django-1.11.2-py2.py3-none-any.whl (6.9MB) 100% |████████████████████████████████| 7.0MB 140kB/s Collecting pytz (from django) Downloading pytz-2017.2-py2.py3-none-any.whl (484kB) 100% |████████████████████████████████| 491kB 1.9MB/s Installing collected packages: pytz, django Successfully installed django-1.11.2 pytz-2017.2 Step Two - Get JunOS pyEZ libraries I already wrote about this earlier in Getting Started with JunOS PyEZ. If you don\u0026rsquo;t already have the JunOS packages installed, check out that page for the instructions.\nStep Three - Create the Project Django is pretty wonderful from my brief experiences with it. They package a number of tools that make starting a new project very simple.\nSo to start off with our project, we\u0026rsquo;re going to use the django-admin tool:\n[root@0x2142-Centos ~]# django-admin startproject junos-dashboard This will create a base directory structure for us to begin our project. We\u0026rsquo;ll dive into what these files and folders are as we touch them - but for now, you\u0026rsquo;ll need to make one minor edit to ~/junos-dashboard/junos-dashboard/settings.py. Open that file up in your favorite text editor, and find the ALLOWED_HOSTS setting. Add in the IP address of your linux VM, like this:\nALLOWED_HOSTS = [\u0026#39;10.2.32.90\u0026#39;] Once that\u0026rsquo;s done, go to the root of your project (~/junos-dashboard/) and test out the web server:\n[root@0x2142-Centos junos-dashboard]# ./manage.py runserver 10.2.32.90:8000 Django includes it\u0026rsquo;s own web server for development and testing, which is extremely helpful. Once you run that command, try to hit your page in a browser and just make sure it loads.\nOnce that\u0026rsquo;s done, make sure you\u0026rsquo;re in the root of your project - and we will create our dashboard application:\n[root@0x2142-Centos junos-dashboard]# django-admin startapp vpn This command just creates another directory structure within your project. I think this is helpful though, because then I don\u0026rsquo;t have to worry about which files I need to create or how they should be laid out - Django just handles that for us.\nStep Four - Getting our web server to return our app page Okay - So next we need to make a couple of minor edits in a few files. Within our app directory, there are two files (views.py and urls.py) that handle the main parts of our web interface. Note - urls.py doesn\u0026rsquo;t exist by default and you will have to create it!\nWithin views.py, we\u0026rsquo;re going to create our dashboard homepage with a quick test response:\n# Import stuff from django.shortcuts import render from django.http import HttpResponse from django.template import loader import sys, ast # This is our index page def index(request): # Return some plain text - just so we know it worked! return HttpResponse(\u0026#34;This page will eventually be a magical dashboard!\u0026#34;) This file is where we will eventually be putting in all the code for our dashboard page. When we hit our webserver, this file is executed to generate the view that we\u0026rsquo;ll see.\nNext, we need to create a urls.py file within the same folder as views.py. This file is just a way to direct traffic. If we receive traffic to a regex of ^$, which is no path, then we are going to redirect it to the index function in views.py:\n# Import stuff from django.conf.urls import url from . import views #Redirect to index function in views.py urlpatterns = [ url(r\u0026#39;^$\u0026#39;, views.index, name=\u0026#39;index\u0026#39;), ] Finally, we\u0026rsquo;re going to grab the urls.py file in our project folder - not the one we just edited within the app. So this should be ~/junos-dashboard/junos-dashboard/urls.py. In this file, we just need to tell the web server to include the urls.py file we created when evaluating traffic. This file should already exist and have content in it for routing traffic to the admin page, so just add an entry under urlpatterns - should look like this:\nurlpatterns = [ url(r\u0026#39;^$\u0026#39;, include(\u0026#39;vpn.urls\u0026#39;)), url(r\u0026#39;^admin/\u0026#39;, admin.site.urls), ] I just want to point out that in this case, we\u0026rsquo;re catching traffic again with the ^$ regex. This is so that any traffic to the main page (in this case http://10.2.32.90:8000) is automatically sent to our vpn app. However, if you had multiple apps running, you might want to give each one it\u0026rsquo;s own directory.\nGo ahead and execute the runserver tool again, and validate that you\u0026rsquo;re able to successfully hit the page and retrieve the test text we put in.\nOkay, I think we\u0026rsquo;re going to stop here for this post. At this point we have Django installed and our project/app created. We also have the beginnings of our dashboard page. In the next post, we\u0026rsquo;ll actually start building out the dashboard interface!\nThis is a multi-part series - Check out the other posts:\nPart 1 - Initial Thoughts Part 3 - Creating the Dashboard Part 4 - Polling the SRX ","permalink":"https://0x2142.com/building-a-vpn-dashboard-using-django-and-junos-pyez-part-2-learning-django/","summary":"In order to build my Juniper SRX VPN dashboard, I opted to use Django. Let\u0026rsquo;s take a quick look at how I got started.","title":"Building a VPN Dashboard using Django and JunOS pyEZ (Part 2 – Learning Django)"},{"content":"So maybe you\u0026rsquo;re like me - you\u0026rsquo;ve done a bit of everything in the past, but now you\u0026rsquo;ve specialized on something (like networking). Know what the best part of that is? Using all that knowledge and experience to make things happen. In this case, I\u0026rsquo;m talking about some of my prior experiences in scripting in automation, paired with some pretty great network APIs.\nOne thing my current job has never had is a good way to view VPN tunnel status between our firewalls. Our Check Point firewalls don\u0026rsquo;t really provide a good high-level view, which has unfortunately caused some confusion around whether or not a site-to-site VPN tunnel is currently established. Luckily, over the past year I\u0026rsquo;ve had the opportunity to install over 20 new Juniper SRX firewalls - mostly made up of SRX 1500 and SRX 345 models. I\u0026rsquo;ve written a bit before about the JunOS APIs and pyEZ (their Python library), but I\u0026rsquo;m always really excited at a new use case for network automation.\nSo recently I decided that it would be great to build a web-based dashboard, which would query all of our SRX firewalls for currently connected VPN tunnels. Approximately 90% of the current tunnels are site-to-site between our own data center locations, with the other 10% being external to customers. My idea was that I would have a simple HTML table, which would show each data center along the top and bottom and whether or not each was connected to each other, kinda like this:\nLocation 1 Location 2 Location 3 Location 4 Location 1 N/A UP UP UP Location 2 UP N/A UP DOWN! Location 3 UP UP N/A UP Location 4 UP DOWN! UP N/A I wasn\u0026rsquo;t really concerned about making it look fancy - just dynamically updatable by whatever backend mechanism I used. Speaking of which, I had also assumed I would just be writing some simple Python script to pull VPN info from each device, then just write it to an HTML file. I haven\u0026rsquo;t really done much web stuff with Python in the past, so I set out to learn a bit and figure out what the best approach might be.\nI ended up settling on trying out Django to write the frontend web stuff. I had never used it before, but I\u0026rsquo;ve been interested in trying - and what\u0026rsquo;s a better time to learn, than when you have something that needs to be accomplished? One thing that really pushed me towards Django for this project was the built-in administration page. This was huge for me, because it meant that I could easily have a way for other people to update the web dashboard. Whenever a new location came online, I wouldn\u0026rsquo;t have to go update the script directly - anyone on my team could log into the admin page and make the changes.\nIn this post I just wanted to get through what my ideas were behind this project. In the next few weeks, I\u0026rsquo;ll begin explaining how I built the dashboard using Django and use pyEZ to scrape VPN status from each firewall.\nThoughts? Drop a comment below!\nThis is a multi-part series - Check out the other posts:\nPart 2 - Leaning Django Part 3 - Creating the Dashboard Part 4 - Polling the SRX ","permalink":"https://0x2142.com/building-a-vpn-dashboard-using-django-and-junos-pyez-part-1-initial-thoughts/","summary":"I decided to build a web GUI to monitor Juniper SRX VPN tunnels. Here\u0026rsquo;s what I was thinking\u0026hellip;","title":"Building a VPN Dashboard using Django and JunOS pyEZ (Part 1 - Initial thoughts)"},{"content":"Back a few months ago, I wrote a bit about why it is important to have a good design for IP addressing schemes (part 1 and part 2). As a brief refresher, the situation I found myself in was an environment where practically everything was assigned a 10.x.x.x/16 subnet - even if we only needed a handful of hosts. When I arrived at the company, we were already down to less than 1/3 of the 10.x.x.x range remaining unallocated (with multiple new locations already being discussed).\nThe IP addressing design that I came up with limited our typical data center deployment from 4-6 /16 blocks to a single /16 block for each location. For all new locations since then, this new design has been used and it has proved to be extremely beneficial. The ability to use proper address summarization has made firewall rules, routing, and VPN tunnel configuration much simpler. But what about all the old locations which still had several /16 blocks? None of these needed more than a single /16, but we have thousands of systems that would need to be re-addressed. Not something that was going to happen overnight. So let\u0026rsquo;s take a look at some of the methods we employed for migrating from one IP addressing scheme to another.\nHave a plan - The first step is to have a good handle on the overall situation and how to get from point A to point B. You\u0026rsquo;re likely going to need buy-in from other teams to help get there, and this could easily be a multi-year project depending on the number of systems. When you meet to discuss the re-addressing project, you need to be pretty strong when describing the benefits of the new system - otherwise no one will want to help.\nEnforce the standard for anything new - The easy target for any transition is to hit new stuff first. For example, we started using the new range in a brand new location first. Anything being deployed that requires a new IP address allocation needs to be using the new scheme. We don\u0026rsquo;t want to perpetuate the scheme we are trying to get rid of.\nTransition (Network Config) - This can be a difficult step that requires a bit of planning. For any existing sites, we need to configure the ability to use both IP address schemes side-by-side until the transition is completed. There are two primary ways to accomplish this that I\u0026rsquo;ve used - either build out a new segmented (VLANed) network, or overlay the existing using secondary IP addresses. Don\u0026rsquo;t forget to propagate routes to the new subnets and ensure that firewall rules match the existing functionality.\nTransition (Infrastructure/Servers) - Once the underlying networking pieces are done, the next step is to begin transitioning services. Again, make sure any new systems getting deployed are now using the new ranges. Then we can take either an active or passive approach. In the passive approach, we are going to essentially just build new systems in the new scheme and wait until the older systems are eventually removed from service. This probably isn\u0026rsquo;t the ideal way to do this - but it\u0026rsquo;s certainly an option. In a more active approach, we would start identifying the older systems to move and making plans to do so (likely in a phased manner). Either method is going to require a serious investment of time, depending on the size of your network.\nLong-Term - This process is never going to be quick or easy, but the end result should be a much better state than we began. In the meantime, maintaining both IP addressing schemes can be quite painful. Make sure that everyone on the team understands the goals of the new scheme, the plan for getting there, and how everything is configured to make it happen. The last thing you want is for someone to try and back out of the move, just because they\u0026rsquo;re not confident in what\u0026rsquo;s going on.\nI also wanted to stress the importance of research throughout this whole process. It\u0026rsquo;s important to try and understand why the original IP addressing was designed the way it was, and what goals they had in mind at the time. It\u0026rsquo;s also important to check the technologies you\u0026rsquo;re using to understand how everything will work. For example, Juniper\u0026rsquo;s SSG (ScreenOS) platform doesn\u0026rsquo;t support utilizing a secondary IP address on an \u0026lsquo;untrust\u0026rsquo; interface (KB5527) - but it works if you use a custom zone name. And Check Point doesn\u0026rsquo;t support secondary IP addresses at all when you are using their ClusterXL protocol (SK89980), instead they actually recommend that you deploy a new VLAN and tagged sub-interface. However, they do support it if you are using VRRP instead.\nThis is in no way a definitive guide on the various ways you might accomplish this - but I wanted to give a bit of background on how we tackled the problem. Unfortunately in my case, most of the older locations have several thousand systems - so I\u0026rsquo;ll be working on this migration for quite a while.\nEver had to migrate to a new IP addressing scheme? What methods did you use? How large was the network? Run into any big problems? Comment below!\n","permalink":"https://0x2142.com/migrating-ip-addressing-schemes/","summary":"Some thoughts on IP addressing migration in a live network, since I\u0026rsquo;ve been working on this a lot lately","title":"Migrating IP Addressing Schemes"},{"content":"Note: I may receive commissions for purchases made through links in this post. This is to help support my blog and does not have any impact on my recommendations.\nAs part of my home lab, I have an older Synology DS411 that I picked up in early 2012. I\u0026rsquo;ve been using the device since then with 4x 3TB drives as an iSCSI backend to my ESX host. Of course, I\u0026rsquo;ve also been using the NAS for general file storage (photos, videos, documents, etc). So in late 2014 I decided that I needed to find a good backup solution for it.\nI started using CrashPlan, because they offer unlimited cloud storage for only $5.99 a month. This was amazing to me, because I had around 1.2TB of data at the time. I found a great community package here, which allowed me to install the backup client directly on the NAS. This ran great until late 2016 when CrashPlan updated to 4.8.0 and ended support for ARM processors (which is what the DS411 uses). For the past 6-7 months, I\u0026rsquo;ve been unable to back up my primary storage device at home. Since my DS411 is reaching the end of it\u0026rsquo;s life anyways, I figured I would just wait until I replaced it with one of the new Synology devices that use an Intel processor.\nWell a few days ago, I received an email from CrashPlan threatening to delete my backups since my NAS hadn\u0026rsquo;t connected in over 6 months. It makes sense why they do that - but I didn\u0026rsquo;t want to lose my backups before I could replace the device. Re-seeding my backups whenever I picked up a replacement NAS would take forever (I think I have ~2TB backed up currently). So I finally forced myself to sit down and find an alternate solution.\nAlright - so as of today I now have a dedicated CentOS VM running on my ESX host, which is connected to the Synology via NFS. This VM is running the CrashPlan client, and my backups have resumed! Here is how I got this all set up:\nSynology Configuration Enable NFS on the Synology\nOpen up the Control Panel and go to File Services Scroll down to the NFS section, and check the box for Enable NFS Click Apply Apply NFS permissions to each share\nStill under Control Panel, visit Shared Folders For each folder you need to back up via CrashPlan: Select the folder and click Edit Click the tab for NFS Permissions, then click Create Enter the IP address for your CentOS VM (or other linux system) Set privilege to Read-Only (you could probably leave this read-write, but CrashPlan only needs read permissions to back up data) Click OK Linux VM Setup Build a CentOS VM - This can be on an ESX host like I used, or just a standalone PC\nDon\u0026rsquo;t forget to use the same IP address that we entered in the Synology for NFS Install packages\nMake sure you get the latest package updates first: yum -y update Install NFS tools: yum -y installnfs-utils nfs-utils-lib Test NFS\nIf you have the packages installed and NFS set up correctly on the Synology, then you should be able to validate the configuration by using showmount. For example, here are the directories that I was using CrashPlan to backup: [root@SynologyBackupVM~]# showmount -e 10.12.32.2 Export list for 10.12.32.2: /volume1/backups 10.12.32.209 /volume1/documents 10.12.32.209 /volume1/homes 10.12.32.209 /volume1/photos 10.12.32.209 Make your local Linux directory structure, which shouldmatch what the Synology structure is. So in my case, I made new directories on my local Linux VM for /volume1/backups, /volume1/documents, etc Edit /etc/fstab to auto-mount your NFS shares on boot. In my case, I added the following lines: # NFS Share location Local Folder 10.12.32.2:/volume1/backups /volume1/backups nfs defaults 0 0 10.12.32.2:/volume1/documents /volume1/documents nfs defaults 0 0 10.12.32.2:/volume1/homes /volume1/homes nfs defaults 0 0 10.12.32.2:/volume1/photo /volume1/photo nfs defaults 0 0 Reboot, and check to make sure each NFS share was actually mounted. If you do a ls /volume1/backups, do yousee all the files on the NAS in that folder? If everything works, then grab the CrashPlan Client installer (4.8.2 was the latest at the time) wget https://download.code42.com/installs/linux/install/CrashPlan/CrashPlan_4.8.2_Linux.tgz Extract the files: tar -xzvf CrashPlan_4.8.2_Linux.tgz Run the installer: cdcrashplan-install/ \u0026amp;\u0026amp; ./install.sh I just let CrashPlan use all the defaults, so I didn\u0026rsquo;t change any options during install Set CrashPlan to start automatically: chkconfig crashplan on Make sure the service is already running: /etc/init.d/crashplan status At this point we should be able to run CrashPlan\u0026rsquo;s backup client on our VM, which will pull the data across the network from the NAS. The last step is to set up our local PC for remote administration of the Linux VM. Unfortunately, this process has become more and more painful as CrashPlan keeps updating their clients. They provide their own documentation on how to do this piece, but I\u0026rsquo;ll summarize what I did here:\nConnecting the CrashPlan UI to the Linux VM Download and install the CrashPlan Client on your PC (In this case, I\u0026rsquo;m using a Windows 10 laptop)\nOn your linux VM, run the following command to findyour authentication token: cat /var/lib/crashplan/.ui_info\nBack on the Windows side, place that token in the following file: C:\\ProgramData\\CrashPlan.ui_info\nYou will replace the existing token in the file Also change the port from 4243 to 4200 Should look something like this when you\u0026rsquo;re done: 4200,3da4v903-7q38-4r52-e67e-79aecxf760c4,127.0.0.1 Download PuTTY, which will be used to create a SSH tunnel to our linux box\nOpen PuTTY and set the following configurations:\nOn the left side, go to Connection \u0026gt; SSH \u0026gt; Tunnels Enter thesource port: 4200 Enter the destination: localhost:4243 Click Add On the left side, go back up to Session Enter the IP address of your Linux VM (optional) Under Saved Sessions, put in a name and clickSave Once that\u0026rsquo;s done, go ahead and clickOpen to connect to your Linux VM\nLog into the VM and just leave the window open.\nNote: This SSH session will need to remain open any time you need to connect to your Linux VM and administer CrashPlan Open the CrashPlan client locally on your Windows machine\nLog into CrashPlan using your account (Should be the same one you were previously using to back up your Synology with)\nCrashPlan may give you a warning about migrating to a new PC, and ask if you want to adopt the backups - You want to acceptthis prompt, and let CrashPlan know that your new Linux VM is a replacement PC for your Synology.\nAs long as everything went according to plan, the CrashPlan client should start scanning the NFS shares on your Linux VM and comparing them to what\u0026rsquo;s already backed up. Once it completes its synchronization, it will initiate the backup processes again.\nI was extremely happy that this worked, because I was able to start backing up my data again (which had apparently almost doubled since the last time CrashPlan was connected). In my case, moving to a Linux VM provided me with much better backup performance as well, since the Synology DS411 only has a 1.6Ghz single-core processor and 512MB of RAM.\nI hope this helps out anyone else out there who may have been trapped in a similar scenario. If you have any questions, please feel free to leave me a comment below!\n","permalink":"https://0x2142.com/synology-backups-with-crashplan/","summary":"In this post, we\u0026rsquo;ll walk through a tutorial on setting up Crashplan to back up a Synology NAS via NFS","title":"How to: Synology Backups with CrashPlan"},{"content":"So it\u0026rsquo;s now been over two months since I finished college and obtained my magical piece of paper. It has been interesting to finally have some free time to do things that I want to do, and not having to constantly balance my time between school and work.\nSo now that I\u0026rsquo;ve had a bit to sit back and take a break, I\u0026rsquo;m starting to begin itching toward certification studies again. I really enjoy certifications because they give me a goal to work towards, and I can study the materials at my own pace.\nFor reference, I currently hold the following active certifications:\nCisco: CCNA, CCNA Security, CCNA Voice (retired), CCDA, CCNP, CCDP\nI\u0026rsquo;ve been looking a bit at the Cisco Cloud and Data Center certification tracks, since I\u0026rsquo;m dealing a lot more with the Nexus switching line and data center technologies overall - but after reviewing the cert syllabus, I\u0026rsquo;m not really feeling very strongly toward those. I\u0026rsquo;m also hesitant because it would mean starting back over at the CCNA-level for the new tracks and working my way back up to the CCNP-level. I\u0026rsquo;ve also previously considered getting my CCNP Security, but I\u0026rsquo;m not actively working in Cisco ASA firewalls much any more.\nThe only next choice in the Cisco world would be going for the CCIE R\u0026amp;S or the CCDE. I\u0026rsquo;ve been considering for a long time that I would eventually like to get there, but those certifications also require a significant investment of time and money. I definitely think the information and skills I would learn along the way would be worth it, and I\u0026rsquo;m beginning to really consider this an option in the near future. I\u0026rsquo;ve spent a bit of time reviewing the exam topics listed on Cisco\u0026rsquo;s site, and debating which of the two would be a better first choice.\nMy other option is pursuing the Juniper side of things. Most of the data centers I manage now are shifting toward Cisco for switching and Juniper for firewalls - so it would certainly benefit me to educate myself further on the Juniper equipment. Until this point, I\u0026rsquo;ve been just learning on the job by buying Juniper SRX firewalls and figuring it out as I go. My only real hesitation on this would be maintaining two separate lines of certifications. Both Juniper and Cisco enforce a 3-year expiration on their certifications, so I would need to keep on top of both - which isn\u0026rsquo;t necessarily a bad thing.\nSo at this point, I really don\u0026rsquo;t have a clear idea of what I want to do. Those are my current thoughts and options, but I\u0026rsquo;m having a hard time settling on what would be the best option for me at this time. I definitely want to start studying for something (and potentially take the exam) before the end of this year though, so I would like to figure it out rather soon.\nIf you have any suggestions or thoughts on the certifications I\u0026rsquo;ve mentioned, leave me a comment below!\n","permalink":"https://0x2142.com/alright-now-what/","summary":"I finally finished college, so what\u0026rsquo;s next for my professional goals?","title":"Alright - Now What?"},{"content":"A while back I wrote some basic information on how to get started implementing multi-homed internet using BGP. The details and configurations listed in that post are enough to get the connection up and running - but not quite in an ideal state. So today I want to share some quick tips that will help you maintain a better and more secure BGP connection.\nSecuring your BGP peering (Know who you\u0026rsquo;re connecting to) BGP is a little different from most other routing protocols, since it uses a single unicast TCP connection between peers to exchange routing updates. Lucky for us, that means that we can easily filter traffic from only known peers. Once you have direct connectivity up between your edge router/firewall and your direct peer, lock down that connection with an ACL. Permit TCP port 179 traffic ONLY from your directly connected peer IP - no one else.\nWhile you\u0026rsquo;re at it, let\u0026rsquo;s take it another step further: Request that your ISP set up BGP authentication. Sure, a majority of BGP implementations today still require use of MD5 for auth (which is terrible) - but some authentication is still better than none. This can usually be arranged at the time of turning up peering. Both sides configure the same authentication password and with any luck the peering still establishes.\nBGP by nature is unfortunately not the most secure protocol - but a few simple steps like this will help ensure you\u0026rsquo;re only connecting out to authorized peers.\nRoute filtering (Don\u0026rsquo;t trust anyone) Usually when you\u0026rsquo;re filling out the BGP peering paperwork for your service provider, they will ask you what kinds of routes you want. In most cases, you should be able to request one of the following:\nDefault only - Exactly what it sounds like. Your provider will only advertise a route for 0.0.0.0/0. In many cases, this is probably what you\u0026rsquo;re going to want. With this type of advertisement, each upstream provider will just give us the same default route to the internet. From there we can weight which one we want to use, and traffic will automatically fail-over to the secondary connection should the primary fail.\nPartial - If for any reason you want to weight routes to certain destinations differently, then we might request this. In this case, you\u0026rsquo;re probably going to still receive 0.0.0.0/0 plus any specific routes you ask for. A good example of this is if we wanted to specifically manipulate routes for a remote office we have. Maybe we want to weight Internet traffic for one uplink, and VPN traffic to a remote office on the other uplink.\nFull - In 99% of typical business cases, this won\u0026rsquo;t be required. This option means the upstream providers will be dumping the entire Internet routing table on you. While this offers you a ton of control over path manipulation, it also requires significant memory resources on your routers in order to maintain that routing table.\nAfter we figure this out, the next step is to make sure we are filtering the routes we accept from the upstream provider. Wait - didn\u0026rsquo;t we just tell them exactly what routes to send us? Why do we need to filter them? Well you can never be too safe here - and we would rather perform an unnecessary filtering than have an ISP accidentally misconfigure route advertisements. So if you\u0026rsquo;re only expecting a route for 0.0.0.0/0, then filter your inbound route advertisements so you only accept that route.\nSame thing goes for outbound route advertisement - if we own a /24 of public IP space, then we only want that range to be advertised out. Some providers may already filter this on their end, but again it doesn\u0026rsquo;t hurt here to be extra cautious. If we are accepting anything other than a default route from our provider, then we run the risk of leaking those additional routes between the two providers - which would lead to inadvertently becoming a transit AS. Chances are pretty good that you don\u0026rsquo;t want that, so make sure you configure filtering for all outbound route advertisements.\nMinimum Advertisement (Oh no, we have to re-address everything) I mentioned this in the original post - but typically when you are peering with two separate upstream providers, you need to advertise no less than a /24. We ran into this at my last job, where we had been provided a /25 by AT\u0026amp;T but we needed to bring in a second carrier via BGP. The reasoning behind this is to keep global routing tables as small as possible, by not allowing them to end up flooded with a ton of routes for smaller subnets. It makes sense, but on the other hand I feel like requiring a /24 in all cases can be a bit wasteful. My last job only required maybe 30 publicly addressable hosts - which meant that the remaining addresses went unused.\nAt any rate - should you find yourself in this scenario then you\u0026rsquo;re going to have to face the inevitable: Renumbering into a new IP space. Any time you have to do this, it\u0026rsquo;s going to be a bit of a pain - but for external addressing like this it might be easier. So in our case, the entire /25 space was hosted on our external firewall then NAT\u0026rsquo;ed into DMZ servers.\nHere is the quick steps that I used to do a side-by-side migration without taking any significant downtime:\nGet the new subnet up and running - assign the interface addresses on your firewall and BGP up and running Assign new IP addresses to all of your existing services Configure NAT rules for the new external IP addresses to the DMZ hosts - while leaving the existing NAT rules for the old subnet (Also make sure your firewall rules permit the same traffic to either IP) Migrate DNS entries externally to point to the new IP space Once traffic stops flowing to the old IP, remove the old NAT As a side note - if you procure redundant internet connections through the same upstream provider, then you might be able to work out something else. They may be able to provide you a private ASN to use, and they will likely accept any minimum advertisement - since they will be summarizing upstream within their network anyways.\nI had a few more things I originally intended to cover here - but it seems that these topics are filling way more space than I thought they would. Specifically, I\u0026rsquo;m thinking about a dedicated post to BGP path manipulation - which is probably something you\u0026rsquo;re going to want to implement after peering is established. Hopefully these tips help! If you have any questions, throw them in the comments below.\n","permalink":"https://0x2142.com/quick-tips-for-better-bgp/","summary":"A lot of BGP setups I run into are bare-bones, but there are a few quick ways to improve your configurations","title":"Quick Tips for Better BGP"},{"content":"\u0026ldquo;The network is slow\u0026rdquo; - Sound like something you\u0026rsquo;ve heard before? What does \u0026lsquo;slow\u0026rsquo; mean anyway? And is it different from yesterday? Sometimes tracking down network \u0026lsquo;slowness\u0026rsquo; can be pretty difficult, especially when you don\u0026rsquo;t have a good baseline of what is normal. This kind of goes back to one of the tips I shared earlier in \u0026lsquo;A Little Bit of Magic\u0026rsquo; - having a baseline and understanding of what is normal on your network will help you find issues much more quickly.\nWhen I started working for a cloud service provider a few years ago, the first thing to start coming up extremely often is network latency and performance issues. These are things I never had to worry too much about previously, as most of my jobs had been with enterprise environments where everyone is on the same LAN (or at least within one state). However, when you get into hosting a Software-as-a-Service cloud on a global scale, then slight performance issues begin to mean big slowdowns for your customers.\nI was amazed at the current network infrastructure monitoring that was in place when I began working for the SaaS provider: A few bare-bones Cacti instances, completely unmanaged by anyone, and not configured to monitor any relevant ports or data. Today that situation is vastly different - I have installed a few different applications that allow us to get alerted on network variances and quickly determine exactly where the issue is. One of the tools that has helped us get to this point is called SmokePing, which I would like to talk about today.\nSetup and Installation I won\u0026rsquo;t get into the details of installing SmokePing, as there are already a number of good tutorials out there (like this one or this one). If you have a decent familiarity with Linux, then the process should be fairly straightforward. Keep in mind that your SmokePing graphs will show latency and packet loss between the machine you have SmokePing installed on and the targets you define. So make sure that you plan out where you deploy your SmokePing machine(s) to provide beneficial information.\nOnce you have SmokePing installed and setup, it\u0026rsquo;s time to start defining targets to monitor. We have over a dozen points of presence globally, so I\u0026rsquo;ve installed SmokePing on a single machine in each location. Each instance has ping targets defined for every network segment within it\u0026rsquo;s own datacenter, network segments in every other datacenter, and some public IP space of every datacenter. So we accomplish latency and packet loss monitoring within the datacenter, across the site-to-site VPNs between each datacenter, and the general internet connections between each datacenter. For certain customers, particularly those who have dedicated MPLS circuits to us, we are also monitoring latency/packet loss to customer endpoints.\nSmokePing also supports deployment in a controller/worker configuration, where you have a single primary configuration/management point and several workers to perform testing. I really want to test this out for our environment, but I haven\u0026rsquo;t quite had the time to dedicate to it. If you\u0026rsquo;re interested though, you can find the details on that here.\nInterpreting the graphs The graphs created by Smokeping might not seem clear the first time you see them. For example, take a look at this:\nThis graph is the result of a standard latency test - 20 pings every 5 minutes. So for every step on the graph, SmokePing draws out the range of responses in those 20 pings - shown by the gray \u0026lsquo;smoke\u0026rsquo;. The darker the gray area, the more pings came back with that response time - and similarly the lighter areas mean that fewer pings had that response time. The solid colored part of the line marks the average response across all 20 pings, and also gives an indication of percentage of packets lost.\nSo the first thing I would notice about this graph is that the average response time is varying quite significantly between about 15ms and 200ms. In a normal healthy network, you should not expect to see such a drastic change in response times like that - some variation is normal, but not to this extreme. Two other things to note from this graph: The time of each latency jump seems to line up almost every 30 minutes, and towards the end we begin seeing some slight packet loss.\nAfter being informed that there was a performance issue between a few different systems, I opened up SmokePing immediately to start looking for anything that jumped out - like the graph above. In this case, this was a 200Mb dedicated MPLS circuit used only for replication traffic between data centers. Every 30 minutes, a replication job was kicking off and saturating the line for a few minutes - which in turn was causing excessive jumps in latency and some minor packet loss.\nAs another example:\nThe first thing you probably notice about the graph above is the sudden stabilization of latency. This graph monitors traffic between two data centers over an IPsec VPN tunnel - and we happened to be suspecting that one of the two peer firewalls was having performance issues. We swapped out to new hardware on one side of the connection, and the latency immediately started flat-lining. A consistent 85ms is way better than averaging anywhere from 90-180ms. (And if you happened to notice the slight packet loss after the new device was implemented - that was actually due to an unrelated upstream provider issue). My point with this graph is really just to show how helpful it is to have the historical data available. It would have been extremely difficult to prove that the one firewall was the root cause of our problems if I didn\u0026rsquo;t have a way to track the issue.\nSo that\u0026rsquo;s a bit about SmokePing and how I\u0026rsquo;ve deployed it within a cloud provider\u0026rsquo;s environment. It\u0026rsquo;s only been up and running for a few months, but I\u0026rsquo;ve already found it to be extremely helpful in troubleshooting performance and latency issues. SmokePing is also extensible via scripting, which can help to collect additional data at the time of an issue. I\u0026rsquo;ve written a few quick scripts to run extended traceroutes during packet loss events, which I might post up here in the future.\nHave you installed SmokePing in your environment? How do you use it? Has it helped you with performance issues?\nComment below!\n","permalink":"https://0x2142.com/tracking-latency-and-packet-loss-with-smokeping/","summary":"Why should you use SmokePing - and a quick look at how to interpret the graphs it generates","title":"Tracking Latency and Packet Loss with SmokePing"},{"content":"Last year we began migrating from our old Juniper SSG firewalls to the new SRX line. After a few months, I\u0026rsquo;ve honestly really started to enjoy working with them - so much that we\u0026rsquo;ve decided to start standardizing our firewall platforms by ditching everything else. So far I\u0026rsquo;ve had the opportunity to install ten SRX 1500s, six SRX 345s, and one SRX 340. Some have been completely new installs for a new location and some have been migrations from other devices. But while most of the process has been surprisingly smooth - there is one thing that keeps coming back up: VPN issues. (Oh, and the fact that pre-15.1X49-D60 doesn\u0026rsquo;t support In-service-upgrades - but don\u0026rsquo;t get me started on that one\u0026hellip;)\nWe run multiple locations around the world, and unfortunately have to keep full mesh VPN connectivity due to the way our systems have been deployed. Today each SRX cluster has around 15 different VPN peers, which are made up of other SRXs, older SSGs, CheckPoint firewalls, Cisco ASAs, and Watchguard firewalls. This is still an on-going process - but I wanted to throw out some of the issues I\u0026rsquo;ve run into so far, and what I\u0026rsquo;ve been able to do to fix them or work around them..\nIssue #1 - VPN is up, but no traffic is flowing across it This one initially took me a minute to figure out. All of our tunnels are route-based, using secure tunnel interfaces. So each VPN is configured with a set security ipsec vpn vpn_name bind-interface st0.x command. I had a set of VPN tunnels between two locations that were not passing traffic, even though a show security ipsec sa showed the tunnels as established. For reference, here is what the config looked like:\nroot@SRX-SITE-A\u0026gt; show configuration security ike respond-bad-spi 1; proposal ike-aes256 { authentication-method pre-shared-keys; dh-group group2; authentication-algorithm sha-256; encryption-algorithm aes-256-cbc; lifetime-seconds 28800; } policy ikepolAES256 { mode main; proposals ike-aes256; pre-shared-key ascii-text xxxxxxxxx; ## SECRET-DATA } gateway gateway-siteB { ike-policy ikepolAES256; address XXX.XXX.XXX.XXX; no-nat-traversal; external-interface reth0.0; } root@SRX-SITE-A\u0026gt; show configuration security ipsec proposal ipsec-aes256 { protocol esp; authentication-algorithm hmac-sha1-96; encryption-algorithm aes-256-cbc; lifetime-seconds 28800; } policy ipsecpolAES256 { perfect-forward-secrecy { keys group2; } proposals ipsec-aes256; } vpn vpn-to-SITE-B { bind-interface st0.1; df-bit clear; ike { gateway gateway-siteB; ipsec-policy ipsecpolAES256; } establish-tunnels immediately; } root@SRX-SITE-A\u0026gt; show configuration interfaces st0 unit 1 { description vpn-to-SITE-B; } The config on both sides practically matched, but there was one thing missing that was preventing the tunnel from passing traffic. Under the st0 configuration, unit 1 (or whichever tunnel interface you might be using) needs to have family inet configured. Even though I\u0026rsquo;m using an unnumbered tunnel interface, this command still needs to exist to tell the SRX that the interface is used for IPv4 traffic. Quick fix, but it\u0026rsquo;s easy to miss.\nIssue #2 - VPN drops every 2-4 hours and doesn\u0026rsquo;t re-establish for another 2-4 hours (or manual SA clearing) The original SRXs that I installed were running JunOS 15.1X49-D40.6. I had at least half a dozen of these devices interconnected with full mesh VPNs, and experienced no issues. However, when I picked up a new set of SRX 1500s a few months back, Juniper had just released 15.1X49-D70.3 - so I upgraded before these were put into production. Strangely enough, when I began migrating tunnels to the new cluster we started to see the VPNs to remote SRXs drop sporadically. The first remote sites to migrate were less of a priority to keep connectivity established, so I took this opportunity to spend a little time figuring out what was going on.\nThe initial issue seemed to be that the VPNs would establish, but only for about 2-4 hours. Then they would drop and not re-establish for 2-4 hours. This seemed a bit weird to me, because the re-key interval was set for 8 hours - which means that re-key wasn\u0026rsquo;t playing into this. Even more weird, whenever the issue occurred - one of the two SRX clusters would always still show the IPSec tunnel as up, while the peer SRX would just keep logging errors about bad SPIs. Clear the stale IPSec security association, and the tunnels re-establish immediately.\nIn order to resolve this, I had to configure both Dead-Peer-Detection and Juniper\u0026rsquo;s VPN monitoring on both sides of the connection - so that each SRX would more actively monitor the tunnel status. Juniper\u0026rsquo;s documentation states that they enable DPD by default, but in an \u0026lsquo;optimized\u0026rsquo; method which only sends a DPD R-U-THERE message under certain conditions. I had to change this to force the SRX to send the DPD messages at regular intervals. Here are the changes I made to fix the issues:\nroot@SRX-SITE-A# set security ike gateway gateway-SITE-B dead-peer-detection always-send root@SRX-SITE-A# set security ipsec vpn vpn-SITE-B vpn-monitor optimized After these changes were in place, I stopped experiencing the issue. Again, these had to be implemented on BOTH sides of the connection. These weren\u0026rsquo;t necessary on the tunnels in-between the SRX clusters on the older firmware version - so there may be some sort of bug between those and the newer firmware.\nIssue #3 - VPN between SRX and CheckPoint duplicates IPSec SA on re-key (sometimes causes tunnel to stop passing traffic) This issue was a complete mess - mostly because of the effort involved in trying to coordinate two separate vendors to work on an issue. New SRX clusters (on 15.1X49D40.6 at the time) had been deployed and all of them had to connect back into our existing CheckPoint locations via IPsec tunnels. All was great, until about two weeks after installation we started seeing some weird tunnel drops. After some troubleshooting on my end, I discovered that watching what happened during the regularly scheduled re-key interval was helpful to see what was going on. Right at the eight hour re-key, the tunnels would try to re-establish but couldn\u0026rsquo;t - and sometimes this led to uni-directional traffic flows across the VPN.\nThe SRX tries to start a soft reset process prior to the re-key interval, so that it can gracefully migrate traffic to the new SPIs. However, something was happening that was causing the SRX to never terminate the old SPIs - so after a while the SRX would try to begin the soft reset process and fail because it had already reached its maximum SPIs for a given peer. Once the re-key interval was reached, the SRX would initiate the hard reset process on the tunnel. The CheckPoint side typically wouldn\u0026rsquo;t notice that anything was going on, and would keep sending traffic down the bad (expired) SPIs. A quick clear security ipsec sa and clear security ike sa would bring the tunnels back up.\nI worked with some great guys on the Advanced JTAC team - but ultimately the SRX configuration and behavior seemed to be exactly what was expected. The only thing we couldn\u0026rsquo;t figure out is why the SRX was holding onto the old IPSec SPIs. So we opened a support case with CheckPoint to see what they had to say. After a few troubleshooting sessions and running a bunch of debugs, the CheckPoint engineer seemed to believe that the issue was on their side. All of our CheckPoint clusters were running R77.10 at the time, but we also tried upgrading to R77.30 which still experienced the issue.\nUltimately, the CheckPoint guy pointed to SK97746, which states that CheckPoint has interoperability issues due to the way it handles the tunnel renegotiation between other vendors. Essentially, as soon as the Phase 1 IKE tunnel re-negotiates, the CheckPoint deletes the Phase 2 tunnel immediately (even when we are working in a tunnel soft reset). This means that the SRX would have believed the tunnel has re-established and keep using the old one until the hard re-key time. However, the CheckPoint had already deleted the old tunnel - which caused the traffic drops. This is fixed using CheckPoint\u0026rsquo;s GUI DB editor tool and making the modifications listed in the support article linked above.\nWhile the CheckPoint side seemed to be responsible, it\u0026rsquo;s still odd that the SRX was never clearing the old SPIs. It might be that it kept them open because the old tunnels were never gracefully closed with the CheckPoint.\nSo there you have it - I hope that these might help someone out who is currently banging their head against a SRX VPN issue. If you\u0026rsquo;ve run into similar issues, drop a comment below!\n","permalink":"https://0x2142.com/post/2017/juniper-srx-vpn-issues/","summary":"After running IPSec VPNs on Juniper SRX for years, this post explores some of the common issues I\u0026rsquo;ve run into","title":"Juniper SRX VPN Issues"},{"content":"If there is one thing you learn very quickly in networking, it\u0026rsquo;s that everything is always the fault of the network. Two systems cannot communicate? Yeah, that\u0026rsquo;s always a network problem. Something is inaccessible? Probably the network. What about that broken toaster? Definitely a network issue.\nStarting off as a less experienced network engineer, this can easily get overwhelming. A ton of other teams blaming the network infrastructure for problems that might be entirely unrelated. However, it seems like a good part of your job will likely be dedicated to proving that the root cause of the problem isn\u0026rsquo;t the network.\nI threw together a few tips that should help you get a better start to defending yourself against the angry sysadmins out there:\nGet a good handle on the problem - Above all else, it\u0026rsquo;s extremely difficult to troubleshoot something without a good description of the problem - so get that first. Which systems are having the problem? Get host names or IP addresses. When did the problem start? Did it ever work? What behavior is being seen? Can it easily be replicated, so that you can watch logs in real-time? If not, get the last date/time that the issue occurred.\nKnow your infrastructure - Being a good network admin means understanding traffic flows and routing through your infrastructure. When someone says two systems are having a problem communicating, you should already have a good idea of what network components reside in the space between them. Are these on the same network segment? Is there a firewall (or multiple) in between? Does this traffic utilize a proxy or load balancer?\nCheck logs - Once you know which systems are in the way, check through logs for those systems. Particularly with firewalls, do your best to filter out logs to see the traffic calls between each system. Seeing ports blocked or traffic being dropped? A lot of firewall platforms will include enough detail in the traffic logs to quickly identify the issue, if it is in fact a network problem.\nThe basics are still important: Check TCP flags - This one has honestly saved me more often than not. Two systems aren\u0026rsquo;t establishing a connection, and the sysadmin says they are just receiving a \u0026ldquo;connection timeout\u0026rdquo; error. Check through the firewall logs - Yeah, we see the typical TCP handshake - but then the remote system sends back a TCP RST to the client. In most cases, this means the connection is actually succeeding from a network perspective. However, the target system is getting something that it doesn\u0026rsquo;t like from the client, so the application kills the session. Same thing goes for a client system sending the RST.\nWireshark - A lot of people see this as the nuclear option. All else has failed, so we have to resort to a packet capture. I used to think this way too until about a year or two ago. The vast amount of information within a full packet capture can easily be overwhelming - but once you get a handle on how to read it, it can also be incredibly useful. Raw packet captures don\u0026rsquo;t lie - and all the information you need is within those details. Start a capture, reproduce the issue, then analyze the results.\nBe patient, and explain your defense - Not everyone is a network admin, and a lot of IT professionals don\u0026rsquo;t necessarily have a good grasp of how networking truly works. So once you\u0026rsquo;ve gathered your defense, be ready to explain it in a way that the other party will understand clearly. There is a huge difference from saying \u0026ldquo;I see TCP RST packets\u0026rdquo; to trying \u0026ldquo;Looks like the connection is succeeding, but the server-side system is resetting the connection\u0026rdquo;. Some people won\u0026rsquo;t want to admit that the problem actually exists with their system either, so be patient and work with them while they figure it out.\nBonus: Know the application - In some of my previous jobs, I was responsible for all systems and applications in the environment - even through I was primarily focused on networking. This experience has helped a ton, because even today I can still speak to how some applications work. I have installed and configured VMware ESX, Windows Server, backup and replication products, and much more. So when an application administrator says they are seeing a particular issue with something, I am more easily able to troubleshoot since I have a basic understanding of the applications they\u0026rsquo;re working with and how those applications communicate. This certainly isn\u0026rsquo;t a required skill - but it does help speed up troubleshooting efforts and minimize confusion around what\u0026rsquo;s going on with the application.\nHave any other tips you would like to share? Throw them in the comments below!\n","permalink":"https://0x2142.com/guilty-until-proven-innocent-how-to-prove-its-not-the-network/","summary":"IT\u0026rsquo;S THE NETWORK! - Sound familiar? Let\u0026rsquo;s take a quick look at how we can counter that blame","title":"Guilty Until Proven Innocent: How to Prove It's Not the Network"},{"content":"Working in IT is always quite an interesting experience. You get to work with a ton of different people from varying backgrounds and skill sets. Sometimes you work with people who have been doing IT their entire life, and sometimes it was a complete career change for others. However, it never ceases to amaze me how many people from any background are embarrassed to admit their own mistakes.\nGrowing in any profession means making mistakes. It\u0026rsquo;s inevitable, and everyone makes them. The critical point is realizing when you\u0026rsquo;ve done something wrong, fixing it, and learning from it. Ever meet someone who seems to just be great at everything they do? Maybe it seems like they never screw up? That\u0026rsquo;s because of years of experience and learning from every mistake they\u0026rsquo;ve made.\nSo a few things I want to get out there:\nYou\u0026rsquo;re allowed to make mistakes - Like I mentioned above, everyone does it. Perfection can be a goal, but it\u0026rsquo;s not realistic. Stuff happens - sometimes something may get messed up because of unknown circumstances, or maybe your own carelessness.\nAdmit fault - One of the most important things about making a mistake is admitting that you did it. There is nothing worse than people who try to hide their mistakes or blame other people - there is just no sense in it. There is a lot more respect for people who are able to admit their mistakes to others, yet this seems to be a fairly rare quality in my experience.\nFigure out what went wrong - Whenever you make a mistake, you need to own it. Take responsibility for hunting down exactly what went wrong and determining how to fix it. It\u0026rsquo;s possible that the issue was inevitable, and something you couldn\u0026rsquo;t plan for. However, in many cases a lot of simple mistakes are preventable by just exerting extra care, planning, or taking the time to do better research.\nFix it - This goes along with what I said in #3 - but take ownership for your mistakes and fix them. Don\u0026rsquo;t pass them off to other people. Don\u0026rsquo;t pretend you don\u0026rsquo;t know whats going on. Just say \u0026ldquo;I screwed up, but I\u0026rsquo;m going to fix it\u0026rdquo;.\nLearn from it - After everything is done, take a step back and look at everything that happened. What can you learn? Everyone hates that sinking feeling that something just went wrong, so why not try to prevent yourself from having to experience that again? Take note of what could have prevented the issue. Make sure you never make that mistake again.\nShare the knowledge - The only thing better than learning from your own mistakes is being able to learn from others, so that you never make the same mistakes. If you\u0026rsquo;ve done something that is worth sharing, then do so. Take a few minutes to sit down with your team and explain the scenario - what went wrong, how it could have been prevented, and what lessons you\u0026rsquo;ve taken away from the incident. You have the power to help less-experienced people to learn how to be better - make the right impression and show them that it\u0026rsquo;s okay to admit fault.\nA lot of this may seem like it should go without saying - that it all should be common sense, right? However, in my experience it seems like a lot of IT admins are more than willing to try and hide their own mistakes, because they think it will make them look bad. In my opinion, hiding your mistakes makes you look far worse than having the ability to admit fault.\nIn one example, I recently worked with an individual who immediately began blaming the network team for an issue he was experiencing. They claimed that the firewall was blocking their communication between two systems where they were trying to install an application. The individual started complaining to management that the network team was holding up their progress because of an incorrect firewall configuration. The network team did everything they could to help troubleshoot - but ultimately it seemed like an application issue. The next day, the admin just came in and said \u0026ldquo;Oh, I don\u0026rsquo;t know what you guys changed but it started working today - so thanks!\u0026rdquo; Did the network team change anything? No. And it was found out later by another admin that the application was mis-configured the whole time. This guy lost a lot of respect from both management and his peers - just because he couldn\u0026rsquo;t admit fault.\nAs another example, I had a previous co-worker once attempt to remove software from a production database cluster in the middle of the day. Of course, this happened to be our busiest database cluster, which ran the backend for a vast majority of our customers. When the software was uninstalling, it dropped all network communication to the database cluster - which forced all customers in the data center offline. The second he realized that something was wrong, the IT admin informed his manager of what he had done. He took ownership and led the issue until it was resolved and customers were back online. The next morning, he made it a point to give a quick summary to the entire IT staff of the issue - and admitted that he screwed up. While it doesn\u0026rsquo;t change the fact that he made a huge mistake, this approach led to a much quicker issue resolution. He would have been in far more trouble if he had tried to hide the fact that he caused this outage event.\nWhat\u0026rsquo;s the biggest mistake you\u0026rsquo;ve made in your IT career? How did you handle it? Share in the comments below!\n","permalink":"https://0x2142.com/youre-not-perfect-admit-your-mistakes/","summary":"One of the easiest ways to build trust with co-workers is to communicate effectively","title":"You're Not Perfect: Admit Your Mistakes"},{"content":"As of the beginning of this month, I have officially completed my four years of trying to balance working full time and going back to school. I finished up my last college classes and now I can sit back and appreciate having some free time to myself again. I\u0026rsquo;ve never been really into the concept of school, but ultimately I went back because I was being pushed to by my previous employer. So I figured that now is just as good a time as any to tackle the topic of which is better - certs or college degrees?\nI talked about this briefly in my initial background story posts, but I went straight from Cisco Networking Academy in high school out to working a full time job at a local IT consulting company. By the time I finished high school, I had already passed the Cisco Certified Network Associate (CCNA) exams and become certified. Having that certification is what got me in the door for a number of interviews, and eventually got me the job at the consulting company. At that point, I really didn\u0026rsquo;t have much else going for me - I didn\u0026rsquo;t have a college education nor any real-world experience. In my time working at this company, I spent a significant amount of time doing self-study and labs for my certification goals. When I got my CCNP certification, I used it along with the experience I had gathered to get my next job. This new employer was heavily focused on their IT staff needing to have a college education - so they pressured me for a while to go back until I eventually gave in.\nI spent a while reviewing many colleges in the area and online, trying to figure out what would meet my needs. I ended up picking out a four-year degree in network security, and opted to go the online-only route because it benefited my schedule better. I packed my classes up to a full-time schedule, because I didn\u0026rsquo;t want a four-year degree to take any longer than four years. At this point, I also had the benefit that my employer was willing to reimburse 100% of the costs - which certainly helped convince me to go back.\nOver the course of the past four years, I have taken many classes that include general IT, development, networking, and security (not including the normal required materials). I found that a significant portion of these classes didn\u0026rsquo;t directly benefit me. A lot of the material was much more focused toward beginners who haven\u0026rsquo;t already been working in the field for six years - which is completely understandable. The most I really got out of this was improving my abilities to push myself through work that I didn\u0026rsquo;t want to do. I did have a few interesting classes, like an Android development course, which I found to be extremely fun even if I probably won\u0026rsquo;t use the knowledge much.\nFour years later and I\u0026rsquo;m done - did I benefit from it? On some level yes, I think I did. At the time of my degree completion, I have now been Cisco certified for ten years and I\u0026rsquo;ve been working in networking nearly the same amount of time. I\u0026rsquo;m already further in my career than I thought I would be at this point, and I\u0026rsquo;m happy with my position and pay (the degree isn\u0026rsquo;t going to change either of these things). At this point in time, finishing the degree is not much more than an accomplishment that I can add to my resume. Sure, having the degree on my resume may get me past HR screening for new jobs and opportunities - but it likely won\u0026rsquo;t actually play much into a company\u0026rsquo;s decision to hire me.\nIn the end I think that both certifications and college education are useful - they can both be great indications to an employer that you\u0026rsquo;ve been trained on certain technologies or fields. However, I think that the actual on-the-job experience is what really matters - and I experienced a direct benefit from getting in the field early and working while all of my friends were still in college. I would not be as far in my career as I am today if I had waited four more years to start working. Unfortunately, I think that we place a little too much importance on completing a formalized degree program, when equivalent experience and certifications may benefit a company more.\nI understand that I had a bit of a unique situation, but I figured it would be worth sharing my experiences and how they have affected my view of college education. I\u0026rsquo;m still happy that I went through with it and completed the degree, but you won\u0026rsquo;t see me throwing a big celebration - except that I\u0026rsquo;m just super glad it\u0026rsquo;s all finished. At this point, I will take a few months to relax and spend time on hobbies - but I do plan on going back to certification studies (Juniper stuff and likely begin working on a CCIE).\nAny thoughts? Comment below with your experiences - I\u0026rsquo;m interested to see if there are many people who have had similar experiences to me, or possibly even the complete opposite.\n","permalink":"https://0x2142.com/college-vs-certification-which-is-better/","summary":"My career path is the reverse of most people I\u0026rsquo;ve met - certifications first, then college much later. What impact has this had on my experiences?","title":"College vs Certification - Which is better?"},{"content":"Note: I may receive commissions for purchases made through links in this post. This is to help support my blog and does not have any impact on my recommendations.\nIf you really want to become great at something, you practice it a ton, right? Well networking and IT work exactly the same. You\u0026rsquo;re not going to become an expect by just reading a ton of tech books and blogs. While those certainly help, there is nothing better than simply getting your hands dirty. Having a good home lab setup is key to truly understanding how things work.\nSo how do you get started? Well the way that I built a home lab over the past 10 years is probably much different from you could today, given the amount of virtualization technologies available. Still, I believe that some physical pieces of equipment are necessary. I took classes in high school toward CCNA certification, and we had a lab of several routers and switches there. Once I got into the real world, I wanted to start working on additional certifications and just improve my skills overall. So I picked up an old Cisco 2611 router and a 2950 switch. I played with these for a bit and used them to get my CCNA Security, which at the time covered the basics of securing Cisco IOS routers and switches.\nAnother year or so down the road and I expanded by picking up a power-over-ethernet switch, and two Cisco 7900 series IP phones. Since I had discovered that the 2611 router could run Cisco\u0026rsquo;s Call Manager Express, I decided to go for the CCNA Voice certification. Having this equipment to work on gave me experience that was much closer to real world, than if I had just studied the textbooks. I could configure things, break things, then sit there for hours until I figured out how to fix my problem. I could configure the entire system, test it all, then tear it down and completely rebuild. Being able to configure the entire CME system from memory gave me a lot of confidence toward taking the certification exam.\nSo do I still have a home lab today? Oh yeah, you bet I do! It\u0026rsquo;s changed quite a bit from what it used to be, but the same concept still applies. I have an entire environment to play with, which allows me to test and learn new technologies outside of work. In fact, my \u0026lsquo;home lab\u0026rsquo; has evolved into just part of my home networks. So here is what I\u0026rsquo;ve got running today:\nCisco ASA 5505 (Probably soon to be replaced with a Juniper SRX 300) Two Cisco 2960G-8TC-L switches Ubiquiti UniFi 802.11n wireless access point Synology DS411 Network Attached Storage with 4x 3TB drives (Soon to be replaced, as it is over 5 years old(Update: Got myself a DS918+!)) A few spare PCs running VMware ESX 6.0 The ASA, switches, and AP run just about all of my home network. I even have the ASA running AnyConnect SSL VPN so I can access my storage at home from anywhere. The Synology has been one of the best additions to my network and lab. For one, it acts as a centralized storage device for my home network. I back up all of my PCs to it, and any digital media I own is also stored on it so I can stream it to devices within my home. For two, the Synology acts as an iSCSI backend to my VMware hosts. This setup allows me much more flexibility with my lab.\nOn the ESX hosts, I have a few VMs for lab use and a few that are for my home network. A GitLab server hosts all of my Git repositories for my own personal coding projects. I have a CentOS box for running the Ubiquiti management web interface. Another few CentOS VMs for running bind DNS, Observium, and Splunk. I also run a personal Minecraft server on there, so it\u0026rsquo;s not all work here 🙂\nI love the idea that at any time I can just go home, spin up a few VMs, and start playing with something new. When I was learning Juniper\u0026rsquo;s SRX platform, I downloaded their free trial of the vSRX and had it running for a while. When I changed jobs, I needed to learn a new web proxy software - so I downloaded their free trial and stood up a VM. You really learn a lot by building a platform from scratch, because you gain a better understanding of what impact certain configuration options have. You also have the freedom to change whatever settings you want and see what they do. I once had an idea for a coding project, so I turned up a VM running RabbitMQ - and spent a weekend learning how it works to see if it would accomplish what I needed for the project.\nSo to sum it up - I just want to say that having a home lab has really contributed a lot to my success. It offers way more flexibility than trying to test something at work, unless they also offer you a complete lab environment. Your lab doesn\u0026rsquo;t have to start off perfect, nor does it need to have expensive equipment - it just needs to help facilitate your ability to learn and gain experience. Have a lab at home? Tell me about it in the comments below! I would love to hear what other people have done.\n","permalink":"https://0x2142.com/why-have-a-home-lab/","summary":"Ever wonder if running a lab at home is worth it? This post explores why I think it\u0026rsquo;s an important investment","title":"Why Have a Home Lab?"},{"content":"Port Security. Always seems like one of those things covered in Cisco exams, yet how many businesses actually use it? For those that aren\u0026rsquo;t implementing it, should they? Or is it too much of a headache?\nSo the concept of port security is fairly simple - We want to secure each individual switch port to a physical layer 2 MAC address, or at least limit how many unique MAC addresses might be learned on an individual port. The technology could be used to just limit the number of simultaneous devices on a port - by just setting a MAC threshold. Or we can also take it to the extreme and lock down each port to a hard-coded MAC address - which will never allow another device to connect. You might be thinking that the second method is absolutely ridiculous, but it really depends on the business needs.\nFirst, let\u0026rsquo;s take brief look at the typical port security configuration and some of the options available.\nSecureSwitch(config)# interface x/x ! Whichever interface we want to lock down SecureSwitch(config-if)# switchport port-security max xx ! Max number of MAC addresses that can be learned SecureSwitch(config-if)# switchport port-security violation xxxxx ! Choose to either restrict or shutdown the port (description below) SecureSwitch(config-if)# switchport port-security ! This actually enables the port security config Fairly straight-forward, right? We choose a port (or you could do a range) and set a few options. The default number of MAC addresses able to be learned on a port is 1, so it\u0026rsquo;s likely you\u0026rsquo;re going to want to change this - unless 1 is all you need. Port security can only be enabled on access ports, so 1 MAC address works in most cases - except where you have a PC daisy-chained off of an IP phone (in which case this will need to be set to 2 or 3).\nNext we set our preferred violation action. This step is pretty important because it defines what happens when the port exceeds it\u0026rsquo;s MAC count. Restrict is the passive approach. If we have two PC\u0026rsquo;s plugged into a single access port (maybe using an unmanaged switch), then the second PC will just never be able to work as long as our max MAC limit is 1. The first PC to connect will be fine, and the switch will log a message and send an SNMP trap when the second MAC is picked up. Shutdown is the more forceful approach. Once that second MAC address turns up on the port, the switch puts the port in an err-disabled state - which shuts down the port to all traffic. This event is also logged and generates an SNMP trap - however the port will not come back online until an administrator manually re-enables it.\nNow that we see a basic config, let\u0026rsquo;s take a look at a few different use cases for this feature. In one of my previous jobs, I worked as a network admin for a local government organization. Port security configuration in that environment was extremely strict. Each switchport was configured to permit only one MAC address, shutdown upon violation, and the switchport port-security mac-address sticky command was also used. This command takes the first MAC address learned on the port and commits it to the running configuration, which means that this MAC is essentially hard-coded to be the only MAC permitted on the port. So in this environment, a single PC was tied to a single port - nothing else could ever be plugged into that port without either shutting down the port or administrator intervention. In a government office, this was absolutely necessary because every device on the network needed to be tracked and personal devices were not permitted to be connected. We needed to know if anything was ever plugged in that wasn\u0026rsquo;t an authorized device - so manual intervention and investigation was a requirement.\nIn a more typical office environment - port security configurations can just be a good security practice without going overboard with it. We never want a user to plug in a rouge switch into our network without our knowledge, right? So maybe we assume each user has an IP phone and PC, and limit the port to 2 MAC addresses. In this case, we can go ahead and just set the port to restrict. We don\u0026rsquo;t want to prevent the user from working if a port violation occurs, nor do we want to spend time resetting the port for them - but we might still want to be notified, especially if it happens often. In addition, port security is an excellent way to secure ports public areas. For example, maybe we have an IP phone or kiosk PC in our lobby. These need access to the network, but we don\u0026rsquo;t want anyone to be able to unplug that device and gain access into our network. In cases like this, it would actually make sense to have the switch only permit access from that single MAC address.\nOutside of the \u0026lsquo;practical\u0026rsquo; use cases, there is also the strictly security side of things. I\u0026rsquo;ve touched on a few considerations already - but there are also certain types of attacks that can be defeated by port security. One of those would be exhausting the CAM table resources. A malicious person could use publicly available tools to spoof MAC addresses in the packets they send to the switch. Tools like this force the switch to learn hundreds of thousands of MAC addresses, which eventually will overload the CAM table. When a switch CAM table becomes full, the switch begins flooding packets out all interfaces. This is because the switch can no longer assign mappings between MAC addresses and the ports they originate from - so the switch has no choice but to flood everything and hope the correct recipient receives the data. For the attacker, this means they can run a packet capture on the port and collect information they wouldn\u0026rsquo;t have otherwise needed to. This scenario could be prevented by implementing port security, which could simply restrict the number of MAC addresses learned off of any individual interface.\nPort security configuration can be implemented in a few different ways depending on your use case. Overall though, it can prove to be a useful way to help implement security controls on your network. What do you think about port security? Extremely useful or does it just get in the way? Comment below and tell me how you have implemented it!\n","permalink":"https://0x2142.com/port-security-worth-the-effort/","summary":"An exploration of practical applications of port-security configurations, based on my past experiences","title":"Port Security: Worth the effort?"},{"content":"Too often, it seems like a common component of office culture is complaining about the issues. \u0026ldquo;Why do we always do things this way? It\u0026rsquo;s not the right or best way.\u0026rdquo; Even when things are running smoothly to most, there will always be someone who believes that things are not being done right. Occasionally you might get lucky and someone will suggest a better option. However, in my experience many of those people only offer the better option as a suggestion, then complain when nothing changes. So let\u0026rsquo;s take a look at how notto fall into that trap.\n1. Identify the problem This can be the both the easy part and the hard part. For example, let\u0026rsquo;s take a recent example at my job: Poor coordination across teams for project work. Awesome, we have identified the problem, right? Well, not quite - that may be a high-level summary of the problem, but why is there poor coordination? Maybe the teams aren\u0026rsquo;t meeting often enough with each other, or maybe those meetings aren\u0026rsquo;t structured in an effective way.\n2. Identify the solution So it\u0026rsquo;s easy for anyone to say \u0026ldquo;I FOUND A PROBLEM\u0026rdquo;, yet it\u0026rsquo;s more difficult to come up with a reasonable and effective solution to that problem. Sit back and evaluate the problem, but make sure you consider the perceptive of both sides. Maybe Team A works better if they have all of the information up front, so they can design a proper solution - yet Team B likes to work as they go, and tackle things as they come up. In this case, we probably won\u0026rsquo;t get very far in asking Team B to schedule architectural/design meetings before starting a project, will we?\n3. Propose the solution This part is important, because we don\u0026rsquo;t want to start making changes without anyone understanding what or why we are doing it. If changes come out of no where, people are more likely to reject them. So maybe we sit down with Team A and Team B and explain our solution: We will hold a quick, high-level design meeting at the beginning of a project - but Team B will be responsible for trying to notify Team A as soon as they identify a new requirement, and Team A will be responsible for identifying when those new requirements warrant a bigger meeting to gather requirements/details.\n4. Make the change This is probably going to be the most difficult step. If you want the change to happen, you cannot stand back and hope that someone else does it. You have to lead the change. For example, if you are on Team A and you propose this idea, then you must hold Team B accountable to sitting down for a requirements-gathering meeting when you think one is needed. Give it a good effort, because if other teammates see you working hard to make working-life better for everyone then they will be more likely to join in.\n5. Re-evaluate and refine No one is perfect, and no idea will ever be absolutely perfect on the first try. So give it a while, then make sure you sit down and take a look at how this change has impacted everything. Has Team A been more productive, since they can get requirements earlier in the process? Has Team B become less productive due to the increase of meetings? You might get lucky and have a fairly smooth transition into a better working environment - but chances are good that the overall change might still need some tweaks. Don\u0026rsquo;t let yourself fall into the \u0026lsquo;set it and forget it\u0026rsquo; mentality - getting better means constant improvement.\nThis might seem like a lot of work just to make a simple change, but it doesn\u0026rsquo;t really have to be. As a real-life example, I recently worked with another team who was starting a new project to install an entirely new application. They began by submitting individual tickets to the network team with bits and pieces of what network changes they believed their project would need. Once I realized this was happening, I asked the person leading the project if they had 30-minutes to sit down and talk that afternoon. It was a very quick meeting where I asked about the application they were installing, what it did, how it was intended to be used, and what other applications/systems they believed it would need access to. I also provided a little insight into why this mattered to me from a network design perspective. From that I had enough of an understanding of their project to put together an effective design from the network side, which makes both of our lives easier - because we don\u0026rsquo;t have to piecemeal it together now then realize later that it wasn\u0026rsquo;t the ideal configuration. After that meeting, the project lead said \u0026ldquo;Man, I always wished we had a lot more meetings like this - this was really helpful\u0026rdquo;.\nThat is the difference between wanting change and driving change. The bottom line is: If you want to inspire positive change - You have to be the catalyst.\nI\u0026rsquo;m sure we have all identified areas of improvement with our workplace. Have you ever been the one to drive change? Leave a comment below, and tell me your story!\n","permalink":"https://0x2142.com/want-change-make-it-happen/","summary":"Some organizations resist change - but with the right planning \u0026amp; motivations, we can push to impove the conditions around us","title":"Want Change? Make it happen!"},{"content":"One thing I have found in IT is that sometimes the real question that needs answering is some form of: What is holding you back? Whether it be people who take longer to get to tasks they don\u0026rsquo;t like, or whole teams who think accomplishing something just isn\u0026rsquo;t possible. Change takes time, and we can get there one step at a time. Sometimes this change may be a problem with company culture, or maybe just individual accountability. There may be a simple question that can help solve this\u0026hellip;\nI learned this from the great boss I had a few years ago. He taught me a lot about how to improve as a professional, which has given me a bunch of habits that I still continue today. Earlier in my career, I would occasionally be given a task where I would put it off a little longer than I should have. For example, I might be asked to add a few new subnets to a site-to-site VPN tunnel between our main office and a remote office. Simple enough to do in theory, yet I might put it off for a day or two longer than needed (especially since it wasn\u0026rsquo;t a high priority task).\nEvery day I would need to provide an update on the tasks I had been assigned, so after a day I would get the question \u0026ldquo;Well, you\u0026rsquo;re hesitating for some reason - So what\u0026rsquo;s going on?\u0026rdquo;. It\u0026rsquo;s a reasonable question and one I think that needs to be asked more often. When I sit back and think about it, maybe I am hesitating because of fear. Maybe I\u0026rsquo;m not confident in the change - Will the change drop the VPN tunnel? What if I lose access to the remote site? Usually this resulted in me realizing that either I just needed to do a little more research what I was doing or I just needed to push myself to get it done.\nWe are all human, and sometimes we let ourselves get held back by simple things. Even at my current job, I have watched people say that they will send an email to a vendor - but then take two days to send it. Could this be helped by just asking them why they are hesitating? Maybe they don\u0026rsquo;t feel comfortable communicating with that particular vendor (for any number of reasons). I\u0026rsquo;ve learned my lesson, and now every time I catch myself thinking \u0026ldquo;I\u0026rsquo;ll get to that later\u0026rdquo; I just ask \u0026ldquo;But why am I waiting\u0026rdquo;? It might seem ridiculous, but it helps. Figuring out what your big hesitation is will help you to stop using it as an excuse.\nPush yourself through it. Be productive. Get things accomplished. Next time you catch yourself holding back, just ask yourself \u0026ldquo;why?\u0026rdquo; - and when you answer that question, hold yourself to addressing it.\n","permalink":"https://0x2142.com/whats-your-hesitation/","summary":"Sometimes we just have to ask ourselves \u0026lsquo;What\u0026rsquo;s holding me back?\u0026rsquo;","title":"What's your hesitation?"},{"content":"I really want to take a moment to talk about how wonderful VRFs/firewall contexts really are. Both technologies essentially allow a network administrator to spin up a virtualized, isolated instance of a network device. I\u0026rsquo;ll be honest and say that I hadn\u0026rsquo;t had the chance to play much with this stuff until just recently - but it makes life a lot easier in a cloud provider environment.\nI\u0026rsquo;ve been looking for a good chance to use VRFs in the past, but in most cases it didn\u0026rsquo;t really make much sense. About a year ago, I had a great opportunity when we needed to build a new data center. The data center was aimed at being lower capacity than most of our other locations, so we had to cut some costs here and there. In all of our other locations we use two physically separate sets of firewalls, one for external traffic and one for internal traffic. In this new location, we opted to save some money by picking up only a single pair of Juniper SRX 345 firewalls.\nI made the decision here to make use of Juniper\u0026rsquo;s virtual routing instances to keep logical separation of internal vs external firewalling, even though it was only a single physical cluster. For one, this would allow existing staff to maintain their current understanding of network architecture. Every data center has the same overall logical traffic flow, even if the physical devices are different. Second, this allowed us to split load across the two devices. Normally we have two physical clusters to handle the traffic load, but in this case we were essentially going to pump the same traffic through one pair of firewalls. Assigning each virtual routing instance into its own redundancy group allowed us to run each firewall instance on a separate device - yet still allow for both instances to run on one in the event of a failure.\nOnce we got that firewall cluster into production, there seemed to be a lot less fear regarding virtualized network contexts. I was able to prove that it worked, and worked well for what we needed at the time. Soon enough I was able to find a few additional places where we could make use of the same concepts. We recently procured quite a few Cisco Nexus 9372PX switches for both new deployments and hardware refreshes. By default these switches already come pre-configured with a out-of-band management VRF, which is already super useful to me. We run all of our device management traffic on a segregated network, so a management VRF allowed me to configure the IP/route information to make all that work - while not interfering with the normal layer 3 operations of the device.\nBeing a cloud provider, most of our customers are completely abstracted from the hardware/software that runs their hosted applications. However, in a few cases there are instances where a customer negotiates for a contract change to say otherwise. For example, a customer might have a special software integration they want to run and have the ability to control - or some customers want a dedicated point-to-point Ethernet connection into one of our data centers for increased reliability. A lot of the background networking work for this in the past was a bit of a pain - but it opened up another opportunity to make use of VRFs. I now have a dedicated customer VRF, which has separate routing configurations than our normal production environment. Customer wants to stand up BGP peers across their direct connection to our data center? Sure, I can isolate that BGP instance in the customer VRF, so there is no conflict with our production routing tables.\nI\u0026rsquo;m sure that my current use cases are probably not the ideal implementations of virtual networking contexts - but they work for what we need and they make life a lot easier. I can see these becoming more and more common in our environment to logically segregate traffic. I am interested to hear how other companies have integrated this type of technology into their networks - so leave a comment below!\n","permalink":"https://0x2142.com/virtual-networking-contexts/","summary":"A brief exploration into why VRFs can be extremely helpful","title":"Virtual Networking Contexts"},{"content":"IT work is never truly right vs wrong. There are thousands of possibilities, and thousands of ways that those possibilities don\u0026rsquo;t turn out the way you had expected. It\u0026rsquo;s easy to walk into a new job, look at the environment, and wonder \u0026ldquo;What were they thinking?\u0026rdquo;. But it\u0026rsquo;s another thing to be in the moment and have to make a split-second decision: Do it quick, or do it the right way?\nI am often an advocate of taking the time to do things the right way the first time. It\u0026rsquo;s extremely time-consuming to go back and fix things a year or two later only because something else requires it. Every time a problem comes up, I race through several different variations of a solution. Usually, there is a \u0026ldquo;No way/Only if we absolutely must\u0026rdquo; idea, a \u0026ldquo;This would work, but not be ideal\u0026rdquo; idea, and a \u0026ldquo;perfect world\u0026rdquo; solution. Often the perfect world idea will take much more time and effort than we currently have to complete - which means that we are too likely to go with the non-ideal solution.\nAt some level, you have to be able to accept that not everything can be done perfectly. We don\u0026rsquo;t live in a perfect world with infinite time and resources. Corners have to be cut in some way or another, but it\u0026rsquo;s up to us to determine where and how. That being said, we shouldn\u0026rsquo;t blindly accept imperfection either. Use the multiple options to fight for a solution that edges toward the perfect world idea. Maybe we accept the temporary solution, only as long as we are allocated time within the near future to make it more ideal. Or it\u0026rsquo;s possible that we accept the temporary solution, but with some mitigating factors.\nFor example, maybe we are told that there is a new product being installed on our network. Servers are already being built and the application will need to be running quickly. However, the application serves a very different purpose than most of the remaining network and it introduces new risks. In the ideal situation, we might create an entirely new virtual network segment to isolate this type of traffic. However, maybe because of the time crunch, we only have enough time to create a new VLAN and put the application behind it\u0026rsquo;s own SVI. Not really a true separation, but it\u0026rsquo;s a start.\nIn this scenario, we could propose two better solutions. First, we may want to suggest that we create the new VLAN/SVI to get the application running immediately, but we allocate time over the next week or two to create VRFs/firewall contexts/etc and completely isolate the application. This would allow us to meet the requirement in the meantime, but work toward our ideal long-term state. Our other solution could be to just extend the VLAN up to a firewall and apply basic filtering - not ideal but this still gets us to a much better state than just allowing the application into our existing environment.\nWhen you look back, it\u0026rsquo;s always easier to see how things could have been done better. Usually you just have to work with the resources you were given at the time, which is often less than perfect. However, we shouldn\u0026rsquo;t settle on just \u0026lsquo;getting it done\u0026rsquo; or insist that it must be perfect every time - but instead push for a middle-ground. Whenever you see yourself in a situation like this, take a moment and outline a few options. Talk to your direct supervisor about them - in most cases they will listen. If we can provide an option that is better than \u0026ldquo;good enough\u0026rdquo; but also doesn\u0026rsquo;t require a significant additional time investment, then they are much more likely to give in. Just be sure to stress the potential risks of taking the easy way out.\n","permalink":"https://0x2142.com/why-isnt-everything-done-the-right-way/","summary":"In an ideal world, everything has the time and money to be done correctly. But what happens when expectations meet limitations?","title":"Why isn't everything done \"the right way\"?"},{"content":"I\u0026rsquo;ve lost track of the amount of times in my career that someone has said \u0026ldquo;How did you do that?\u0026rdquo;, \u0026ldquo;Wow that\u0026rsquo;s amazing!\u0026rdquo; or \u0026ldquo;I would have never figured that out\u0026rdquo;. My answer is typically that it involved a little bit of magic - but then I follow it up with an actual explanation. For those less technical, some things really can seem like a bit of magic. Solved an outage in minutes? Knew about an impending issue before it happened? Yeah - that can be quite magical.\nSo I have a few posts that I plan on scattering here and there, which will cover some tips on how to become a networking magician. I will aim to provide detail behind some of the expert intuition and skills which can amaze and confuse others. Let\u0026rsquo;s get started with Magic Tip #1:\nMonitor your network No, really, just monitor your network. I don\u0026rsquo;t mean \u0026ldquo;Oh, there is a ping alert for that switch\u0026rdquo; - I mean pay attention. How will you ever know what an anomaly looks like, if you don\u0026rsquo;t have an internal baseline of what your network should look like? This tip really takes time, but I\u0026rsquo;ve found that it pays off in the long run.\nI use a couple of open-source tools and applications, like Observium and SmokePing, to track metrics on my networks. I spend a quick 5-10 minutes each morning quickly skimming through the pretty graphs to get an idea of how we are performing today. About once every or every other week, I will spend a bit more time for a deeper dive into the metrics. However, the important thing here is not the time spent, but the fact that I look at these. In the back of my mind, I keep a mental note of the general averages for bandwidth, latency, packet loss, etc.\nOnce in a while, I might look at a graph and notice that something is a bit off. Defining the word \u0026lsquo;off\u0026rsquo; in this sense is difficult. Maybe a router interface that averages 20Mb/s spiked to over 40Mb/s through the night. Maybe traffic was actually far lower than the average. Sometimes I might see a slight increase or drop in latency between a pair of data centers. Some of these things could mean absolutely nothing - but in many cases they are an indicator of something else.\nAs an example to this - A few weeks ago, I noticed that the average latency between two data centers had increased slightly, and SmokePing was reporting occasional packet loss of up to 5%. I also track historical traceroute tests - so when I reviewed those, I found that the upstream carrier\u0026rsquo;s route had changed about 2-3 hops out. No big issues - but I made a note of these findings. A few days later, we began experiencing a spike in packet loss between those two data centers. Rather than being caught completely off-guard, I already had all of the information I needed to work with the upstream carrier. Issue resolved - quickly, simply, and without wasting time during a network degradation event.\nLet me just reiterate that I don\u0026rsquo;t expect everyone out there to stare at bandwidth graphs all day long - that\u0026rsquo;s not going to get you anywhere. However, we do need to spend a little bit of time giving our network the attention that it deserves, even if its just a quick check-up every day. Once you have a good idea of how things typically operate, it can be much simpler to pinpoint issues and get ahead of them - which means being resolved without wasting time.\nEver had someone claim you\u0026rsquo;ve performed magic? Tell me about your experiences in the comments!\n","permalink":"https://0x2142.com/a-little-bit-of-magic/","summary":"A good network engineer can almost feel like a magician - fixing things behind the scenes \u0026amp; detecting problems early.","title":"A Little Bit of Magic"},{"content":"There are quite a few things that you don\u0026rsquo;t realize how great they are until you don\u0026rsquo;t have them anymore. For me, one of those things was standard guidelines for device configurations. At my last job, documented standards were extremely important - we had them for everything. While some devices might ultimately be configured in a slightly different manner to accommodate their specific purpose, the underlying basics were all configured exactly the same. Fast forward to where I am at now, and when I started there was no such thing. One device might be configured for management access only over the out of band interface, while a few others might allow management traffic over everyinterface. Some devices had SNMP configured, some didn\u0026rsquo;t, and yet others had default credentials still enabled.\nThe problem here stemmed from the fact that there were no documented standards in place. An engineer was given a device to configure, and it was configured depending on who did it and what they felt needed configuring. In a few cases, this actually led to unnecessary security risks being introduced into the environment because something was left enabled. In one instance, this included open root SSH logins via the Internet to a production firewall. Scary, huh?\nSo how do we go about changing this? Here is a quick little guide I threw together on my method for tackling the situation:\n1. Define a standard Begin creating a baseline document, whether it be a spreadsheet, word doc, or a wiki page. Start small and choose a single system, like your external firewalls for example.\n2. Research best practices Check out the vendor\u0026rsquo;s website to see what they recommend. There are also some amazing free resources out there like the Center for Internet Security\u0026rsquo;s configuration benchmarks, Do your research - there is plenty available to help you.\n3. Figure out what\u0026rsquo;s best for your network Not all of the best practices or security hardening guides will be a perfect fit for your environment. So it will take a little manual review to see what actually fits. For example, many of these guides recommend disabling local authentication in exchange for something centralized like TACACS+ or RADIUS. But if you don\u0026rsquo;t have that available, then you\u0026rsquo;re going to stick with local authentication. This can still be a great time to find room for future improvement projects though.\n4. Test If you have a development or test environment available, then run a device or two through your checklist and make sure there are no big issues. If you don\u0026rsquo;t have a dedicated test area, then try and choose a low-impact device - where not much will be impacted if the changes go wrong.\n5. Roll out the changes Make sure you have a list of every device that needs to be touched, so that you have a way to validate. Then make the configuration changes to get each device into compliance with your new standards. Have a validation/testing checklist ready, so that you can quickly ensure that no production traffic was impacted\n6. Train your peers Configuration standards only work well as long as everyonefollows them. It only takes one person to ignore the checklist and potentially expose a vulnerability. So take an afternoon, schedule a training session with your team. Help them understand the importance of maintaining these standards, and train them on how to apply the changes (if necessary).\n7. Automate This part is optional, but highly recommended. If nothing else, spend the time to automate verification of the standards - which will make it easy to locate a device that falls out of compliance. If you or your team have the skill set, then automate the entire process from initial deployment to continuous validation. Why is this the last step, instead of being included with the roll out? I am a firm believer that you should completely understand how your device functions and reacts to changes before automating those changes.\nSo that\u0026rsquo;s more or less how I worked to implement a standardized configuration at my current job. I began with a completely new device platform that we were integrating into our environment, then began to go back to older device platforms. It might be a lot of upfront work, but it certainly helps me sleep better at night not having to wonder if there might be one device out there that\u0026rsquo;s misconfigured (and will cause an issue later, due to that misconfiguration).\nSo let me know in the comments below - have you ever implemented something like this? If so, what did you do differently? If not, then let me know if you give this a try!\n","permalink":"https://0x2142.com/the-argument-for-standardized-configurations/","summary":"Snowflake network designs always make sense at the time. But what happens when there are no standards?","title":"The Argument for Standardized Configurations"},{"content":"Note: I may receive commissions for purchases made through links in this post. This is to help support my blog and does not have any impact on my recommendations.\nThis guide is written for CentOS 7. If you\u0026rsquo;re running another distro, find your dependencies here.\nLast year we had to begin migrating off of some of our older Juniper SSG firewalls since we were beginning to push them to their throughput limits. We evaluated a couple of vendors but ultimately decided to stay with Juniper and purchase SRX 1500 firewalls, which are capable of up to 10G throughput. After a while of working with these firewalls, I have to say they\u0026rsquo;re pretty solid devices and I\u0026rsquo;m extremely happy with them. One of the main reasons I like them so much is the ease of automation, which is what we\u0026rsquo;re going to dive into today. If you need a device for lab/test - Amazon has the SRX 300 for less than $300. I\u0026rsquo;ll likely be picking one up in the near future for easier automation testing.\nJuniper provides an awesome library for SRX management called PyEZ. Here is what we need to get the toolkit ready to use in our scripts:\nInstall dependencies I run CentOS here, so I just needed to grab the following packages:\nyum -y install python-devel libxml2-devel libxslt-devel gcc openssl openssl-devel libffi-devel Just a quick note - Juniper\u0026rsquo;s web page doesn\u0026rsquo;t actually mention openssl-devel, but their tools will fail to install without it\nGet pip If you don\u0026rsquo;t have it already, then download the pip installer here. Then just run the following:\npython get-pip.py Install PyEZ Once everything else is set, this is the easy part:\npip install junos-eznc After all that is done, we can get to the exciting part: automating something so you don\u0026rsquo;t have to do it anymore! So here is what we\u0026rsquo;re going to throw in our script just to get started:\n# Import the JunOS modules we need from jnpr.junos import Device # Define the login credentials and address for the target firewall # Need to provide device address, user account, and password # Note: Juniper also provides authentication via key-pair, which would be more secure than username/password srx = Device(\u0026#39;10.10.10.10\u0026#39;, user=\u0026#39;deviceuser\u0026#39;, pass=\u0026#39;devicepass\u0026#39;) # Open the connection srx.open() Once you have that going, it\u0026rsquo;s pretty easy to start making calls to collect data or change the configuration. In one of the first projects I used this for, I was making use of the API to reset VPN tunnels. This was due to an issue with a cross-vendor tunnel, which would occasionally break during VPN re-keys and only negotiate a uni-directional tunnel. So the script was written to detect a uni-directional flow of traffic, then log into the SRX and reset the VPN - which would force a renegotiation and fix the issue.\nSo in order to accomplish the SRX-side of that script, I used the following to reset the IKE and IPSec security associations:\n# Clear IKE security association for a given peer response = srx.rpc.clear_ike_security_association(peer_address=\u0026#39;20.20.20.20\u0026#39;) # Check to see if command was accepted if response == True: print \u0026#34;IKE SA Cleared\u0026#34; # Clear IPSec security association for a given peer # Note: In this case, you need to provide the index ID # Get the index ID from running \u0026#39;*show security ipsec sa\u0026#39; *on the SRX response = srx.rpc.clear_ipsec_security_association(index=\u0026#39;123456\u0026#39;) # Same thing - Check to make sure the command succeeded if response == True: print \u0026#34;IPSec SA Cleared\u0026#34; This provides a pretty basic example of how easy it is to control the SRX via a Python script. You might be wondering: This is great, but how do I find the names of those calls? Well, Juniper made that extremely simple as well! Just take any command on the SRX and pipe it through display xml rpc and the device will tell you exactly what you need. For example:\nroot@SRX123\u0026gt; show security ipsec sa | display xml rpc \u0026lt;rpc-reply xmlns:junos=\u0026#34;http://xml.juniper.net/junos/15.1X49/junos\u0026#34;\u0026gt; \u0026lt;rpc\u0026gt; \u0026lt;get-security-associations-information\u0026gt; \u0026lt;/get-security-associations-information\u0026gt; \u0026lt;/rpc\u0026gt; \u0026lt;cli\u0026gt; \u0026lt;banner\u0026gt;{primary:node0}\u0026lt;/banner\u0026gt; \u0026lt;/cli\u0026gt; \u0026lt;/rpc-reply\u0026gt; In this case, we are running the command which would normally print all of the connected IPSec tunnels. So the section under rpc is where we want to look. Now just take the get-security-associations-information, and change the dashes to underscores. So to use this in a script we would call srx.rpc.get_security_associations_information(). I\u0026rsquo;ve actually used this command to build a VPN status dashboard using Python and Django, which I accomplished by just pulling all the IPSec tunnels and parsing them into a web table.\nAs a final note, I would highly recommend creating a separate service account on the SRX for scripting. A separate user account with limited permissions would be the recommended way to go here. It might seem like a pain to set up, but it\u0026rsquo;s worth it in terms of security. Here is what I have configured on my SRX firewalls for the API service account:\nsystem { login { class api-class { permissions security-control; allow-commands \u0026#34;(clear security ike)|(clear security ipsec)\u0026#34;; deny-commands \u0026#34;(clear)|(file)|(file show)|(help)|(load)|(monitor)|(op)|(request)|(save)|(set)|(start)|(test)\u0026#34;; } user apiuser { full-name API_Service_Acct; uid 125; class api-class; authentication { encrypted-password *\u0026lt;encrypted string here\u0026gt;* } } } } So using the above example, you should only define the necessary commands in the allow-commands section. If the account is ever compromised, we are severely limiting the amount of damage that could be done. A little extra effort, but potentially a big payoff.\nSo what did you think of this tutorial? Helpful? Have questions? Let me know in the comments below!\n","permalink":"https://0x2142.com/getting-started-with-junos-pyez/","summary":"Quickly get started using Juniper\u0026rsquo;s Python SDK \u0026amp; interact with JunOS devices","title":"Getting Started with JunOS PyEZ"},{"content":"Even when you\u0026rsquo;re ten years or so into your career, you can always stand to learn something. It\u0026rsquo;s important that no matter how experienced you get, you always keep an open mind to other people\u0026rsquo;s ideas or opinions. As an example to this, I would like to share the story of this blog name.\nBack when I worked at a local IT consulting company, they hired a network admin who had worked as several large service providers in the past. He was very experienced and intelligent, and was able to walk into the organization and immediately begin making positive changes. Exactly the type of person that you would want to hire, right?\nWell after a few months in, he began checking through some of the equipment we had in our spare store-room. A bunch of Cisco routers and switches, some older than others. After a week, he began complaining about how the devices had sat on the shelves too long. It seemed as though the flash memory was degraded, which caused the devices to not retain their configuration settings. Almost every device he checked through seemed to be experiencing this issue. What else can you do at this point but throw out the bad hardware?\nSo I decided to pick up one of the devices to see what he was talking about. After all, I was still very early in my career - so if I could stand to learn something from how the devices were behaving, I wanted to see it. So I boot up an old Cisco 2610 router and make a few configuration changes. Save, reboot, and sure enough my changes were gone. However, I had also just been studying how to password reset these devices - since I had a pile of them that needed to be reset. Part of resetting the devices was booting into rommon mode and changing the configuration register value to a hex value of 0x2142.\nSo what is 0x2142? It\u0026rsquo;s a hex value that tells the router upon boot to ignore any saved configuration. Of course that easily explained the \u0026ldquo;degraded flash\u0026rdquo; issue that the experienced network admin had seen. So I changed the configuration register back to 0x2102, made a few more configuration changes, then rebooted. Sure enough, everything was still there. So I went and told the network admin what I had found. \u0026ldquo;Oh, checking up on me, huh?\u0026rdquo;\nThis story has been a bit of a running joke for a while. But really the importance is that even when you\u0026rsquo;re extremely intelligent and experienced, you can still overlook simple things. He had been password resetting the devices, but never reverting the configuration register values back to the defaults. Even when you think you might know everything, you should still keep an open mind - because even someone with no experience might have a different view on something. Sure, this wasn\u0026rsquo;t really a big \u0026ldquo;save the day\u0026rdquo; moment, but it helped to show that guy that I had some idea of what I was talking about. From then on, he actually began to work with me on understanding more networking concepts and started asking me to help out with some more of the work he was doing.\nWhat was the most ridiculous simple mistake you\u0026rsquo;ve made? And how did you find out about it? Share in the comments!\n","permalink":"https://0x2142.com/the-small-things-0x2142/","summary":"Sometimes the small details are what matter the most","title":"The Small Things (0x2142)"},{"content":"A few years back I worked for an organization that had a single 100Mb Internet connection. Not bad for just typical corporate traffic, but we also hosted our production web site out of that location as well. An incident occurred where our website was down due to Internet issues during an extremely inconvenient time. So we decided to procure a second Internet uplink through a different provider. At the time, I had no practical experience doing something like this - yet I was put in charge of the project. Let\u0026rsquo;s go over some of what I learned\u0026hellip;\nThe easy part of the whole process is the first step - ordering a second Internet connection. Our CIO at the time placed a few calls and had a quote back pretty quickly. A local carrier was willing to run new fiber cables to our building in less than a month. Depending on how important uptime is to your organization, this is the point where you might want to ask about a diverse path into the building. If both connections run though the same physical paths, then a single incident could still cause an outage. For example - I once worked somewhere where the redundant Internet connections shared the same telephone poll across the street. So even though the connections were redundant, a single accident involving that telephone poll and both connections were severed.\nNext - Ask about IP space. In terms of IPv4, the general rule for external BGP peering is that ISP\u0026rsquo;s don\u0026rsquo;t like to accept any prefixes smaller than a /24. In our case, we had a single /25 block already allocated by our current provider - which wasn\u0026rsquo;t going to work. Luckily, the new service provider offered to give up a free /24 block along with the installation costs. Unfortunately, this meant that we had to re-address all of our public-facing services, which is almost always a pain to do. I have a few tips for this, which helped us to minimize downtime - but that\u0026rsquo;s a story for another time.\nNext, we need to obtain a globally unique Autonomous System (AS) number, which will be used to advertise our network to the world. Since we were located in North America, we went though ARIN for this process - which was fairly painless. Sign up for an account, prove that you\u0026rsquo;re associated with the business, fill out a few forms to justify your need, and then just wait for the approval. One thing to watch out for is 2-byte vs 4-byte AS numbers. 2-byte is the standard and has been around forever, but only allows for up to 65,535 unique IDs. A 4-byte ASN allows for significantly more unique IDs, but I have actually run into instances where an ISP doesn\u0026rsquo;t support these. I would hope that in most cases a 4-byte ASN will be just fine, but it might be worth asking your ISP just in case.\nAt this point, you should be ready to hit the ground running as soon as that second Internet uplink is installed. This is also assuming you already run a router or multilayer switch on the edge of your network, which also has BGP capabilities. So let\u0026rsquo;s get down to the fun stuff - an extremely basic configuration to peer between two ISPs. I\u0026rsquo;ll dedicate another post to additional recommended settings and configurations - but for now let\u0026rsquo;s focus on getting this running. The configuration sample below is aimed at Cisco devices, but the same concepts apply to most vendors:\nEdgeRouter(config)# router bgp *\u0026lt;YOUR AS NUMBER\u0026gt; *! The AS number provided by ARIN EdgeRouter(config-router)# network *\u0026lt;YOUR LOCAL SUBNET\u0026gt;* ! The subnet we need to advertise out both ISPs EdgeRouter(config-router)# neighbor *\u0026lt;ISP1 PEER IP\u0026gt;* remote-as *\u0026lt;ISP1 ASN\u0026gt;* ! Provided by the first ISP - Their remote peer IP and ASN EdgeRouter(config-router)# neighbor *\u0026lt;ISP2 PEER IP\u0026gt; *remote-as *\u0026lt;ISP2 ASN\u0026gt; *! Provided by the second ISP As I mentioned, this config is very basic and will just accomplish what we need to get going. Follow up with a quick show ip bgp neighbors and hopefully you\u0026rsquo;ll see two peers in the established state. Any other state indicates a problem bringing up the peer connection. I won\u0026rsquo;t get into too much detail here - but check the physical connection, ping the peer, and make sure there are no firewalls blocking TCP port 179 between the peer addresses.\nHope this was helpful! Comment below and let me know how your experiences have gone with this type of setup - and look forward to a few more posts regarding BGP peering setup with multiple ISPs.\n","permalink":"https://0x2142.com/bgp-getting-started-with-multi-homed-internet/","summary":"Exploring which design/setup questions to ask, and how to begin a basic configuration","title":"BGP: Getting Started with Multi-homed Internet"},{"content":"Last week in IP Address Design (Part 1) we discussed an example of a bad design for IP allocations and the problems that it caused. This week we will continue by discussing the proposed solution and how it resolved those issues.\nThe problems with our IP Addressing scheme bothered me quite a lot - especially because IP Addressing design doesn\u0026rsquo;t really seem to be something you can easily go back and fix. We are in a somewhat unique case since we often open new locations, which is a perfect opportunity to make a positive change going forward. About a year ago, I heard that we would be opening four new data center locations in the near future. So I finally sat down and figured out a new scheme, which ultimately we deployed to all new locations.\nMy first goal was to start making more proper use of address space, while still making it somewhat easy to remember. As I stated in the last post, our largest data center was only using about 4,000 addresses. I began the design by trying to figure out a good starting point. A single /16 is probably still too large, but if I split up a /16 into two /17s then people will get confused about where a subnet lives. Remember that we were migrating from a very simple scheme in the past, where the second octet dictated the network location. So for the sake of simplicity, I started the design using a single /16 per data center.\nNext, I needed to split up that /16 into classless subnets which could be routed in a somewhat meaningful fashion within the data center. In also trying to keep human usability in mind, I decided to split the main /16 assignment into two /17s. The top /17 subnet would be designated to all edge subnets, like the DMZ and Out of Band Management - both of which were directly terminated off of the external firewall set. The bottom /17 would be designated for all internal, protected subnets. This included anything behind the internal firewall set, like our primary internal network and some of the new isolated network segments we had built.\nSo here is the final scheme:\n10.15.0.0/16 - Overall data center allocation\n10.15.0.0/17 - Edge subnets\n10.15.0.0/18 - Main DMZ (10.15.0.0-10.15.63.255) 10.15.64.0/21 - Out of band management (10.15.64.0-10.15.71.255) 10.15.72.0/21 - Misc DMZ VLAN (10.15.72.0-10.15.79.255) 10.15.80.0/20 - Unused (10.15.80.0-10.15.97.255) 10.15.128.0/17 - Internal subnets\n10.15.128.0/18 - Main Internal subnet (10.15.128.0-10.15.191.255) 10.15.192.0/22 - Protected subnet 1 (10.15.192.0-10.15.195.255) 10.15.196.0/22 - Protected subnet 2 (10.15.196.0-10.15.199.255) 10.15.200.0/21 - Unused (10.15.200.0-10.15.200.207.255) 10.15.208.0/20 - Unused (10.15.208.0-10.15.223.255) 10.15.224.0/19 - Unused (10.15.224.0-10.15.255.255) Now the first thing you may notice is that there is a large amount of unused IP space - but I\u0026rsquo;m accepting that as potential for future growth. Even the large /18 allocations will allow for over 16,000 hosts, which may be more than we will need in the foreseeable future. However, as I mentioned earlier I needed to balance conservation and efficiency with human readability.\nSo how does this help some of our problems? We\u0026rsquo;ve already addressed the problem of IP exhaustion by dropping each data center to a single /16 subnet rather than several /16s. Routing tables are immensely simplified now due to summarization. Oh, I need a route to that other data center? Sure, now it is only a single /16 route to the VPN peer for that location. Once the traffic gets over to that local network, then we can worry about trying to route the individual allocations within there. Even then, within the data center I only need a handful of small routes. The external firewall can point the whole 10.15.128.0/17 subnet to the internal firewall set and let it handle routing from there. And finally - that pesky problem of exponential VPN tunnels. Now that each data center has a single /16, we only have to create a single tunnel between two locations which saves us a ton of valuable CPU on the VPN gateways.\nNow, obviously these benefits only apply to locations where the new IP addressing scheme is the only addressing scheme. For connections back to a legacy data center, we would still have a single /16 on one side of the VPN while the other side had 4-6 /16 subnets. Even so, the VPN tunnels required for that configuration are significantly less than before. So to wrap this up, the design was proposed to the team and we decided to go with it for the four new data center builds. It is working quite well so far - and we are beginning to have conversations on back-porting this design to the legacy data centers (which will be another post for another time).\nHave you ever had to re-design an IP addressing scheme? or have you ever been bothered by the current design and wished you could change it? Comment with your thoughts!\n","permalink":"https://0x2142.com/ip-address-design-part-2/","summary":"Part 2 - Re-designing an IP addressing scheme to solve current (and future)","title":"IP Address Design (Part 2)"},{"content":"Hello there! Thanks for checking out my blog. If you find content here that is helpful to you - please share a link with your friends or co-workers!\nThe intent of this blog is to share career experiences, certification studies, bits of configuration, opinions on technology, or whatever else I happen to come up with. While my primary focus will be networking \u0026amp; network automation, there may also be occasional content on other technical topics - just depending on what I\u0026rsquo;m getting into at the time! More or less, it will likely just be a bunch of nonsense. Enjoy it 🙂\nYou can also find me here:\nYouTube Mastodon/ActivityPub GitHub LinkedIn Thanks!!!\nMatt Schmitz - CCIE #63461\nStandard Disclaimer: All opinions on this blog are my own, and do not represent any vendor or current/former employer.\nWant to support this blog? ","permalink":"https://0x2142.com/about/","summary":"\u003cp\u003eHello there! Thanks for checking out my blog. If you find content here that is helpful to you - please share a link with your friends or co-workers!\u003c/p\u003e\n\u003cp\u003eThe intent of this blog is to share career experiences, certification studies, bits of configuration, opinions on technology, or whatever else I happen to come up with. While my primary focus will be networking \u0026amp; network automation, there may also be occasional content on other technical topics - just depending on what I\u0026rsquo;m getting into at the time! More or less, it will likely just be a bunch of nonsense. Enjoy it 🙂\u003c/p\u003e","title":"About"},{"content":"It\u0026rsquo;s funny when you think about basic networking concepts and wonder if they will ever actually prove to be useful. Kind of like that \u0026ldquo;Do I really need to learn complex geometry? When am I ever going to use this?\u0026rdquo;. What I\u0026rsquo;m here to talk about today is IP Addressing design. In many cases this will be something that is already in place and fairly solid, so there won\u0026rsquo;t be much to think about. This was the case at every company I worked at until the most recent one, which is a local cloud service provider. The type of architecture required for this environment is a bit different from what I\u0026rsquo;ve previously worked with.\nSo here is my first architecture tip:\nNo matter how small your organization is today, think about how your proposed design might look 5-10 years down the road.\nThe problem that I ran into here was that this cloud provider was still using an IP addressing design which was originally designed for a different set of needs. The design was intended to support the business back when we had two data centers and no one thought we would expand. Well, today we have over a dozen locations and there are constant discussions about adding more.\nLet\u0026rsquo;s start with the original design, why it was a good idea, and why it doesn\u0026rsquo;t scale well today. Every data center location was assigned a few standard blocks of IP Addresses, where each block corresponded to a logical network location. The 10.0.0.0/8 space was used for this, and broken into the following blocks:\n10.1.0.0/16 - Reserved 10.11.0.0/16 - DMZ 10.111.0.0/16 - Out of band Management 10.211.0.0/16 - Internal network This was the bare minimum that each location received, in some cases another /16 or two might be allocated. So first, let\u0026rsquo;s cover the reasons why this was a good design for the time. All subnets were terminated at classful boundaries, which means there was never confusion on a subnet mask. The association of the second octet to network region made the subnets easy to remember - it was quick for anyone to say \u0026ldquo;10.2xx? Oh yeah that\u0026rsquo;s an internal segment\u0026rdquo;. Also, with a minimum of four /16 blocks, we would practically never run out of IP space in each location (\u0026gt;260,000 usable addresses). All that being said, the addressing scheme was perfect for what it was designed for: Easy to be read and remembered by humans.\nWhile that may have been great for two data center locations, it doesn\u0026rsquo;t really scale well about eight years later. So let\u0026rsquo;s take a look at why this design doesn\u0026rsquo;t work in the long run. After we reached the number of locations we have today, we are left with only ~40 /16 blocks unused in the 10.x.x.x block. That means we have room for ten or less new locations, before we completely exhaust that IP space. Next, after some quick research it turns out that even our largest location was only consuming about 4,000 addresses - not even 2% of the total addresses allocated. Routing tables in each data center were a nightmare, because each location had to have several discontiguous /16 blocks routed back to it. And to top it all off - it turns out that our site-to-site VPN tunnel architecture between locations was configured to use subnet-pair tunnels. This meant that for each pair of data centers (4 /16s per site), there would be 16 VPN tunnels. While 16 isn\u0026rsquo;t a lot, that really grows exponentially when we add more locations which are all configured for full-mesh VPN connectivity.\nI\u0026rsquo;m trying to keep these posts somewhat manageable - so look for a continuation of this post next week, where I\u0026rsquo;ll discuss the solution to this problem and how we implemented it.\n","permalink":"https://0x2142.com/post/2016/ip-address-design-part-1/","summary":"A quick look at an IP addressing scheme that was never designed to scale, and quickly hit its limits.","title":"IP Address Design (Part 1)"},{"content":"This post is a continuation of last week\u0026rsquo;s \u0026ldquo;First, A Bit of Background\u0026rdquo;\nSo once I had that magical CCNP certification, I finally felt like I needed to move on. I had gained as much experience from that first job as I thought I would, which meant that I needed to start looking. I got some help from a co-worker of mine at the time, who gave me some wonderful resume tips (which I will share in a future post). Two months and a handful of interviews later, and I found myself jumping on a contract-to-hire position for a local government organization.\nThe three and a half years spent with this organization taught me so much. I had a great boss, to whom I owe many personal improvements that helped me get where I am today. I walked into the place in a role that was technically supposed to be a Junior Systems Administrator, but the position was much more widely focused than that. I did everything and anything, including managing an Avaya phone system, desktop support, networking, Windows administration, and even a bit of VMware ESX. Obviously, I began to lean more and more toward the networking side of the house, as the team was relatively well split in terms of specializations. One guy loved virtualization and storage, another loved application support, and I owned all things networking.\nAnother thing this job brought me was the push I needed to go back to school. The organization didn\u0026rsquo;t like to hire people without a college degree, but I managed to make it in under a very rare set of circumstances. Unfortunately, that meant that I was constantly told that I really need to go back to school and get a degree. After a short while, I gave in and picked up a four-year online degree program in Network Security.\nThis place was my first real experience in actually owning a network. Having complete control and being able to call it my own. I spent the first couple of months doing exploratory research - what did we have running and how was it configured. Then I built a list of recommendations for things I thought needed to be improved. After a few years, I had replaced almost every device (many were end of life) and made the network significantly more secure and resilient. I had many great learning opportunities in managing my own time and building project plans. I designed network upgrades and made detailed plans to make it all work - and it did, surprisingly.\nWhile that job was an absolutely amazing experience for me in terms of personal and career growth, I eventually reached a point where those things slowed down. Soon the negative aspects of the job were starting to outweigh the positives, and so I began my job search once more. A friend of mine, who I had previously worked with at the consulting company, ended up referring me to a position with a company he worked for. The position was a Network Administrator for a local cloud Software as a Service provider.\nI didn\u0026rsquo;t know it when I took the job, but I ended up walking into an environment where I had the most experience on the team. For having several datacenters around the world, the network architecture left much to be desired - A lot of designs built upon the need of the moment and not the future. At the time of this writing, I\u0026rsquo;m still with this company - and I\u0026rsquo;ve already gained quite a different set of skill and experiences: Being the senior team member, designing scalable network architecture, and learning the ability to lead others.\nI\u0026rsquo;m going to stop here with my story for now - but hopefully this provides a bit of context around where my experiences and insight have come from. I have a lot of future post ideas which will build upon everything that I have learned over the past ten years. Thanks for reading!\n","permalink":"https://0x2142.com/background-story-continued/","summary":"(Part 2 of 2) A brief summary of my networking \u0026amp; career experience","title":"Background Story (Continued)"},{"content":"I wanted to start off my providing a little background on myself. Hopefully this will put some context around my future posts.\nIn the beginning - I started off doing some minor PC repair for family and friends. Really quite minor stuff, like replacing power supplies, reinstalling the operating system, or troubleshooting application issues. The technical work really was fun for me, but at that point I had never considered the possibility of it becoming a career. It just seemed like a fun hobby that was great to do in my spare time.\nAfter I completed my second year of high school, I found out that I would have to change schools. Luckily, I found out that my new high school offered this fun program called the Cisco Networking Academy. The program was three hours a day for two years, and taught all of the networking fundamentals necessary to pass the Cisco Certified Network Associate (CCNA) exam. I quickly found that this is something that I truly enjoy doing and I was actually good at it. We had quite a few networking professionals come into the class over those two years and tell stories of how successful a career in computer networking could be. That was the point where I realized that this might actually be a career option - so I went with it.\nWithin two months of finishing high school, I took and passed the CCNA exam. Cisco certified at the age of eighteen, and now left wondering how to find a job. My next stroke of luck came in the form of a family member who had actively been working in IT for about 10-15 years already. She sat down with me and helped me build my first resume, then showed me where to post it online. Within a few weeks, I began receiving calls from recruiters in the area about a variety of positions. \u0026ldquo;Level 1 Help desk? No, I want to be a Network Engineer making ALL THE MONIES\u0026rdquo;. Of course at the time, I had no idea that jumping directly into a network engineer position was very unlikely - especially given that I had no real world experience yet.\nA couple interviews and a few months later, and I happened upon a local IT consulting company. I remember interviewing with the manager at the time and mentioning how difficult it was to find a job, since everyone wants you to have experience but no one wants to help you get it. Well, he decided that he was willing to help out and offered me a job as a Level 1 Network Operations Center Engineer.\nI spent nearly four long years at that job. I was new to the field so I took advantage of every opportunity they offered me. Certification training? Yes. Networking projects? Yes. Consulting for a variety of businesses? Yep! The company culture was heavily focused on making money quickly, which meant that they didn\u0026rsquo;t always take care of the employees very well - but there is something to be said about the amount of varied experience I gained, especially for my first real tech job. While I was working here, I also added onto my collection of Cisco certifications: CCNA Voice, CCNA Security, CCDA. I finally finished up by achieving one of my goals of becoming CCNP certified.\nSo this has been part one of my history, and to make this a bit more readable I\u0026rsquo;m going to split it into two postings. Continue the story in the next post!\n","permalink":"https://0x2142.com/first-a-bit-of-background/","summary":"(Part 1 of 2) A brief summary of my networking \u0026amp; career experience","title":"First, A Bit of Background"},{"content":"Over the years I have made several attempts at starting a blog. A few on networking, general IT, or whatever came to mind. They all end up the same - I start off strong and fall off quick. Finally, I believe I\u0026rsquo;ve realized what my problem is: I always assumed that a successful blog had to be purpose-built and constantly kept up to date with new and exciting content.\nSo here I am again, giving this another shot. This time I won\u0026rsquo;t be backing myself into a corner from the start. This blog is intended to be networking oriented but with a bit of a wider focus. I\u0026rsquo;ve already come up with quite a few ideas for content I would like to write here, so I\u0026rsquo;m more prepared. That being said - I\u0026rsquo;m not committing to regular updates or always exciting content. When I have something I feel is worth sharing, I will share it.\nSo to provide a general overview, here is the outline of topic ideas I have for this blog:\nEducation/Certification Studies- Every network admin has a blog to document their road to CCIE certification, right? This has certainly been one of my goals over the years, so I\u0026rsquo;ll be writing about the awesome things that I learn. This is also meant to include general networking education topics, since you can never stop learning.\nCareer - I\u0026rsquo;ve been Cisco certified and working in networking for nearly ten years, but that doesn\u0026rsquo;t mean everyone has. I\u0026rsquo;ve finally reached the point in my career where I\u0026rsquo;m meeting a lot of people new to the field and I\u0026rsquo;m able to help guide them. So I would like to share some of the career advice that I have, both from my own experiences and advice that I have received from others.\nNetwork Design/Architecture- This stuff is really important, as I\u0026rsquo;ve run into more than enough situations where a network wasn\u0026rsquo;t originally designed for the type of workload it handles today. I want to cover both network design topics, as well as why it is important. I have some stories to share on how bad network architectures can have significant consequences.\nA Little Bit of Magic - This is probably my favorite topic. You ever work with an IT professional who just somehow knows how to fix everything? The person who can pick up almost any technology and become an overnight expert in it? Well, I would like to share some of my insight into how this type of thing is accomplished, and why it just seems so magical.\nThis likely won\u0026rsquo;t be everything I cover here, but these are the primary topics. I\u0026rsquo;m going to give this site my best shot over the next few months - so let\u0026rsquo;s see how it goes.\nFeel free to bug me in the comments with any questions!\n","permalink":"https://0x2142.com/a-new-start/","summary":"A quick look at my intentions for this blog","title":"A New Start"}]