add ssl certificate fields for Monitor in default_config.xml
authorStephen Soltesz <soltesz@cs.princeton.edu>
Tue, 1 Sep 2009 00:01:13 +0000 (00:01 +0000)
committerStephen Soltesz <soltesz@cs.princeton.edu>
Tue, 1 Sep 2009 00:01:13 +0000 (00:01 +0000)
add ssl certificate generation for Monitor in plc.d/ssl

separate bootcd values from bootmanager configuration
build.sh now collects all the certificates that it needs to authenticate the
https sessions. As well, the BM configuration includes several new values that
control how and where it uploads logs.

BOOT_API_SERVER remains as the API server for all API calls
BOOT_SERVER is the host from which the bootstrapfs and other files are fetched.
MONITOR_SERVER is the host where logs are uploaded
UPLOAD_LOG_SCRIPT is the path to the upload script or entry point.

If the configuration value PLC_MONITOR_ENABLED=false, then MONITOR_SERVER=BOOT_SERVER
It is now much easier to extend BM in the future to upload additional files
such as for hardware information, SMART data, commands run by root users
during the session, etc.

build.sh
source/BootManager.py
source/BootServerRequest.py
source/configuration
source/steps/InstallBootstrapFS.py
source/steps/InstallPartitionDisks.py
source/steps/ReadNodeConfiguration.py
source/steps/WriteNetworkConfig.py

index 5fa20a8..411111c 100755 (executable)
--- a/build.sh
+++ b/build.sh
@@ -85,6 +85,19 @@ echo '($UUDECODE | /bin/tar -C /tmp -xj) << _EOF_' >> $DEST_SCRIPT
 sed -i -e "s@^BOOT_API_SERVER.*@BOOT_API_SERVER=https://$PLC_API_HOST:443/$PLC_API_PATH/@" \
     $srcdir/source/configuration
 
+sed -i -e "s@^BOOT_SERVER.*@BOOT_SERVER=$PLC_BOOT_HOST@" $srcdir/source/configuration
+if [ "$PLC_MONITOR_ENABLED" = "1" ]; then
+    MONITOR_SERVER=$PLC_MONITOR_HOST
+else
+    MONITOR_SERVER=$PLC_BOOT_HOST
+fi
+sed -i -e "s@^MONITOR_SERVER.*@MONITOR_SERVER=$MONITOR_SERVER@" $srcdir/source/configuration
+
+install -D -m 644 $PLC_BOOT_CA_SSL_CRT $srcdir/source/cacert/$PLC_BOOT_HOST/cacert.pem
+if [ -f $PLC_MONITOR_CA_SSL_CRT ] ; then 
+       install -D -m 644 $PLC_MONITOR_CA_SSL_CRT $srcdir/source/cacert/$PLC_MONITOR_HOST/cacert.pem
+fi
+
 # Replace the default debug SSH key
 if [ -f "$PLC_DEBUG_SSH_KEY_PUB" ] ; then
     install -D -m 644 "$PLC_DEBUG_SSH_KEY_PUB" $srcdir/source/debug_files/debug_root_ssh_key
index 370044f..714aa15 100755 (executable)
@@ -18,7 +18,7 @@ import BootServerRequest
 
 # all output is written to this file
 BM_NODE_LOG= "/tmp/bm.log"
-UPLOAD_LOG_SCRIPT = "/boot/upload-bmlog.php"
+VARS_FILE = "configuration"
 
 # the new contents of PATH when the boot manager is running
 BIN_PATH= ('/usr/local/bin',
@@ -27,7 +27,43 @@ BIN_PATH= ('/usr/local/bin',
            '/usr/sbin',
            '/bin',
            '/sbin')
-           
+
+def read_configuration_file(filename):
+    # read in and store all variables in VARS_FILE into each line
+    # is in the format name=val (any whitespace around the = is
+    # removed. everything after the = to the end of the line is
+    # the value
+    vars = {}
+    vars_file= file(filename,'r')
+    validConfFile = True
+    for line in vars_file:
+        # if its a comment or a whitespace line, ignore
+        if line[:1] == "#" or string.strip(line) == "":
+            continue
+
+        parts= string.split(line,"=")
+        if len(parts) != 2:
+            validConfFile = False
+            raise Exception( "Invalid line in vars file: %s" % line )
+
+        name= string.strip(parts[0])
+        value= string.strip(parts[1])
+        value= value.replace("'", "")   # remove quotes
+        value= value.replace('"', "")   # remove quotes
+        vars[name]= value
+
+    vars_file.close()
+    if not validConfFile:
+        raise Exception( "Unable to read configuration vars." )
+
+    # find out which directory we are running it, and set a variable
+    # for that. future steps may need to get files out of the bootmanager
+    # directory
+    current_dir= os.getcwd()
+    vars['BM_SOURCE_DIR']= current_dir
+
+    return vars
+
 ##############################
 class log:
 
@@ -40,6 +76,14 @@ class log:
         except:
             print( "bootmanager log : Unable to open output file %r, continuing"%OutputFilePath )
             self.OutputFile= None
+
+        self.VARS = None
+        try:
+            vars = read_configuration_file(VARS_FILE)
+            self.VARS = vars
+        except Exception, e:
+            self.LogEntry( str(e) )
+            return
     
     def LogEntry( self, str, inc_newline= 1, display_screen= 1 ):
         now=time.strftime(log.format, time.localtime())
@@ -72,22 +116,24 @@ class log:
         if self.OutputFile is not None:
             self.OutputFile.flush()
 
-            self.LogEntry( "Uploading logs to %s" % UPLOAD_LOG_SCRIPT )
+            self.LogEntry( "Uploading logs to %s" % self.VARS['UPLOAD_LOG_SCRIPT'] )
             
             self.OutputFile.close()
             self.OutputFile= None
 
-            bs_request = BootServerRequest.BootServerRequest()
-            bs_request.MakeRequest(PartialPath = UPLOAD_LOG_SCRIPT,
+            hostname= self.VARS['INTERFACE_SETTINGS']['hostname'] + "." + \
+                      self.VARS['INTERFACE_SETTINGS']['domainname']
+            bs_request = BootServerRequest.BootServerRequest(self.VARS)
+            bs_request.MakeRequest(PartialPath = self.VARS['UPLOAD_LOG_SCRIPT'],
                                    GetVars = None, PostVars = None,
-                                   FormData = ["log=@" + self.OutputFilePath],
+                                   FormData = ["log=@" + self.OutputFilePath,
+                                   "hostname=" + hostname, "type=bm.log"],
                                    DoSSL = True, DoCertCheck = True)
 
 ##############################
 class BootManager:
 
     # file containing initial variables/constants
-    VARS_FILE = "configuration"
 
     # the set of valid node run states
     NodeRunStates = {'reinstall':None,
@@ -105,46 +151,16 @@ class BootManager:
 
         # set to 1 if we can run after initialization
         self.CAN_RUN = 0
-             
-        # read in and store all variables in VARS_FILE into each line
-        # is in the format name=val (any whitespace around the = is
-        # removed. everything after the = to the end of the line is
-        # the value
-        vars = {}
-        vars_file= file(self.VARS_FILE,'r')
-        validConfFile = True
-        for line in vars_file:
-            # if its a comment or a whitespace line, ignore
-            if line[:1] == "#" or string.strip(line) == "":
-                continue
-
-            parts= string.split(line,"=")
-            if len(parts) != 2:
-                self.LOG.LogEntry( "Invalid line in vars file: %s" % line )
-                validConfFile = False
-                break
-
-            name= string.strip(parts[0])
-            value= string.strip(parts[1])
-            vars[name]= value
-
-        vars_file.close()
-        if not validConfFile:
-            self.LOG.LogEntry( "Unable to read configuration vars." )
-            return
-
-        # find out which directory we are running it, and set a variable
-        # for that. future steps may need to get files out of the bootmanager
-        # directory
-        current_dir= os.getcwd()
-        vars['BM_SOURCE_DIR']= current_dir
 
+        if log.VARS:
+            # this contains a set of information used and updated by each step
+            self.VARS= log.VARS
+        else:
+            return
+             
         # not sure what the current PATH is set to, replace it with what
         # we know will work with all the boot cds
         os.environ['PATH']= string.join(BIN_PATH,":")
-                   
-        # this contains a set of information used and updated by each step
-        self.VARS= vars
 
         self.CAN_RUN= 1
 
index 5313a23..4968ac6 100644 (file)
@@ -36,46 +36,29 @@ class BootServerRequest:
     # /mnt/cdrom is typically after the machine has come up,
     # and /usr is when the boot cd is running
     CDROM_MOUNT_PATH = ("/mnt/cdrom/","/usr/")
+    BOOTSERVER_CERTS= {}
+    MONITORSERVER_CERTS= {}
+    BOOTCD_VERSION=""
+    HTTP_SUCCESS=200
+    HAS_BOOTCD=0
+    USE_PROXY=0
+    PROXY=0
 
-    # this is the server to contact if we don't have a bootcd
-    DEFAULT_BOOT_SERVER = "boot.planet-lab.org"
-
-    BOOTCD_VERSION_FILE = "bootme/ID"
-    BOOTCD_SERVER_FILE = "bootme/BOOTSERVER"
-    BOOTCD_SERVER_CERT_DIR = "bootme/cacert"
-    CACERT_NAME = "cacert.pem"
-    
-    # location of file containing http/https proxy info, if needed
-    PROXY_FILE = '/etc/planetlab/http_proxy'
-
+    # in seconds, how maximum time allowed for connect
+    DEFAULT_CURL_CONNECT_TIMEOUT=30
+    # in seconds, maximum time allowed for any transfer
+    DEFAULT_CURL_MAX_TRANSFER_TIME=3600
     # location of curl executable, if pycurl isn't available
     # and the DownloadFile method is called (backup, only
     # really need for the boot cd environment where pycurl
     # doesn't exist
     CURL_CMD = 'curl'
+    CURL_SSL_VERSION=3
 
-    # in seconds, how maximum time allowed for connect
-    DEFAULT_CURL_CONNECT_TIMEOUT = 30
-
-    # in seconds, maximum time allowed for any transfer
-    DEFAULT_CURL_MAX_TRANSFER_TIME = 3600
-
-    CURL_SSL_VERSION = 3
-
-    HTTP_SUCCESS = 200
-
-    # proxy variables
-    USE_PROXY = 0
-    PROXY = 0
-
-    # bootcd variables
-    HAS_BOOTCD = 0
-    BOOTCD_VERSION = ""
-    BOOTSERVER_CERTS= {}
-
-    def __init__(self, verbose=0):
+    def __init__(self, vars, verbose=0):
 
         self.VERBOSE= verbose
+        self.VARS=vars
             
         # see if we have a boot cd mounted by checking for the version file
         # if HAS_BOOTCD == 0 then either the machine doesn't have
@@ -87,7 +70,7 @@ class BootServerRequest:
 
             os.system("/bin/mount %s > /dev/null 2>&1" % path )
                 
-            version_file= path + self.BOOTCD_VERSION_FILE
+            version_file= self.VARS['BOOTCD_VERSION_FILE'] % {'path' : path}
             self.Message( "Looking for version file %s" % version_file )
 
             if os.access(version_file, os.R_OK) == 0:
@@ -97,7 +80,6 @@ class BootServerRequest:
                 self.HAS_BOOTCD=1
                 break
 
-
         if self.HAS_BOOTCD:
 
             # check the version of the boot cd, and locate the certs
@@ -116,29 +98,34 @@ class BootServerRequest:
             # right now, all the versions of the bootcd are supported,
             # so no need to check it
             
-            # create a list of the servers we should
-            # attempt to contact, and the certs for each
-            server_list= path + self.BOOTCD_SERVER_FILE
-            self.Message( "Getting list of servers off of cd from %s." %
-                          server_list )
-            
-            bootservers_f= file(server_list,"r")
-            bootservers= bootservers_f.readlines()
-            bootservers_f.close()
+            self.Message( "Getting server from configuration" )
             
+            bootservers= [ self.VARS['BOOT_SERVER'] ]
             for bootserver in bootservers:
                 bootserver = string.strip(bootserver)
-                cacert_path= "%s/%s/%s/%s" % \
-                             (path,self.BOOTCD_SERVER_CERT_DIR,
-                              bootserver,self.CACERT_NAME)
+                cacert_path= "%s/%s/%s" % \
+                             (self.VARS['SERVER_CERT_DIR'] % {'path' : path},
+                              bootserver,self.VARS['CACERT_NAME'])
                 if os.access(cacert_path, os.R_OK):
                     self.BOOTSERVER_CERTS[bootserver]= cacert_path
 
+            monitorservers= [ self.VARS['MONITOR_SERVER'] ]
+            for monitorserver in monitorservers:
+                monitorserver = string.strip(monitorserver)
+                cacert_path= "%s/%s/%s" % \
+                             (self.VARS['SERVER_CERT_DIR'] % {'path' : path},
+                              monitorserver,self.VARS['CACERT_NAME'])
+                if os.access(cacert_path, os.R_OK):
+                    self.MONITORSERVER_CERTS[monitorserver]= cacert_path
+
             self.Message( "Set of servers to contact: %s" %
                           str(self.BOOTSERVER_CERTS) )
+            self.Message( "Set of servers to upload to: %s" %
+                          str(self.MONITORSERVER_CERTS) )
         else:
             self.Message( "Using default boot server address." )
-            self.BOOTSERVER_CERTS[self.DEFAULT_BOOT_SERVER]= ""
+            self.BOOTSERVER_CERTS[self.VARS['DEFAULT_BOOT_SERVER']]= ""
+            self.MONITORSERVER_CERTS[self.VARS['DEFAULT_BOOT_SERVER']]= ""
 
 
     def CheckProxy( self ):
@@ -146,11 +133,11 @@ class BootServerRequest:
         self.USE_PROXY= 0
         self.Message( "Checking existance of proxy config file..." )
         
-        if os.access(self.PROXY_FILE, os.R_OK) and \
-               os.path.isfile(self.PROXY_FILE):
-            self.PROXY= string.strip(file(self.PROXY_FILE,'r').readline())
+        if os.access(self.VARS['PROXY_FILE'], os.R_OK) and \
+               os.path.isfile(self.VARS['PROXY_FILE']):
+            self.PROXY= string.strip(file(self.VARS['PROXY_FILE'],'r').readline())
             self.USE_PROXY= 1
-            self.Message( "Using proxy %s." % self.PROXY )
+            self.Message( "Using proxy %s." % self.PROXY)
         else:
             self.Message( "Not using any proxy." )
 
@@ -246,11 +233,15 @@ class BootServerRequest:
 
         # now, attempt to make the request, starting at the first
         # server in the list
+        if FormData:
+            cert_list = self.MONITORSERVER_CERTS
+        else:
+            cert_list = self.BOOTSERVER_CERTS
         
-        for server in self.BOOTSERVER_CERTS:
+        for server in cert_list:
             self.Message( "Contacting server %s." % server )
                         
-            certpath = self.BOOTSERVER_CERTS[server]
+            certpath = cert_list[server]
 
             
             # output what we are going to be doing
@@ -335,7 +326,6 @@ class BootServerRequest:
 
                 if DoSSL:
                     cmdline = cmdline + "--sslv%d " % self.CURL_SSL_VERSION
-
                     if DoCertCheck:
                         cmdline = cmdline + "--cacert %s " % certpath
                  
index 152cc75..e21933a 100644 (file)
@@ -6,10 +6,24 @@
 # the current version of the bootmanager
 VERSION=3.2
 
-
+# this is the server to contact if we don't have a bootcd
+DEFAULT_BOOT_SERVER=boot.planet-lab.org
 # full url to which api server to contact
 BOOT_API_SERVER=https://boot.planet-lab.org:443/PLCAPI/
 
+# keep redundant information to plc_config for simplicity
+BOOT_SERVER=boot.planet-lab.org
+
+# hostname for MyOps server
+MONITOR_SERVER=monitor.planet-lab.org
+#UPLOAD_LOG_SCRIPT=/monitor/upload
+UPLOAD_LOG_SCRIPT=/boot/upload-bmlog.php
+
+# bootcd variables : use %(path)s for path relative to bootcd
+BOOTCD_VERSION_FILE='%(path)s/bootme/ID'
+SERVER_CERT_DIR=/tmp/source/cacert
+CACERT_NAME=cacert.pem
+
 
 # path to store temporary files during the install,
 # do not include trailing slashes
@@ -80,3 +94,7 @@ INSTALL_LANGS=en_US
 
 # number of auth failures before starting debug mode
 NUM_AUTH_FAILURES_BEFORE_DEBUG=2
+
+
+# location of file containing http/https proxy info, if needed
+PROXY_FILE=/etc/planetlab/http_proxy
index 72cfe16..2c49acc 100644 (file)
@@ -73,7 +73,7 @@ def Run( vars, log ):
         log.write( "Missing partition in PARTITIONS: %s\n" % part )
         return 0   
 
-    bs_request= BootServerRequest.BootServerRequest()
+    bs_request= BootServerRequest.BootServerRequest(vars)
     
     log.write( "turning on swap space\n" )
     utils.sysexec( "swapon %s" % PARTITIONS["swap"], log )
index 0e65270..5bafb9f 100644 (file)
@@ -67,7 +67,7 @@ def Run( vars, log ):
     except ValueError, var:
         raise BootManagerException, "Variable in vars, shouldn't be: %s\n" % var
 
-    bs_request= BootServerRequest.BootServerRequest()
+    bs_request= BootServerRequest.BootServerRequest(vars)
 
     
     # disable swap if its on
index caef8fe..e51e9b0 100644 (file)
@@ -473,7 +473,7 @@ def __parse_configuration_file( vars, log, file_contents ):
         log.write( "Configuration file does not contain the node_id value.\n" )
         log.write( "Querying PLC for node_id.\n" )
 
-        bs_request= BootServerRequest.BootServerRequest()
+        bs_request= BootServerRequest.BootServerRequest(vars)
         
         postVars= {"mac_addr" : INTERFACE_SETTINGS["mac"]}
         result= bs_request.DownloadFile( "%s/getnodeid.php" %
index 6c3b2b8..cef1f08 100644 (file)
@@ -132,7 +132,7 @@ def Run( vars, log ):
     except :
         log.write(" .. Failed.  Using old method. -- stack trace follows\n")
         traceback.print_exc(file=log.OutputFile)
-        bs= BootServerRequest.BootServerRequest()
+        bs= BootServerRequest.BootServerRequest(vars)
         if bs.BOOTSERVER_CERTS:
             print >> plc_config, "PLC_BOOT_HOST='%s'" % bs.BOOTSERVER_CERTS.keys()[0]
         print >> plc_config, "PLC_API_HOST='%s'" % host