Runtime : Ensure that a random PSK secret compatible with stunnel is generated if -s commandline switch is used. Stunnel service is restarted after generation, using spawn_subprocess. TODO : give stunnel restart command as a commandline parameter.
authorEdouard Tisserant
Tue, 23 Oct 2018 16:19:20 +0200
changeset 2323 33a0dbabccd3
parent 2322 7ce4e5cf6339
child 2324 1cf3768ebf85
Runtime : Ensure that a random PSK secret compatible with stunnel is generated if -s commandline switch is used. Stunnel service is restarted after generation, using spawn_subprocess. TODO : give stunnel restart command as a commandline parameter.
Beremiz_service.py
runtime/Stunnel.py
--- a/Beremiz_service.py	Tue Oct 23 16:13:34 2018 +0200
+++ b/Beremiz_service.py	Tue Oct 23 16:19:20 2018 +0200
@@ -50,7 +50,7 @@
     print("""
 Usage of Beremiz PLC execution service :\n
 %s {[-n servicename] [-i IP] [-p port] [-x enabletaskbar] [-a autostart]|-h|--help} working_dir
-  -n  zeroconf service name (default:disabled)
+  -n  service name (default:None, zeroconf discovery disabled)
   -i  IP address of interface to bind to (default:localhost)
   -p  port number default:3000
   -h  print this help text and quit
@@ -59,7 +59,7 @@
   -t  enable/disable Twisted web interface (0:disable 1:enable) (default:1)
   -w  web server port or "off" to disable web server (default:8009)
   -c  WAMP client config file (can be overriden by wampconf.json in project)
-  -s  WAMP client secret, given as a file (can be overriden by wamp.secret in project)
+  -s  PSK secret path (default:PSK disabled)
   -e  python extension (absolute path .py)
 
            working_dir - directory where are stored PLC files
@@ -78,7 +78,7 @@
 interface = ''
 port = 3000
 webport = 8009
-wampsecret = None
+pskpath = None
 wampconf = None
 servicename = None
 autostart = False
@@ -120,7 +120,7 @@
     elif o == "-c":
         wampconf = None if a == "off" else a
     elif o == "-s":
-        wampsecret = None if a == "off" else a
+        pskpath = None if a == "off" else a
     elif o == "-e":
         fnameanddirname = list(os.path.split(os.path.realpath(a)))
         fnameanddirname.reverse()
@@ -489,6 +489,12 @@
     sys.path.append(extension_folder)
     execfile(os.path.join(extension_folder, extention_file), locals())
 
+# Service name is used as an ID for stunnel's PSK
+# Some extension may set 'servicename' to a computed ID or Serial Number
+# instead of using commandline '-n'
+if servicename is not None and pskpath is not None:
+    from runtime.Stunnel import ensurepsk
+    ensurepsk(servicename, pskpath)
 
 runtime.CreatePLCObjectSingleton(
     WorkingDir, argv, statuschange, evaluator, pyruntimevars)
@@ -511,7 +517,7 @@
     if havewamp:
         try:
             WC.SetServer(pyroserver)
-            WC.RegisterWampClient(wampconf, wampsecret)
+            WC.RegisterWampClient(wampconf, pskpath)
             WC.RegisterWebSettings(NS)
         except Exception:
             LogMessageAndException(_("WAMP client startup failed. "))
--- a/runtime/Stunnel.py	Tue Oct 23 16:13:34 2018 +0200
+++ b/runtime/Stunnel.py	Tue Oct 23 16:19:20 2018 +0200
@@ -1,10 +1,21 @@
 import os
-from binascii import hexlify
+#from binascii import hexlify
+from runtime.spawn_subprocess import call
 
 restart_stunnel_cmdline = ["/etc/init.d/S50stunnel","restart"]
 
+# stunnel takes no encoding for psk, so we try to lose minimum entropy 
+# by using all possible chars except '\0\n\r' (checked stunnel parser to be sure)
+translator = ''.join([(lambda c: '#' if c in '\0\n\r' else c)(chr(i)) for i in xrange(256)])
+
 def pskgen(ID, pskpath):
-    secretstring = hexlify(os.urandom(256))
+    secret = os.urandom(256) # 2048 bits is still safe nowadays
+
+    # following makes 512 length string, rejected by stunnel
+    # using binascii hexlify loses 50% entropy
+    # secretstring = hexlify(secret)
+
+    secretstring = secret.translate(translator)
     pskstring = ID+":"+secretstring
     with open(pskpath, 'w') as f:
         f.write(pskstring)
@@ -14,5 +25,5 @@
     # check if already there
     if not os.path.exists(pskpath):
         # create if needed
-        pskgen(IS, pskpath)
+        pskgen(ID, pskpath)