Select Git revision
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
zonedl.py 4.96 KiB
#!/usr/bin/env python3
import requests
import bs4
import argparse
import getpass
import subprocess
import sys
from pathlib import Path
parser = argparse.ArgumentParser(
description='Downloads a zone file from RWTH DNS-Admin-Portal.')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--list', action='store_true', default=False,
help='list available zones')
group.add_argument('--zone', type=int, nargs='+', help='download zone by id')
group.add_argument('--domain', nargs='+', help='download zone by name')
parser.add_argument('--passwordstore', action='store', default=None,
help='password store entry used for login to the portal')
parser.add_argument('dest', action='store', default='-', nargs='?',
help='destination for downloaded zone(s), - for stdout')
args = parser.parse_args()
NOC_PREFIX = 'https://noc-portal.rz.rwth-aachen.de'
DNS_ADMIN = NOC_PREFIX + '/dnsadmin'
ZONE_FILE = DNS_ADMIN + '/zones/{}-{}/pre_deploy_preview'
SHIB_PREFIX = 'https://sso.rwth-aachen.de'
SHIB_AUTH = SHIB_PREFIX + '/idp/profile/SAML2/Redirect/SSO'
SHIB_REDIRECT = NOC_PREFIX + '/Shibboleth.sso/SAML2/POST'
if args.passwordstore:
prc = subprocess.run(['pass', 'show', args.passwordstore],
stdout=subprocess.PIPE, check=True)
USERNAME = prc.stdout.splitlines()[1].strip()
PASSWORD = prc.stdout.splitlines()[0].strip()
else:
USERNAME = input('Username: ')
PASSWORD = getpass.getpass()
def get_zones(session):
r = session.get(DNS_ADMIN)
if r.url.startswith(SHIB_AUTH):
res, session, r = shib_auth(session, r)
if not res:
return {}
b = bs4.BeautifulSoup(r.text, 'lxml')
z = b.find(id='zones_table').find('tbody')
d = {}
for zone in z.find_all('tr'):
a = zone.find('td').find('a')
d[a.text] = int(a['href'].split('/')[-1].split('-', maxsplit=1)[0])
return d
def get_zone_file(session, zone):
r = session.get(ZONE_FILE.format(str(zone[0]),
str(zone[1].replace('.', '-'))))
if r.url.startswith(SHIB_AUTH):
res, session, r = shib_auth(session, r)
if not res:
return ''
b = bs4.BeautifulSoup(r.text, 'lxml')
t = b.find(id='content-wrapper').find('div', class_='zone-content').text
return t.replace('<br>', '').replace('\n\n', '\n')
def shib_auth(session, resp, iterations=0):
b = bs4.BeautifulSoup(resp.text, 'lxml')
form = b.find('form')
data = {}
if form['name'] == 'loginformular':
data = {'j_username': USERNAME,
'j_password': PASSWORD,
'donotcache': 'true',
'_shib_idp_revokeConsent': 'false',
'_eventId_proceed': '',
}
elif form['name'] == 'form1':
data = {'shib_idp_ls_exception.shib_idp_session_ss': '',
'shib_idp_ls_success.shib_idp_session_ss': 'false',
'shib_idp_ls_value.shib_idp_session_ss': '',
'shib_idp_ls_exception.shib_idp_persistent_ss': '',
'shib_idp_ls_success.shib_idp_persistent_ss': 'false',
'shib_idp_ls_value.shib_idp_persistent_ss': '',
'shib_idp_ls_supported': '',
'_eventId_proceed': '',
}
r = session.post(SHIB_PREFIX+form['action'], data=data)
b = bs4.BeautifulSoup(r.text, 'lxml')
f = b.find('form')['action']
if not f.startswith(SHIB_AUTH[len(SHIB_PREFIX):]):
relaystate = b.find('input', attrs={'name': 'RelayState'})['value']
samlresponse = b.find('input', attrs={'name': 'SAMLResponse'})['value']
r = session.post(f, data={'RelayState': relaystate,
'SAMLResponse': samlresponse})
return True, session, r
else:
if iterations == 0:
return shib_auth(session, r, 1)
# authentication failed
return False, session, r
s = requests.session()
d = get_zones(s)
if args.dest == '-':
fp = sys.stdout
elif Path(args.dest).is_dir():
fp = None
else:
fp = open(args.dest, 'w')
if args.list:
if fp is None:
fp = open(str(Path(args.dest) / 'list'), 'w')
for k in d:
print('{}\t{}'.format(d[k], k), file=fp)
sys.exit(0)
wanted = []
if args.domain:
for k in d:
if k in args.domain:
wanted.append((d[k], k))
for a in args.domain:
if a not in d:
print('Domain {} is not available, skipping'.format(a),
file=sys.stderr)
else:
zones = []
for k in d:
if d[k] in args.zone:
wanted.append((d[k], k))
zones.append(d[k])
for a in args.zone:
if a not in zones:
print('Zone {} is not available, skipping'.format(a),
file=sys.stderr)
if fp is None:
for w in wanted:
with open(str(Path(args.dest) / str(w[0])), 'w') as fp:
fp.write(get_zone_file(s, w))
else:
for w in wanted:
fp.write(get_zone_file(s, w))