-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbindmanager
More file actions
executable file
·406 lines (364 loc) · 15.6 KB
/
bindmanager
File metadata and controls
executable file
·406 lines (364 loc) · 15.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
(c) 2014, Ravi Bhure <ravibhure@gmail.com>
This file is part of bindadmin (https://github.com/ravibhure/bindadmin)
This script does bind update changes of your zone, based on inputs provided by user or by reading in a CSV file,
then Publishing the Zones that were updated trough the BIND API using zone allow rndc-key
The credentials are read in from a configuration file in
the same directory.
The file is named config.cfg in the format:
[defaults]
user = user_name
password = password
[database]
dbuser = bindadmin
dbpass = password
dbname = dnsdb
dbhost = localhost
[securekey]
keyname = key_name
key = XXXXXXXXXXX==
Usage: %python bindmanager.py [-h]
Options
-h, --help Show this help message and exit
-F, FILE, --File=FILE Add CSV file to search through for bulk IP address change.
This script is more clearly used, stuff written and managed from following git repo written by Juned Memon:
https://github.com/junaid18183/zonemanage
'''
from lib.bind import *
myname = os.path.basename(__file__)
def show(args):
"""
Figure out server IP or name for a given 'zone'.
Raise an exception if no suitable record is found.
"""
if args.zone:
zone = args.zone.lower()
zoneid = zonedetails(zone)
content = args.content
name = args.name
list = args.list
try:
if content:
pass
elif name:
pass
elif list:
pass
else:
raise
except Exception, ex:
logger.error("At least one from 'name' or 'content' required while looking for any object.")
logger.info("Usages: %s show -h" % myname)
sys.exit(1)
# Parse hostname
try:
if name:
is_valid = re.match("^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$", name)
if is_valid:
search = name.lower()
if args.zone:
search = find_hostname(zone, search)
sql = """ select * from records where domain_id='%s' and name like '%s.%s' """ % (zoneid, '%' + search + '%', zone)
else:
sql = """ select * from records where domain_id like '%s' and name like '%s' """ % ('%', '%' + search + '%')
else:
raise
elif content:
search = content
if args.zone:
sql = """ select * from records where domain_id='%s' and content like '%s' """ % (zoneid, '%' + search + '%')
else:
sql = """ select * from records where domain_id like '%s' and content like '%s' """ % ('%', '%' + search + '%')
elif list:
if args.zone:
sql = """ select * from records where domain_id='%s' """ % (zoneid)
else:
sql = """ select * from domains """
print chk_domain(sql)
sys.exit(0)
except Exception, ex:
if name:
search = name
else:
search = content
logger.error("While searching given value, probabily '%s' is not valid a search " % search)
sys.exit(1)
print check(sql)
def addrecord(args):
""" Connects to the zone specified by the user and add record to its fields. """
name = args.name.lower()
type = args.type.upper()
content = args.content
jissueid = args.jiraid
zone = args.zone.lower()
zoneid = zonedetails(zone)
# Parse hostname
name = find_hostname(zone, name)
# Jira Login
jirauser, jirapw = jira_login()
# Jira Validation
try:
j_valid, j_status = get_issue_by_id(serverurl,jirauser,jirapw,jissueid)
if j_valid:
if j_status:
statuscode = val_status(j_status)
logger.info("Hello %s: You are authorized to run bindadmin, '%s' status is '%s' ... -:)" % (jirauser,jissueid,statuscode))
else:
raise
except Exception, e:
logger.error("Jira issue '%s' does not exist or you don't have permission to view it, please check jira and rerun again" % jissueid)
sys.exit(1)
# User confirm
user_input(jirauser, 'Do you want to step through add records in the zone database')
# Refer valid zone from named.conf
v_zone = parse_named_file(named_file, zone)
if v_zone:
pass
else:
logger.error("Error: '%s' is not present in '%s', please check.." % (zone, named_file))
sys.exit(1)
if args.ttl:
ttl = args.ttl
try:
ttl = int(ttl)
except ValueError:
logger.error("ttl '%s' is not an int!" % ttl)
sys.exit(1)
else:
ttl = '86400'
logger.info("TTL is not provided, using default value '86400' seconds ie. 24 hours!")
# Validation
gitInit(zonepath)
# Validation..valdation and more
try:
if name:
is_valid = re.match("^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$", name)
if is_valid:
try:
if type in SUPPORTED_RECORD_TYPES:
if type == 'A':
try:
if content:
is_valid = re.match("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$", content)
if is_valid:
validation(zoneid, name, zone, type, content)
revzone = revzoneName(content)
ptr_zone = parse_named_file(named_file, revzone)
if ptr_zone:
pass
else:
logger.info("""
zone "%s" IN {
type master;
file "%s";
allow-update { key "%s"; };
};
""" % (revzone, revzone, load_mycnf()["keyname"]))
logger.error("'%s' not present in '%s', please check.. and add it correctly to refer and do reload named service." % (revzone, named_file))
sys.exit()
else:
raise
else:
raise
except Exception, ex:
logger.error("'%s' is not a valid ip" % content)
sys.exit(1)
elif type == 'CNAME':
try:
if content:
is_valid = re.match("^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$", content)
if is_valid:
validation(zoneid, name, zone, type, content)
else:
raise
else:
raise
except Exception, ex:
logger.error("'%s' is not a valid hostname" % content)
sys.exit(1)
elif type == 'TXT':
try:
if len(content) > 255:
raise
else:
validation(zoneid, name, zone, type, content)
pass
except Exception, ex:
logger.error("Error! Only 255 characters allowed!, provide TXT content under 255 char")
sys.exit(1)
else:
validation(zoneid, name, zone, type, content)
pass
else:
raise
except Exception, ex:
logger.error("'%s' is not a valid record type, reffer one from '%s'" % (type, SUPPORTED_RECORD_TYPES))
sys.exit(1)
else:
raise
elif content:
is_valid = re.match("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$", ip)
if is_valid:
validation(zoneid, name, zone, type, content)
pass
else:
raise
except Exception, ex:
logger.error("'%s', probabily it is not valid hostname/ip" % name)
sys.exit(1)
logger.info("Sanity check went good for '%s' and given args input" % zone)
sql = """ insert into records (domain_id, name,type,content,ttl,prio) select id, '%s.%s', '%s', '%s', '%s', 0 from domains where id='%s' """ % (name, zone, type, content, ttl, zoneid)
# Created lock to prevent rerun multples
lockme()
revertzone(zone)
result = execute(sql)
logger.info("Successfully added record '%s in db." % name)
action = 'add'
data = "%s.%s. %s %s %s" % (name, zone, ttl, type, content)
nsfile(action, zone, data)
if type == 'A':
revname = revName(content)
data = "%s %s %s %s" % (revname, ttl, 'PTR', name)
nsfile(action, revzone, data, nfile=ptrtemplate)
dnsupdate(zone)
if type == 'A':
dnsupdate(revzone, nfile=ptrtemplate)
if check_zone(zonepath, zone):
logger.info("Validity check went good for '%s'" % zone)
if type == 'A':
reloadzone(zone)
#### Delay for 1 seconds ####
time.sleep(2)
reloadzone(revzone)
else:
reloadzone(zone)
archivezone(zone, jirauser, jissueid)
logger.info("Successfully added record '%s'" % name)
# Release lock once all went good
release_lock()
return True
else:
revertzone(zone)
sql = """ delete from records where domain_id='%s' and name='%s.%s' and content='%s' and type='%s' """ % (zoneid, name, zone, content, type)
execute(sql)
# "Task pending to remove entry from db"
reloadzone(zone)
release_lock()
raise
logger.error("in '%s' zone file, please check, we have reverted to fixed it" % zone)
sys.exit(1)
def deleterecord(args):
""" Connects to the zone specified by the user and delete record to its fields. """
name = args.name.lower()
zone = args.zone.lower()
jissueid = args.jiraid
type = args.type.upper()
zoneid = zonedetails(zone)
# Parse hostname
name = find_hostname(zone, name)
# Jira Login
jirauser, jirapw = jira_login()
# Jira Validation
try:
j_valid, j_status = get_issue_by_id(serverurl,jirauser,jirapw,jissueid)
if j_valid:
if j_status:
statuscode = val_status(j_status)
logger.info("Hello %s: You are authorized to run bindadmin, '%s' status is '%s' ... -:)" % (jirauser,jissueid,statuscode))
else:
raise
except Exception, e:
logger.error("Jira issue '%s' does not exist or you don't have permission to view it, please check jira and rerun again" % jissueid)
sys.exit(1)
# User confirm
user_input(jirauser, 'Do you want to step through remove record(s) from the zone database')
# Refer valid zone from named.conf
v_zone = parse_named_file(named_file, zone)
if v_zone:
pass
else:
logger.error("Error: '%s' is not present in '%s', please check.." % (zone, named_file))
sys.exit(1)
# Validation
gitInit(zonepath)
if args.content and type:
content = args.content
sql = """ delete from records where domain_id='%s' and name='%s.%s' and content='%s' and type='%s' """ % (zoneid, name, zone, content, type)
data = "%s.%s. %s %s" % (name, zone, type, content)
elif type:
try:
if type in SUPPORTED_RECORD_TYPES:
sql = """ delete from records where domain_id='%s' and name='%s.%s' and type='%s' """ % (zoneid, name, zone, type)
data = "%s.%s. %s" % (name, zone, type)
else:
raise
except Exception, ex:
logger.error("'%s' is not a valid record type, reffer one from '%s'" % (type, SUPPORTED_RECORD_TYPES))
sys.exit(1)
else:
sql = """ delete from records where domain_id='%s' and name='%s.%s' """ % (zoneid, name, zone)
lockme()
logger.info("Sanity check went good for '%s' and given args input" % zone)
revertzone(zone)
result = execute(sql)
logger.info("Successfully removed record '%s' from db." % name)
ttl = '86400'
action = 'delete'
nsfile(action, zone, data)
dnsupdate(zone)
if type == 'A':
logger.warning("Please check and remove associate PTR for this if unused..")
if check_zone(zonepath, zone):
logger.info("Validity check went good for '%s'" % zone)
reloadzone(zone)
archivezone(zone, jirauser, jissueid)
logger.info("Successfully removed record '%s'" % name)
# Release lock once all went good
release_lock()
return True
else:
revertzone(zone)
reloadzone(zone)
release_lock()
raise
logger.error("in '%s' zone file, please check, we have reverted to fixed it" % zone)
sys.exit(1)
def main():
"""
Figure out what you want to do from bindadmin, and then do the
needful (at the earliest).
"""
parser = argparse.ArgumentParser(description="Queries the zone database for Build information", epilog="To know more, write to: %s" % authoremail)
subparsers = parser.add_subparsers()
# toggle show record
parser_show = subparsers.add_parser('show',help="Show's your defined search from given zone")
parser_show.add_argument("-z", "--zone", help="Set the zone to be check",required=False)
parser_show.add_argument("-n", "--name", help="The record name to be check",required=False)
parser_show.add_argument("-c", "--content", help="The record value or ip address to be check",required=False)
parser_show.add_argument("-l", "--list", action='store_true', help="List all record values for specified zone",required=False)
parser_show.set_defaults(func=show)
# toggle add record
parser_add = subparsers.add_parser('add', help="Add the given record to the zone")
parser_add.add_argument("-n", "--name", help="The record name to add",required=True)
parser_add.add_argument("-t", "--type", help="The record type to add for record name",required=True)
parser_add.add_argument("-c", "--content", help="The record value to add for record name",required=True)
parser_add.add_argument("-z", "--zone", help="Set the zone to be updated",required=True)
parser_add.add_argument("-j", "--jiraid", help="Jira ID for auth and request tracking",required=True)
parser_add.add_argument("--ttl", help="The record ttl to add for record name",required=False)
parser_add.set_defaults(func=addrecord)
# toggle delte record
parser_delete = subparsers.add_parser('delete', help="Remove the given record from the zone(s)")
parser_delete.add_argument("-n", "--name", help="The record name to delete",required=True)
parser_delete.add_argument("-t", "--type", help="The record type to delete for record name",required=True)
parser_delete.add_argument("-c", "--content", help="The record value to add for record name",required=False)
parser_delete.add_argument("-z", "--zone", help="Set the zone to be updated",required=True)
parser_delete.add_argument("-j", "--jiraid", help="Jira ID for auth and request tracking",required=True)
parser_delete.set_defaults(func=deleterecord)
args = parser.parse_args()
args.func(args)
return 0
# +----------------------------------------------------------------------+
if __name__ == "__main__":
sys.exit(main())