<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Juniper on 0x2142 | Networking Nonsense</title>
    <link>https://0x2142.com/tags/juniper/</link>
    <description>Recent content in Juniper on 0x2142 | Networking Nonsense</description>
    <image>
      <title>0x2142 | Networking Nonsense</title>
      <url>https://0x2142.com/logo.jpg</url>
      <link>https://0x2142.com/logo.jpg</link>
    </image>
    <generator>Hugo -- 0.143.1</generator>
    <language>en-us</language>
    <lastBuildDate>Mon, 22 Mar 2021 11:46:00 +0000</lastBuildDate>
    <atom:link href="https://0x2142.com/tags/juniper/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>JNCIA-Cloud Study Resources (All Free!)</title>
      <link>https://0x2142.com/jncia-cloud-study-resources-all-free/</link>
      <pubDate>Mon, 22 Mar 2021 11:46:00 +0000</pubDate>
      <guid>https://0x2142.com/jncia-cloud-study-resources-all-free/</guid>
      <description>Let&amp;rsquo;s look at some free resources for studying for the JNCIA-Cloud!</description>
      <content:encoded><![CDATA[<p><sup>The post below was contributed by guest author: <a href="https://twitter.com/NikkiMegaplaza">Nicole Henry</a></sup></p>
<hr>
<p>Hey everyone. I&rsquo;ve got another present for you - Free study resources to use while studying for Juniper Network&rsquo;s JNCIA-Cloud cert (As a reminder, I also gave a list of <a href="/jncia-junos-study-resources/">Free study resources for the JNCIA-Junos exam</a>).</p>
<h2 id="1-juniper-open-learning">1) Juniper Open Learning</h2>
<p>If you were familiar with Junos Genius, it was discontinued, but all the Associate level material can be now found <a href="https://learningportal.juniper.net/juniper/user_activity_info.aspx?id=11478&amp;ref=0x2142.com">Juniper Open Learning</a> for free. Lots of videos &amp; practice exams to get your prepared for the Associate level exams!!</p>
<h2 id="2-juniper-networks-day-one-books">2) Juniper Networks Day One Books</h2>
<p>Description: &ldquo;Day One Books cover networking technologies using step-by-step instructions and practical examples written by working engineers.&rdquo; Here&rsquo;s the link to the <a href="https://www.juniper.net/documentation/jnbooks/en_US/day-one-books/?ref=0x2142.com">Day One Books</a>; there are numerous Day One Books, and I guarantee you&rsquo;ll learn a lot. Me, personally, I like to read with physical books, so I bought the &ldquo;Day One: Data Center Fundamentals&rdquo; book for myself. But here&rsquo;s the <a href="https://www.juniper.net/documentation/en_US/day-one-books/DC_Fundamentals.pdf?ref=0x2142.com">pdf version</a> !!!!</p>
<h2 id="3-documentation-aka-techlibrary">3) Documentation (aka &lsquo;TechLibrary&rsquo;)</h2>
<p>First: For any cert, print &amp; follow the <a href="https://www.juniper.net/us/en/training/certification/certification-tracks/cloud-track/?tab=jncia-cloud&amp;ref=0x2142.com">Exam Objectives</a>.</p>
<p>Second: As you&rsquo;ll see from the Exam Objectives, there are a lot of Juniper solutions on this exam. Luckily their &lsquo;TechLibrary&rsquo; is FILLED with all the info you need about the specific products. I really hope you like to read; you&rsquo;ll be doing a lot of that. Tehehehee!</p>
<h2 id="4-network-fun-times-blog">4) Network Fun Times blog</h2>
<p>This guy <a href="https://twitter.com/NetworkFunTimes?ref=0x2142.com">Chris</a> (who is a Juniper Ambassador!!) made a LENGTHY blog post about what he used to study &amp; <a href="https://www.networkfuntimes.com/jncia-cloud-the-ultimate-resource-for-junipers-sdn-certification/?ref=0x2142.com">pass the JNCIA-Cloud</a>. I visited this blog post countless times b/c it is just THAT GOOD. Use it. Bookmark it.</p>
<p>And that&rsquo;s it! Those are all the resources I used to pass the JNCIA-CLOUD. All FREE! I hope you found this thread useful.</p>
<ul>
<li>Nicole</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>JNCIA-Junos Study Resources (All Free!)</title>
      <link>https://0x2142.com/jncia-junos-study-resources/</link>
      <pubDate>Wed, 03 Feb 2021 10:12:03 +0000</pubDate>
      <guid>https://0x2142.com/jncia-junos-study-resources/</guid>
      <description>Thinking about going for the JNCIA-Junos? Here&amp;rsquo;s some great free resources to get you started!</description>
      <content:encoded><![CDATA[<p><sup>The post below was contributed by guest author: <a href="https://twitter.com/NikkiMegaplaza">Nicole Henry</a></sup></p>
<hr>
<p>Hi, hello. Nicole here.</p>
<p>Do you want to diversify your skillset &amp; learn JunOS? Do you want to be able to add JNCIA-Junos to your resume? Here&rsquo;s a thread of the <del>free</del> resources I used!</p>
<p><a href="https://twitter.com/NikkiMegaplaza/status/1351695140310675457">Link to the original twitter thread</a></p>
<h2 id="1-junos-genius">1) Junos Genius</h2>
<p><a href="https://learningportal.juniper.net/juniper/default.aspx">Junos Genius</a> is an amazing, stupendous, fantastic resource. USE THIS!! Create an account, scroll down to Juniper Open Learning, &amp; select JNCIA-Junos (or whichever cert track in which you&rsquo;re interested). At the time of this post, once you finish all the videos &amp; practice test(s), there will be a voucher test. If you pass the voucher test, you&rsquo;ll receive a voucher for 75% off any Associate Level exam. Again, USE JUNOS GENIUS!! Watch all the vids, take all the practice tests.</p>
<p><strong>UPDATE</strong>
All the material from Junos Genius has been moved to <a href="https://learningportal.juniper.net/juniper/default.aspx">Juniper Open Learning</a> within the Learning Portal. The content is the same, just in a new location.</p>
<h2 id="2-day-one-beginners-guide-to-learning-junos">2) &ldquo;Day One: Beginner&rsquo;s Guide to Learning Junos&rdquo;</h2>
<p>It&rsquo;s easy to read and is a very nice complement to Junos Genius. I HIGHLY recommend it. <a href="https://www.juniper.net/documentation/en_US/day-one-books/junos-beginners-guide.pdf">Here&rsquo;s the pdf</a>.</p>
<h2 id="-3-junos-for-ios-engineers">## 3) &ldquo;Junos for IOS Engineers&rdquo;</h2>
<p>If you&rsquo;re familiar with Cisco IOS, &ldquo;<a href="https://www.juniper.net/documentation/jnbooks/en_US/day-one-books/">Junos for IOS Engineers</a>&rdquo; is another option for you. (click the link &amp; scroll down until you see the book). There are a bunch of Day One books, I&rsquo;ve already read 2, so much information!!!</p>
<blockquote>
<p>Side note: Juniper Networks has a collection of books written by industry professionals on a variety of topics. These books are called <a href="https://www.juniper.net/documentation/jnbooks/en_US/day-one-books/">Day One books</a>. They can be found here. And if you&rsquo;re like me and you like physical books, you have the option to buy books from the virtual <a href="https://store.vervante.com/c/v/category_order.html?base_cat=Juniper%20Networks%3aShop%20Day%20One%20Books&amp;pard=juniper">bookstore</a> !!</p></blockquote>
<h2 id="4-juniper-vlabs">4) Juniper vLabs</h2>
<p>Don&rsquo;t have any physical equipment?? No problem!! Use <a href="https://jlabs.juniper.net/vlabs/">vLabs</a> for practice!!! Definitely watch the video on the home page before you get started; it&rsquo;s a good introduction to vlabs &amp; how to use it. Super highly recommend vLabs.</p>
<h2 id="5-youtube">5) Youtube</h2>
<p>Here&rsquo;s some random youtube videos that I really liked:</p>
<ul>
<li>&ldquo;<a href="https://www.youtube.com/watch?v=n_SdUg1JERY&amp;ref=0x2142.com">Using Juniper for the First Time | JunOS CLI</a>&rdquo;</li>
<li>&ldquo;<a href="https://www.youtube.com/watch?v=d6JalryPoNc&amp;ref=0x2142.com">Interface Naming Conventions</a>&rdquo;</li>
<li>&ldquo;<a href="https://www.youtube.com/watch?v=VNLPnFEzcco&amp;ref=0x2142.com">Juniper Device Interfaces</a>&rdquo;</li>
<li>&ldquo;<a href="https://www.youtube.com/watch?v=EmHYaQxft94&amp;ref=0x2142.com">Juniper Networks JNCIA-Junos Certification Practice Test</a>&rdquo;</li>
</ul>
<p>And that&rsquo;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.</p>
<p>(Shoutout to Matt for letting me use his site to make this post)</p>
]]></content:encoded>
    </item>
    <item>
      <title>Where is all the Automation?</title>
      <link>https://0x2142.com/where-is-all-the-automation/</link>
      <pubDate>Tue, 24 Jul 2018 10:00:03 +0000</pubDate>
      <guid>https://0x2142.com/where-is-all-the-automation/</guid>
      <description>Learning Python for network automation doesn&amp;rsquo;t have to be scary. Let&amp;rsquo;s look at how to get started</description>
      <content:encoded><![CDATA[<p><sup><em>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.</em></sup></p>
<hr>
<p><em>The future is APIs! SD-EVERYTHING! Automation! Orchestration! Artificial Intelligence and Machine Learning!</em>
Sound familiar? It&rsquo;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&rsquo;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&rsquo;ve gone to this year has asked attendees the same questions: &ldquo;How many of you are leveraging the APIs in your network hardware/software?&rdquo;. And every time the same answer - maybe two or three people in a room of 40 raise their hands.</p>
<p>So where is the problem? Is all of this just marketing fluff or am I just talking to the wrong people?</p>
<p>Let&rsquo;s think about this from a typical network admin&rsquo;s perspective. Shifting from traditional CLI to automation and APIs can seem difficult or overwhelming. Let&rsquo;s say I want to automate a new VLAN deployment. <em>Oh, you&rsquo;re telling me I need to stop and learn vendor APIs… but before that I need to understand how to write scripts. But I&rsquo;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&rsquo;t want to be a developer!</em></p>
<p>Okay, 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&rsquo;t to turn networkers into developers - It&rsquo;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&rsquo;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.</p>
<p>I&rsquo;ve heard a lot of the same things over the past few years:</p>
<blockquote>
<p><em>&ldquo;Programming is difficult&rdquo; or &ldquo;I don&rsquo;t know where to start&rdquo;</em></p></blockquote>
<p>Try learning Python. It&rsquo;s simple to get started and you can build from there.</p>
<blockquote>
<p><em>&ldquo;I don&rsquo;t know what an API is or how to use it&rdquo;</em></p></blockquote>
<p>Don&rsquo;t worry about that yet - start with learning the basics and APIs will make sense later.</p>
<blockquote>
<p><em>&ldquo;I&rsquo;m not a developer&rdquo;</em></p></blockquote>
<p>No one is asking you to be one! But learning the basics of scripting and automation gives you a whole new toolset to solve problems.</p>
<p>For me personally - I would never want to be a developer. I can&rsquo;t stand the thought of coming into work every day and just writing code. Some people might enjoy that, but for me it doesn&rsquo;t sound like fun. However - I enjoy writing scripts to solve problems, especially when it ends up making my job easier. I think that&rsquo;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&hellip; 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.</p>
<p>Still not interested? That&rsquo;s okay too. Traditional networking isn&rsquo;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&rsquo;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&rsquo;t be exclusively tied to a vendor to help them - and instead they will be empowered to solve more problems themselves.</p>
<p>Where do you get started? I already wrote a bit earlier this year on a few resources for learning Python - which you can find <a href="/you-should-automate-something-this-year/">here</a>. I also wanted to point out some other great resources that are a bit more specific to using those skills for network automation:</p>
<ul>
<li>
<p><a href="https://pynet.twb-tech.com/email-signup.html">Python For Network Engineers</a> - Don&rsquo;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&rsquo;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.</p>
</li>
<li>
<p><a href="https://developer.cisco.com">Cisco DevNet</a> - 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&rsquo;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.</p>
</li>
<li>
<p><a href="https://amzn.to/2L7EiL1">Network Programmability and Automation</a> - 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!</p>
</li>
</ul>
<hr>
<p>So - 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.</p>
<p>Have suggestions on where else to learn? Comment below!</p>
]]></content:encoded>
    </item>
    <item>
      <title>Tips for Working with Vendor Support</title>
      <link>https://0x2142.com/tips-for-working-with-vendor-support/</link>
      <pubDate>Wed, 09 May 2018 12:00:24 +0000</pubDate>
      <guid>https://0x2142.com/tips-for-working-with-vendor-support/</guid>
      <description>The key to a successful support case is to work with your vendor, not against them&amp;hellip;</description>
      <content:encoded><![CDATA[<p>This post has been on my mind for a while now. I&rsquo;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&rsquo;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&rsquo;ve been taught by former co-workers.</p>
<p>On the other side of things, I&rsquo;ve worked with people over the years who haven&rsquo;t had quite the same experiences as I have. Some of them don&rsquo;t typically call vendors for support - or maybe they&rsquo;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&rsquo;t push hard enough on an issue or stress the importance.</p>
<p>I feel that in a majority of cases, it shouldn&rsquo;t be difficult to get the results you want out of a vendor support case. So I&rsquo;ve put together a list of my own personal tips and guidelines for working with technical support.</p>
<h2 id="the-vendor-is-your-partner---not-the-enemy">The Vendor is your partner - Not the enemy</h2>
<p>I&rsquo;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&rsquo;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&rsquo;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 <em>before</em> you contact them.</p>
<p>Always remember how difficult the job of a technical support rep can be. They&rsquo;re likely sitting in a call center, just waiting for the next case. They may have in-depth knowledge of their product, but they&rsquo;re walking into your network completely blind. They won&rsquo;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?</p>
<h2 id="have-realistic-expectations">Have realistic expectations</h2>
<p>Especially when you&rsquo;re new to a career in IT, it can be hard to gauge what you can and can&rsquo;t ask of your tech support rep. One of my former jobs had a policy that for every ticket with AT&amp;T we opened, we would be required to call back every hour for a status update. If there wasn&rsquo;t one, then we were supposed to demand escalation to a manager. That policy might make sense if it&rsquo;s a critical issue, but what about something that&rsquo;s not? Have realistic expectations about what your vendor can do.</p>
<p>Troubleshooting takes time. If your support engineer grabs a bunch of logs and says they&rsquo;ll need to get back to you - then you&rsquo;ll need to give them the time they need. Feel free to ask for a time estimate - but if they say they&rsquo;ll have something to you by the following day, don&rsquo;t start bugging them every hour.</p>
<p>Remember that your support engineer&rsquo;s job is to help you. If you don&rsquo;t feel like that&rsquo;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&rsquo;t super familiar with the problem you&rsquo;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&rsquo;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&rsquo;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&rsquo;t help, you can also usually reach out to your local account representative and ask them to help push the issue for you.</p>
<p>It&rsquo;s also very helpful to have an idea of your vendor&rsquo;s support policies. Have a question about how to set up a new feature? Some vendors don&rsquo;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&rsquo;re not 100% confident in your changes, then it&rsquo;s better to open a proactive case beforehand.</p>
<h2 id="be-clear-about-the-impact">Be clear about the impact</h2>
<p>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&rsquo;s a high priority issue for your business.</p>
<p>Usually when I open a high severity case, I&rsquo;ll let the engineer know: &ldquo;Just so we&rsquo;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&rdquo;. High severity cases can be stressful for both sides, and I try to be clear about the impact without making that worse.</p>
<p>On the other side of things, if there is a low severity issue - don&rsquo;t blow it out of proportions. I&rsquo;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&rsquo;t warrant getting half a dozen TAC engineers on a conference bridge.</p>
<h2 id="in-case-of-emergency">In case of emergency</h2>
<p>Emergency situations are a completely different subject - so I want to spend a bit of time covering them separately. It&rsquo;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.</p>
<p><strong>Step one</strong> - <em>Always</em> call into your vendors support line. You don&rsquo;t want to open a web/email case and wait around for a technician. This might seem obvious, but I&rsquo;ve known a lot of people who complain about the vendor&rsquo;s response on a critical issue when they opened a case via a support portal. Find the vendor&rsquo;s support number (or have it saved somewhere) and call them.</p>
<p><strong>Step two</strong> - 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.</p>
<p><strong>Step three</strong> - 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.</p>
<p><strong>Step four</strong> - Keep troubleshooting on track. As I&rsquo;ve stated before, you know your environment/network better than your vendor does. If they start looking at something you don&rsquo;t believe is related, you need to guide them back to the main problem.</p>
<p>In addition, if you feel after a bit that the troubleshooting isn&rsquo;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&rsquo;ve even had situations before where the technician said &ldquo;Well, I think we need to do X to fix it&rdquo;, and I&rsquo;ve just asked them to see what their peers think. You would rather be sure about a change, than make the issue worse.</p>
<p><strong>Step five</strong> - 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&rsquo;re willing to just wait on hold. Once they&rsquo;re off the phone, it&rsquo;s easy for your technician to get dragged into another issue.</p>
<p>If 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.</p>
<hr>
<p>These are just some of my own personal tips that have worked for me. Support calls with vendors don&rsquo;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.</p>
<p>I hope these are useful - Let me know in the comments if you have any additional tips!</p>
]]></content:encoded>
    </item>
    <item>
      <title>Juniper SRX - Automated Route Monitoring</title>
      <link>https://0x2142.com/juniper-srx-automated-route-monitoring/</link>
      <pubDate>Tue, 13 Mar 2018 11:00:20 +0000</pubDate>
      <guid>https://0x2142.com/juniper-srx-automated-route-monitoring/</guid>
      <description>How I automated BGP routing table monitoring for Juniper SRX firewalls</description>
      <content:encoded><![CDATA[<p><sup><em>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.</em></sup></p>
<hr>
<p>I have always said that I&rsquo;m not sure I could write code for a living, but I do really enjoy writing scripts that make my life easier. Today&rsquo;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 <a href="https://www.amazon.com/gp/product/B01ICEO2U4/ref=as_li_qf_asin_il_tl?ie=UTF8&amp;tag=0x2142-20&amp;creative=9325&amp;linkCode=as2&amp;creativeASIN=B01ICEO2U4&amp;linkId=35fbe8300af4e5d1e26e7a860782b3ca">Amazon</a> 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 🙂</p>
<p>Anyways - I had a requirement to monitor my SRX clusters for route changes. Specifically I&rsquo;m using this with devices where I have implemented customer peering via BGP. The SRXs don&rsquo;t natively offer any form of monitoring and alerting for this, and the current monitoring applications at my disposal don&rsquo;t either. So I decided to write something myself, which took significantly less time than I had assumed.</p>
<p>Code has been posted up to my <a href="https://github.com/0x2142/juniper-srx-scripts/blob/master/checkBGP.py">GitHub</a> - but I&rsquo;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.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># Imports</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">jnpr.junos</span> <span class="kn">import</span> <span class="n">Device</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">jnpr.junos.op.routes</span> <span class="kn">import</span> <span class="n">RouteTable</span>
</span></span></code></pre></div><p>On my initial research to see how easy this would be to pull off, I found a nifty thing in the <a href="/getting-started-with-junos-pyez/">JunOS PyEZ</a> package. Turns out they already offer a module literally called RouteTable that can pull the information I need.</p>
<p>Originally, 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.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1">#################</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Required values</span>
</span></span><span class="line"><span class="cl"><span class="c1">#################</span>
</span></span><span class="line"><span class="cl"><span class="n">deviceName</span> <span class="o">=</span> <span class="s1">&#39;SRXFirewall&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">deviceIP</span> <span class="o">=</span> <span class="s1">&#39;0.0.0.0&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">apiuser</span> <span class="o">=</span> <span class="s1">&#39;username&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">apipassword</span> <span class="o">=</span> <span class="s1">&#39;password&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">SMTP</span> <span class="o">=</span> <span class="s1">&#39;smtp-alias&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">fromName</span> <span class="o">=</span> <span class="s1">&#39;BGP Monitor&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">fromAddr</span> <span class="o">=</span> <span class="s1">&#39;bgpmonitor@domain.com&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">toName</span> <span class="o">=</span> <span class="s1">&#39;contactName&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">toAddr</span> <span class="o">=</span> <span class="s1">&#39;contact@domain.com&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">prefixDict</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;FriendlyName&#34;</span><span class="p">:</span> <span class="s2">&#34;Prefix&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Route-to-Inet&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0.0/0&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">################</span>
</span></span></code></pre></div><p>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 &ldquo;Customer1&rdquo;: &ldquo;10.10.10.0/24&rdquo;</p>
<p>Alright - now let&rsquo;s skip to the good stuff:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># Function to check received BGP routes</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">checkBGP</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Open SRX session</span>
</span></span><span class="line"><span class="cl">        <span class="n">dev</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">except</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Pull device routing table, then keep only BGP originated routes</span>
</span></span><span class="line"><span class="cl">    <span class="n">allroutes</span> <span class="o">=</span> <span class="n">RouteTable</span><span class="p">(</span><span class="n">dev</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">bgp</span> <span class="o">=</span> <span class="n">allroutes</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">protocol</span><span class="o">=</span><span class="s2">&#34;bgp&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">keys</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Close SRX session</span>
</span></span><span class="line"><span class="cl">    <span class="n">dev</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</span></span></code></pre></div><p>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 <code>bgp</code>.</p>
<p>So 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:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="n">tempfile</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">tempfile</span><span class="p">,</span> <span class="s1">&#39;w+b&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">a</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">a</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">bgp</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Local file used to keep track of BGP learned routes</span>
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">tempfile</span><span class="p">,</span> <span class="s1">&#39;ab&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">a</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">lastroutes</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">readlines</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Compare if routes are different</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">str</span><span class="p">(</span><span class="n">bgp</span><span class="p">)</span> <span class="o">==</span> <span class="nb">str</span><span class="p">(</span><span class="n">lastroutes</span><span class="p">[</span><span class="mi">0</span><span class="p">]):</span>
</span></span><span class="line"><span class="cl">            <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">str</span><span class="p">(</span><span class="n">bgp</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">str</span><span class="p">(</span><span class="n">lastroutes</span><span class="p">[</span><span class="mi">0</span><span class="p">]):</span>
</span></span><span class="line"><span class="cl">            <span class="k">pass</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Delete file, then re-create with new route list</span>
</span></span><span class="line"><span class="cl">    <span class="c1">#os.remove(tempfile)</span>
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">tempfile</span><span class="p">,</span> <span class="s1">&#39;w+b&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">a</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">a</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">bgp</span><span class="p">))</span>
</span></span></code></pre></div><p>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&rsquo;t match then something has changed.</p>
<p>Once we know something has changed, we&rsquo;ll go ahead and find out exactly which route entry is missing:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl">    <span class="c1"># Create Status list, by checking received routes in bgp object</span>
</span></span><span class="line"><span class="cl">    <span class="n">status</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">name</span><span class="p">,</span><span class="n">prefix</span> <span class="ow">in</span> <span class="n">prefixDict</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">prefix</span> <span class="ow">in</span> <span class="n">bgp</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">status</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">&#34;</span><span class="si">%s</span><span class="s2"> - RECEIVED&#34;</span> <span class="o">%</span> <span class="n">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">prefix</span> <span class="ow">in</span> <span class="n">bgp</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">status</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">&#34;</span><span class="si">%s</span><span class="s2"> - MISSING&#34;</span> <span class="o">%</span> <span class="n">name</span><span class="p">)</span>
</span></span></code></pre></div><p>This is where our prefixDict from earlier comes into play. We&rsquo;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.</p>
<p>Lastly, we&rsquo;ll go ahead and compose our alert email to send out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl">    <span class="c1"># Send alert message</span>
</span></span><span class="line"><span class="cl">    <span class="n">sendMail</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">lastroutes</span><span class="p">[</span><span class="mi">0</span><span class="p">]),</span> <span class="nb">str</span><span class="p">(</span><span class="n">bgp</span><span class="p">),</span> <span class="n">status</span><span class="p">)</span>
</span></span></code></pre></div><p>I&rsquo;m not going to cover the email function here, since it&rsquo;s pretty straightforward.</p>
<p>That&rsquo;s it! The existence of the JunOS RouteTables module made creating this script a piece of cake. I&rsquo;m considering adding onto this later with some additional functionality, so be sure to check my <a href="https://github.com/0x2142/juniper-srx-scripts">GitHub</a> repo if you&rsquo;re interested.</p>
<p>Hope this is useful to someone else out there - Let me know in the comments!</p>
]]></content:encoded>
    </item>
    <item>
      <title>How to Improve: Stop Doing, Start Understanding</title>
      <link>https://0x2142.com/how-to-improve-stop-doing-start-understanding/</link>
      <pubDate>Tue, 28 Nov 2017 08:07:54 +0000</pubDate>
      <guid>https://0x2142.com/how-to-improve-stop-doing-start-understanding/</guid>
      <description>The best way to truly understand a technology is to dig deeper than surface-level configurations</description>
      <content:encoded><![CDATA[<p>There is a key to being successful at just about any IT job: Stop just doing work, and start understanding what you&rsquo;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.</p>
<p>In 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&rsquo;re likely to get a similar result every time.</p>
<p>This is the point where I have seen far too many people stop though. They are happy with doing their job, and don&rsquo;t necessarily want to progress their career or maybe they don&rsquo;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&rsquo;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.</p>
<p>For me, a true understanding of a technology means that you&rsquo;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.</p>
<p>Let 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.</p>
<p>Another 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.</p>
<p>Another example is something that I see quite often. An engineer is asked to implement something - let&rsquo;s say a new port configuration for a server. They follow their known process for implementing this change, but something doesn&rsquo;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&rsquo;t know why it didn&rsquo;t work the first time, or what caused it to work the second time - but it works now, so they aren&rsquo;t concerned with it. However, it&rsquo;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&rsquo;t work - but now they know what that command actually does, which could be beneficial in a future scenario.</p>
<p>As 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&rsquo;t handle running into a problem and not knowing exactly what the cause was. An issue that &ldquo;fixes itself&rdquo; is never an acceptable answer to me, because if something caused the problem once then it can certainly happen again. I don&rsquo;t enjoy having to blindly configure an option on a system without knowing what&rsquo;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&rsquo;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.</p>
<p>Especially if you&rsquo;re still in the beginning stages of your career, I can&rsquo;t stress enough how important it is to understand the technologies you&rsquo;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&rsquo;t settle for &ldquo;It just works because it does&rdquo;. One of the key skills I&rsquo;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.</p>
<p>Have 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!</p>
]]></content:encoded>
    </item>
    <item>
      <title>What&#39;s Going Out of Your Network?</title>
      <link>https://0x2142.com/whats-going-out-of-your-network/</link>
      <pubDate>Tue, 21 Nov 2017 08:00:53 +0000</pubDate>
      <guid>https://0x2142.com/whats-going-out-of-your-network/</guid>
      <description>Ever consider enabling firewall filtering for outgoing traffic from your network? Let&amp;rsquo;s look at why that could be interesting&amp;hellip;</description>
      <content:encoded><![CDATA[<p>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&rsquo;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 <a href="https://www.amazon.com/gp/product/B01ICEO2U4/ref=as_li_qf_asin_il_tl?ie=UTF8&amp;tag=0x2142-20&amp;creative=9325&amp;linkCode=as2&amp;creativeASIN=B01ICEO2U4&amp;linkId=35fbe8300af4e5d1e26e7a860782b3ca">Juniper SRX300</a> - 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.</p>
<p>Anyways - after I cut over to my new firewall, I&rsquo;ve been digging through logs to make sure that I didn&rsquo;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&rsquo;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&rsquo;t sure about.</p>
<p>So that brings me to my question of the day: Do you know what&rsquo;s going out of your network?</p>
<p>A lot of people I know only use firewalls to block inbound access, both in homes and businesses. For homes it&rsquo;s more understandable since most average people aren&rsquo;t network admins. However, it still surprises me how many businesses are willing to add a &lsquo;permit any any&rsquo; out to the internet. Yes, I block all traffic by default through my home firewall, both inbound and outbound. Yes, it&rsquo;s a bit of a pain sometimes when something isn&rsquo;t quite working right - but it&rsquo;s usually a quick ACL change, and overall I would rather take the minor inconvenience for the security gains.</p>
<p>When 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&rsquo;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&rsquo;t really documented well by the console manufacturer, and meant that I spent a while between browsing forums and some trial and error.</p>
<p>This 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&rsquo;t make me feel comfortable. Could it be a legitimate application, or is it malware? A few quick searches on the internet don&rsquo;t turn up anything immediately helpful. For the time being, I&rsquo;ll keep stuff like this blocked until I have time to spin up some packet captures to see what this traffic is actually doing.</p>
<p>For a business I feel like this type of thing is even more important than just what I&rsquo;m doing at home. You certainly don&rsquo;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&rsquo;t have the time or see the value. However, I&rsquo;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.</p>
<p>If you&rsquo;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&rsquo;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&rsquo;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.</p>
<p>How do you have your firewalls configured today? Do you permit everything or are you very restrictive? Comment below - I&rsquo;m curious to see what other people are doing.</p>
]]></content:encoded>
    </item>
    <item>
      <title>SRX High CPU: httpd</title>
      <link>https://0x2142.com/srx-httpd-high-cpu/</link>
      <pubDate>Tue, 05 Sep 2017 08:00:12 +0000</pubDate>
      <guid>https://0x2142.com/srx-httpd-high-cpu/</guid>
      <description>How to manage stuck httpd sessions that are causing 100% CPU utilization</description>
      <content:encoded><![CDATA[<p>Over the past few years of my Juniper SRX adventures, I&rsquo;ve run into a few cases where the Routing Engine (RE) CPU is pegged at 100%. From what I&rsquo;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.</p>
<p>In a few occasional cases, the CPU issue doesn&rsquo;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&rsquo;s not always possible.</p>
<p>So the first thing we want to do is log into our SRX firewall and check the current CPU utilization for our RE processor:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">{primary:node0}
</span></span><span class="line"><span class="cl">root@test-srx&gt; show chassis routing-engine node 0 
</span></span><span class="line"><span class="cl">node0:
</span></span><span class="line"><span class="cl">--------------------------------------------------------------------------
</span></span><span class="line"><span class="cl">Routing Engine status:
</span></span><span class="line"><span class="cl">    Temperature                  41 degrees C / 105 degrees F
</span></span><span class="line"><span class="cl">    CPU temperature              70 degrees C / 158 degrees F
</span></span><span class="line"><span class="cl">    Total memory               4096 MB Max 1556 MB used ( 38 percent)
</span></span><span class="line"><span class="cl">      Control plane memory     2976 MB Max 804 MB used ( 27 percent)
</span></span><span class="line"><span class="cl">      Data plane memory        1120 MB Max 773 MB used ( 69 percent)
</span></span><span class="line"><span class="cl">    5 sec CPU utilization:
</span></span><span class="line"><span class="cl">      User                       41 percent
</span></span><span class="line"><span class="cl">      Background                  0 percent
</span></span><span class="line"><span class="cl">      Kernel                     59 percent
</span></span><span class="line"><span class="cl">      Interrupt                   0 percent
</span></span><span class="line"><span class="cl">      Idle                        0 percent
</span></span><span class="line"><span class="cl">    Model                           RE-SRX345
</span></span><span class="line"><span class="cl">    Serial ID                       XX1000XX0002
</span></span><span class="line"><span class="cl">    Start time                      2016-09-01 02:49:50 UTC
</span></span><span class="line"><span class="cl">    Uptime                          351 days, 13 hours, 28 minutes, 47 seconds
</span></span><span class="line"><span class="cl">    Last reboot reason              0x1:power cycle/failure
</span></span><span class="line"><span class="cl">    Load averages:                  1 minute   5 minute   15 minute
</span></span><span class="line"><span class="cl">                                        1.29       1.27        1.10
</span></span></code></pre></div><p>So we can see that over the past 5 seconds, there is 0% idle CPU - It&rsquo;s all split between User and Kernel. Some higher-end SRX models will also show utilization for 1 minute, 5 minutes, and 15 minutes.</p>
<p>Next, we want to confirm which process is consuming that CPU:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">{primary:node0}
</span></span><span class="line"><span class="cl">root@test-srx&gt; show system processes extensive node 0
</span></span><span class="line"><span class="cl">node0:
</span></span><span class="line"><span class="cl">--------------------------------------------------------------------------
</span></span><span class="line"><span class="cl">last pid: 25330;  load averages:  1.16,  1.24,  1.10  up 351+13:29:51    16:19:11
</span></span><span class="line"><span class="cl">165 processes: 21 running, 132 sleeping, 12 waiting
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Mem: 354M Active, 191M Inact, 1253M Wired, 585M Cache, 112M Buf, 1595M Free
</span></span><span class="line"><span class="cl">Swap:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  PID USERNAME     THR PRI NICE   SIZE    RES STATE  C   TIME   WCPU COMMAND
</span></span><span class="line"><span class="cl">1635 root           7  76    0  1192M   113M RUN    0    ??? 281.93% flowd_octeon_hm
</span></span><span class="line"><span class="cl">14607 nobody         3  76    0 14848K  6308K ucondt 0  25:03 83.45% httpd
</span></span><span class="line"><span class="cl">   21 root           1 171   52     0K    16K RUN    0 6952.9  0.00% idle: cpu0
</span></span><span class="line"><span class="cl"> 1679 root           1  76    0 48580K 24476K select 0  90.2H  0.00% mib2d
</span></span><span class="line"><span class="cl"> 1715 root           1  76    0 35264K 19520K select 0  49.0H  0.00% snmpd
</span></span><span class="line"><span class="cl">   23 root           1 -20 -139     0K    16K RUN    0  29.9H  0.00% swi7: clock
</span></span><span class="line"><span class="cl"> 1681 root           1   4    0   101M 68284K kqread 0  28.0H  0.00% rpd
</span></span><span class="line"><span class="cl">   22 root           1 -40 -159     0K    16K WAIT   0  26.0H  0.00% swi2: netisr 0
</span></span><span class="line"><span class="cl">  &lt;-- Output Truncated --&gt;
</span></span></code></pre></div><p>In this case it&rsquo;s pretty clear that httpd is the top offender for CPU usage. You might also notice the process named &lsquo;flowd_octeon_hm&rsquo;. This is part of the firewall processes, so don&rsquo;t be surprised if this process is also one of the top. It&rsquo;s pretty normal for this process to show &gt;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&rsquo;ll cover in another post.</p>
<p>So how do we fix the httpd problem? Reboot the SRX? Well, yeah that would probably fix it - but there is an easier way:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">{primary:node0}
</span></span><span class="line"><span class="cl">root@test-srx&gt; restart web-management
</span></span><span class="line"><span class="cl">Web management gatekeeper process started, pid 25343
</span></span></code></pre></div><p>One quick command and we&rsquo;ve restarted all of the web management processes, including httpd. So now you&rsquo;ll want to give the SRX a few seconds to recover itself - then run the <em>show system processes extensive</em>command again:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">{primary:node0}
</span></span><span class="line"><span class="cl">root@test-srx&gt; show chassis routing-engine node 0
</span></span><span class="line"><span class="cl">node0:
</span></span><span class="line"><span class="cl">--------------------------------------------------------------------------
</span></span><span class="line"><span class="cl">Routing Engine status:
</span></span><span class="line"><span class="cl">    Temperature                 41 degrees C / 105 degrees F
</span></span><span class="line"><span class="cl">    CPU temperature             69 degrees C / 156 degrees F
</span></span><span class="line"><span class="cl">    Total memory              4096 MB Max  1556 MB used ( 38 percent)
</span></span><span class="line"><span class="cl">      Control plane memory    2976 MB Max   804 MB used ( 27 percent)
</span></span><span class="line"><span class="cl">      Data plane memory       1120 MB Max   773 MB used ( 69 percent)
</span></span><span class="line"><span class="cl">    5 sec CPU utilization:
</span></span><span class="line"><span class="cl">      User                       6 percent
</span></span><span class="line"><span class="cl">      Background                 0 percent
</span></span><span class="line"><span class="cl">      Kernel                     3 percent
</span></span><span class="line"><span class="cl">      Interrupt                  0 percent
</span></span><span class="line"><span class="cl">      Idle                      91 percent
</span></span><span class="line"><span class="cl">    Model                          RE-SRX345
</span></span><span class="line"><span class="cl">    Serial ID                      XX1000XX0002
</span></span><span class="line"><span class="cl">    Start time                     2016-09-01 02:49:50 UTC
</span></span><span class="line"><span class="cl">    Uptime                         351 days, 13 hours, 32 minutes, 52 seconds
</span></span><span class="line"><span class="cl">    Last reboot reason             0x1:power cycle/failure
</span></span><span class="line"><span class="cl">    Load averages:                 1 minute   5 minute  15 minute
</span></span><span class="line"><span class="cl">                                       0.35       0.99       1.04
</span></span></code></pre></div><p>Looks much better, with 91% idle CPU!</p>
<p>Even 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 <a href="http://www.observium.org">Observium</a> 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.</p>
<p>Hope this helps!</p>
]]></content:encoded>
    </item>
    <item>
      <title>SRX Basics: Redunancy Groups and Failover</title>
      <link>https://0x2142.com/srx-basics-redunancy-groups-and-failover/</link>
      <pubDate>Tue, 18 Jul 2017 08:00:24 +0000</pubDate>
      <guid>https://0x2142.com/srx-basics-redunancy-groups-and-failover/</guid>
      <description>Some background &amp;amp; configuration examples for Juniper SRX redundancy groups</description>
      <content:encoded><![CDATA[<p>In last weeks post, we took a look at how to set up a <a href="/srx-basics-clustering/">chassis cluster</a> on a Juniper SRX Firewall. So now that we have a basic cluster setup - Let&rsquo;s explore some of the additional options and configuration items.</p>
<h2 id="redundant-ethernet-interfaces">Redundant Ethernet Interfaces</h2>
<p>So first thing is first - Once you have a cluster configured, you&rsquo;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&rsquo;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:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">root@testsrx# set interfaces xe-0/0/16 gigether-options redundant-parent reth1
</span></span><span class="line"><span class="cl">root@testsrx# set interfaces xe-7/0/16 gigether-options redundant-parent reth1
</span></span><span class="line"><span class="cl">root@testsrx# set interfaces reth1 redundant-ether-options redundancy-group 1
</span></span></code></pre></div><p>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.</p>
<p>You&rsquo;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&rsquo;ll use the following command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">root@testsrx# set chassis cluster reth-count 5
</span></span></code></pre></div><h2 id="redundancy-groups">Redundancy Groups</h2>
<p>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.</p>
<p>A 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&rsquo;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.</p>
<p>In most configurations, dumping all of your reth interfaces into RG1 will be sufficient. You&rsquo;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&rsquo;ll have to configure each cluster member with a priority:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">root@testsrx# set chassis cluster redundancy-group 0 node 0 priority 200
</span></span><span class="line"><span class="cl">root@testsrx# set chassis cluster redundancy-group 0 node 1 priority 50
</span></span><span class="line"><span class="cl">root@testsrx# set chassis cluster redundancy-group 1 node 0 priority 200
</span></span><span class="line"><span class="cl">root@testsrx# set chassis cluster redundancy-group 1 node 1 priority 50
</span></span><span class="line"><span class="cl">root@testsrx# set chassis cluster redundancy-group 1 preempt
</span></span></code></pre></div><p>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&rsquo;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).</p>
<h2 id="a-note-about-rg0">A Note About RG0</h2>
<p>You might notice from the last post, that you&rsquo;re output of <strong>show chassis cluster status</strong> 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.</p>
<p>For example, whichever node is primary for RG0 is the only node that collects interface and monitoring statistics. If you&rsquo;re using a monitoring tool that polls data from both of your SRXs, then the secondary for RG0 will report <strong>nothing</strong> about it&rsquo;s interfaces, CPU, etc. This is also true if you log into the actual SRX itself - a <strong>show interfaces</strong> will actually return a bunch of default values, including showing that your ports are half-duplex. Don&rsquo;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.</p>
<p>Due 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&rsquo;s also worth noting that whichever SRX is primary for RG0 is also the node you&rsquo;re going to need to log into for configuration changes - even if all of your other redundancy groups are the other SRX.</p>
<p>Weird, right?</p>
<p>Oh, 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&rsquo;t a graceful sync of all of that data.</p>
<h2 id="interface-monitoring">Interface Monitoring</h2>
<p>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&rsquo;re using an SRX cluster for your edge firewall, and you want it to automatically fail over if the primary loses it&rsquo;s internet uplink.</p>
<p>Note that you must configure the <em>physical</em> interfaces here, not the redundant ethernet interfaces:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">root@testsrx# set chassis cluster redundancy-group 1 interface-monitor xe-0/0/16 weight 160
</span></span></code></pre></div><p>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&rsquo;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&rsquo;s priority will go back up to 200. Then node0 will take back ownership of RG1.</p>
<h2 id="manual-failover">Manual Failover</h2>
<p>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:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">root@testsrx&gt; show chassis cluster status
</span></span><span class="line"><span class="cl">Cluster ID: 5
</span></span><span class="line"><span class="cl">Node       Priority       Status       Preempt       Manual failover
</span></span><span class="line"><span class="cl">Redundancy group: 0 , Failover count: 0
</span></span><span class="line"><span class="cl">node0      200            primary      no            no 
</span></span><span class="line"><span class="cl">node1      50             secondary    no            no
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Redundancy group: 1 , Failover count: 0
</span></span><span class="line"><span class="cl">node0      200            primary      yes           no 
</span></span><span class="line"><span class="cl">node1      50             secondary    yes           no
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">root@testsrx&gt; request chassis cluster failover redundancy-group 1 node 1 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">root@testsrx&gt; show chassis cluster status 
</span></span><span class="line"><span class="cl">Cluster ID: 5
</span></span><span class="line"><span class="cl">Node       Priority       Status       Preempt       Manual failover
</span></span><span class="line"><span class="cl">Redundancy group: 0 , Failover count: 0
</span></span><span class="line"><span class="cl">node0     200             primary      no            no 
</span></span><span class="line"><span class="cl">node1     50              secondary    no            no
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Redundancy group: 1 , Failover count: 1
</span></span><span class="line"><span class="cl">node0     200             secondary    yes           yes
</span></span><span class="line"><span class="cl">node1     255             primary      yes           yes
</span></span></code></pre></div><p>Okay - so let&rsquo;s talk about a few things that have happened here. I always recommend that you run a <strong>show chassis cluster status</strong> 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.</p>
<p>You might also notice that the priorities have changed, and the devices are marked as being in a manual failover state. This is important, because <strong>you cannot manually fail back until you reset this state</strong>. That&rsquo;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:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">root@testsrx&gt; request chassis cluster failover reset redundancy-group 1
</span></span></code></pre></div><hr>
<p>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!</p>
]]></content:encoded>
    </item>
    <item>
      <title>SRX Basics: Clustering</title>
      <link>https://0x2142.com/srx-basics-clustering/</link>
      <pubDate>Tue, 11 Jul 2017 08:00:07 +0000</pubDate>
      <guid>https://0x2142.com/srx-basics-clustering/</guid>
      <description>A short tutorial on how to configure Juniper SRX clustering</description>
      <content:encoded><![CDATA[<p>So you just unboxed a brand new pair of Juniper SRX firewalls - now what? Well, the first thing you&rsquo;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.</p>
<p>So let&rsquo;s take a look at what we need to do!</p>
<h2 id="physical-configuration">Physical configuration</h2>
<p>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&rsquo;ll want to get the fabric port working as well - here is what both ports are used for:</p>
<p><strong>HA Control Port</strong> - 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.</p>
<p><strong>Fabric Port</strong> - 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)</p>
<p>The 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&rsquo;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 <a href="https://kb.juniper.net/InfoCenter/index?page=content&amp;id=KB15356&amp;actp=METADATA">this page</a>.</p>
<p>Once those ports are connected, go ahead and power on both devices!</p>
<h2 id="junos-config">JunOS Config</h2>
<p>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.</p>
<p>When you first boot each device, you&rsquo;ll log in with <strong>root</strong> and no password. Then you&rsquo;ll be dropped into the JunOS shell, and you&rsquo;ll need to type <strong>cli</strong> to start the JunOS command-line interface. Then type <strong>configure</strong> to get into the configuration mode.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">root@testsrx% cli
</span></span><span class="line"><span class="cl">root@testsrx&gt; configure
</span></span><span class="line"><span class="cl">root@testsrx#
</span></span></code></pre></div><p>In the config mode, we&rsquo;ll need to set a root password before we can enable the clustering. This password must match on both devices!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">root@testsrx# set system root-authentication plain-text-password
</span></span><span class="line"><span class="cl">New Password: &lt;type your root password here&gt;
</span></span><span class="line"><span class="cl">Retype new password: &lt;and again...&gt;
</span></span></code></pre></div><p>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&rsquo;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.</p>
<p>In 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:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">root@testsrx# delete interfaces
</span></span><span class="line"><span class="cl">root@testsrx# delete security
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">After all that is done, we need to commit our changes:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">```text
</span></span><span class="line"><span class="cl">root@testsrx# commit
</span></span></code></pre></div><p>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.</p>
<p>So one thing to note here - each cluster will be configured with a cluster-id. This <strong>MUST</strong>be 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&rsquo;ll use cluster-id 5 in this example.</p>
<p>On whichever SRX you want to be the primary node:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">root@testsrx# exit
</span></span><span class="line"><span class="cl">root@testsrx&gt; set chassis cluster cluster-id 5 node 0 reboot
</span></span></code></pre></div><p>I personally like to give the primary a minute or to into the boot process before I configure the secondary, but we&rsquo;ll do so with a similar command (just specifying node 1 instead of 0):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">root@testsrx&gt; set chassis cluster cluster-id 5 node 1 reboot
</span></span></code></pre></div><p>After both nodes come back online, log into node 0 and run the following command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">root@testsrx&gt; show chassis cluster status 
</span></span><span class="line"><span class="cl">Cluster ID: 5
</span></span><span class="line"><span class="cl">Node       Priority      Status      Preempt      Manual failover
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Redundancy group: 0 , Failover count: 0
</span></span><span class="line"><span class="cl">node0      100           primary     no           no 
</span></span><span class="line"><span class="cl">node1      100           secondary   no           no
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Redundancy group: 1 , Failover count: 0
</span></span><span class="line"><span class="cl">node0      100           primary     yes          no 
</span></span><span class="line"><span class="cl">node1      100           secondary   yes          no
</span></span></code></pre></div><p>Perfect! Now let&rsquo;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.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">root@testsrx&gt; configure
</span></span><span class="line"><span class="cl">root@testsrx# set interface fab0 fabric-options member-interfaces ge-0/0/10
</span></span><span class="line"><span class="cl">root@testsrx# set interface fab1 fabric-options member-interfaces ge-5/0/10
</span></span><span class="line"><span class="cl">root@testsrx# commit
</span></span></code></pre></div><p>Now that we&rsquo;re in a cluster, all of this configuration can be done on node0 - but note that in this case the secondary device&rsquo;s ports all start with ge-5/x/x. This is another oddity of JunOS - that numbering scheme isn&rsquo;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&rsquo;re working with. If you ever need to check this - you can run <strong>show interface terse</strong> to list all interfaces in the cluster.</p>
<p>As a final verification that all our ports are up, drop out of config mode and run <strong>show chassis cluster interfaces:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">root@testsrx&gt; show chassis cluster interfaces
</span></span><span class="line"><span class="cl">Control link 0 name: ge-0/0/1
</span></span><span class="line"><span class="cl">Control link status: Up
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Fabric interfaces:
</span></span><span class="line"><span class="cl">Name Child-interface Status
</span></span><span class="line"><span class="cl">fab0 ge-0/0/10 up
</span></span><span class="line"><span class="cl">fab0
</span></span><span class="line"><span class="cl">fab1 ge-5/0/10 up
</span></span><span class="line"><span class="cl">fab1
</span></span><span class="line"><span class="cl">Fabric link status: up
</span></span></code></pre></div><p>Hooray! We now have a functioning SRX cluster!</p>
<p>Sometimes if this doesn&rsquo;t work, the output of <strong>show chassis cluster status</strong> will show the secondary node as <strong>disabled</strong> or <strong>lost.</strong> I&rsquo;ve found that <strong>lost</strong> usually indicates a conflicting configuration on the cluster interfaces (like leaving the default IPs configured). If you see <strong>disabled</strong>, try rebooting the secondary node again - and if that doesn&rsquo;t work, then you may need to disable clustering on both nodes and re-configure. This can be done using the <strong>set chassis cluster disable reboot</strong> command.</p>
<p>Next week, we&rsquo;ll look at redundancy-groups, performing manual failovers, and setting up interface monitoring for automatic failovers. Hope this was helpful!</p>
]]></content:encoded>
    </item>
    <item>
      <title>Building a VPN Dashboard using Django and JunOS pyEZ (Part 4 – Polling the SRX)</title>
      <link>https://0x2142.com/building-a-vpn-dashboard-using-django-and-junos-pyez-part-4-polling-the-srx/</link>
      <pubDate>Tue, 04 Jul 2017 08:00:31 +0000</pubDate>
      <guid>https://0x2142.com/building-a-vpn-dashboard-using-django-and-junos-pyez-part-4-polling-the-srx/</guid>
      <description>This post focuses on the Juniper SRX side of my VPN dashboard - writing the Python scripting to query VPN status from each device</description>
      <content:encoded><![CDATA[<p>This is a multi-part series - If you just hit this page, please check out the prior posts first!</p>
<ul>
<li><a href="/building-a-vpn-dashboard-using-django-and-junos-pyez-part-1-initial-thoughts/">Part 1 - Initial Thoughts</a></li>
<li><a href="/building-a-vpn-dashboard-using-django-and-junos-pyez-part-2-learning-django/">Part 2 - Leaning Django</a></li>
<li><a href="/building-a-vpn-dashboard-using-django-and-junos-pyez-part-3-creating-the-dashboard/">Part 3 - Creating the Dashboard</a></li>
</ul>
<hr>
<p>Alright - We&rsquo;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.</p>
<p>For 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&rsquo;re going to create a <em>management</em> folder then a <em>commands</em> folder within that. In here you can name your script whatever you want, so I went with cronpoller.py.</p>
<p>The 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.</p>
<p>First thing we need to do is import a few things we will need:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">django.core.management.base</span> <span class="kn">import</span> <span class="n">BaseCommand</span><span class="p">,</span> <span class="n">CommandError</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">vpn.models</span> <span class="kn">import</span> <span class="n">Firewall</span><span class="p">,</span> <span class="n">Datacenter</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">jnpr.junos</span> <span class="kn">import</span> <span class="n">Device</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">lxml</span> <span class="kn">import</span> <span class="n">etree</span>
</span></span></code></pre></div><p>We have some Django modules for treating this as a management command - and we&rsquo;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&rsquo;t forget to <a href="/getting-started-with-junos-pyez/">install pyEZ</a>!). The last one is going to be used to parse the responses from our SRX into something we can use.</p>
<p>Alright - so in order to build a management command, we have to create a class called <em>Command,</em> then define our functions within that. Within the <em>Command</em> class, Django will look for a function called <em>handle</em> to execute. In my final script, I have that function plus one called <em>getVPNStatus</em>. Let&rsquo;s start with putting together <em>getVPNStatus:</em></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># This function will poll the device for status </span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">getVPNStatus</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fw</span><span class="p">,</span> <span class="n">dc</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">connectedlist</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># So we generate a JunOS pyEZ device connection using the information about the </span>
</span></span><span class="line"><span class="cl">    <span class="c1"># firewall that we gather from the database object</span>
</span></span><span class="line"><span class="cl">    <span class="n">device</span> <span class="o">=</span> <span class="n">Device</span><span class="p">(</span><span class="n">fw</span><span class="o">.</span><span class="n">firewall_manageip</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">user</span><span class="o">=</span><span class="n">fw</span><span class="o">.</span><span class="n">firewall_user</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">password</span><span class="o">=</span><span class="n">fw</span><span class="o">.</span><span class="n">firewall_pass</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Try to open a connection out to the target device</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">dev</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">except</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># If for some reason this doesn&#39;t work, just return UNREACHABLE - which</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># we&#39;ll assume means the device is down</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;UNREACHABLE&#34;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="c1"># Here is where we poll the SRX for a list of all IPSec Security Associations.</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># The equivalent of the &#39;show security ipsec sa&#39; command</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">etree</span><span class="o">.</span><span class="n">tostring</span><span class="p">(</span><span class="n">dev</span><span class="o">.</span><span class="n">rpc</span><span class="o">.</span><span class="n">get_security_associations_information</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># The SRX returns a response in XML, which we&#39;ll need to dig through</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Credit to the guys over at &lt;a href=&#34;http://packetpushers.net/parsing-junos-xml-python/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Packet Pushers&lt;/a&gt; for a great post explaining how to</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># parse these responses</span>
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">response</span><span class="p">)</span> <span class="k">as</span> <span class="n">a</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">xmldoc</span> <span class="o">=</span> <span class="n">etree</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">docroot</span> <span class="o">=</span> <span class="n">xmldoc</span><span class="o">.</span><span class="n">getroot</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">rootchildren</span> <span class="o">=</span> <span class="n">docroot</span><span class="o">.</span><span class="n">iter</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">child</span> <span class="ow">in</span> <span class="n">rootchildren</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># For each IPSec SA returned, we need to find the remote gateway IP, which </span>
</span></span><span class="line"><span class="cl">            <span class="c1"># we use to tie the connection back to the connected datacenter</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">child</span><span class="o">.</span><span class="n">tag</span> <span class="o">==</span> <span class="s2">&#34;sa-remote-gateway&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">connectedlist</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">child</span><span class="o">.</span><span class="n">text</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">   
</span></span><span class="line"><span class="cl">    <span class="c1"># Once we&#39;ve built our list, send it back!</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">connectedlist</span>
</span></span></code></pre></div><p>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 <em>handle</em> function, which will do all of the remaining work.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">handle</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">options</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Let&#39;s quickly create two lists - based on our firewall and datacenter models</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># This actually polls the database for anything that&#39;s been created</span>
</span></span><span class="line"><span class="cl">    <span class="n">dclist</span> <span class="o">=</span> <span class="n">Datacenter</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="s1">&#39;datacenter_code&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">fwlist</span> <span class="o">=</span> <span class="n">Firewall</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="s1">&#39;firewall_name&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="c1"># This will loop through each firewall, then call the getVPNStatus function</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">fw</span> <span class="ow">in</span> <span class="n">Firewall</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="s1">&#39;firewall_name&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">statuslist</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">getVPNStatus</span><span class="p">(</span><span class="n">fw</span><span class="p">,</span> <span class="n">dclist</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Once we have our response, we&#39;re going to check the returned list of connected</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># gateways against our list of datacenters from the database</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">dc</span> <span class="ow">in</span> <span class="n">dclist</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">remoteFW</span> <span class="ow">in</span> <span class="n">fwlist</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># If we find another datacenter in the connected gateway list, add </span>
</span></span><span class="line"><span class="cl">            <span class="c1"># that datacenter to the vpnstatus list as &#34;VPNUP&#34;, otherwise assume it&#39;s down</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">remoteFW</span><span class="o">.</span><span class="n">firewall_vpnip</span> <span class="ow">in</span> <span class="n">statuslist</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">vpnstatus</span><span class="p">[</span><span class="n">dc</span><span class="o">.</span><span class="n">datacenter_code</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&#34;VPNUP&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="k">break</span>
</span></span><span class="line"><span class="cl">            <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">vpnstatus</span><span class="p">[</span><span class="n">dc</span><span class="o">.</span><span class="n">datacenter_code</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&#34;VPNDOWN&#34;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="c1"># After all that is done - we just save the new vpnstatus list to the firewall_vpnstatus</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># field in the database</span>
</span></span><span class="line"><span class="cl">    <span class="n">fw</span><span class="o">.</span><span class="n">firewall_vpnstatus</span> <span class="o">=</span> <span class="n">vpnstatus</span>
</span></span><span class="line"><span class="cl">    <span class="n">fw</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="n">update_fields</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;firewall_vpnstatus&#34;</span><span class="p">])</span>
</span></span></code></pre></div><p>I&rsquo;ve said this before, but I think it&rsquo;s worth noting again - This probably isn&rsquo;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&rsquo;ve put together so far. In fact, I think it&rsquo;s worth stating that this will probably break in a fantastically horrific manner. This isn&rsquo;t a finished product, but pretty much just version 0.1 - the base functionality works, but it&rsquo;s in desperate need of refinement.</p>
<p>Alright - 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:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">*/10  * * * * * python /root/junos-dashboard/manage.py cronpoller
</span></span></code></pre></div><p>After letting the script poll my firewalls - I ended up with a dashboard that looks like this:</p>
<p><img alt="dashboard-complete" loading="lazy" src="/content/images/2021/01/dashboard-complete.png#center"></p>
<h2 id="future-improvements">Future improvements</h2>
<p>Of course, I still have a few things I would like to add or improve on. I&rsquo;ve been keeping a list of some ideas that I&rsquo;ve had, which I&rsquo;ll share here:</p>
<ul>
<li>Add a timestamp to each firewall that contains the last poll time (in case something gets missed or hasn&rsquo;t updated yet)</li>
<li>Possibly add ability to click a &lsquo;down&rsquo; VPN cell to force clear SA</li>
<li>Set up email alerts from the cronpoller to automatically notify me of a failure</li>
<li>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</li>
<li>Ability to click on any firewall name and get additional info - like uptime, JunOS version, CPU/Memory</li>
</ul>
<hr>
<p>I finally got around to getting myself a <a href="https://github.com/0x2142">GitHub</a> account, so I might put the final code up there once I&rsquo;m done. I also have a number of other JunOS scripts that are probably worth posting up there as well.</p>
<p>Well, 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!</p>
]]></content:encoded>
    </item>
    <item>
      <title>Building a VPN Dashboard using Django and JunOS pyEZ (Part 3 – Creating the Dashboard)</title>
      <link>https://0x2142.com/building-a-vpn-dashboard-using-django-and-junos-pyez-part-3-creating-the-dashboard/</link>
      <pubDate>Tue, 27 Jun 2017 08:00:19 +0000</pubDate>
      <guid>https://0x2142.com/building-a-vpn-dashboard-using-django-and-junos-pyez-part-3-creating-the-dashboard/</guid>
      <description>This post focuses on the web GUI side of the VPN dashboard - storing &amp;amp; displaying the information that gets collected</description>
      <content:encoded><![CDATA[<p>This is a multi-part series - If you just hit this page, please check out the prior posts first!</p>
<ul>
<li><a href="/building-a-vpn-dashboard-using-django-and-junos-pyez-part-1-initial-thoughts/">Part 1 - Initial Thoughts</a></li>
<li><a href="/building-a-vpn-dashboard-using-django-and-junos-pyez-part-2-learning-django/">Part 2 - Leaning Django</a></li>
</ul>
<hr>
<p>In the last post, we got Django working well enough to start serving up web requests. That&rsquo;s a great start - So now it&rsquo;s on to actually building the project. We&rsquo;re going to cover this in two parts: the front-end dashboard, and the back-end scripts. In this post, I&rsquo;m going to show you how I began putting everything together to assemble the front-end HTML dashboard.</p>
<p>As 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&rsquo;ll be spending our time today. There are two files in our app folder that will need a few edits:</p>
<p><strong>views.py</strong> - This is where we originally created our basic index page, which currently just spits out a message. So we&rsquo;ll need to add code in here to render our dashboard instead.</p>
<p><strong>models.py</strong> - In here we will define our objects and the characteristics about those objects that we want to store/retrieve in the sqlite database.</p>
<p>Let&rsquo;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:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">unicode_literals</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Datacenter</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">datacenter_code</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Firewall</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">firewall_name</span>
</span></span></code></pre></div><p>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&rsquo;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.</p>
<p>Okay, 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:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Datacenter</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">datacenter_code</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="s1">&#39;Datacenter Code&#39;</span><span class="p">,</span> <span class="n">max_length</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">datacenter_active</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">BooleanField</span><span class="p">(</span><span class="s1">&#39;Datacenter Active?&#39;</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span></code></pre></div><p>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&rsquo;ll also need to collect this firewall&rsquo;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&rsquo;ll also need to collect a username/password for the SRX API, as well as a valid management IP to reach the API.</p>
<p>Let&rsquo;s take a moment though - because I stopped here and realized that I&rsquo;ll need to collect user/password data in a form, then it&rsquo;s going to be stored in a sqlite database. Oh, and of course it will be unencrypted. I didn&rsquo;t really like that idea - so I did some research on how to encrypt one of the object attributes. Turns out, it&rsquo;s actually pretty easy since someone has already created a module to do exactly that.</p>
<p>So let&rsquo;s grab that module real quick:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">[root@0x2142-Centos vpn]# pip install django-encrypted-fields
</span></span></code></pre></div><p>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 <a href="https://github.com/defrex/django-encrypted-fields">django-encrypted-fields</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">[root@0x2142-Centos junos-dashboard]# mkdir fieldkeys
</span></span><span class="line"><span class="cl">[root@0x2142-Centos junos-dashboard]# keyczart create --location=fieldkeys --purpose=crypt
</span></span><span class="line"><span class="cl">[root@0x2142-Centos junos-dashboard]# keyczart addkey --location=fieldkeys --status=primary --size=256
</span></span></code></pre></div><p>Next, we just need to add this keyfile into our settings.py:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ENCRYPTED_FIELDS_KEYDIR = &#39;/root/junos-dashboard/fieldkeys&#39;
</span></span></code></pre></div><p>Alright - back to actually creating the attributes we need for our firewalls. Since we&rsquo;re using this new module, we&rsquo;ll need to add an additional import statement - otherwise, we are just adding the fields I covered earlier:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">encrypted_fields</span> <span class="kn">import</span> <span class="n">EncryptedCharField</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Firewall</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">firewall_name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="s1">&#39;Firewall Name&#39;</span><span class="p">,</span> <span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">firewall_location</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="s1">&#39;Datacenter&#39;</span><span class="p">,</span> <span class="n">on_delete</span><span class="o">=</span><span class="n">models</span><span class="o">.</span><span class="n">CASCADE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">firewall_active</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">BooleanField</span><span class="p">(</span><span class="s1">&#39;Firewall Active?&#39;</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">firewall_manageip</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">GenericIPAddressField</span><span class="p">(</span><span class="s1">&#39;Management IP&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">firewall_vpnip</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">GenericIPAddressField</span><span class="p">(</span><span class="s1">&#39;VPN Interface IP&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">firewall_user</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="s1">&#39;API User&#39;</span><span class="p">,</span> <span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">,</span> <span class="n">blank</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">firewall_pass</span> <span class="o">=</span> <span class="n">EncryptedCharField</span><span class="p">(</span><span class="s1">&#39;API Pass&#39;</span><span class="p">,</span> <span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">,</span> <span class="n">blank</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">firewall_vpnstatus</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">TextField</span><span class="p">(</span><span class="n">editable</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">blank</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span></code></pre></div><p>A few things to note here - The firewall_location attribute will actually query the table of datacenter objects - so we&rsquo;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&rsquo;m a terrible programmer, I&rsquo;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&rsquo;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.</p>
<p>I also found out later that just because I encrypted the field doesn&rsquo;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&rsquo;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.</p>
<p>So here is what I threw into the forms.py file to handle password input:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">django.forms</span> <span class="kn">import</span> <span class="n">ModelForm</span><span class="p">,</span> <span class="n">PasswordInput</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">Firewall</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">FirewallForm</span><span class="p">(</span><span class="n">ModelForm</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">firewall_pass</span> <span class="o">=</span> <span class="n">CharField</span><span class="p">(</span><span class="n">widget</span><span class="o">=</span><span class="n">forms</span><span class="o">.</span><span class="n">PasswordInput</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">model</span> <span class="o">=</span> <span class="n">Firewall</span>
</span></span><span class="line"><span class="cl">        <span class="n">fields</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;firewall_pass&#39;</span><span class="p">]</span>
</span></span></code></pre></div><p>That was actually way easier than I had anticipated.</p>
<p>Awesome, now our work on the models is completely done - why don&rsquo;t we log into the administrative  web interface and take a look at how it all turned out?</p>
<p>Here is a view of the datacenter page - where we can add a new location:</p>
<p><img alt="image" loading="lazy" src="/content/images/2021/01/djangoadmin-datacenters.png#center"></p>
<p>And now for the firewall objects - with all the fields we need to get the job done:</p>
<p><img alt="image" loading="lazy" src="/content/images/2021/01/djangoadmin-firewalls.png#center"></p>
<p>Looks pretty good, huh? And we didn&rsquo;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.</p>
<hr>
<p>Alright - now let&rsquo;s start work on our views.py file, so we can actually start putting all this together in our dashboard!</p>
<p>Within our views.py file, we already had our index function, which just returned a string of text. We&rsquo;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&rsquo;s status, and build our little HTML table. I&rsquo;m not going to go into great detail on this - but check out the comments for more information on what&rsquo;s going on here:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># This is our function to build the HTML table - We&#39;re going to be expecting this function</span>
</span></span><span class="line"><span class="cl"><span class="c1"># to be fed a list of firewalls and datacenters</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">buildrows</span><span class="p">(</span><span class="n">firewall_list</span><span class="p">,</span> <span class="n">datacenter_list</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">firewallstatus</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># We will iterate through each firewall and compile each row for our table</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">fw</span> <span class="ow">in</span> <span class="n">firewall_list</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># The first cell in our row is going to be the firewall name and it&#39;s own VPN peer IP</span>
</span></span><span class="line"><span class="cl">        <span class="n">onerow</span> <span class="o">=</span> <span class="s2">&#34;&lt;td&gt;&lt;b&gt;</span><span class="si">%s</span><span class="s2">&lt;/b&gt;&lt;i&gt;</span><span class="si">%s</span><span class="s2">&lt;/i&gt;&lt;/td&gt;&#34;</span> <span class="o">%</span> <span class="p">(</span><span class="n">fw</span><span class="o">.</span><span class="n">firewall_name</span><span class="p">,</span> <span class="n">fw</span><span class="o">.</span><span class="n">firewall_vpnip</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Now we iterate through every datacenter, in order to see which ones we have a</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># connection to from this firewall</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">dc</span> <span class="ow">in</span> <span class="n">datacenter_list</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># First thing is first - If this firewall contains the name of the datacenter</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># then we&#39;ll print N/A, since we wouldn&#39;t expect a VPN to itself </span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="nb">str</span><span class="p">(</span><span class="n">dc</span><span class="o">.</span><span class="n">datacenter_code</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">str</span><span class="p">(</span><span class="n">fw</span><span class="o">.</span><span class="n">firewall_name</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">                <span class="n">onerow</span> <span class="o">+=</span> <span class="p">(</span><span class="s2">&#34;&lt;td&gt;N/A&lt;/td&gt;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">continue</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># Also, if there is no VPN status in the database, then just print &#39;No Status&#39;</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="ow">not</span> <span class="n">fw</span><span class="o">.</span><span class="n">firewall_vpnstatus</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">onerow</span> <span class="o">+=</span> <span class="p">(</span><span class="s2">&#34;&lt;td&gt;No Status&lt;/td&gt;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">continue</span>
</span></span><span class="line"><span class="cl">     
</span></span><span class="line"><span class="cl">            <span class="c1"># This portion is pretty self-explanatory - We parse the vpnstatus field in the</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># database. If the state of the connection is &#39;VPNUP&#39;, then we print a cell for</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># that datacenter with the text &#39;UP&#39;. If the state is &#39;VPNDOWN&#39;, then we print</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># &#39;DOWN&#39;. And if nothing matches, print &#39;No Status&#39;</span>
</span></span><span class="line"><span class="cl">            <span class="n">statuslist</span> <span class="o">=</span> <span class="n">ast</span><span class="o">.</span><span class="n">literal_eval</span><span class="p">(</span><span class="n">fw</span><span class="o">.</span><span class="n">firewall_vpnstatus</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">status</span> <span class="o">=</span> <span class="n">statuslist</span><span class="p">[</span><span class="n">dc</span><span class="o">.</span><span class="n">datacenter_code</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="n">status</span> <span class="o">==</span> <span class="s2">&#34;VPNUP&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                    <span class="n">onerow</span> <span class="o">+=</span> <span class="p">(</span><span class="s2">&#34;&lt;td class=</span><span class="se">\&#34;</span><span class="s2">vpnup</span><span class="se">\&#34;</span><span class="s2">&gt;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="n">onerow</span> <span class="o">+=</span> <span class="p">(</span><span class="s2">&#34;UP&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">elif</span> <span class="n">status</span> <span class="o">==</span> <span class="s2">&#34;VPNDOWN&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                    <span class="n">onerow</span> <span class="o">+=</span> <span class="p">(</span><span class="s2">&#34;&lt;td class=</span><span class="se">\&#34;</span><span class="s2">vpndown</span><span class="se">\&#34;</span><span class="s2">&gt;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="n">onerow</span> <span class="o">+=</span> <span class="p">(</span><span class="s2">&#34;DOWN&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                    <span class="n">onerow</span> <span class="o">+=</span> <span class="p">(</span><span class="s2">&#34;&lt;td&gt;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="n">onerow</span> <span class="o">+=</span> <span class="p">(</span><span class="s2">&#34;No Status&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="n">onerow</span> <span class="o">+=</span> <span class="p">(</span><span class="s2">&#34;&lt;/td&gt;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">onerow</span> <span class="o">+=</span> <span class="p">(</span><span class="s2">&#34;&lt;td&gt;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">onerow</span> <span class="o">+=</span> <span class="p">(</span><span class="s2">&#34;No Status&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">onerow</span> <span class="o">+=</span> <span class="p">(</span><span class="s2">&#34;&lt;/td&gt;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Once complete, we append this row to the status list</span>
</span></span><span class="line"><span class="cl">        <span class="n">firewallstatus</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">onerow</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># All done! Return our list of statuses!</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">firewallstatus</span>
</span></span></code></pre></div><p>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:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span><span class="err">u&#39;US-1&#39;:</span> <span class="err">&#39;VPNUP&#39;,</span> <span class="err">u&#39;US-2&#39;:</span> <span class="err">&#39;VPNUP&#39;,</span> <span class="err">u&#39;EU-1&#39;:</span> <span class="err">&#39;VPNUP&#39;,</span> <span class="err">u&#39;EU-2&#39;:</span> <span class="err">&#39;VPNUP&#39;,</span> <span class="err">u&#39;CN-1&#39;:</span> <span class="err">&#39;VPNDOWN&#39;</span><span class="p">}</span>
</span></span></code></pre></div><p>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.</p>
<p>With that done, our index function within views.py needs a little work to start displaying our table. You&rsquo;ll also notice we&rsquo;re importing the models we created, as well as a template loader - which I&rsquo;ll get to in a moment. So here is what that function will look like when we&rsquo;re done:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">Firewall</span><span class="p">,</span> <span class="n">Datacenter</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">django.template</span> <span class="kn">import</span> <span class="n">loader</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">sys</span><span class="o">,</span> <span class="nn">ast</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">index</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">firewall_list</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="n">datacenter_list</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="n">firewall_status</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># This will grab each datacenter object (if they&#39;re marked active) from the </span>
</span></span><span class="line"><span class="cl">    <span class="c1"># database and add them to a list</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">each</span> <span class="ow">in</span> <span class="n">Datacenter</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="s1">&#39;datacenter_code&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">each</span><span class="o">.</span><span class="n">datacenter_active</span> <span class="o">==</span> <span class="kc">True</span><span class="p">:</span> <span class="n">datacenter_list</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">each</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Same thing here - grab our firewalls and append to a list</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">each</span> <span class="ow">in</span> <span class="n">Firewall</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="s1">&#39;firewall_name&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">each</span><span class="o">.</span><span class="n">firewall_active</span> <span class="o">==</span> <span class="kc">True</span><span class="p">:</span> <span class="n">firewall_list</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">each</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Now we pass those lists to the buildrows function to assemble our HTML table</span>
</span></span><span class="line"><span class="cl">    <span class="n">firewall_status</span> <span class="o">=</span> <span class="n">buildrows</span><span class="p">(</span><span class="n">firewall_list</span><span class="p">,</span> <span class="n">datacenter_list</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># We&#39;re also going to apply a template to make the page look fancy</span>
</span></span><span class="line"><span class="cl">    <span class="n">template</span> <span class="o">=</span> <span class="n">loader</span><span class="o">.</span><span class="n">get_template</span><span class="p">(</span><span class="s1">&#39;web/index.html&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># This is taking all of our lists, and passing them into our HTML template</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># so that it can build the page semi-automatically</span>
</span></span><span class="line"><span class="cl">    <span class="n">context</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;datacenter_list&#39;</span><span class="p">:</span><span class="n">datacenter_list</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;firewall_list&#39;</span><span class="p">:</span> <span class="n">firewall_list</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;firewall_status&#39;</span><span class="p">:</span><span class="n">firewall_status</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">HttpResponse</span><span class="p">(</span><span class="n">template</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="n">context</span><span class="p">,</span><span class="n">request</span><span class="p">))</span>
</span></span></code></pre></div><p>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.</p>
<p>So 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&rsquo;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.</p>
<p>Then, it&rsquo;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:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>SRX VPN Dashboard<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">align</span><span class="o">=</span><span class="s">&#34;center&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    {% load static %}
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- Load the CSS template here --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/css&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{% static &#39;style.css&#39; %}&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- Add a header to the table --&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;table-title&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">h3</span><span class="p">&gt;</span>SRX VPN Dashboard<span class="p">&lt;/</span><span class="nt">h3</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- Create our first row, which will be a list of each data center location --&gt;</span>
</span></span><span class="line"><span class="cl">    {% if firewall_list %}
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">table</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;container&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">tr</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">th</span><span class="p">&gt;&lt;/</span><span class="nt">th</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        {% for datacenter in datacenter_list %}
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">th</span><span class="p">&gt;</span>{{ datacenter.datacenter_code }}<span class="p">&lt;/</span><span class="nt">th</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        {% endfor %}
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">tr</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c">&lt;!-- Then we add a row for each firewall, followed by its status 
</span></span></span><span class="line"><span class="cl"><span class="c">             for each datacenter --&gt;</span>
</span></span><span class="line"><span class="cl">        {% for firewall in firewall_status %}
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">tr</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            {{firewall|safe}}
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">tr</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        {%endfor%}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">table</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="c">&lt;!-- If something goes wrong, we just print an error --&gt;</span>
</span></span><span class="line"><span class="cl">    {% else %}
</span></span><span class="line"><span class="cl">        No firewalls were found.
</span></span><span class="line"><span class="cl">    {% endif %}
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Guess what? The dashboard is complete! Now we can run our Django server using &lsquo;./manage.py runserver&rsquo; and take a look at what we have created.</p>
<p><img alt="image" loading="lazy" src="/content/images/2021/01/dashboard-nodata.png#center"></p>
<p>Pretty simplistic - but we&rsquo;re getting very close to what I wanted to accomplish. The dashboard loads and generates the table, but it doesn&rsquo;t have any data yet.</p>
<p>In the next post - We&rsquo;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!</p>
<hr>
<p>This is a multi-part series - Check out the other posts:</p>
<ul>
<li><a href="/building-a-vpn-dashboard-using-django-and-junos-pyez-part-1-initial-thoughts/">Part 1 - Initial Thoughts</a></li>
<li><a href="/building-a-vpn-dashboard-using-django-and-junos-pyez-part-2-learning-django/">Part 2 - Leaning Django</a></li>
<li><a href="/building-a-vpn-dashboard-using-django-and-junos-pyez-part-4-polling-the-srx/">Part 4 - Polling the SRX</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Building a VPN Dashboard using Django and JunOS pyEZ (Part 2 – Learning Django)</title>
      <link>https://0x2142.com/building-a-vpn-dashboard-using-django-and-junos-pyez-part-2-learning-django/</link>
      <pubDate>Tue, 13 Jun 2017 08:00:18 +0000</pubDate>
      <guid>https://0x2142.com/building-a-vpn-dashboard-using-django-and-junos-pyez-part-2-learning-django/</guid>
      <description>In order to build my Juniper SRX VPN dashboard, I opted to use Django. Let&amp;rsquo;s take a quick look at how I got started.</description>
      <content:encoded><![CDATA[<p>This is a multi-part series - If you just hit this page, please check out the prior post first!</p>
<ul>
<li><a href="/building-a-vpn-dashboard-using-django-and-junos-pyez-part-1-initial-thoughts/">Part 1 - Initial Thoughts</a></li>
</ul>
<hr>
<p>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.</p>
<p>The 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:</p>
<ul>
<li><a href="https://docs.djangoproject.com/en/1.11/intro/install/">Installing Django</a></li>
<li><a href="https://docs.djangoproject.com/en/1.11/intro/tutorial01/">Writing your first app</a> - Credit to this page for some parts of the examples below</li>
</ul>
<p>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.</p>
<p><strong>Note:</strong> Just as a pure warning - I&rsquo;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&rsquo;t mean I write perfect or even great code. (I learned Python from <a href="https://learnpythonthehardway.org/book/">Learn Python The Hard Way</a> - I highly recommend this site, as it was a great resource!)</p>
<p>Alright, with that out of the way - Let&rsquo;s get started!</p>
<h2 id="step-one---installing-django">Step One - Installing Django</h2>
<p>First thing is first, I learned Python on the 2.7.x chain, and I&rsquo;ve been terrible about forcing myself to switch to 3.x. So for the purposes of this tutorial, everything I&rsquo;ve written was intended for Python 2.7.5.
Installing the actual Django package is a super simple task (if you use <a href="https://pip.pypa.io/en/latest/installing/#installing-with-get-pip-py">pip</a>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">[root@0x2142-Centos ~]# pip install django
</span></span><span class="line"><span class="cl">Collecting django
</span></span><span class="line"><span class="cl"> Downloading Django-1.11.2-py2.py3-none-any.whl (6.9MB)
</span></span><span class="line"><span class="cl"> 100% |████████████████████████████████| 7.0MB 140kB/s
</span></span><span class="line"><span class="cl">Collecting pytz (from django)
</span></span><span class="line"><span class="cl"> Downloading pytz-2017.2-py2.py3-none-any.whl (484kB)
</span></span><span class="line"><span class="cl"> 100% |████████████████████████████████| 491kB 1.9MB/s
</span></span><span class="line"><span class="cl">Installing collected packages: pytz, django
</span></span><span class="line"><span class="cl">Successfully installed django-1.11.2 pytz-2017.2
</span></span></code></pre></div><h2 id="step-two---get-junos-pyez-libraries">Step Two - Get JunOS pyEZ libraries</h2>
<p>I already wrote about this earlier in <a href="/getting-started-with-junos-pyez">Getting Started with JunOS PyEZ</a>. If you don&rsquo;t already have the JunOS packages installed, check out that page for the instructions.</p>
<h2 id="step-three---create-the-project">Step Three - Create the Project</h2>
<p>Django is pretty wonderful from my brief experiences with it. They package a number of tools that make starting a new project very simple.</p>
<p>So to start off with our project, we&rsquo;re going to use the django-admin tool:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">[root@0x2142-Centos ~]# django-admin startproject junos-dashboard
</span></span></code></pre></div><p>This will create a base directory structure for us to begin our project. We&rsquo;ll dive into what these files and folders are as we touch them - but for now, you&rsquo;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:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ALLOWED_HOSTS = [&#39;10.2.32.90&#39;]
</span></span></code></pre></div><p>Once that&rsquo;s done, go to the root of your project (~/junos-dashboard/) and test out the web server:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">[root@0x2142-Centos junos-dashboard]# ./manage.py runserver 10.2.32.90:8000
</span></span></code></pre></div><p>Django includes it&rsquo;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.</p>
<p>Once that&rsquo;s done, make sure you&rsquo;re in the root of your project - and we will create our dashboard application:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">[root@0x2142-Centos junos-dashboard]# django-admin startapp vpn
</span></span></code></pre></div><p>This command just creates <em>another</em> directory structure within your project. I think this is helpful though, because then I don&rsquo;t have to worry about which files I need to create or how they should be laid out - Django just handles that for us.</p>
<h2 id="step-four---getting-our-web-server-to-return-our-app-page">Step Four - Getting our web server to return our app page</h2>
<p>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&rsquo;t exist by default and you will have to create it!</p>
<p>Within views.py, we&rsquo;re going to create our dashboard homepage with a quick test response:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># Import stuff</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">django.shortcuts</span> <span class="kn">import</span> <span class="n">render</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">django.http</span> <span class="kn">import</span> <span class="n">HttpResponse</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">django.template</span> <span class="kn">import</span> <span class="n">loader</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">sys</span><span class="o">,</span> <span class="nn">ast</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># This is our index page</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">index</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Return some plain text - just so we know it worked!</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">HttpResponse</span><span class="p">(</span><span class="s2">&#34;This page will eventually be a magical dashboard!&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>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&rsquo;ll see.</p>
<p>Next, 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:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># Import stuff</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">django.conf.urls</span> <span class="kn">import</span> <span class="n">url</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">.</span> <span class="kn">import</span> <span class="n">views</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#Redirect to index function in views.py</span>
</span></span><span class="line"><span class="cl"><span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl"> <span class="n">url</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;^$&#39;</span><span class="p">,</span> <span class="n">views</span><span class="o">.</span><span class="n">index</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">&#39;index&#39;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span>
</span></span></code></pre></div><p>Finally, we&rsquo;re going to grab the urls.py file in our <em>project</em> 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:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl"> <span class="n">url</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;^$&#39;</span><span class="p">,</span> <span class="n">include</span><span class="p">(</span><span class="s1">&#39;vpn.urls&#39;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl"> <span class="n">url</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;^admin/&#39;</span><span class="p">,</span> <span class="n">admin</span><span class="o">.</span><span class="n">site</span><span class="o">.</span><span class="n">urls</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span>
</span></span></code></pre></div><p>I just want to point out that in this case, we&rsquo;re catching traffic again with the ^$ regex. This is so that any traffic to the main page (in this case <a href="http://10.2.32.90:8000">http://10.2.32.90:8000</a>) is automatically sent to our vpn app. However, if you had multiple apps running, you might want to give each one it&rsquo;s own directory.</p>
<p>Go ahead and execute the runserver tool again, and validate that you&rsquo;re able to successfully hit the page and retrieve the test text we put in.</p>
<hr>
<p>Okay, I think we&rsquo;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&rsquo;ll actually start building out the dashboard interface!</p>
<hr>
<p>This is a multi-part series - Check out the other posts:</p>
<ul>
<li><a href="/building-a-vpn-dashboard-using-django-and-junos-pyez-part-1-initial-thoughts/">Part 1 - Initial Thoughts</a></li>
<li><a href="/building-a-vpn-dashboard-using-django-and-junos-pyez-part-3-creating-the-dashboard/">Part 3 - Creating the Dashboard</a></li>
<li><a href="/building-a-vpn-dashboard-using-django-and-junos-pyez-part-4-polling-the-srx/">Part 4 - Polling the SRX</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Building a VPN Dashboard using Django and JunOS pyEZ (Part 1 - Initial thoughts)</title>
      <link>https://0x2142.com/building-a-vpn-dashboard-using-django-and-junos-pyez-part-1-initial-thoughts/</link>
      <pubDate>Wed, 07 Jun 2017 08:32:04 +0000</pubDate>
      <guid>https://0x2142.com/building-a-vpn-dashboard-using-django-and-junos-pyez-part-1-initial-thoughts/</guid>
      <description>I decided to build a web GUI to monitor Juniper SRX VPN tunnels. Here&amp;rsquo;s what I was thinking&amp;hellip;</description>
      <content:encoded><![CDATA[<p>So maybe you&rsquo;re like me - you&rsquo;ve done a bit of everything in the past, but now you&rsquo;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&rsquo;m talking about some of my prior experiences in scripting in automation, paired with some pretty great network APIs.</p>
<p>One thing my current job has never had is a good way to view VPN tunnel status between our firewalls. Our Check Point firewalls don&rsquo;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&rsquo;ve had the opportunity to install over 20 new Juniper SRX firewalls - mostly made up of SRX 1500 and SRX 345 models. I&rsquo;ve written a bit before about the JunOS APIs and pyEZ (their Python library), but I&rsquo;m always really excited at a new use case for network automation.</p>
<p>So 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:</p>
<table>
  <thead>
      <tr>
          <th></th>
          <th><strong>Location 1</strong></th>
          <th><strong>Location 2</strong></th>
          <th><strong>Location 3</strong></th>
          <th><strong>Location 4</strong></th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Location 1</strong></td>
          <td>N/A</td>
          <td>UP</td>
          <td>UP</td>
          <td>UP</td>
      </tr>
      <tr>
          <td><strong>Location 2</strong></td>
          <td>UP</td>
          <td>N/A</td>
          <td>UP</td>
          <td>DOWN!</td>
      </tr>
      <tr>
          <td><strong>Location 3</strong></td>
          <td>UP</td>
          <td>UP</td>
          <td>N/A</td>
          <td>UP</td>
      </tr>
      <tr>
          <td><strong>Location 4</strong></td>
          <td>UP</td>
          <td>DOWN!</td>
          <td>UP</td>
          <td>N/A</td>
      </tr>
  </tbody>
</table>
<p>I wasn&rsquo;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&rsquo;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.</p>
<p>I ended up settling on trying out <a href="https://www.djangoproject.com">Django</a> to write the frontend web stuff. I had never used it before, but I&rsquo;ve been interested in trying - and what&rsquo;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&rsquo;t have to go update the script directly - anyone on my team could log into the admin page and make the changes.</p>
<p>In this post I just wanted to get through what my ideas were behind this project. In the next few weeks, I&rsquo;ll begin explaining how I built the dashboard using Django and use pyEZ to scrape VPN status from each firewall.</p>
<p>Thoughts? Drop a comment below!</p>
<hr>
<p>This is a multi-part series - Check out the other posts:</p>
<ul>
<li><a href="/building-a-vpn-dashboard-using-django-and-junos-pyez-part-2-learning-django/">Part 2 - Leaning Django</a></li>
<li><a href="/building-a-vpn-dashboard-using-django-and-junos-pyez-part-3-creating-the-dashboard/">Part 3 - Creating the Dashboard</a></li>
<li><a href="/building-a-vpn-dashboard-using-django-and-junos-pyez-part-4-polling-the-srx/">Part 4 - Polling the SRX</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Migrating IP Addressing Schemes</title>
      <link>https://0x2142.com/migrating-ip-addressing-schemes/</link>
      <pubDate>Wed, 24 May 2017 08:00:27 +0000</pubDate>
      <guid>https://0x2142.com/migrating-ip-addressing-schemes/</guid>
      <description>Some thoughts on IP addressing migration in a live network, since I&amp;rsquo;ve been working on this a lot lately</description>
      <content:encoded><![CDATA[<p>Back a few months ago, I wrote a bit about why it is important to have a good design for IP addressing schemes (<a href="/ip-address-design-part-1/">part 1</a> and <a href="/ip-address-design-part-2/">part 2</a>). 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).</p>
<p>The 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&rsquo;s take a look at some of the methods we employed for migrating from one IP addressing scheme to another.</p>
<ul>
<li>
<p><strong>Have a plan</strong> - The first step is to have a good handle on the overall situation and how to get from point A to point B. You&rsquo;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.</p>
</li>
<li>
<p><strong>Enforce the standard for anything new</strong> - 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&rsquo;t want to perpetuate the scheme we are trying to get rid of.</p>
</li>
<li>
<p><strong>Transition (Network Config)</strong> - 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&rsquo;ve used - either build out a new segmented (VLANed) network, or overlay the existing using secondary IP addresses. Don&rsquo;t forget to propagate routes to the new subnets and ensure that firewall rules match the existing functionality.</p>
</li>
<li>
<p><strong>Transition (Infrastructure/Servers)</strong> - 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&rsquo;t the ideal way to do this - but it&rsquo;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.</p>
</li>
<li>
<p><strong>Long-Term</strong> - 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&rsquo;re not confident in what&rsquo;s going on.</p>
</li>
</ul>
<p>I also wanted to stress the importance of research throughout this whole process. It&rsquo;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&rsquo;s also important to check the technologies you&rsquo;re using to understand how everything will work. For example, Juniper&rsquo;s SSG (ScreenOS) platform doesn&rsquo;t support utilizing a secondary IP address on an &lsquo;untrust&rsquo; interface (<a href="https://kb.juniper.net/InfoCenter/index?page=content&amp;id=KB5527&amp;actp=METADATA">KB5527</a>) - but it works if you use a custom zone name. And Check Point doesn&rsquo;t support secondary IP addresses at all when you are using their ClusterXL protocol (<a href="https://supportcenter.checkpoint.com/supportcenter/portal?eventSubmit_doGoviewsolutiondetails=&amp;solutionid=sk89980">SK89980</a>), 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.</p>
<p>This 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&rsquo;ll be working on this migration for quite a while.</p>
<p>Ever 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!</p>
]]></content:encoded>
    </item>
    <item>
      <title>Quick Tips for Better BGP</title>
      <link>https://0x2142.com/quick-tips-for-better-bgp/</link>
      <pubDate>Tue, 02 May 2017 10:26:42 +0000</pubDate>
      <guid>https://0x2142.com/quick-tips-for-better-bgp/</guid>
      <description>A lot of BGP setups I run into are bare-bones, but there are a few quick ways to improve your configurations</description>
      <content:encoded><![CDATA[<p>A while back I wrote some basic information on how to get started implementing <a href="/bgp-getting-started-with-multi-homed-internet">multi-homed internet using BGP</a>. 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.</p>
<h2 id="securing-your-bgp-peeringknow-who-youre-connecting-to">Securing your BGP peering (Know who you&rsquo;re connecting to)</h2>
<p>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 <strong>ONLY</strong> from your directly connected peer IP - no one else.</p>
<p>While you&rsquo;re at it, let&rsquo;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.</p>
<p>BGP by nature is unfortunately not the most secure protocol - but a few simple steps like this will help ensure you&rsquo;re only connecting out to authorized peers.</p>
<h2 id="route-filtering-dont-trust-anyone">Route filtering (Don&rsquo;t trust anyone)</h2>
<p>Usually when you&rsquo;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:</p>
<p><strong>Default only</strong> - 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&rsquo;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.</p>
<p><strong>Partial</strong> - If for any reason you want to weight routes to certain destinations differently, then we might request this. In this case, you&rsquo;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.</p>
<p><strong>Full</strong> - In 99% of typical business cases, this won&rsquo;t be required. This option means the upstream providers will be dumping the <em>entire</em> 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.</p>
<p>After we figure this out, the next step is to make sure we are filtering the routes we accept from the upstream provider. Wait - didn&rsquo;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&rsquo;re only expecting a route for 0.0.0.0/0, then filter your inbound route advertisements so you only accept that route.</p>
<p>Same 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&rsquo;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&rsquo;t want that, so make sure you configure filtering for all outbound route advertisements.</p>
<h2 id="minimumadvertisement-oh-no-we-have-to-re-address-everything">Minimum Advertisement (Oh no, we have to re-address everything)</h2>
<p>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&amp;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.</p>
<p>At any rate - should you find yourself in this scenario then you&rsquo;re going to have to face the inevitable: Renumbering into a new IP space. Any time you have to do this, it&rsquo;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&rsquo;ed into DMZ servers.</p>
<p>Here is the quick steps that I used to do a side-by-side migration without taking any significant downtime:</p>
<ul>
<li>Get the new subnet up and running - assign the interface addresses on your firewall and BGP up and running</li>
<li>Assign new IP addresses to all of your existing services</li>
<li>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)</li>
<li>Migrate DNS entries externally to point to the new IP space</li>
<li>Once traffic stops flowing to the old IP, remove the old NAT</li>
</ul>
<p>As a side note - if you procure redundant internet connections through the <em>same</em> 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.</p>
<hr>
<p>I 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&rsquo;m thinking about a dedicated post to BGP path manipulation - which is probably something you&rsquo;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.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Juniper SRX VPN Issues</title>
      <link>https://0x2142.com/post/2017/juniper-srx-vpn-issues/</link>
      <pubDate>Tue, 18 Apr 2017 08:00:02 +0000</pubDate>
      <guid>https://0x2142.com/post/2017/juniper-srx-vpn-issues/</guid>
      <description>After running IPSec VPNs on Juniper SRX for years, this post explores some of the common issues I&amp;rsquo;ve run into</description>
      <content:encoded><![CDATA[<p>Last year we began migrating from our old Juniper SSG firewalls to the new SRX line. After a few months, I&rsquo;ve honestly really started to enjoy working with them - so much that we&rsquo;ve decided to start standardizing our firewall platforms by ditching everything else. So far I&rsquo;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&rsquo;t support In-service-upgrades - but don&rsquo;t get me started on that one&hellip;)</p>
<p>We 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&rsquo;ve run into so far, and what I&rsquo;ve been able to do to fix them or work around them..</p>
<h2 id="issue-1---vpn-is-up-but-no-traffic-is-flowing-across-it">Issue #1 - VPN is up, but no traffic is flowing across it</h2>
<p>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 <code>set security ipsec vpn vpn_name bind-interface st0.x</code> command. I had a set of VPN tunnels between two locations that were not passing traffic, even though a <code>show security ipsec sa</code> showed the tunnels as established. For reference, here is what the config looked like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">root@SRX-SITE-A&gt; show configuration security ike
</span></span><span class="line"><span class="cl">respond-bad-spi 1;
</span></span><span class="line"><span class="cl">proposal ike-aes256 {
</span></span><span class="line"><span class="cl"> authentication-method pre-shared-keys;
</span></span><span class="line"><span class="cl"> dh-group group2;
</span></span><span class="line"><span class="cl"> authentication-algorithm sha-256;
</span></span><span class="line"><span class="cl"> encryption-algorithm aes-256-cbc;
</span></span><span class="line"><span class="cl"> lifetime-seconds 28800;
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">policy ikepolAES256 {
</span></span><span class="line"><span class="cl"> mode main;
</span></span><span class="line"><span class="cl"> proposals ike-aes256;
</span></span><span class="line"><span class="cl"> pre-shared-key ascii-text xxxxxxxxx; ## SECRET-DATA
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">gateway gateway-siteB {
</span></span><span class="line"><span class="cl"> ike-policy ikepolAES256;
</span></span><span class="line"><span class="cl"> address XXX.XXX.XXX.XXX;
</span></span><span class="line"><span class="cl"> no-nat-traversal;
</span></span><span class="line"><span class="cl"> external-interface reth0.0;
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">root@SRX-SITE-A&gt; show configuration security ipsec
</span></span><span class="line"><span class="cl">proposal ipsec-aes256 {
</span></span><span class="line"><span class="cl"> protocol esp;
</span></span><span class="line"><span class="cl"> authentication-algorithm hmac-sha1-96;
</span></span><span class="line"><span class="cl"> encryption-algorithm aes-256-cbc;
</span></span><span class="line"><span class="cl"> lifetime-seconds 28800;
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">policy ipsecpolAES256 {
</span></span><span class="line"><span class="cl"> perfect-forward-secrecy {
</span></span><span class="line"><span class="cl"> keys group2;
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> proposals ipsec-aes256;
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">vpn vpn-to-SITE-B {
</span></span><span class="line"><span class="cl"> bind-interface st0.1;
</span></span><span class="line"><span class="cl"> df-bit clear;
</span></span><span class="line"><span class="cl"> ike {
</span></span><span class="line"><span class="cl"> gateway gateway-siteB;
</span></span><span class="line"><span class="cl"> ipsec-policy ipsecpolAES256;
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl"> establish-tunnels immediately;
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">root@SRX-SITE-A&gt; show configuration interfaces st0
</span></span><span class="line"><span class="cl">unit 1 {
</span></span><span class="line"><span class="cl"> description vpn-to-SITE-B;
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>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 <code>family inet</code> configured. Even though I&rsquo;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&rsquo;s easy to miss.</p>
<h2 id="issue-2---vpn-drops-every-2-4-hours-and-doesnt-re-establish-for-another-2-4-hours-or-manual-sa-clearing">Issue #2 - VPN drops every 2-4 hours and doesn&rsquo;t re-establish for another 2-4 hours (or manual SA clearing)</h2>
<p>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.</p>
<p>The 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&rsquo;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.</p>
<p>In order to resolve this, I had to configure both Dead-Peer-Detection and Juniper&rsquo;s VPN monitoring on both sides of the connection - so that each SRX would more actively monitor the tunnel status. <a href="https://www.juniper.net/documentation/en_US/junos/topics/concept/ipsec-dead-peer-detection-understanding.html">Juniper&rsquo;s documentation</a> states that they enable DPD by default, but in an &lsquo;optimized&rsquo; 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:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">root@SRX-SITE-A# set security ike gateway gateway-SITE-B dead-peer-detection always-send
</span></span><span class="line"><span class="cl">root@SRX-SITE-A# set security ipsec vpn vpn-SITE-B vpn-monitor optimized
</span></span></code></pre></div><p>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&rsquo;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.</p>
<h2 id="issue-3---vpn-between-srx-and-checkpoint-duplicates-ipsec-sa-on-re-key-sometimes-causes-tunnel-to-stop-passing-traffic">Issue #3 - VPN between SRX and CheckPoint duplicates IPSec SA on re-key (sometimes causes tunnel to stop passing traffic)</h2>
<p>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&rsquo;t - and sometimes this led to uni-directional traffic flows across the VPN.</p>
<p>The 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&rsquo;t notice that anything was going on, and would keep sending traffic down the bad (expired) SPIs. A quick <code>clear security ipsec sa</code> and <code>clear security ike sa</code> would bring the tunnels back up.</p>
<p>I 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&rsquo;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.</p>
<p>Ultimately, the CheckPoint guy pointed to <a href="https://supportcenter.checkpoint.com/supportcenter/portal?eventSubmit_doGoviewsolutiondetails=&amp;solutionid=sk97746">SK97746</a>, 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&rsquo;s GUI DB editor tool and making the modifications listed in the support article linked above.</p>
<p>While the CheckPoint side seemed to be responsible, it&rsquo;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.</p>
<p>So 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&rsquo;ve run into similar issues, drop a comment below!</p>
]]></content:encoded>
    </item>
    <item>
      <title>College vs Certification - Which is better?</title>
      <link>https://0x2142.com/college-vs-certification-which-is-better/</link>
      <pubDate>Tue, 28 Mar 2017 08:00:58 +0000</pubDate>
      <guid>https://0x2142.com/college-vs-certification-which-is-better/</guid>
      <description>My career path is the reverse of most people I&amp;rsquo;ve met - certifications first, then college much later. What impact has this had on my experiences?</description>
      <content:encoded><![CDATA[<p>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&rsquo;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?</p>
<p>I talked about this briefly in my initial <a href="/first-a-bit-of-background/">background</a> <a href="/background-story-continued/">story</a> 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&rsquo;t have much else going for me - I didn&rsquo;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.</p>
<p>I 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&rsquo;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.</p>
<p>Over 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&rsquo;t directly benefit me. A lot of the material was much more focused toward beginners who haven&rsquo;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&rsquo;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&rsquo;t use the knowledge much.</p>
<p>Four years later and I&rsquo;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&rsquo;ve been working in networking nearly the same amount of time. I&rsquo;m already further in my career than I thought I would be at this point, and I&rsquo;m happy with my position and pay (the degree isn&rsquo;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&rsquo;t actually play much into a company&rsquo;s decision to hire me.</p>
<p>In the end I think that both certifications and college education are useful - they can both be great indications to an employer that you&rsquo;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.</p>
<p>I 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&rsquo;m still happy that I went through with it and completed the degree, but you won&rsquo;t see me throwing a big celebration - except that I&rsquo;m just super glad it&rsquo;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).</p>
<p>Any thoughts? Comment below with your experiences - I&rsquo;m interested to see if there are many people who have had similar experiences to me, or possibly even the complete opposite.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Why Have a Home Lab?</title>
      <link>https://0x2142.com/why-have-a-home-lab/</link>
      <pubDate>Tue, 21 Mar 2017 08:00:20 +0000</pubDate>
      <guid>https://0x2142.com/why-have-a-home-lab/</guid>
      <description>Ever wonder if running a lab at home is worth it? This post explores why I think it&amp;rsquo;s an important investment</description>
      <content:encoded><![CDATA[<p><sup><em>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.</em></sup></p>
<hr>
<p>If you really want to become great at something, you practice it a ton, right? Well networking and IT work exactly the same. You&rsquo;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.</p>
<p>So 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.</p>
<p>Another 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&rsquo;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.</p>
<p>So do I still have a home lab today? Oh yeah, you bet I do! It&rsquo;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 &lsquo;home lab&rsquo; has evolved into just part of my home networks.
So here is what I&rsquo;ve got running today:</p>
<ul>
<li>Cisco ASA 5505 (Probably soon to be replaced with a <a href="https://www.amazon.com/gp/product/B01ICEO2U4/ref=as_li_qf_asin_il_tl?ie=UTF8&amp;tag=0x2142-20&amp;creative=9325&amp;linkCode=as2&amp;creativeASIN=B01ICEO2U4&amp;linkId=35fbe8300af4e5d1e26e7a860782b3ca">Juniper SRX 300</a>)</li>
<li>Two Cisco 2960G-8TC-L switches</li>
<li><a href="https://www.amazon.com/gp/product/B015PR20GY/ref=as_li_qf_asin_il_tl?ie=UTF8&amp;tag=0x2142-20&amp;creative=9325&amp;linkCode=as2&amp;creativeASIN=B015PR20GY&amp;linkId=864af1f40df3f986b49741655d21e926">Ubiquiti UniFi</a> 802.11n wireless access point</li>
<li>Synology DS411 Network Attached Storage with 4x 3TB drives (Soon to be replaced, as it is over 5 years old(Update: Got myself a <a href="https://www.amazon.com/gp/product/B075N1Z9LT/ref=as_li_qf_asin_il_tl?ie=UTF8&amp;tag=0x2142-20&amp;creative=9325&amp;linkCode=as2&amp;creativeASIN=B075N1Z9LT&amp;linkId=c2891aca5bc28b1ebf25847b6e687135">DS918+</a>!))</li>
<li>A few spare PCs running VMware ESX 6.0</li>
</ul>
<p>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.</p>
<p>On 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&rsquo;s not all work here 🙂</p>
<p>I 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&rsquo;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.</p>
<p>So 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&rsquo;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.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Getting Started with JunOS PyEZ</title>
      <link>https://0x2142.com/getting-started-with-junos-pyez/</link>
      <pubDate>Tue, 24 Jan 2017 08:00:20 +0000</pubDate>
      <guid>https://0x2142.com/getting-started-with-junos-pyez/</guid>
      <description>Quickly get started using Juniper&amp;rsquo;s Python SDK &amp;amp; interact with JunOS devices</description>
      <content:encoded><![CDATA[<p><sup>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.</sup></p>
<hr>
<blockquote>
<p>This guide is written for CentOS 7. If you&rsquo;re running another distro, find your dependencies <a href="https://www.juniper.net/techpubs/en_US/junos-pyez1.0/topics/task/installation/junos-pyez-server-installing.html">here</a>.</p></blockquote>
<p>Last 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&rsquo;re pretty solid devices and I&rsquo;m extremely happy with them. One of the main reasons I like them so much is the ease of automation, which is what we&rsquo;re going to dive into today. If you need a device for lab/test - Amazon has the <a href="https://www.amazon.com/gp/product/B01ICEO2U4/ref=as_li_qf_asin_il_tl?ie=UTF8&amp;tag=0x2142-20&amp;creative=9325&amp;linkCode=as2&amp;creativeASIN=B01ICEO2U4&amp;linkId=35fbe8300af4e5d1e26e7a860782b3ca">SRX 300</a> for less than $300. I&rsquo;ll likely be picking one up in the near future for easier automation testing.</p>
<p>Juniper provides an awesome library for SRX management called PyEZ. Here is what we need to get the toolkit ready to use in our scripts:</p>
<h2 id="install-dependencies">Install dependencies</h2>
<p>I run CentOS here, so I just needed to grab the following packages:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">yum -y install python-devel libxml2-devel libxslt-devel gcc openssl openssl-devel libffi-devel
</span></span></code></pre></div><p>Just a quick note - Juniper&rsquo;s web page doesn&rsquo;t actually mention openssl-devel, but their tools will fail to install without it</p>
<h2 id="get-pip">Get pip</h2>
<p>If you don&rsquo;t have it already, then download the pip installer <a href="https://bootstrap.pypa.io/get-pip.py">here</a>. Then just run the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">python get-pip.py
</span></span></code></pre></div><h2 id="install-pyez">Install PyEZ</h2>
<p>Once everything else is set, this is the easy part:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">pip install junos-eznc
</span></span></code></pre></div><p>After all that is done, we can get to the exciting part: automating something so you don&rsquo;t have to do it anymore! So here is what we&rsquo;re going to throw in our script just to get started:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># Import the JunOS modules we need</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">jnpr.junos</span> <span class="kn">import</span> <span class="n">Device</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Define the login credentials and address for the target firewall</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Need to provide device address, user account, and password</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Note: Juniper also provides authentication via key-pair, which would be more secure than username/password</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">srx</span> <span class="o">=</span> <span class="n">Device</span><span class="p">(</span><span class="s1">&#39;10.10.10.10&#39;</span><span class="p">,</span> <span class="n">user</span><span class="o">=</span><span class="s1">&#39;deviceuser&#39;</span><span class="p">,</span> <span class="k">pass</span><span class="o">=</span><span class="s1">&#39;devicepass&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Open the connection</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">srx</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
</span></span></code></pre></div><p>Once you have that going, it&rsquo;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.</p>
<p>So in order to accomplish the SRX-side of that script, I used the following to reset the IKE and IPSec security associations:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># Clear IKE security association for a given peer</span>
</span></span><span class="line"><span class="cl"><span class="n">response</span> <span class="o">=</span> <span class="n">srx</span><span class="o">.</span><span class="n">rpc</span><span class="o">.</span><span class="n">clear_ike_security_association</span><span class="p">(</span><span class="n">peer_address</span><span class="o">=</span><span class="s1">&#39;20.20.20.20&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Check to see if command was accepted</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">response</span> <span class="o">==</span> <span class="kc">True</span><span class="p">:</span> <span class="nb">print</span> <span class="s2">&#34;IKE SA Cleared&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Clear IPSec security association for a given peer</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Note: In this case, you need to provide the index ID</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Get the index ID from running &#39;*show security ipsec sa&#39; *on the SRX</span>
</span></span><span class="line"><span class="cl"><span class="n">response</span> <span class="o">=</span> <span class="n">srx</span><span class="o">.</span><span class="n">rpc</span><span class="o">.</span><span class="n">clear_ipsec_security_association</span><span class="p">(</span><span class="n">index</span><span class="o">=</span><span class="s1">&#39;123456&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Same thing - Check to make sure the command succeeded</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">response</span> <span class="o">==</span> <span class="kc">True</span><span class="p">:</span> <span class="nb">print</span> <span class="s2">&#34;IPSec SA Cleared&#34;</span>
</span></span></code></pre></div><p>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 <code>display xml rpc</code> and the device will tell you exactly what you need. For example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl">root@SRX123&gt; show security ipsec sa | display xml rpc
</span></span><span class="line"><span class="cl"><span class="nt">&lt;rpc-reply</span> <span class="na">xmlns:junos=</span><span class="s">&#34;http://xml.juniper.net/junos/15.1X49/junos&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;rpc&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;get-security-associations-information&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/get-security-associations-information&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/rpc&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;cli&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;banner&gt;</span>{primary:node0}<span class="nt">&lt;/banner&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/cli&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/rpc-reply&gt;</span>
</span></span></code></pre></div><p>In this case, we are running the command which would normally print all of the connected IPSec tunnels. So the section under <em>rpc</em> 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&rsquo;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.</p>
<p>As a final note, I would <strong>highly</strong> 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&rsquo;s worth it in terms of security. Here is what I have configured on my SRX firewalls for the API service account:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">system</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="err">login</span> <span class="err">{</span>
</span></span><span class="line"><span class="cl">        <span class="err">class</span> <span class="err">api-class</span> <span class="err">{</span>
</span></span><span class="line"><span class="cl">            <span class="err">permissions</span> <span class="err">security-control;</span>
</span></span><span class="line"><span class="cl">            <span class="err">allow-commands</span> <span class="nt">&#34;(clear security ike)|(clear security ipsec)&#34;</span><span class="err">;</span>
</span></span><span class="line"><span class="cl">            <span class="err">deny-commands</span> <span class="s2">&#34;(clear)|(file)|(file show)|(help)|(load)|(monitor)|(op)|(request)|(save)|(set)|(start)|(test)&#34;</span><span class="err">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="err">user</span> <span class="err">apiuser</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="err">full-name</span> <span class="err">API_Service_Acct;</span>
</span></span><span class="line"><span class="cl">            <span class="err">uid</span> <span class="err">125;</span>
</span></span><span class="line"><span class="cl">            <span class="err">class</span> <span class="err">api-class;</span>
</span></span><span class="line"><span class="cl">            <span class="err">authentication</span> <span class="err">{</span>
</span></span><span class="line"><span class="cl">                <span class="err">encrypted-password</span> <span class="err">*&lt;encrypted</span> <span class="err">string</span> <span class="err">here&gt;*</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="err">}</span>
</span></span><span class="line"><span class="cl">    <span class="err">}</span>
</span></span><span class="line"><span class="cl"><span class="err">}</span>
</span></span></code></pre></div><p>So using the above example, you should only define the necessary commands in the <em>allow-commands</em> 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.</p>
<p>So what did you think of this tutorial? Helpful? Have questions? Let me know in the comments below!</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
