Scott Ware Writes About Automation and Dynamically Changing Firewall Policies
Automation is huge, and updating firewall policies that can dynamically change can be a huge task as well. This article, written by Scott Ware, describes how to leverage Infoblox's API to key on certain objects to create a firewall address group that can update dynamically, using code written in the Go programming language.
[Reprinted with permission]
I was having some discussions a while back regarding automation with some of my fellow Juniper Ambassadors, and one of the topics that came up was how cool it would be to have the ability for an SRX to dynamically update address groups based on, for example, changes to hosts/networks within IPAM (Infoblox).
If you haven't heard of Infoblox, it is an enterprise DNS/DHCP solution...and one of the best if not the best, IMO.
I have been hacking away at it lately, and I have a working solution that I'll show you. I'm using all standard/builtin Go libraries, and the go-junos library for my interaction with the SRX.
What does the script do?
Basically, it does the following:
- Query Infoblox based on the given search term, and return all host addresses that match.
- Currently this example only searches for hosts, but I'm going to work at searching for networks as well.
- You can also search fields other than the "comments" one that I am using in this example (e.g. "subnet_org").
- Once we have all the addresses, it creates a temp file to write to, generating the address-book/address-set configuration in set format.
- Connect to the given SRX, and commit the configuration
- Displays our changes from the previous configuration (compares to rollback 1).
When we query Infoblox, I am choosing to return the results in XML format (default is JSON). Here's a sample of what it looks like:
<SPAN class="hljs-tag"><<SPAN class="hljs-title">list</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">value</SPAN> <SPAN class="hljs-attribute">type</SPAN>=<SPAN class="hljs-value">"object"</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">_ref</SPAN>></SPAN>record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LmNvbS5tZWlqZXIuYmxnOTc1LncwOTc1ZXVzckwMTAw:sdubs-fw/Internal<SPAN class="hljs-tag"></<SPAN class="hljs-title">_ref</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">view</SPAN>></SPAN>Internal<SPAN class="hljs-tag"></<SPAN class="hljs-title">view</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">ipv4addrs</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">list</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">value</SPAN> <SPAN class="hljs-attribute">type</SPAN>=<SPAN class="hljs-value">"object"</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">configure_for_dhcp</SPAN> <SPAN class="hljs-attribute">type</SPAN>=<SPAN class="hljs-value">"boolean"</SPAN>></SPAN>false<SPAN class="hljs-tag"></<SPAN class="hljs-title">configure_for_dhcp</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">host</SPAN>></SPAN>sdubs-fw<SPAN class="hljs-tag"></<SPAN class="hljs-title">host</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">ipv4addr</SPAN>></SPAN>1.1.1.1<SPAN class="hljs-tag"></<SPAN class="hljs-title">ipv4addr</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">_ref</SPAN>></SPAN>record:host_ipv4addr/ZG5zLmhvc3RfYWRkcmVzcyQuX2RlZmF1bHQuY29tLm1laWplci5ibGc5NzUudzA5NzVldXNyaTAxMDAuMTAuMTcuMi4xOS4:1.1.1.1/sdubs-fw/Internal<SPAN class="hljs-tag"></<SPAN class="hljs-title">_ref</SPAN>></SPAN> <SPAN class="hljs-tag"></<SPAN class="hljs-title">value</SPAN>></SPAN> <SPAN class="hljs-tag"></<SPAN class="hljs-title">list</SPAN>></SPAN> <SPAN class="hljs-tag"></<SPAN class="hljs-title">ipv4addrs</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">name</SPAN>></SPAN>sdubs-fw<SPAN class="hljs-tag"></<SPAN class="hljs-title">name</SPAN>></SPAN> <SPAN class="hljs-tag"></<SPAN class="hljs-title">value</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">value</SPAN> <SPAN class="hljs-attribute">type</SPAN>=<SPAN class="hljs-value">"object"</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">_ref</SPAN>></SPAN>record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LmNvbS5tZWlqZXIuYmxnOTc1LncwOTc1ZXVzckwMTAw:sdubs-ap/Internal<SPAN class="hljs-tag"></<SPAN class="hljs-title">_ref</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">view</SPAN>></SPAN>Internal<SPAN class="hljs-tag"></<SPAN class="hljs-title">view</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">ipv4addrs</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">list</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">value</SPAN> <SPAN class="hljs-attribute">type</SPAN>=<SPAN class="hljs-value">"object"</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">configure_for_dhcp</SPAN> <SPAN class="hljs-attribute">type</SPAN>=<SPAN class="hljs-value">"boolean"</SPAN>></SPAN>false<SPAN class="hljs-tag"></<SPAN class="hljs-title">configure_for_dhcp</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">host</SPAN>></SPAN>sdubs-ap<SPAN class="hljs-tag"></<SPAN class="hljs-title">host</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">ipv4addr</SPAN>></SPAN>1.1.1.2<SPAN class="hljs-tag"></<SPAN class="hljs-title">ipv4addr</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">_ref</SPAN>></SPAN>record:host_ipv4addr/ZG5zLmhvc3RfYWRkcmVzcyQuX2RlZmF1bHQuY29tLm1laWplci5ibGc5NzUudzA5NzVldXNyaTAxMDAuMTAuMTcuMi4xOS4:1.1.1.1/sdubs-ap/Internal<SPAN class="hljs-tag"></<SPAN class="hljs-title">_ref</SPAN>></SPAN> <SPAN class="hljs-tag"></<SPAN class="hljs-title">value</SPAN>></SPAN> <SPAN class="hljs-tag"></<SPAN class="hljs-title">list</SPAN>></SPAN> <SPAN class="hljs-tag"></<SPAN class="hljs-title">ipv4addrs</SPAN>></SPAN> <SPAN class="hljs-tag"><<SPAN class="hljs-title">name</SPAN>></SPAN>sdubs-ap<SPAN class="hljs-tag"></<SPAN class="hljs-title">name</SPAN>></SPAN> <SPAN class="hljs-tag"></<SPAN class="hljs-title">value</SPAN>></SPAN> <SPAN class="hljs-tag"></<SPAN class="hljs-title">list</SPAN>></SPAN>
And after we commit our configuration to the SRX, here's what the config diff looks like:
[edit security address-book global] address ware-lan { ... } <SPAN class="hljs-addition">+ address sdubs-fw 1.1.1.1/32;</SPAN> <SPAN class="hljs-addition">+ address sdubs-ap 1.1.1.2/32;</SPAN> [edit security address-book global] <SPAN class="hljs-addition">+ address-set Dynamic-IPAM-Hosts {</SPAN> <SPAN class="hljs-addition">+ address sdubs-fw;</SPAN> <SPAN class="hljs-addition">+ address sdubs-ap;</SPAN> <SPAN class="hljs-addition">+ }</SPAN>
And with that...here's the full code for the script. Also available as a Github Gist.
For this example, I made a binary and ran it, but you can just do a
go run <script>
if you'd like.
<SPAN class="hljs-function">package main <SPAN class="hljs-title">import</SPAN> <SPAN class="hljs-params">( <SPAN class="hljs-string">"crypto/tls"</SPAN> <SPAN class="hljs-string">"encoding/xml"</SPAN> <SPAN class="hljs-string">"flag"</SPAN> <SPAN class="hljs-string">"fmt"</SPAN> <SPAN class="hljs-string">"github.com/scottdware/go-junos"</SPAN> <SPAN class="hljs-string">"io/ioutil"</SPAN> <SPAN class="hljs-string">"net/http"</SPAN> <SPAN class="hljs-string">"os"</SPAN> <SPAN class="hljs-string">"path/filepath"</SPAN> <SPAN class="hljs-string">"time"</SPAN> )</SPAN> <SPAN class="hljs-comment">// dynamicHosts parses the overall XML returned from the Infoblox query.</SPAN> type dynamicHosts <SPAN class="hljs-keyword">struct</SPAN> </SPAN>{ XMLName xml.Name `xml:<SPAN class="hljs-string">"list"</SPAN>` Hosts []hostEntry `xml:<SPAN class="hljs-string">"value>ipv4addrs>list>value"</SPAN>` } <SPAN class="hljs-comment">// hostEntry parses the XML for each individual host.</SPAN> type hostEntry <SPAN class="hljs-keyword">struct</SPAN> { Name <SPAN class="hljs-keyword">string</SPAN> `xml:<SPAN class="hljs-string">"host"</SPAN>` Address <SPAN class="hljs-keyword">string</SPAN> `xml:<SPAN class="hljs-string">"ipv4addr"</SPAN>` } <SPAN class="hljs-keyword">var</SPAN> ( searchString = flag.String(<SPAN class="hljs-string">"search"</SPAN>, <SPAN class="hljs-string">""</SPAN>, <SPAN class="hljs-string">"Search string"</SPAN>) ipamGM = flag.String(<SPAN class="hljs-string">"gm"</SPAN>, <SPAN class="hljs-string">""</SPAN>, <SPAN class="hljs-string">"IPAM/Infoblox Grid master (hostname or IP)"</SPAN>) ipamUser = flag.String(<SPAN class="hljs-string">"ibuser"</SPAN>, <SPAN class="hljs-string">""</SPAN>, <SPAN class="hljs-string">"IPAM/Infoblox username"</SPAN>) ipamPass = flag.String(<SPAN class="hljs-string">"ibpassword"</SPAN>, <SPAN class="hljs-string">""</SPAN>, <SPAN class="hljs-string">"IPAM/Infoblox password"</SPAN>) srxHost = flag.String(<SPAN class="hljs-string">"srx"</SPAN>, <SPAN class="hljs-string">""</SPAN>, <SPAN class="hljs-string">"SRX to configure"</SPAN>) srxUser = flag.String(<SPAN class="hljs-string">"user"</SPAN>, <SPAN class="hljs-string">""</SPAN>, <SPAN class="hljs-string">"SRX username"</SPAN>) srxPass = flag.String(<SPAN class="hljs-string">"password"</SPAN>, <SPAN class="hljs-string">""</SPAN>, <SPAN class="hljs-string">"SRX password"</SPAN>) groupName = flag.String(<SPAN class="hljs-string">"group"</SPAN>, <SPAN class="hljs-string">"Dynamic-IPAM"</SPAN>, <SPAN class="hljs-string">"Name of the group/address-set to create"</SPAN>) ) <SPAN class="hljs-function">func <SPAN class="hljs-title">init</SPAN><SPAN class="hljs-params">()</SPAN> </SPAN>{ flag.Usage = func() { fmt.Println(<SPAN class="hljs-string">"srx-ipam version 0.1\n"</SPAN>) fmt.Printf(<SPAN class="hljs-string">"Usage: %s <options>\n\n"</SPAN>, filepath.Base(os.Args[<SPAN class="hljs-number">0</SPAN>])) flag.PrintDefaults() os.Exit(<SPAN class="hljs-number">0</SPAN>) } } <SPAN class="hljs-comment">// queryIPAM searches throughout Infoblox for the given search string and returns</SPAN> <SPAN class="hljs-comment">// addresses that we will build our address-set for.</SPAN> <SPAN class="hljs-function">func <SPAN class="hljs-title">queryIPAM</SPAN><SPAN class="hljs-params">()</SPAN> <SPAN class="hljs-params">(*dynamicHosts, error)</SPAN> </SPAN>{ <SPAN class="hljs-keyword">var</SPAN> data dynamicHosts reqURL := fmt.Sprintf(<SPAN class="hljs-string">"https://%s/wapi/v1.0/record:host?comment~:=%s"</SPAN>, *ipamGM, *searchString) req, err := http.NewRequest(<SPAN class="hljs-string">"GET"</SPAN>, reqURL, nil) <SPAN class="hljs-keyword">if</SPAN> err != nil { <SPAN class="hljs-keyword">return</SPAN> nil, err } req.SetBasicAuth(*ipamUser, *ipamPass) req.Header.Set(<SPAN class="hljs-string">"Accept"</SPAN>, <SPAN class="hljs-string">"application/xml"</SPAN>) tr := &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: <SPAN class="hljs-keyword">true</SPAN>, }, } client := &http.Client{Transport: tr} res, err := client.Do(req) <SPAN class="hljs-keyword">if</SPAN> err != nil { <SPAN class="hljs-keyword">return</SPAN> nil, err } defer res.Body.Close() body, _ := ioutil.ReadAll(res.Body) err = xml.Unmarshal([]<SPAN class="hljs-keyword">byte</SPAN>(body), &data) <SPAN class="hljs-keyword">if</SPAN> err != nil { <SPAN class="hljs-keyword">return</SPAN> nil, err } <SPAN class="hljs-keyword">return</SPAN> &data, nil } <SPAN class="hljs-function">func <SPAN class="hljs-title">main</SPAN><SPAN class="hljs-params">()</SPAN> </SPAN>{ flag.Parse() <SPAN class="hljs-comment">// Run our query against IPAM/Infoblox to get our addresses.</SPAN> d, err := queryIPAM() <SPAN class="hljs-keyword">if</SPAN> err != nil { fmt.Println(err) } <SPAN class="hljs-comment">// Open a temp file to write our config to.</SPAN> tmp, err := ioutil.TempFile(<SPAN class="hljs-string">"."</SPAN>, <SPAN class="hljs-string">"srxconfig-"</SPAN>) <SPAN class="hljs-keyword">if</SPAN> err != nil { fmt.Println(err) } <SPAN class="hljs-comment">// Create our address entries first.</SPAN> <SPAN class="hljs-keyword">for</SPAN> _, ae := range d.Hosts { addressEntry := fmt.Sprintf(<SPAN class="hljs-string">"set security address-book global address %s %s/32\n"</SPAN>, ae.Name, ae.Address) _, err := tmp.Write([]<SPAN class="hljs-keyword">byte</SPAN>(addressEntry)) <SPAN class="hljs-keyword">if</SPAN> err != nil { fmt.Println(err) } } <SPAN class="hljs-comment">// Create the address-set/group and assign the addresses to it.</SPAN> <SPAN class="hljs-keyword">for</SPAN> _, <SPAN class="hljs-keyword">as</SPAN> := range d.Hosts { addressSetEntry := fmt.Sprintf(<SPAN class="hljs-string">"set security address-book global address-set %s address %s\n"</SPAN>, *groupName, <SPAN class="hljs-keyword">as</SPAN>.Name) _, err := tmp.Write([]<SPAN class="hljs-keyword">byte</SPAN>(addressSetEntry)) <SPAN class="hljs-keyword">if</SPAN> err != nil { fmt.Println(err) } } <SPAN class="hljs-comment">// Close our temp file as we are finished writing our config to it.</SPAN> err = tmp.Close() <SPAN class="hljs-keyword">if</SPAN> err != nil { fmt.Printf(<SPAN class="hljs-string">"Could not close the temp file: %s"</SPAN>, err) } <SPAN class="hljs-comment">// Connect to our SRX.</SPAN> jnpr, err := junos.NewSession(*srxHost, *srxUser, *srxPass) <SPAN class="hljs-keyword">if</SPAN> err != nil { fmt.Println(err) } <SPAN class="hljs-comment">// Load our configuration from the temp file we wrote to earlier.</SPAN> err = jnpr.LoadConfig(tmp.Name(), <SPAN class="hljs-string">"set"</SPAN>, <SPAN class="hljs-keyword">false</SPAN>) <SPAN class="hljs-keyword">if</SPAN> err != nil { fmt.Println(err) } <SPAN class="hljs-comment">// Rollback the commit after 1 minute for testing purposes.</SPAN> jnpr.CommitConfirm(<SPAN class="hljs-number">1</SPAN>) <SPAN class="hljs-comment">// Print the changes out to the console.</SPAN> changes, _ := jnpr.ConfigDiff(<SPAN class="hljs-number">1</SPAN>) fmt.Println(changes) <SPAN class="hljs-comment">// Remove our temp file.</SPAN> time.Sleep(<SPAN class="hljs-number">2</SPAN> * time.Second) err = os.Remove(tmp.Name()) <SPAN class="hljs-keyword">if</SPAN> err != nil { fmt.Printf(<SPAN class="hljs-string">"Could not remove temp file: %s"</SPAN>, err) } }
Display the usage/help by using the -h
or -help
flag:
srx-ipam version <SPAN class="hljs-number">0.1</SPAN> Usage: srx-ipam <options> -gm=<SPAN class="hljs-string">""</SPAN>: IPAM/<SPAN class="hljs-function">Infoblox Grid <SPAN class="hljs-title">master</SPAN> <SPAN class="hljs-params">(hostname or IP)</SPAN> -<SPAN class="hljs-keyword">group</SPAN></SPAN>=<SPAN class="hljs-string">"Dynamic-IPAM"</SPAN>: Name of the <SPAN class="hljs-keyword">group</SPAN>/address-<SPAN class="hljs-keyword">set</SPAN> to create -ibpassword=<SPAN class="hljs-string">""</SPAN>: IPAM/Infoblox password -ibuser=<SPAN class="hljs-string">""</SPAN>: IPAM/Infoblox username -password=<SPAN class="hljs-string">""</SPAN>: SRX password -search=<SPAN class="hljs-string">""</SPAN>: Search <SPAN class="hljs-keyword">string</SPAN> -srx=<SPAN class="hljs-string">""</SPAN>: SRX to configure -user=<SPAN class="hljs-string">""</SPAN>: SRX username
How to run the script:
srx-ipam -gm infoblox.company.com -ibuser admin -ibpassword secret -srx sdubs-fw -user admin -password juniper123 -<SPAN class="hljs-keyword">group</SPAN> Dynamic-IPAM-Hosts -search ware
This script is run manually now, but you could easily create a scheduled job, or maybe with some more coding, check for changes every so often and then run a script like this to publish the updates hosts/networks to your SRX.
[reprinted with permission - see the original post here ]
Categories
- All Categories
- 5.1K Forums
- 4.6K Critical Network Services
- 463 Security
- Visibility and Insights
- Ideas Portal
- Webinars & Events
- 266 Resources
- 266 News & Announcements
- Knowledge Base Articles
- Infoblox Documentation Portal
- Infoblox Blog
- Support Portal
- 4 Members Hub
- 4 Getting Started with Community
- Community Support