From 594adb98773d365efda5a7449f66042015645f7f Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Fri, 29 Oct 2010 20:24:31 +0200 Subject: Log script options to logfile Uses a new subclass IPAOptionParser in scripts instead of OptionParser from the standard python library. IPAOptionParser uses its own IPAOption class to store options, which adds a new 'sensitive' attribute. https://fedorahosted.org/freeipa/ticket/393 --- install/tools/ipa-dns-install | 13 ++++++---- install/tools/ipa-ldap-updater | 10 +++++--- install/tools/ipa-replica-install | 12 +++++---- install/tools/ipa-server-install | 23 ++++++++++------- ipa-client/ipa-install/ipa-client-install | 12 +++++---- ipapython/config.py | 42 ++++++++++++++++++++++++++++++- 6 files changed, 83 insertions(+), 29 deletions(-) diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install index ece77ec7c..8502e3b63 100755 --- a/install/tools/ipa-dns-install +++ b/install/tools/ipa-dns-install @@ -19,7 +19,6 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -from optparse import OptionParser import traceback from ipaserver.plugins.ldap2 import ldap2 @@ -28,11 +27,12 @@ from ipaserver.install.installutils import * from ipapython import version from ipapython import ipautil, sysrestore from ipalib import api, errors, util +from ipapython.config import IPAOptionParser def parse_options(): - parser = OptionParser(version=version.VERSION) + parser = IPAOptionParser(version=version.VERSION) parser.add_option("-p", "--ds-password", dest="dm_password", - help="admin password") + sensitive=True, help="admin password") parser.add_option("-d", "--debug", dest="debug", action="store_true", default=False, help="print debugging information") parser.add_option("--ip-address", dest="ip_address", help="Master Server IP Address") @@ -46,6 +46,7 @@ def parse_options(): default=False, help="unattended installation never prompts the user") options, args = parser.parse_args() + safe_options = parser.get_safe_opts(options) if options.forwarders and options.no_forwarders: parser.error("You cannot specify a --forwarder option together with --no-forwarders") @@ -56,7 +57,7 @@ def parse_options(): if not options.forwarders and not options.no_forwarders: parser.error("You must specify at least one --forwarder option or --no-forwarders option") - return options + return safe_options, options def resolve_host(host_name): ip = None @@ -76,7 +77,7 @@ def resolve_host(host_name): return ip def main(): - options = parse_options() + safe_options, options = parse_options() if os.getegid() != 0: print "Must be root to setup server" @@ -85,6 +86,8 @@ def main(): standard_logging_setup("/var/log/ipaserver-install.log", options.debug, filemode='a') print "\nThe log file for this installation can be found in /var/log/ipaserver-install.log" + logging.debug('%s was invoked with options: %s' % (sys.argv[0], safe_options)) + global fstore fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore') diff --git a/install/tools/ipa-ldap-updater b/install/tools/ipa-ldap-updater index f3b83ce06..51db0124e 100755 --- a/install/tools/ipa-ldap-updater +++ b/install/tools/ipa-ldap-updater @@ -25,7 +25,7 @@ import sys try: - from optparse import OptionParser + from ipapython.config import IPAOptionParser from ipapython import ipautil, config from ipaserver.install import installutils from ipaserver.install.ldapupdate import LDAPUpdate, BadSyntax, UPDATES_DIR @@ -44,7 +44,7 @@ error was: def parse_options(): usage = "%prog [options] input_file(s)\n" usage += "%prog [options]\n" - parser = OptionParser(usage=usage, formatter=config.IPAFormatter()) + parser = IPAOptionParser(usage=usage, formatter=config.IPAFormatter()) parser.add_option("-d", "--debug", action="store_true", dest="debug", help="Display debugging information about the update(s)") @@ -59,10 +59,11 @@ def parse_options(): config.add_standard_options(parser) options, args = parser.parse_args() + safe_options = parser.get_safe_opts(options) config.init_config(options) - return options, args + return safe_options, options, args def get_dirman_password(): """Prompt the user for the Directory Manager password and verify its @@ -75,7 +76,7 @@ def get_dirman_password(): def main(): loglevel = logging.INFO - options, args = parse_options() + safe_options, options, args = parse_options() if options.debug: loglevel = logging.DEBUG @@ -95,6 +96,7 @@ def main(): logging.basicConfig(level=loglevel, format='%(levelname)s %(message)s', filename='/var/log/ipaupgrade.log') + logging.debug('%s was invoked with arguments %s and options: %s' % (sys.argv[0], args, safe_options)) realm = krbV.default_context().default_realm upgrade = IPAUpgrade(realm, files, live_run=not options.test) upgrade.create_instance() diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install index e54101202..e4aae4aa3 100755 --- a/install/tools/ipa-replica-install +++ b/install/tools/ipa-replica-install @@ -31,6 +31,7 @@ from ipaserver.install import bindinstance, httpinstance, ntpinstance, certs from ipaserver.plugins.ldap2 import ldap2 from ipapython import version from ipalib import api, errors, util +from ipapython.config import IPAOptionParser CACERT="/usr/share/ipa/html/ca.crt" @@ -50,14 +51,13 @@ class ReplicaConfig: self.subject_base = "O=IPA" def parse_options(): - from optparse import OptionParser usage = "%prog [options] REPLICA_FILE" - parser = OptionParser(usage=usage, version=version.VERSION) + parser = IPAOptionParser(usage=usage, version=version.VERSION) parser.add_option("-N", "--no-ntp", dest="conf_ntp", action="store_false", help="do not configure ntp", default=True) parser.add_option("-d", "--debug", dest="debug", action="store_true", default=False, help="gather extra debugging information") - parser.add_option("-p", "--password", dest="password", + parser.add_option("-p", "--password", dest="password", sensitive=True, help="Directory Manager (existing master) password") parser.add_option("--setup-dns", dest="setup_dns", action="store_true", default=False, help="configure bind with our zone") @@ -70,6 +70,7 @@ def parse_options(): help="Do not use DNS for hostname lookup during installation") options, args = parser.parse_args() + safe_options = parser.get_safe_opts(options) if len(args) != 1: parser.error("you must provide a file generated by ipa-replica-prepare") @@ -84,7 +85,7 @@ def parse_options(): elif not options.forwarders and not options.no_forwarders: parser.error("You must specify at least one --forwarder option or --no-forwarders option") - return options, args[0] + return safe_options, options, args[0] def get_dirman_password(): return installutils.read_password("Directory Manager (existing master)", confirm=False, validate=False) @@ -261,8 +262,9 @@ def check_bind(): sys.exit(1) def main(): - options, filename = parse_options() + safe_options, options, filename = parse_options() installutils.standard_logging_setup("/var/log/ipareplica-install.log", options.debug) + logging.debug('%s was invoked with argument "%s" and options: %s' % (sys.argv[0], filename, safe_options)) if not ipautil.file_exists(filename): sys.exit("Replica file %s does not exist" % filename) diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install index ebf71666e..c5b3333b9 100755 --- a/install/tools/ipa-server-install +++ b/install/tools/ipa-server-install @@ -35,7 +35,6 @@ import signal import shutil import glob import traceback -from optparse import OptionParser from ConfigParser import RawConfigParser import random @@ -55,6 +54,7 @@ from ipaserver.plugins.ldap2 import ldap2 from ipapython import sysrestore from ipapython.ipautil import * from ipalib import api, errors, util +from ipapython.config import IPAOptionParser pw_name = None @@ -65,7 +65,7 @@ MAXINT_32BIT = 2147483648 def parse_options(): namespace = random.randint(1000000, (MAXINT_32BIT - 1000000)) - parser = OptionParser(version=version.VERSION) + parser = IPAOptionParser(version=version.VERSION) parser.add_option("-u", "--user", dest="ds_user", help="ds user") parser.add_option("-r", "--realm", dest="realm_name", @@ -73,10 +73,12 @@ def parse_options(): parser.add_option("-n", "--domain", dest="domain_name", help="domain name") parser.add_option("-p", "--ds-password", dest="dm_password", - help="admin password") - parser.add_option("-P", "--master-password", dest="master_password", + sensitive=True, help="admin password") + parser.add_option("-P", "--master-password", + dest="master_password", sensitive=True, help="kerberos master password (normally autogenerated)") - parser.add_option("-a", "--admin-password", dest="admin_password", + parser.add_option("-a", "--admin-password", + sensitive=True, dest="admin_password", help="admin user kerberos password") parser.add_option("-d", "--debug", dest="debug", action="store_true", default=False, help="print debugging information") @@ -108,9 +110,9 @@ def parse_options(): help="PKCS#12 file containing the Directory Server SSL certificate") parser.add_option("--http_pkcs12", dest="http_pkcs12", help="PKCS#12 file containing the Apache Server SSL certificate") - parser.add_option("--dirsrv_pin", dest="dirsrv_pin", + parser.add_option("--dirsrv_pin", dest="dirsrv_pin", sensitive=True, help="The password of the Directory Server PKCS#12 file") - parser.add_option("--http_pin", dest="http_pin", + parser.add_option("--http_pin", dest="http_pin", sensitive=True, help="The password of the Apache Server PKCS#12 file") parser.add_option("--no-host-dns", dest="no_host_dns", action="store_true", default=False, @@ -126,6 +128,7 @@ def parse_options(): action="store_true", help="Don't install allow_all HBAC rule") options, args = parser.parse_args() + safe_options = parser.get_safe_opts(options) if not options.setup_dns: if options.forwarders: @@ -167,7 +170,7 @@ def parse_options(): if (options.external_cert_file and not os.path.isabs(options.external_cert_file)): parser.error("--external-cert-file must use an absolute path") - return options + return safe_options, options def signal_handler(signum, frame): global ds @@ -432,7 +435,7 @@ def main(): global uninstalling ds = None - options = parse_options() + safe_options, options = parse_options() if os.getegid() != 0: print "Must be root to set up server" @@ -450,6 +453,8 @@ def main(): if dsinstance.DsInstance().is_configured() or cainstance.CADSInstance().is_configured(): sys.exit("IPA server is already configured on this system.") + logging.debug('%s was invoked with options: %s' % (sys.argv[0], safe_options)) + global fstore fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore') diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install index b1e001cd3..8f4b9d2bb 100755 --- a/ipa-client/ipa-install/ipa-client-install +++ b/ipa-client/ipa-install/ipa-client-install @@ -28,7 +28,6 @@ try: import logging import tempfile import getpass - from optparse import OptionParser import ipaclient.ipadiscovery import ipaclient.ipachangeconf import ipaclient.ntpconf @@ -36,6 +35,7 @@ try: from ipapython import sysrestore from ipapython import version from ipapython import certmonger + from ipapython.config import IPAOptionParser import SSSDConfig from ConfigParser import RawConfigParser except ImportError: @@ -50,7 +50,7 @@ error was: client_nss_nickname = 'IPA Machine Certificate - %s' % socket.getfqdn() def parse_options(): - parser = OptionParser(version=version.VERSION) + parser = IPAOptionParser(version=version.VERSION) parser.add_option("--domain", dest="domain", help="domain name") parser.add_option("--server", dest="server", help="IPA server") parser.add_option("--realm", dest="realm_name", help="realm name") @@ -66,7 +66,7 @@ def parse_options(): help="do not configure sssd", default=True, dest="sssd") parser.add_option("-N", "--no-ntp", action="store_false", help="do not configure ntp", default=True, dest="conf_ntp") - parser.add_option("-w", "--password", dest="password", + parser.add_option("-w", "--password", dest="password", sensitive=True, help="password to join the IPA realm (assumes bulk password unless principal is also set)"), parser.add_option("-W", dest="prompt_password", action="store_true", default=False, @@ -83,11 +83,12 @@ def parse_options(): default=False, help="uninstall an existing installation") options, args = parser.parse_args() + safe_opts = parser.get_safe_opts(options) if (options.server and not options.domain): parser.error("--server cannot be used without providing --domain") - return options + return safe_opts, options def logging_setup(options): # Always log everything (i.e., DEBUG) to the log @@ -500,8 +501,9 @@ def configure_sssd_conf(fstore, cli_domain, cli_server, options): return 0 def main(): - options = parse_options() + safe_options, options = parse_options() logging_setup(options) + logging.debug('%s was invoked with options: %s' % (sys.argv[0], safe_options)) dnsok = False env={"PATH":"/bin:/sbin:/usr/kerberos/bin:/usr/kerberos/sbin:/usr/bin:/usr/sbin"} diff --git a/ipapython/config.py b/ipapython/config.py index 12d916cff..4df47dea1 100644 --- a/ipapython/config.py +++ b/ipapython/config.py @@ -18,7 +18,7 @@ # import ConfigParser -from optparse import OptionParser, IndentedHelpFormatter +from optparse import Option, Values, OptionParser, IndentedHelpFormatter import socket import ipapython.dnsclient @@ -46,6 +46,46 @@ class IPAFormatter(IndentedHelpFormatter): ret += "%s %s\n" % (spacing, line) return ret +class IPAOption(Option): + """ + optparse.Option subclass with support of options labeled as + security-sensitive such as passwords. + """ + ATTRS = Option.ATTRS + ["sensitive"] + +class IPAOptionParser(OptionParser): + """ + optparse.OptionParser subclass that uses IPAOption by default + for storing options. + """ + def __init__(self, + usage=None, + option_list=None, + option_class=IPAOption, + version=None, + conflict_handler="error", + description=None, + formatter=None, + add_help_option=True, + prog=None): + OptionParser.__init__(self, usage, option_list, option_class, + version, conflict_handler, description, + formatter, add_help_option, prog) + + def get_safe_opts(self, opts): + """ + Returns all options except those with sensitive=True in the same + fashion as parse_args would + """ + all_opts_dict = dict([ (o.dest, o) for o in self._get_all_options() if hasattr(o, 'sensitive') ]) + safe_opts_dict = {} + + for option, value in opts.__dict__.iteritems(): + if all_opts_dict[option].sensitive != True: + safe_opts_dict[option] = value + + return Values(safe_opts_dict) + def verify_args(parser, args, needed_args = None): """Verify that we have all positional arguments we need, if not, exit.""" if needed_args: -- cgit