diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 17c009b0..08fd95b0 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -60,6 +60,7 @@ jobs: # uses a compiled language - run: | + sudo apt update sudo apt install g++ cmake make libsigc++-2.0-dev libgsm1-dev libpopt-dev tcl-dev libgcrypt11-dev libspeex-dev libasound2-dev libopus-dev librtlsdr-dev libjsoncpp-dev libcurl4-openssl-dev qtbase5-dev qttools5-dev qttools5-dev-tools mkdir -p src/build cd src/build diff --git a/src/svxlink/reflector/genpw/.gitignore b/src/svxlink/reflector/genpw/.gitignore new file mode 100644 index 00000000..76dfb233 --- /dev/null +++ b/src/svxlink/reflector/genpw/.gitignore @@ -0,0 +1,2 @@ +README.html +db diff --git a/src/svxlink/reflector/genpw/README.adoc b/src/svxlink/reflector/genpw/README.adoc new file mode 100644 index 00000000..e90c0098 --- /dev/null +++ b/src/svxlink/reflector/genpw/README.adoc @@ -0,0 +1,103 @@ += SvxReflector Password Generator +:toc2: + +== Introduction +This is a simple web utility used to safely generate a password for a SvxLink +node to log in to the SvxReflector. The utility offer random generated +passwords to a client which can then be picked up by the reflector sysop to be +configured in the reflector configuration file. The generated password is +protected in transit by using a SSL/TLS encrypted connection. + + +== Installation +This utility should be installed on a webserver where the SvxReflector sysop +have shell access. It does not have to be the same server as where the +SvxReflector server is running. + +The Apache webserver must be installed as a prerequisite. That setup is not +described in this documentation. The webserver also need to be set up with a +valid SSL/TLS server certificate. + +It is of course possible to use other web servers but this is not described in +this document. + +In short, the installation is done by installing a couple of dependencies and +then copy the genpw files in place. + +[source,bash] +---- +apt update +apt install libapache2-mod-wsgi-py3 python3-flask +mkdir -p /var/www/genpw/db +cp genpw.* /var/www/genpw/ +cp getpw.py /usr/local/bin/ +useradd -rU svxlink +chown svxlink:svxlink /var/www/genpw/db +chmod 2770 /var/www/genpw/db +usermod -aG sysop1 svxlink +usermod -aG sysop2 svxlink +---- + + +== Apache Config Example +When all genpw files have been put in place by following the instructions above +Apache must be configured with something like the example below. + +[source,apache] +---- + + + # ... + # Other setup + # ... + + # Make sure that SSL/TLS encryption is enabled/required + SSLEngine on + SSLCertificateFile /etc/ssl/certs/example.org.crt + SSLCertificateKeyFile /etc/ssl/private/example.org.key + SSLCertificateChainFile /etc/ssl/certs/example.org-intermediate.pem + SSLProtocol -all +TLSv1.2 +TLSv1.3 + + WSGIDaemonProcess genpw user=svxlink group=svxlink threads=1 home=/var/www/genpw + WSGIScriptAlias /genpw /var/www/genpw/genpw.wsgi + + + WSGIProcessGroup genpw + WSGIApplicationGroup %{GLOBAL} + Order deny,allow + Allow from all + + + +---- + +[source,bash] +---- +systemctl restart apache2 +---- + + +== Usage +Send a URL to the SvxLink node sysop that look something like the one below. + + https://www.example.com/genpw?callsign=SM0XYZ + +A new password will be generated each time the link is clicked. After the +SvxLink node sysop have clicked on the link to generate a password he should +send back the timestamp to the SvxReflector sysop in order for him to verify +that the password with the correct timestamp is used. + +The SvxLink node sysop must not reload the page after generating a password +that is then used to configure the SvxLink node. Since the SvxReflector sysop +only see the latest generated password it will then be overwritten and thus +wrong. + +The SvxReflector node sysop will run the getpw.py script on the webserver to retrieve the password to configure. + + $ getpw.py SM0XYZ + SM0XYZ: "{"remote_addr": "11.22.33.44", "ts": "2021-02-14T19:51:30.291Z", "password": "!_2>4kGUHM7vorUctVJFaxZ! + + + SvxReflector Password Generator + + +

SvxReflector Password Generator

+Copy the callsign and password configuration to your SvxLink ReflectorLogic +configuration. A new password is generated every time this page is reloaded so +when you have settled for a password, don't reload. Send the timestamp to the +SvxReflector administrator as a verification of that the correct password +generation is chosen. +

+Timestamp: {{ data.ts }} +

+

+[ReflectorLogic]
+...
+CALLSIGN={{ data.callsign }}
+AUTH_KEY="{{ data.password }}"
+...
+
+
+ + +''' + +def random_pw_string(length): + alphabet = string.ascii_letters + string.digits \ + + '!#$%&*+:;<=>?_' + alphabet.translate({ord('"'): None, ord('\\'): None}) + result_str = ''.join((random.choice(alphabet) for i in range(length))) + return result_str + +app = Flask(__name__) + +@app.route('/') +def genpw(): + callsign = escape(request.args.get("callsign")) + if callsign == 'None' or len(callsign) == 0: + response = make_response('BAD REQUEST\n', 400) + response.mimetype = "text/plain" + else: + frac, epoch = math.modf(time.time()) + frac_ms = int(1000 * frac) + ts = '%s.%03uZ' % (time.strftime('%Y-%m-%dT%H:%M:%S', \ + time.gmtime(epoch)), frac_ms) + pw = random_pw_string(30) + data = { + 'remote_addr': request.remote_addr, + 'ts': ts, + 'password': pw + } + os.umask(0o007) + with dbm.open('db/svxreflector-genpw', 'c', 0o660) as db: + db[callsign] = json.dumps(data) + data['callsign']=callsign + #print(data) + response = make_response(render_template_string(page_template, data=data), 200) + return response + +if __name__ == '__main__': + app.run(debug=True) diff --git a/src/svxlink/reflector/genpw/genpw.wsgi b/src/svxlink/reflector/genpw/genpw.wsgi new file mode 100644 index 00000000..42140694 --- /dev/null +++ b/src/svxlink/reflector/genpw/genpw.wsgi @@ -0,0 +1 @@ +from genpw import app as application diff --git a/src/svxlink/reflector/genpw/getpw.py b/src/svxlink/reflector/genpw/getpw.py new file mode 100755 index 00000000..a84564c5 --- /dev/null +++ b/src/svxlink/reflector/genpw/getpw.py @@ -0,0 +1,15 @@ +#!/usr/bin/python3 + +import dbm +import sys +import os + +os.umask(0o007) +with dbm.open('/var/www/genpw/db/svxreflector-genpw', 'c', 0o660) as db: + if len(sys.argv) > 1: + callsign = sys.argv[1] + print(callsign + ': "' + db[callsign].decode() + '"') + del db[callsign] + else: + for k in db.keys(): + print(k.decode() + ': ' + db[k].decode())