# SPDX-License-Identifier: AGPL-3.0-or-later # kpov_util should be imported by add_assignment.py instructions = { 'si': '''\

Ustvari dva navidezna računalnika: SimpleArbiter in RadiusServer. Na RadiusServer namesti FreeRadius ter MySQL.

Ustvari podatkovno bazo MySQL z imenom {{MYSQL_DB_NAME}}. Ustvari uporabnika za MySQL z imenom {{MYSQL_ADMIN_USER}} in geslom {{MYSQL_ADMIN_PASSWORD}}, ki naj ima poln dostop do te baze. Prijava za tega uporabnika mora biti omogočena tudi s SimpleArbiter.

Nastavi FreeRadius tako, da bo podatke o uporabnikih in geslih pobiral iz baze MySQL z imenom {{MYSQL_DB_NAME}}. Podatkovna shema (imena tabel) naj ostane privzeta.

Dostop do strežnika Radius na RadiusServer s SimpleArbiter naj bo mogoč ob uporabi skrivnosti {{RADIUS_SECRET}}.

V bazi ustvari vnos, ki bo omogočil, da se na RadiusServer s pomočjo protokola Radius avtenticira uporabnik {{RADIUS_USERNAME}} z geslom {{RADIUS_PASSWORD}}.

Nastavi PAM za prijavo (login) tako, da bo dovolj, če se uporabnik na SSH predstavi z uporabniškim imenom in geslom, ki sta veljavna na FreeRadius, ne glede na /etc/shadow oziroma /etc/password. ''', 'en': '''\

Create two virtual machines: SimpleArbiter and RadiusServer. On RadiusServer, install FreeRadius and MySQL.

Create a MySQL database named {{MYSQL_DB_NAME}}. Create a mysql user with the username {{MYSQL_ADMIN_USER}} and password {{MYSQL_ADMIN_PASSWORD}}. Make sure this user can access the database from SimpleArbiter and has administrative rights over the {{MYSQL_DB_NAME}} database.

Set up FreeRadius so that the data about users and passwords is stored in the MySQL database. Keep the default schema (table names).

Make the Radius server on RadiusServer accessible from SimpleArbiter using {{RADIUS_SECRET}} as the secret.

Create an entry in the database which will enable a user with the username {{RADIUS_USERNAME}} to authenticate themself against the Radius server using the password {{RADIUS_PASSWORD}}.

Set up PAM to enable login over SSH using a username and password which are valida on the FreeRadius server, regardless of the entries in /etc/shadow and/or /etc/password. ''', } #KABOOM computers = { 'RadiusServer': { 'disks': [ { 'name': 'student-RadiusServer', }, ], 'network_interfaces': [{'network': 'net1'}], 'flavor': 'm1.tiny', 'config_drive': False }, 'SimpleArbiter': { 'disks': [ { 'name': 'simpleArbiterDhcpGW', }, ], 'network_interfaces': [{'network': 'net1'}, {'network': 'test-net'}], 'flavor': 'm1.tiny', 'config_drive': False } } networks = { 'net1': {'public': False}, 'test-net': {'public': True} } params_meta = { 'IP_RS': {'descriptions': {'si': 'Naslov RadiusServer', 'en': 'RadiusServer IP address'}, 'w': True, 'public':True, 'type': 'IP', 'generated': False}, 'RADIUS_SECRET':{'descriptions': {'si': 'Skrivnost RADIUS', 'en': 'RADIUS secret'}, 'w': False, 'public':True, 'type': 'password', 'generated': True}, 'RADIUS_USERNAME': {'descriptions': {'si': 'Uporabniško ime', 'en': 'Username'}, 'w': True, 'public':True, 'type': 'username', 'generated': False}, 'RADIUS_PASSWORD': {'descriptions': {'si': 'Geslo uporabnika', 'en': 'Password'}, 'w': False, 'public':True, 'type': None, 'generated': True}, 'MYSQL_DB_NAME': {'descriptions': {'si': 'Ime baze v mysql', 'en': 'Database name'}, 'w': False, 'public':True, 'type': None, 'generated': True}, 'MYSQL_ADMIN_USER':{'descriptions': {'si': 'Uporabniško ime za dostop do MySQL', 'en': 'MySQL username'}, 'w': False, 'public':True, 'type': 'username', 'generated': True}, 'MYSQL_ADMIN_PASSWORD': {'descriptions': {'si': 'Geslo za dostop do MySQL', 'en': 'MySQL password'}, 'w': True, 'public':True, 'type': 'password', 'generated': True}, 'MYSQL_SEED':{'descriptions': {'si': 'seed', 'en': 'seed'}, 'w': False, 'public':True, 'type': None, 'generated': True}, } def task(IP_RS, RADIUS_SECRET, RADIUS_USERNAME, RADIUS_PASSWORD, MYSQL_DB_NAME, MYSQL_ADMIN_USER, MYSQL_ADMIN_PASSWORD, MYSQL_SEED): import collections import random import pexpect r = random.Random(MYSQL_SEED) MYSQL_TEST_USER = kpov_util.username_gen(r) MYSQL_TEST_PASSWORD = kpov_util.alnum_gen(r, 7) RADIUS_NEW_PASSWORD = kpov_util.alnum_gen(r, 7) results = collections.defaultdict(str) # Testiranje radius strežnika results['Test_RadiusServer'] = pexpect.run('radtest {0} {1} {2} 1812 {3}'.format( RADIUS_USERNAME, RADIUS_PASSWORD, IP_RS, RADIUS_SECRET)) # Testiranje podatkovne base mysql mysql = pexpect.spawn('mysql -u {MYSQL_ADMIN_USER} -p{MYSQL_ADMIN_PASSWORD} -h {IP_RS}'.format(**locals())) mysql.expect("mysql>") results['mysql_login'] = mysql.before mysql.sendline('USE {MYSQL_DB_NAME}'.format(**locals())) mysql.expect("mysql>") results['database_connect'] = mysql.before mysql.sendline('SELECT UserName, Value FROM radcheck;') mysql.expect("mysql>") results['select_from_users'] = mysql.before mysql.sendline("INSERT INTO radcheck (UserName, Attribute, Value, Op) VALUES ('{MYSQL_TEST_USER}', 'Cleartext-Password', '{MYSQL_TEST_PASSWORD}', ':=');".format(**locals())) mysql.expect("mysql>") results['radtest_OK'] = pexpect.run('radtest {0} {1} {2} 1812 {3}'.format( MYSQL_TEST_USER, MYSQL_TEST_PASSWORD, IP_RS, RADIUS_SECRET)) results['radtest_NOK'] = pexpect.run('radtest {0} {1} {2} 1812 {3}'.format( MYSQL_TEST_USER, "Flügzeug", IP_RS, RADIUS_SECRET)) results['radtest_NOK'] = pexpect.run('radtest {0} {1} {2} 1812 {3}'.format( MYSQL_TEST_USER, "Flügzeug", IP_RS, RADIUS_SECRET)) mysql.sendline("UPDATE radcheck SET value='{RADIUS_NEW_PASSWORD}' where UserName='{RADIUS_USERNAME}' and Attribute='Cleartext-Password';".format(**locals())) results.update(kpov_util.ssh_test(IP_RS, RADIUS_USERNAME, RADIUS_NEW_PASSWORD)) mysql.sendline("UPDATE radcheck SET value='{RADIUS_PASSWORD}' where UserName='{RADIUS_USERNAME}' and Attribute='Cleartext-Password';".format(**locals())) mysql.expect('mysql>') mysql.sendline("DELETE FROM radcheck where UserName='{MYSQL_TEST_USER}' and Attribute='Cleartext-Password';".format(**locals())) mysql.expect('mysql>') mysql.sendline('\q'); # TODO Testiranje PAM s testnim uporabnikom return results def gen_params(user_id, params_meta): params = dict() r = random.Random(user_id) params['RADIUS_SECRET'] = kpov_util.alnum_gen(r, 8) params['RADIUS_PASSWORD'] = kpov_util.alnum_gen(r, 8) params['RADIUS_USERNAME'] = kpov_util.username_gen(r) params['MYSQL_ADMIN_USER'] = kpov_util.alnum_gen(r, 6) params['MYSQL_ADMIN_PASSWORD'] = kpov_util.alnum_gen(r, 6) params['MYSQL_DB_NAME'] = kpov_util.alnum_gen(r, 4) params['MYSQL_SEED'] = str(r.random()) return params def task_check(results, params): import re import pickle score = 0 hints = [] r = random.Random(params['MYSQL_SEED']) MYSQL_TEST_USER = kpov_util.username_gen(r) MYSQL_TEST_PASSWORD = kpov_util.alnum_gen(r, 7) RADIUS_NEW_PASSWORD = kpov_util.alnum_gen(r, 7) s = r"Sent Access-Request Id [0-9]+ from ([0-9]|\.)+:[0-9]+ to {IP_RS}:1812 length [0-9]+\r\n\tUser-Name = \"{RADIUS_USERNAME}\"\r\n\tUser-Password = \"{RADIUS_PASSWORD}\".*Access-Accept Id [0-9]+ from {IP_RS}".format(**params) #with open('test.pickle', 'w') as f: # pickle.dump({'pattern': s, 'res': results['Test_RadiusServer']}, f) if re.search(s, results['Test_RadiusServer'], flags=re.DOTALL): # print "Test OK" score += 2 else: hints.append('radtest connect output incorrect:' + results['Test_RadiusServer']) print((results['Test_RadiusServer'], s)) # Testiranje podatkovne base mysql s = "Welcome to the MySQL monitor.*Type 'help;' or '\\\\h' for help\\. Type '\\\\c' to clear the current input statement\\.\r\n" if re.search(s, results['mysql_login'], flags=re.DOTALL): # print "mysql_login OK" score += 1 else: hints.append("mysql connection string incorrect") print((results['mysql_login'], s)) s = " USE {MYSQL_DB_NAME}\r\nReading table information.*Database changed\r\n".format(**params) if re.search(s, results['database_connect'], flags=re.DOTALL): # print "database_connect OK" score += 1 else: hints.append('mysql table information string incorrect') print((results['database_connect'],)) s = " SELECT UserName, Value FROM radcheck;\r\n.*{RADIUS_USERNAME} *| *{RADIUS_PASSWORD}".format(**params) if re.search(s, results['select_from_users'], flags=re.DOTALL): # print "select_from_users OK" score += 2 else: hints.append('mysql user entry in table check failed') print((results['select_from_users'], )) s = r"Sent Access-Request Id [0-9]+ from ([0-9]|\.)+:[0-9]+ to {0}:1812 length [0-9]+\r\n\tUser-Name = \"{1}\"\r\n\tUser-Password = \"{2}\".*Access-Accept Id [0-9]+ from {0}".format(params['IP_RS'], MYSQL_TEST_USER, MYSQL_TEST_PASSWORD) if re.search(s, results['radtest_OK'], flags=re.DOTALL): # print "radtest_OK OK" score += 2 else: hints.append('radtest output incorrect:' + results['radtest_OK']) print((s, results['radtest_OK'])) s = r"Sent Access-Request Id [0-9]+ from ([0-9]|\.)+:[0-9]+ to {0}:1812 length [0-9]+\r\n\tUser-Name = \"{1}\"\r\n\tUser-Password = \"Flügzeug\".*Access-Reject Id [0-9]+ from {0}".format(params['IP_RS'], MYSQL_TEST_USER) if re.search(s, results['radtest_NOK'], flags=re.DOTALL): # print "radtest_NOK OK" score += 1 else: hints.append('radtest negative output incorrect: ' + results['radtest_NOK']) print((results['radtest_NOK'], s)) s = "{RADIUS_USERNAME}@.*:".format(**params) if re.search(s, results['motd'], flags=re.DOTALL): # print "login_test OK" score += 1 else: hints.append('login test failed') print((results['login_test'],s)) return score, hints def prepare_disks(templates, task_params, global_params): write_default_config(templates['simpleArbiterDhcpGW'], global_params)