OpenSecurity/bin/opensecurityd.pyw
author Oliver Maurhart <oliver.maurhart@ait.ac.at>
Wed, 30 Apr 2014 12:10:34 +0200
changeset 138 8e133512ab7a
parent 131 8b4df474e577
child 139 76ac81b519cf
permissions -rwxr-xr-x
more gently termination of opensecurityd server
     1 #!/bin/env python
     2 # -*- coding: utf-8 -*-
     3 
     4 # ------------------------------------------------------------
     5 # opensecurityd
     6 # 
     7 # the opensecurityd as RESTful server
     8 #
     9 # Autor: Oliver Maurhart, <oliver.maurhart@ait.ac.at>
    10 #
    11 # Copyright (C) 2013 AIT Austrian Institute of Technology
    12 # AIT Austrian Institute of Technology GmbH
    13 # Donau-City-Strasse 1 | 1220 Vienna | Austria
    14 # http://www.ait.ac.at
    15 #
    16 # This program is free software; you can redistribute it and/or
    17 # modify it under the terms of the GNU General Public License
    18 # as published by the Free Software Foundation version 2.
    19 # 
    20 # This program is distributed in the hope that it will be useful,
    21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
    22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    23 # GNU General Public License for more details.
    24 # 
    25 # You should have received a copy of the GNU General Public License
    26 # along with this program; if not, write to the Free Software
    27 # Foundation, Inc., 51 Franklin Street, Fifth Floor, 
    28 # Boston, MA  02110-1301, USA.
    29 # ------------------------------------------------------------
    30 
    31 
    32 # ------------------------------------------------------------
    33 # imports
    34 
    35 import json
    36 import os
    37 import os.path
    38 import subprocess
    39 import sys
    40 import tempfile
    41 import web
    42 
    43 import vmmanager
    44 
    45 # local
    46 from cygwin import Cygwin
    47 from environment import Environment
    48 from opensecurity_util import logger
    49 
    50 
    51 # ------------------------------------------------------------
    52 # const
    53 
    54 __version__ = "0.2.5"
    55 
    56 
    57 """All the URLs we know mapping to class handler"""
    58 opensecurity_urls = (
    59     '/browsing',                        'os_browsing',          # http://localhost:8080/browsing                                GET
    60     '/fetch_initial_image',             'os_fetch_image',       # http://localhost:8080/fetch_initial_image                     GET
    61     '/init',                            'os_init',              # http://localhost:8080/init                                    GET
    62     '/initial_image',                   'os_initial_image',     # http://localhost:8080/initial_image                           GET
    63     '/sdvms',                           'os_sdvms',             # http://localhost:8080/sdvms                                   GET, PUT
    64     '/sdvms/(.*)/application/(.*)',     'os_sdvm_application',  # http://localhost:8080/sdvms/[VMNAME]/application/[COMMAND]    GET
    65     '/sdvms/(.*)/ip',                   'os_sdvm_ip',           # http://localhost:8080/sdvms/[VMNAME]/ip                       GET
    66     '/sdvms/(.*)/start',                'os_sdvm_start',        # http://localhost:8080/sdvms/[VMNAME]/start                    GET
    67     '/sdvms/(.*)/stop',                 'os_sdvm_stop',         # http://localhost:8080/sdvms/[VMNAME]/stop                     GET
    68     '/sdvms/(.*)',                      'os_sdvm',              # http://localhost:8080/sdvms/[VMNAME]                          GET, DELETE
    69     '/setup',                           'os_setup',             # http://localhost:8080/setup                                   GET
    70     '/vms',                             'os_vms',               # http://localhost:8080/vms                                     GET
    71     '/vms/(.*)',                        'os_vm',                # http://localhost:8080/vms/[VMNAME]                            GET
    72     '/update_template',                 'os_update_template',   # http://localhost:8080/update_template                         GET
    73     '/terminate',                       'os_terminate',         # http://localhost:8080/terminate                               GET
    74     '/',                                'os_root'               # http://localhost:8080/                                        GET
    75 )
    76 
    77 
    78 # ------------------------------------------------------------
    79 # vars
    80 
    81 # Global VMManager instance
    82 gvm_mgr = None
    83 
    84 # server instance
    85 server = None
    86 
    87 
    88 # ------------------------------------------------------------
    89 # code
    90 
    91 
    92 class os_browsing:
    93     """OpenSecurity '/browsing' handler
    94     
    95     - GET: Start and prepare a new SecurityVM for Internet Browsing. Return the name of the VM.
    96     """
    97     
    98     def GET(self):
    99         log_call(web.ctx.environ)
   100         global gvm_mgr
   101         try:
   102             result = gvm_mgr.handleBrowsingRequest()
   103             return result
   104         except:
   105             raise web.internalerror()
   106 
   107        
   108 class os_fetch_image:
   109     """OpenSecurity '/fetch_initial_image' handler
   110     
   111     - GET: fetch the initial image from the X-Net Servers
   112             The initial image is stored in the
   113             Virtual Box default machine path.
   114             The result to this call is a temprary file
   115             which shows the progress (or error state)
   116             of this call.
   117     """
   118     
   119     def GET(self):
   120         
   121         log_call(web.ctx.environ)
   122         global gvm_mgr
   123 
   124         trace_file_name = os.path.join(tempfile.gettempdir(), 'OpenSecurity_fetch_image.log')
   125         trace_file = open(trace_file_name, 'w+')
   126 
   127         machine_folder = Cygwin.cygPath(gvm_mgr.getMachineFolder()) 
   128         download_initial_image_script = '/OpenSecurity/bin/download_initial_image.sh \'' + machine_folder + '\''
   129         Cygwin.bashExecute(download_initial_image_script, wait_return = False, stdout = trace_file, stderr = trace_file) 
   130 
   131         res = '{ "fetch_log": "' + trace_file_name.replace('\\', '\\\\') + '" }'
   132         return res
   133 
   134 class os_init:
   135     """OpenSecurity '/init' handler
   136     
   137     - GET: Do initial import of OsecVM.ova
   138     """
   139     
   140     def GET(self):
   141         log_call(web.ctx.environ)
   142         global gvm_mgr
   143 
   144         trace_file_name = os.path.join(tempfile.gettempdir(), 'OpenSecurity_initial_import.log')
   145         trace_file = open(trace_file_name, 'w+')
   146 
   147         vm_image = Cygwin.cygPath(gvm_mgr.getMachineFolder()) + '/OsecVM.ova'
   148         initial_import_script = '/OpenSecurity/bin/initial_vm.sh \'' + vm_image + '\''
   149         Cygwin.bashExecute(initial_import_script, wait_return = False, stdout = trace_file, stderr = trace_file) 
   150 
   151         res = '{ "init_log": "' + trace_file_name.replace('\\', '\\\\') + '" }'
   152         return res
   153 
   154 
   155 class os_initial_image:
   156     """OpenSecurity '/initial_image' handler
   157     
   158     - GET: Return what we have as initial image.
   159     """
   160     
   161     def GET(self):
   162         log_call(web.ctx.environ)
   163         global gvm_mgr
   164         t = os.path.join(gvm_mgr.systemProperties['Default machine folder'], 'OsecVM.ova')
   165         res = ''
   166         if os.path.isfile(t):
   167             res = '{"initial_template": { '
   168             res += '"name": "OsecVM.ova", '
   169             res += '"path": "' + t.replace('\\', '\\\\') + '", '
   170             res += '"size": ' + str(os.path.getsize(t)) + ', ' 
   171             res += '"date": ' + str(os.path.getmtime(t)) + ''
   172             res += '}}'
   173         return res
   174 
   175 
   176 class os_root:
   177     """OpenSecurity '/' handler
   178     
   179     - GET: give information about current installation.
   180     """
   181     
   182     def GET(self):
   183         log_call(web.ctx.environ)
   184         global gvm_mgr
   185 
   186         # create a json string and pretty print it
   187         res = '{"os_server": { '
   188         res += '"version": "' + __version__ + '" '
   189         res += ', "virtual box systemproperties": ' + str(gvm_mgr.systemProperties).replace("'", '"') 
   190         res += ', "current temporary folder": "' + tempfile.gettempdir().replace('\\', '\\\\') + '"'
   191 
   192         try:
   193             res += ', "whoami": "' + Cygwin.bashExecute('whoami')[1].strip() + '"'
   194         except:
   195             res += ', "whoami": "FAILED"'
   196 
   197         try:
   198             res += ', "mount": ' + str(Cygwin.bashExecute('mount')[1].split('\n')[:-1]).replace("'", '"')
   199         except:
   200             res += ', "mount": "FAILED"'
   201 
   202         try:
   203             res += ', "cygpath --windows ~": "' + Cygwin.bashExecute('cygpath --windows ~')[1].strip().replace('\\', '\\\\') + '"'
   204         except:
   205             res += ', "cygpath --windows ~": "FAILED"'
   206 
   207 
   208         res += ', "status message": "' + gvm_mgr.status_message.replace('"', "'") + '"'
   209 
   210         res += '}}'
   211 
   212         # loading it into json and print it again ensures
   213         # we really do have a valid RFC conform json string
   214         # created (as long as the python json module is RFC conform)
   215         return json.dumps(json.loads(res), indent = 4)
   216 
   217 
   218 class os_sdvm:
   219     """OpenSecurity '/sdvms/[VM]' handler
   220     
   221     - GET: Information about a specific SecurityVM
   222     - DELETE: Remove a specific
   223     """
   224     
   225     def GET(self, name):
   226         log_call(web.ctx.environ)
   227         global gvm_mgr
   228         return gvm_mgr.getVMInfo(name)
   229 
   230     def DELETE(self, name):
   231         log_call(web.ctx.environ)
   232         global gvm_mgr
   233         return gvm_mgr.removeVM(name)
   234             
   235 
   236 class os_sdvm_application:
   237     """OpenSecurity '/sdvms/[VM]/application/[CMD]' handler
   238     
   239     - GET: start application with given command in the VM.
   240     """
   241     
   242     def GET(self, name, command):
   243         log_call(web.ctx.environ)
   244         global gvm_mgr
   245         command = '/' + command
   246         result = Cygwin.sshExecuteX11(command, gvm_mgr.getHostOnlyIP(name), 'osecuser', Cygwin.cygPath(gvm_mgr.getMachineFolder()) + '/' + name + '/dvm_key'  )
   247         self.poweroffVM(name)
   248         return gvm_mgr.removeVM(name)
   249     
   250 
   251 class os_sdvm_ip:
   252     """OpenSecurity '/sdvms/[VM]/ip' handler
   253     
   254     - GET: give IP of SecurityVM.
   255     """
   256     
   257     def GET(self, name):
   258         log_call(web.ctx.environ)
   259         global gvm_mgr
   260         return gvm_mgr.getHostOnlyIP(name)
   261             
   262 
   263 class os_sdvm_start:
   264     """OpenSecurity '/sdvms/[VM]/start' handler
   265     
   266     - GET: Start specific SecuirtyVM.
   267     """
   268     
   269     def GET(self, name):
   270         log_call(web.ctx.environ)
   271         global gvm_mgr
   272         return gvm_mgr.startVM(name)
   273             
   274 
   275 class os_sdvm_stop:
   276     """OpenSecurity '/sdvms/[VM]/stop' handler
   277     
   278     - GET: stop specific Secuirty VM.
   279     """
   280     
   281     def GET(self, name):
   282         log_call(web.ctx.environ)
   283         global gvm_mgr
   284         return gvm_mgr.stopVM(name)
   285             
   286 
   287 class os_sdvms:
   288     """OpenSecurity '/sdvms' handler
   289     
   290     - GET: list all available secuirty VMs.
   291     - POST: create new security vm.
   292     """
   293     
   294     def GET(self):
   295         """get the list of SDVMs"""
   296         log_call(web.ctx.environ)
   297         global gvm_mgr
   298         return gvm_mgr.listSDVM() 
   299             
   300     def POST(self):
   301         """create a new SDVM"""
   302         log_call(web.ctx.environ)
   303         global gvm_mgr
   304         
   305         # get a new vm-name
   306         name = gvm_mgr.generateSDVMName()
   307         try:
   308             gvm_mgr.createVM(name)
   309         except:
   310             raise web.internalerror()
   311             
   312         return name
   313             
   314 
   315 class os_setup:
   316     """OpenSecurity '/setup' handler
   317     
   318     - GET: Give user some info how to setup the OpenSecurity envvironment
   319     """
   320     
   321     def GET(self):
   322 
   323         log_call(web.ctx.environ)
   324 
   325         page = """
   326         <html>
   327         <body>
   328         <h1>Setup OpenSecurity</h1>
   329         In order to setup OpenSecurity an inital VM image has to be downloaded and imported:<br/>
   330         <ul>
   331             <li>Download initial VM image: <a href="/fetch_initial_image">fetch_initial_image</a>
   332             <li>Import initial VM: <a href="/init">init</a>
   333         </ul>
   334         </body>
   335         </html>
   336         """
   337         return page
   338 
   339 
   340 class os_terminate:
   341     """OpenSecurity '/terminate' handler
   342     
   343     - GET: terminate the opensecurityd.
   344 
   345     YES: this here is bonkers. But the web.py http
   346     server runs infinite until a SystemExit exception
   347     or KeyboardInterrupt exception is raised.
   348 
   349     see: site-packages/web/httpserver.py - line 157ff
   350     see: site-packages/web/wsgiserver/__init__.py - line 1682ff
   351 
   352     So, we invoke a sys.exit(0) here to trigger server.stop().
   353 
   354     TODO: need to find a better way doing this, and not via the
   355           REST api. Maybe hack web.py server code?
   356     """
   357     
   358     def GET(self):
   359         log_call(web.ctx.environ)
   360         global server
   361         server.stop()
   362         global gvm_mgr
   363         gvm_mgr.cleanup()
   364         sys.exit(0)
   365         return None
   366 
   367 
   368 class os_update_template:
   369     """OpenSecurity '/update_template' handler
   370     
   371     - GET: update template vm
   372     """
   373     
   374     def GET(self):
   375         #return gvm_mgr.guestExecute('SecurityDVM', 'sudo apt-get -y update')
   376         global gvm_mgr
   377         log_call(web.ctx.environ)
   378         return gvm_mgr.updateTemplate()
   379 
   380 
   381 class os_vm:
   382     """OpenSecurity '/vms/[VM]' handler
   383     
   384     - GET: list information of arbitrary VM.
   385     """
   386     
   387     def GET(self, name):
   388         log_call(web.ctx.environ)
   389         global gvm_mgr
   390         return gvm_mgr.getVMInfo(name)
   391             
   392 
   393 class os_vms:
   394     """OpenSecurity '/vms' handler
   395     
   396     - GET: list all (also non Security) VMs.
   397     """
   398     
   399     def GET(self):
   400         log_call(web.ctx.environ)
   401         global gvm_mgr
   402         return str(gvm_mgr.listVM()).replace("'",'"')
   403             
   404 
   405 def log_call(web_environ):
   406     """log the incoming call to the REST api"""
   407     try:
   408         call = 'REST ' +  web_environ['REQUEST_METHOD'] + ' ' + web_environ['REQUEST_URI'] + ' from ' + web_environ['REMOTE_ADDR'] + ':' + web_environ['REMOTE_PORT']
   409         logger.debug(call)
   410     except:
   411         pass
   412 
   413 
   414 def main():
   415     """main startup for the opensecuirityd"""
   416 
   417     logger.debug('Starting OpenSecurity REST server')
   418 
   419     # ensure a VMManger is yet loaded
   420     global gvm_mgr
   421     gvm_mgr = vmmanager.VMManager.getInstance()
   422     
   423     # tweak sys.argv to control wep.py server start behavior
   424     sys.argv = [__file__, "8080"]
   425     global server
   426     server = web.application(opensecurity_urls, globals(), autoreload = False)
   427     server.run()
   428     
   429     logger.debug('Stopped OpenSecurity REST server')
   430 
   431 
   432 def stop():
   433     """stop the opensecuirityd"""
   434 
   435     # calling sys.exit() raises a SystemExit exception
   436     # of the WSGI Server to let it wind down
   437     # gracefully
   438     sys.exit(0)
   439 
   440 
   441 
   442 # start
   443 if __name__ == "__main__":
   444     main()
   445