├── srx-list
├── Feed
├── schema.xml
├── manifest.xml
├── README.md
└── dynamic-policy.py
/srx-list:
--------------------------------------------------------------------------------
1 | 192.168.0.2
2 |
--------------------------------------------------------------------------------
/Feed:
--------------------------------------------------------------------------------
1 | {"filter":{},"version":"1428028377.4-20","schema_version":"398490f187","previous_version":null}
2 | #del
3 | #add
4 | #end
5 | e9e635c8001b078ef30e9cd992401e32
6 |
--------------------------------------------------------------------------------
/schema.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /schema.xml
5 |
6 |
7 |
8 | /
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # dynamic-policy - Dynamic creation of SRX address entries
2 |
3 | The SRX firewall can be configured to monitor a central web server for a list of IP Addresses to create a dynamic address book using the```security-intelligence```feature-set.
4 |
5 | This software will need to be installed on an existing web-server and the python file modified to point to the correct base directory of the web-server. Clone all files and copy the schema.xml and manifest.xml file to the base of the web server.
6 |
7 | Configure the SRX security-intelligence module to point to you web server - the auth-token is required to commit (32 ascii chars), but is only used by Junos Space to authenticate the device:
8 |
9 | ```
10 | set services security-intelligence url http://192.168.1.1/manifest.xml
11 | set services security-intelligence authentication auth-token 12345678901234567890121234567890
12 | ```
13 | The```manifest.xml```file provides a list of available "feeds" of IP addresses eg:
14 |
15 | ```
16 |
17 |
18 |
19 | /schema.xml
20 |
21 |
22 |
23 | /
24 |
25 |
26 |
27 |
28 | ```
29 |
30 | The SRX will refresh```manifest.xml``` every```update_interval```seconds, as specified in the manifest.
31 |
32 | A feed is essentially a list of IPs that have some common purpose eg: a blacklist, whitelist, prefixes that identify a cloud provider etc.
33 |
34 | Addresses are grouped into feeds and stored in independent feed files.
35 |
36 | Within the SRX, a dynamic-address is created as follows:
37 |
38 | ```set security dynamic-address address-name MY-BLACKLIST profile category IPFilter feed BADSITES```
39 |
40 | The SRX then consults the```manifest.xml```file for a feed named BADSITES, downloads it from the web server and then installs it into the dynamic-address entry.
41 |
42 | The SRX will now consult the feed every update interval and dynamically add or remove addresses as they are available, all without requiring a configuration change.
43 |
44 | **NOTE:** As of this writing this has been tested on Junos 12.3X48D10 and 15.1X49-D110 code releases, dynamic-address entries can only be used in security policies; NAT rules only consult the global and zone-based address-books for address entries.
45 |
46 | # Usage
47 | **Create a new feed**
48 |
49 | ```
50 | dynamic-policy.py new MYFEED
51 | ```
52 |
53 | **Add an address to a feed**
54 |
55 | ```
56 | dynamic-policy.py add MYFEED 192.168.88.0/24
57 | ```
58 |
59 | **Remove an address from a feed**
60 |
61 | ```
62 | dynamic-policy.py del MYFEED 192.168.88.0/24
63 | ```
64 |
--------------------------------------------------------------------------------
/dynamic-policy.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | #import xml.etree.ElementTree as ET
4 | from xml.dom import minidom
5 | from xml.etree import ElementTree as ET
6 | from xml.etree.ElementTree import Element, SubElement, Comment, tostring
7 |
8 | import time
9 | import sys
10 | import re
11 | import netaddr
12 | import fileinput
13 | import hashlib
14 | import shutil
15 | import json
16 | from pprint import pprint
17 | from jnpr.junos import Device
18 | import getpass
19 | from jnpr.junos import Device
20 | from jnpr.junos.utils.config import Config
21 |
22 |
23 |
24 |
25 | feed_location='/var/www/'
26 | temp_location='/var/tmp/'
27 |
28 |
29 | def prettify(elem):
30 | rough_string = ET.tostring(elem, 'utf-8')
31 | reparsed = minidom.parseString(rough_string)
32 | return reparsed.toprettyxml(indent=" ")
33 |
34 | def modify_file(file_name,pattern,value=""):
35 | fh=fileinput.input(file_name,inplace=True)
36 | for line in fh:
37 | replacement=line + value
38 | line=re.sub(pattern,replacement,line)
39 | sys.stdout.write(line)
40 | fh.close()
41 |
42 | def delete_line(pattern):
43 | readFile = open(tempFeed,'r')
44 | lines = readFile.readlines()
45 | readFile.close()
46 |
47 | writeFile = open(tempFeed,'w')
48 |
49 | for line in lines:
50 | if line != pattern+"\n":
51 | #print "Not found--->" + line
52 | writeFile.write(line)
53 |
54 | writeFile.close()
55 |
56 |
57 | def update_manifest(feedname):
58 | ts = int(time.time())
59 | print "Modifying total number of objects for -> " + feedname
60 | with open(tempFeed,'r') as feed:
61 | count = sum(1 for line in feed) - 5
62 | print "Revised address count of -> " + str(count) + "\n"
63 | feed.close()
64 |
65 | tree = ET.parse(tempManifest)
66 | root = tree.getroot()
67 |
68 | for feed in root.iter('feed'):
69 | name = feed.get('name')
70 | #print name
71 | if name == str(feedname):
72 | feed.set('data_ts',str(ts))
73 | feed.set('objects',str(count))
74 |
75 | tree.write(tempManifest)
76 |
77 |
78 | def create_manifest_entry(feedname):
79 | ts = str(int(time.time()))
80 | print "Inserting new feed into manifest file located at " + feed_location
81 |
82 | tree = ET.parse(tempManifest)
83 | root = tree.getroot()
84 |
85 | category = root.find('category')
86 | feed = ET.SubElement(category,'feed',dict(data_ts=ts, name=feedname, objects="0", options="", types="ip_addr ip_range", version=feedname))
87 | data = ET.SubElement(feed,'data')
88 | url = ET.SubElement(data,'url')
89 | url.text='/'
90 |
91 | text=(prettify(root))
92 | cleantext= "".join([s for s in text.strip().splitlines(True) if s.strip()])
93 |
94 | with open(tempManifest,'w') as file:
95 | file.write(cleantext)
96 |
97 |
98 |
99 |
100 |
101 | def copy_feed_to_tempFeed():
102 | shutil.copyfile(feed,tempFeed)
103 | readFile = open(tempFeed)
104 | lines = readFile.readlines()
105 | readFile.close()
106 | writeFile = open(tempFeed,'w')
107 | writeFile.writelines([item for item in lines[:-1]])
108 | writeFile.close()
109 |
110 | def copy_tempFeed_to_feed():
111 | shutil.copyfile(tempFeed,feed)
112 |
113 | def copy_tempManifest_to_Manifest():
114 | shutil.copyfile(tempManifest,manifest)
115 |
116 | def copy_Manifest_to_tempManifest():
117 | shutil.copyfile(manifest,tempManifest)
118 |
119 | def create_newFeed(name):
120 | shutil.copyfile('Feed',name)
121 |
122 |
123 | def calculate_md5():
124 | with open(tempFeed) as file:
125 | data = file.read()
126 | md5_returned = hashlib.md5(data).hexdigest()
127 | file.close()
128 |
129 | writeFile = open(tempFeed,'a')
130 | writeFile.write(md5_returned)
131 | writeFile.close()
132 |
133 |
134 |
135 | if (sys.argv[1]=='add'):
136 | feed=feed_location + str(sys.argv[2])
137 | tempFeed=temp_location + str(sys.argv[2])
138 | manifest=feed_location+'manifest.xml'
139 | tempManifest=temp_location+'manifest.xml'
140 | copy_feed_to_tempFeed()
141 | copy_Manifest_to_tempManifest()
142 | ip = netaddr.IPNetwork(sys.argv[3])
143 | feedname = sys.argv[2]
144 | address = ip.ip
145 | size = ip.size
146 | adj_size = size -1
147 | value = ip.value
148 | print "\nAdding address of -> " + str(sys.argv[3]) +" (including " + str(adj_size) + " subequent hosts)"
149 |
150 | if adj_size == 0:
151 | newentry = '{"1":' + str(value) +'}'
152 | else:
153 | newentry = '{"2":[' + str(value) + ',' + str(adj_size) +']}'
154 |
155 | #print newentry
156 | modify_file(tempFeed,'#add',newentry)
157 | calculate_md5()
158 | update_manifest(feedname)
159 | copy_tempFeed_to_feed()
160 | copy_tempManifest_to_Manifest()
161 |
162 |
163 | if sys.argv[1]=='del':
164 | feed=feed_location + str(sys.argv[2])
165 | tempFeed=temp_location + str(sys.argv[2])
166 | manifest=feed_location+'manifest.xml'
167 | tempManifest=temp_location+'manifest.xml'
168 | copy_feed_to_tempFeed()
169 | copy_Manifest_to_tempManifest()
170 | ip = netaddr.IPNetwork(sys.argv[3])
171 | feedname = sys.argv[2]
172 | address = ip.ip
173 | size = ip.size
174 | adj_size = size -1
175 | value = ip.value
176 | print "\nRemoving address of -> " + str(sys.argv[3]) +" (including " + str(adj_size) + " subequent hosts)"
177 |
178 | if adj_size == 0:
179 | oldline = '{"1":' + str(value) +'}'
180 | else:
181 | oldline = '{"2":[' + str(value) + ',' + str(adj_size) +']}'
182 | delete_line(oldline)
183 |
184 | calculate_md5()
185 | update_manifest(feedname)
186 | copy_tempFeed_to_feed()
187 | copy_tempManifest_to_Manifest()
188 |
189 |
190 | if sys.argv[1]=='list':
191 | feed=feed_location + str(sys.argv[2])
192 |
193 | pattern_network = '{"(\d+)":\[\d+,\d+\]}'
194 | pattern_host = '{"(\d+)":\d+}'
195 | pattern_ip_network = '{"\d+":\[(\d+),\d+]}'
196 | pattern_ip_host = '{"\d+":(\d+)}'
197 | pattern_range = '\d+":\[\d+,(\d+)]}'
198 |
199 | with open(feed,'r') as file:
200 | lines = file.readlines()
201 |
202 | for line in lines:
203 | host = re.search(pattern_host,line)
204 | network = re.search(pattern_network,line)
205 | if host:
206 | ip = str(netaddr.IPAddress(re.findall(pattern_ip_host,line)[0]))
207 | print "Host entry: " + ip
208 |
209 | elif network:
210 | #ip = re.findall(pattern_ip_network,line)[0]
211 | ip = str(netaddr.IPAddress(re.findall(pattern_ip_network,line)[0]))
212 | range = re.findall(pattern_range,line)[0]
213 | print "Network Entry: " + ip + " (+" + range + " hosts)"
214 |
215 |
216 | if sys.argv[1]=='new':
217 | name = str(sys.argv[2])
218 | feed=feed_location + str(sys.argv[2])
219 | manifest=feed_location+'manifest.xml'
220 | tempManifest=temp_location+'manifest.xml'
221 | copy_Manifest_to_tempManifest()
222 | print name
223 | create_newFeed(feed)
224 | create_manifest_entry(name)
225 | copy_tempManifest_to_Manifest()
226 | #print "Completed, add the following line to your SRX to accept feed:\n set security dynamic-address address-name "+name+ " profile category IPFilter feed "+name
227 | username = raw_input("Please enter your SRX Username:")
228 | password = getpass.getpass()
229 | srx_list = 'srx-list'
230 | srxs = open(srx_list,'r')
231 | for srx in srxs:
232 | print "Logging into SRX "+srx
233 | login=str(srx)
234 | dev = Device(host=login,user=username,password=password)
235 | dev.open()
236 | dev.timeout = 300
237 | cu = Config(dev)
238 | set_cmd = 'set security dynamic-address address-name '+name+' profile category IPFilter feed '+name
239 | cu.load(set_cmd, format='set')
240 | print "Applying changes, please wait...."
241 | cu.commit()
242 | dev.close()
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 | if sys.argv[1]=='drop':
252 | name = feed_location + str(sys.argv[2])
253 | print name
254 | print "do this next...."
255 |
256 |
257 | if sys.argv[1]=='setup':
258 | print "Kick off initial setup process, copy files to target directories etc"
259 |
260 |
--------------------------------------------------------------------------------