def show_configuration(args): config = {} for port in ports_iter(): print('Port {}:'.format(port)) config = port_config(port) pformat = pprint.pformat(config) pformat = re.sub(r'^', ' ', pformat, 0, re.MULTILINE) print(pformat) print('Client configuration:') config = {} set_setting(config, 'server_url', get_client_setting('server_url') or 'MISSING') if get_client_setting('ssl:ca_path'): set_setting(config, 'ssl:ca_path', get_client_setting('ssl:ca_path')) pformat = pprint.pformat(config) pformat = re.sub(r'^', ' ', pformat, 0, re.MULTILINE) print(pformat)
def main(args): if args.yes: maybe_changed = partial(maybe_changed_extended, use_default=True) else: maybe_changed = maybe_changed_extended generate_key('server', gpg_user_ids['server']) generate_key('client', gpg_user_ids['client']) import_key('server', gpg_user_ids['client']) import_key('client', gpg_user_ids['server']) default = not (get_client_setting('loaded') and get_server_setting('loaded')) do_config = default if args.yes else \ get_bool('Do you want to configure things interactively?', default) server_changed = client_changed = False if do_config: if isinstance(get_server_setting('port'), int): # Otherwise, the settings file has been edited to make the port # either a list of ports or a mapping, and we don't want to try to # configure it here. server_changed |= maybe_changed( 'server', 'port', get_int, 'What port should the server listen on?') server_changed |= maybe_changed( 'server', 'local_port', get_int, 'What local-only port should the server use?') configure_ssl = True port = get_server_setting('port') if isinstance(port, dict): for port_number, port_settings in port.items(): if 'ssl' in port_settings: # If there are already port-specific SSL settings, then # don't try to configure SSL in this script. configure_ssl = False if configure_ssl: default = bool( get_server_setting('ssl:certificate', None) or get_server_setting('ssl:key', None)) configure_ssl = maybe_get_bool( 'Do you want the server to use SSL?', default, args.yes) if not configure_ssl: if get_server_setting('ssl:certificate', None): set_server_setting('ssl:certificate', None) server_changed = True if get_server_setting('ssl:key', None): set_server_setting('ssl:key', None) server_changed = True else: while True: server_changed |= maybe_changed('server', 'ssl:certificate', get_string, 'SSL certificate file path:') if os.path.exists(get_server_setting('ssl:certificate')): break print('That file does not exist.') while True: server_changed |= maybe_changed('server', 'ssl:key', get_string, 'SSL key file path:') if os.path.exists(get_server_setting('ssl:key')): break print('That file does not exist.') server_changed |= maybe_changed('server', 'database:host', get_string_or_list, 'Database host:port:') if get_server_setting('database:host'): server_changed |= maybe_changed('server', 'database:replicaset', get_string_none, 'Replicaset name:', empty_ok=True) server_changed |= maybe_changed('server', 'database:name', get_string, 'Database name:') server_changed |= maybe_changed('server', 'database:username', get_string_none, 'Database username:'******'database:username'): server_changed |= maybe_changed('server', 'database:password', get_string, 'Database password:'******'Server', maybe_changed) server_changed |= maybe_changed( 'server', 'audit_cron:enabled', get_bool, 'Do you want to enable the audit cron job?') if get_server_setting('audit_cron:enabled'): server_changed |= maybe_changed( 'server', 'audit_cron:email', get_string, 'What email address should get the audit output?') port = get_server_setting('port') if port == 443: sample_url = 'https://hostname' elif port == 80: sample_url = 'http://hostname' else: sample_url = 'http://hostname:{}'.format(port) prompt = 'URL base, e.g., {}, for clients to reach server:'.format( sample_url) client_changed |= maybe_changed('client', 'server_url', get_string, prompt) client_changed |= maybe_changed('client', 'geolocation_api_key', get_string, 'Google geolocation API key, if any:', empty_ok=True) prompter = partial(get_int, minimum=1) client_changed |= maybe_changed( 'client', 'schedule:collect_interval', prompter, 'How often (minutes) do you want to collect data?') client_changed |= maybe_changed( 'client', 'schedule:submit_interval', prompter, 'How often (minutes) do you want re-try submits?') client_changed |= configure_logging('Client', maybe_changed) save_server_settings() if server_changed: print('Saved server settings.') save_client_settings() if client_changed: print('Saved client settings.') service_file = '/etc/systemd/system/penguindome-server.service' service_exists = os.path.exists(service_file) default = not service_exists if service_exists: prompt = ("Do you want to replace the server's systemd " "configuration?") else: prompt = 'Do you want to add the server to systemd?' do_service = maybe_get_bool(prompt, default, args.yes) if do_service and not args.skipsystemctl: with NamedTemporaryFile('w+') as temp_service_file: temp_service_file.write( dedent('''\ [Unit] Description=PenguinDome Server After=network.target [Service] Type=simple ExecStart={server_exe} [Install] WantedBy=multi-user.target '''.format(server_exe=os.path.join(top_dir, 'bin', 'server')))) temp_service_file.flush() os.chmod(temp_service_file.name, 0o644) shutil.copy(temp_service_file.name, service_file) subprocess.check_output(('systemctl', 'daemon-reload'), stderr=subprocess.STDOUT) service_exists = True if service_exists and not args.skipsystemctl: try: subprocess.check_output( ('systemctl', 'is-enabled', 'penguindome-server'), stderr=subprocess.STDOUT) except Exception: if maybe_get_bool('Do you want to enable the server?', True, args.yes): subprocess.check_output( ('systemctl', 'enable', 'penguindome-server'), stderr=subprocess.STDOUT) is_enabled = True else: is_enabled = False else: is_enabled = True if is_enabled: try: subprocess.check_output( ('systemctl', 'status', 'penguindome-server'), stderr=subprocess.STDOUT) except Exception: if maybe_get_bool('Do you want to start the server?', True, args.yes): subprocess.check_output( ('systemctl', 'start', 'penguindome-server'), stderr=subprocess.STDOUT) else: if maybe_get_bool('Do you want to restart the server?', server_changed, args.yes): subprocess.check_output( ('systemctl', 'restart', 'penguindome-server'), stderr=subprocess.STDOUT) if get_server_setting('audit_cron:enabled'): cron_file = '/etc/cron.d/penguindome-audit' cron_exists = os.path.exists(cron_file) if cron_exists: prompt = 'Do you want to replace the audit crontab?' else: prompt = 'Do you want to install the audit crontab?' do_crontab = maybe_get_bool(prompt, args.audit_crontab or not cron_exists, args.audit_crontab or args.yes) if do_crontab: email = get_server_setting('audit_cron:email') minute = int(random.random() * 60) minute2 = (minute + 1) % 60 crontab = dedent('''\ MAILTO={email} {minute2} * * * * root "{top_dir}/bin/issues" audit --cron '''.format(minute2=minute2, email=email, top_dir=top_dir)) with NamedTemporaryFile('w+') as temp_cron_file: temp_cron_file.write(crontab) temp_cron_file.flush() os.chmod(temp_cron_file.name, 0o644) shutil.copy(temp_cron_file.name, cron_file) print('Installed {}'.format(cron_file)) if (client_changed or not glob.glob(os.path.join(releases_dir, '*.tar.asc')) and not args.skipbuildrelease): if client_changed: prompt = ('Do you want to build a release with the new client ' 'settings?') else: prompt = 'Do you want to build a client release?' if maybe_get_bool(prompt, True, args.yes): # Sometimes sign fails the first time because of GnuPG weirdness. # The client_release script will call sign as well, but we call it # first just in case it fails the first time. try: subprocess.check_output((os.path.join('bin', 'sign'), ), stderr=subprocess.STDOUT) except Exception: pass subprocess.check_output((os.path.join('bin', 'client_release'), ), stderr=subprocess.STDOUT) print('Done!')
def configure_client(args): changed = False url = get_client_setting('server_url') if url: url = list(urlparse(url)) else: url = ['', '', '', '', '', ''] if ':' in url[1]: hostname, port = url[1].split(':') port = int(port) else: hostname = url[1] port = 443 if url[0] == 'https' else 80 if args.hostname: if hostname != args.hostname: hostname = args.hostname changed = True if args.port: if port != args.port: port = args.port changed = True if port == 443 and args.ssl is None: args.ssl = True if args.ssl: if url[0] != 'https': url[0] = 'https' changed = True elif args.ssl is False: if url[0] != 'http': url[0] = 'http' changed = True if port == 443 and url[0] != 'https': print("\n" "WARNING: Are you sure you don't want to use SSL on port 443?\n") verbose_port = port or 80 try: server_port_ssl = get_port_setting( verbose_port, 'ssl:enabled', bool(get_port_setting(verbose_port, 'ssl:certificate'))) except: server_port_ssl = False if server_port_ssl and url[0] != 'https': print('\n' 'WARNING: Port {} on the server is using SSL.\n' ' Does the client need to?\n'.format(verbose_port)) elif not server_port_ssl and url[0] != 'http': print('\n' 'WARNING: Port {} on the server is not using SSL.\n' ' Are you sure the client should?\n'.format( verbose_port)) if not hostname: sys.exit('You must specify hostname.') if url[0] not in ('http', 'https'): changed = True url[0] = 'https' if port == 443 else 80 if port and ((url[0] == 'http' and port != 80) or (url[0] == 'https' and port != 443)): loc = '{}:{}'.format(hostname, port) if url[1] != loc: changed = True url[1] = loc else: url[1] = hostname if args.ssl_ca_file is False: if get_client_setting('ssl:ca_path'): set_client_setting('ssl:ca_path', None) changed = True elif args.ssl_ca_file: try: server_port = int(args.ssl_ca_file) except: pass else: args.ssl_ca_file = get_port_setting(server_port, 'ssl:certificate') if not args.ssl_ca_file: sys.exit('Server port {} does not have an SSL certificate.' .format(server_port)) client_file = os.path.join('client', 'cacert.pem') if not os.path.exists(args.ssl_ca_file): sys.exit('The file {} does not exist.'.format(args.ssl_ca_file)) if not (os.path.exists(client_file) and filecmp.cmp(args.ssl_ca_file, client_file)): shutil.copy(args.ssl_ca_file, client_file) changed = True if get_client_setting('ssl:ca_path') != client_file: set_client_setting('ssl:ca_path', client_file) changed = True url = urlunparse(url) url = re.sub(r'/+$', '', url) if changed: set_client_setting('server_url', url) save_client_settings() print('Updated client configuration.') show_configuration(args) print("\n" "WARNING: Don't forget to build a new client release.\n") else: print('Client configuration unchanged.')
def configure_port(args, add=False): if args.ssl_self_signed and (args.certificate or args.key): sys.exit('--certificate and --key are incompatible with ' '--ssl-self-signed.') changed = False port = args.port ports = get_server_setting('port', None) if not ports: ports = [] elif isinstance(ports, int): ports = [ports] if isinstance(ports, list): ports = {port: {} for port in ports} set_server_setting('port', ports) if port in ports: if add: sys.exit('Port {} is already present.'.format(args.port)) which = 'Configured' else: ports[port] = {} changed = True which = 'Added' if not ports[port]: ports[port] = {} port_settings = ports[port] gps = partial(get_port_setting, port) def ss(setting, value): nonlocal changed set_setting(port_settings, setting, value) changed = True if args.deprecated is not None: if bool(gps('deprecated')) != args.deprecated: ss('deprecated', args.deprecated) if args.ssl_self_signed: cert_file, key_file = make_self_signed_cert(args.ssl_self_signed) ss('ssl:certificate', cert_file) ss('ssl:key', key_file) if args.ssl is not False: args.ssl = True if args.certificate and gps('ssl:certificate') != args.certificate: if not (args.key or gps('ssl:key')): sys.exit('You must specify both a certificate and a key.') if not os.path.exists(args.certificate): sys.exit('The certificate file {} does not exist'.format( args.certificate)) ss('ssl:certificate', args.certificate) if args.key and gps('ssl:key') != args.key: if not gps('ssl:certificate'): sys.exit('You must specify both a certificate and a key.') if not os.path.exists(args.key): sys.exit('The key file {} does not exist'.format( args.key)) ss('ssl:key', args.key) if args.ssl: if not (gps('ssl:certificate') and gps('ssl:key')): sys.exit('You must specify a certificate and key to enable SSL.') if not gps('ssl:enabled', bool(gps('ssl:certificate'))): ss('ssl:enabled', True) elif args.ssl is False: if gps('ssl:enabled', bool(gps('ssl:certificate'))): ss('ssl:enabled', False) if changed: save_server_settings() url = get_client_setting('server_url') if url: url = urlparse(url) if ':' in url[1]: _, client_port = url[1].split(':') client_port = int(client_port) else: client_port = {'http': 80, 'https': 443}[url[0]] client_ssl = {'http': False, 'https': True}[url[0]] if port == client_port: if client_ssl != gps( 'ssl:enabled', bool(gps('ssl:certificate'))): print('\n' 'WARNING: Client is configured to use port {} and {}' 'using SSL.\n' ' Should it be?\n'.format( port, '' if client_ssl else 'not ')) if gps('deprecated'): print('\n' 'WARNING: Client is configured to use deprecated ' 'port {}.\n' ' Do you need to change the client port?'. format(port)) print('{} port {}.'.format(which, port)) print("\n" "WARNING: Don't forget to restart the server.\n") if args.ssl_self_signed and args.ssl: print("\n" "WARNING: Don't forget to configure client CA file\n" " (see help for 'configure-client').\n") show_configuration(args) else: print('No changes.')