/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Björn Påhlsson
  • Date: 2011-12-12 18:30:47 UTC
  • mto: This revision was merged to the branch mainline in revision 524.
  • Revision ID: belorn@recompile.se-20111212183047-wabvrkb3ozotp6d3
Mandos: refactoring the code handling settings from config files.
        fixed bug with enable/disable clients that has expired while shutdown.

Show diffs side-by-side

added added

removed removed

Lines of Context:
85
85
    except ImportError:
86
86
        SO_BINDTODEVICE = None
87
87
 
88
 
 
89
88
version = "1.4.1"
90
89
stored_state_file = "clients.pickle"
91
90
 
432
431
                          "created", "enabled", "fingerprint",
433
432
                          "host", "interval", "last_checked_ok",
434
433
                          "last_enabled", "name", "timeout")
 
434
    client_defaults = { "timeout": "5m",
 
435
                        "extended_timeout": "15m",
 
436
                        "interval": "2m",
 
437
                        "checker": "fping -q -- %%(host)s",
 
438
                        "host": "",
 
439
                        "approval_delay": "0s",
 
440
                        "approval_duration": "1s",
 
441
                        "approved_by_default": "True",
 
442
                        "enabled": "True",
 
443
                        }
435
444
    
436
445
    def timeout_milliseconds(self):
437
446
        "Return the 'timeout' attribute in milliseconds"
447
456
    
448
457
    def approval_delay_milliseconds(self):
449
458
        return timedelta_to_milliseconds(self.approval_delay)
450
 
    
451
 
    def __init__(self, name = None, config=None):
 
459
 
 
460
    @staticmethod
 
461
    def config_parser(config):
 
462
        """ Construct a new dict of client settings of this form:
 
463
        { client_name: {setting_name: value, ...}, ...}
 
464
        with exceptions for any special settings as defined above"""
 
465
        settings = {}
 
466
        for client_name in config.sections():
 
467
            section = dict(config.items(client_name))
 
468
            client = settings[client_name] = {}
 
469
            
 
470
            # Default copying each value from config to new dict
 
471
            for setting, value in section.iteritems():
 
472
                client[setting] = value
 
473
            
 
474
            # Reformat values from string types to Python types
 
475
            client["approved_by_default"] = config.getboolean(
 
476
                client_name, "approved_by_default")
 
477
            client["enabled"] = config.getboolean(client_name, "enabled")
 
478
            
 
479
            client["fingerprint"] = (section["fingerprint"].upper()
 
480
                                     .replace(" ", ""))
 
481
            if "secret" in section:
 
482
                client["secret"] = section["secret"].decode("base64")
 
483
            elif "secfile" in section:
 
484
                with open(os.path.expanduser(os.path.expandvars
 
485
                                             (section["secfile"])),
 
486
                          "rb") as secfile:
 
487
                    client["secret"] = secfile.read()
 
488
            else:
 
489
                raise TypeError("No secret or secfile for section %s"
 
490
                                % section)
 
491
            client["timeout"] = string_to_delta(section["timeout"])
 
492
            client["extended_timeout"] = string_to_delta(
 
493
                section["extended_timeout"])
 
494
            client["interval"] = string_to_delta(section["interval"])
 
495
            client["approval_delay"] = string_to_delta(
 
496
                section["approval_delay"])
 
497
            client["approval_duration"] = string_to_delta(
 
498
                section["approval_duration"])
 
499
 
 
500
        return settings
 
501
        
 
502
        
 
503
    def __init__(self, config, name = None):
452
504
        """Note: the 'checker' key in 'config' sets the
453
505
        'checker_command' attribute and *not* the 'checker'
454
506
        attribute."""
455
507
        self.name = name
456
 
        if config is None:
457
 
            config = {}
458
508
        logger.debug("Creating client %r", self.name)
459
509
        # Uppercase and remove spaces from fingerprint for later
460
510
        # comparison purposes with return value from the fingerprint()
461
511
        # function
462
 
        self.fingerprint = (config["fingerprint"].upper()
463
 
                            .replace(" ", ""))
 
512
        self.fingerprint = config["fingerprint"]
464
513
        logger.debug("  Fingerprint: %s", self.fingerprint)
465
 
        if "secret" in config:
466
 
            self.secret = config["secret"].decode("base64")
467
 
        elif "secfile" in config:
468
 
            with open(os.path.expanduser(os.path.expandvars
469
 
                                         (config["secfile"])),
470
 
                      "rb") as secfile:
471
 
                self.secret = secfile.read()
472
 
        else:
473
 
            raise TypeError("No secret or secfile for client %s"
474
 
                            % self.name)
475
 
        self.host = config.get("host", "")
 
514
        self.secret = config["secret"]
 
515
        self.host = config["host"]
476
516
        self.created = datetime.datetime.utcnow()
477
 
        self.enabled = config.get("enabled", True)
 
517
        self.enabled = config["enabled"]
478
518
        self.last_approval_request = None
479
519
        if self.enabled:
480
520
            self.last_enabled = datetime.datetime.utcnow()
482
522
            self.last_enabled = None
483
523
        self.last_checked_ok = None
484
524
        self.last_checker_status = None
485
 
        self.timeout = string_to_delta(config["timeout"])
486
 
        self.extended_timeout = string_to_delta(config
487
 
                                                ["extended_timeout"])
488
 
        self.interval = string_to_delta(config["interval"])
 
525
        self.timeout = config["timeout"]
 
526
        self.extended_timeout = config["extended_timeout"]
 
527
        self.interval = config["interval"]
489
528
        self.checker = None
490
529
        self.checker_initiator_tag = None
491
530
        self.disable_initiator_tag = None
497
536
        self.checker_command = config["checker"]
498
537
        self.current_checker_command = None
499
538
        self.approved = None
500
 
        self.approved_by_default = config.get("approved_by_default",
501
 
                                              True)
 
539
        self.approved_by_default = config["approved_by_default"]
502
540
        self.approvals_pending = 0
503
 
        self.approval_delay = string_to_delta(
504
 
            config["approval_delay"])
505
 
        self.approval_duration = string_to_delta(
506
 
            config["approval_duration"])
 
541
        self.approval_delay = config["approval_delay"]
 
542
        self.approval_duration = config["approval_duration"]
507
543
        self.changedstate = (multiprocessing_manager
508
544
                             .Condition(multiprocessing_manager
509
545
                                        .Lock()))
1665
1701
    def sub_process_main(self, request, address):
1666
1702
        try:
1667
1703
            self.finish_request(request, address)
1668
 
        except:
 
1704
        except Exception:
1669
1705
            self.handle_error(request, address)
1670
1706
        self.close_request(request)
1671
1707
    
2065
2101
                                % server_settings["servicename"]))
2066
2102
    
2067
2103
    # Parse config file with clients
2068
 
    client_defaults = { "timeout": "5m",
2069
 
                        "extended_timeout": "15m",
2070
 
                        "interval": "2m",
2071
 
                        "checker": "fping -q -- %%(host)s",
2072
 
                        "host": "",
2073
 
                        "approval_delay": "0s",
2074
 
                        "approval_duration": "1s",
2075
 
                        }
2076
 
    client_config = configparser.SafeConfigParser(client_defaults)
 
2104
    client_config = configparser.SafeConfigParser(Client.client_defaults)
2077
2105
    client_config.read(os.path.join(server_settings["configdir"],
2078
2106
                                    "clients.conf"))
2079
2107
    
2180
2208
        client_class = functools.partial(ClientDBusTransitional,
2181
2209
                                         bus = bus)
2182
2210
    
2183
 
    special_settings = {
2184
 
        # Some settings need to be accessd by special methods;
2185
 
        # booleans need .getboolean(), etc.  Here is a list of them:
2186
 
        "approved_by_default":
2187
 
            lambda section:
2188
 
            client_config.getboolean(section, "approved_by_default"),
2189
 
        "enabled":
2190
 
            lambda section:
2191
 
            client_config.getboolean(section, "enabled"),
2192
 
        }
2193
 
    # Construct a new dict of client settings of this form:
2194
 
    # { client_name: {setting_name: value, ...}, ...}
2195
 
    # with exceptions for any special settings as defined above
2196
 
    client_settings = dict((clientname,
2197
 
                           dict((setting,
2198
 
                                 (value
2199
 
                                  if setting not in special_settings
2200
 
                                  else special_settings[setting]
2201
 
                                  (clientname)))
2202
 
                                for setting, value in
2203
 
                                client_config.items(clientname)))
2204
 
                          for clientname in client_config.sections())
2205
 
    
 
2211
    client_settings = Client.config_parser(client_config)
2206
2212
    old_client_settings = {}
2207
2213
    clients_data = []
2208
2214
    
2241
2247
                    pass
2242
2248
            
2243
2249
            # Clients who has passed its expire date can still be
2244
 
            # enabled if its last checker was sucessful.  Clients
 
2250
            # enabled if its last checker was successful.  Clients
2245
2251
            # whose checker failed before we stored its state is
2246
2252
            # assumed to have failed all checkers during downtime.
2247
2253
            if client["enabled"]:
2248
 
                if client["expires"] <= (datetime.datetime
2249
 
                                         .utcnow()):
2250
 
                    # Client has expired
2251
 
                    if client["last_checker_status"] != 0:
 
2254
                if datetime.datetime.utcnow() >= client["expires"]:
 
2255
                    if not client["last_checked_ok"]:
 
2256
                        logger.warning(
 
2257
                            "disabling client {0} - Client never "
 
2258
                            "performed a successfull checker"
 
2259
                            .format(client["name"]))
 
2260
                        client["enabled"] = False
 
2261
                    elif client["last_checker_status"] != 0:
 
2262
                        logger.warning(
 
2263
                            "disabling client {0} - Client "
 
2264
                            "last checker failed with error code {1}"
 
2265
                            .format(client["name"],
 
2266
                                    client["last_checker_status"]))
2252
2267
                        client["enabled"] = False
2253
2268
                    else:
2254
2269
                        client["expires"] = (datetime.datetime
2259
2274
                                      .Condition
2260
2275
                                      (multiprocessing_manager
2261
2276
                                       .Lock()))
 
2277
            client["checker"] = None
2262
2278
            if use_dbus:
2263
2279
                new_client = (ClientDBusTransitional.__new__
2264
2280
                              (ClientDBusTransitional))
2300
2316
    for clientname in set(old_client_settings) - set(client_settings):
2301
2317
        del tcp_server.clients[clientname]
2302
2318
    for clientname in set(client_settings) - set(old_client_settings):
2303
 
        tcp_server.clients[clientname] = (client_class(name
2304
 
                                                       = clientname,
 
2319
        tcp_server.clients[clientname] = (client_class(name = clientname,
2305
2320
                                                       config =
2306
2321
                                                       client_settings
2307
2322
                                                       [clientname]))
2322
2337
            # "pidfile" was never created
2323
2338
            pass
2324
2339
        del pidfilename
2325
 
        
2326
2340
        signal.signal(signal.SIGINT, signal.SIG_IGN)
2327
2341
    
2328
2342
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
2407
2421
                
2408
2422
                # A list of attributes that will not be stored when
2409
2423
                # shutting down.
2410
 
                exclude = set(("bus", "changedstate", "secret"))
 
2424
                exclude = set(("bus", "changedstate", "secret",
 
2425
                               "checker"))
2411
2426
                for name, typ in (inspect.getmembers
2412
2427
                                  (dbus.service.Object)):
2413
2428
                    exclude.add(name)
2500
2515
    # Must run before the D-Bus bus name gets deregistered
2501
2516
    cleanup()
2502
2517
 
2503
 
 
2504
2518
if __name__ == '__main__':
2505
2519
    main()