def make(self, log): output_dir = settings.get('install_path') + 'etc/modsec/' output_file = output_dir + 'rules.conf' # if os.path.exists(output_dir): # shutil.rmtree(output_dir) filelist = glob.glob(os.path.join(output_dir, "*.data")) filelist.extend(glob.glob(os.path.join(output_dir, "*.mapping"))) filelist.extend(glob.glob(os.path.join(output_dir, "*.conf"))) for f in filelist: os.remove(f) if not os.path.exists(output_dir): os.makedirs(output_dir) with open(output_file, 'w+') as output: with open(self.build_dir + 'ModSecurity/modsecurity.conf-recommended') as base_config: for line in base_config: if line.startswith('SecRuleEngine '): output.write('SecRuleEngine On\n') elif line.startswith('SecAuditLog '): output.write('SecAuditLog ' + settings.get('install_path') + 'var/log/modsec_audit.log\n') else: output.write(line) for builder in get_enabled_ruleset_builders(): input_file = builder.rule_output_file() input_dir = builder.rule_output_dir() with open(input_file) as input: for line in input: output.write(line) for data_file in glob.glob(input_dir + '*.data'): shutil.copy(data_file, output_dir) source_map = self.build_dir + 'ModSecurity/unicode.mapping' target_map = output_dir + 'unicode.mapping' shutil.copy(source_map, target_map) return 0 # 0 = successful bash command
def get_config_arg_file(self): """ Returns the name of the file that lists arguments that are passed to the configure command. When overridden, this method can return an array of filenames instead. """ return settings.get('install_path') + 'etc/build-config/' + self.slug
def write_primary_logrotate(): """ Write out a configuration file for logrotated to rotate the global php log files. Also add an include directive that will include all website logrotated files. Args: domain - The domain name """ install_path = settings.get('install_path') filename = primary_logrotate_file() version_list = get_versions() if len(version_list) == 0: if os.path.exists(filename): os.remove(filename) else: with open(filename, 'w+') as output: output.write('include ' + install_path + 'etc/logrotate.d/php-sites/*\n\n') for version in version_list: output.write( '/opt/php-' + version + '/var/log/*.log {\n\ weekly\n\ rotate 52\n\ dateext\n\ compress\n\ copytruncate\n\ missingok\n\ postrotate\n\ /bin/kill -USR1 `cat /opt/php-' + version + '/var/run/php-fpm.pid 2>/dev/null` 2>/dev/null || true\n\ endscript\n\ }\n\n')
def list_frozen(): frozen_list = [] freeze_file = settings.get('install_path') + 'etc/build-freeze' with open(freeze_file) as frozen: for line in frozen: frozen_list.append(line.strip()) return frozen_list
def unfreeze(slug): """ Allow a builder to update it's source code to a newer version. This will not start a build. """ freeze_file = settings.get('install_path') + 'etc/build-freeze' return file_filter.RemoveExact(freeze_file, slug).run()
def freeze(slug): """ Prevent a builder from updating it's source code to a newer version. The software will still be re-built if it's dependants are upated. """ freeze_file = settings.get('install_path') + 'etc/build-freeze' return file_filter.AppendUnique(freeze_file, slug).run()
def create_one_time_login(domain): """ Create a PHP file to give a one-time login into a WordPress site without a password. There is no safty measure to remove this link if it is not used. Args: domain - The domain that needs a one-time login """ sys_user = nginx.user_from_domain(domain) passcode = input_util.random_string(40, False) passname = input_util.random_string(40, False) docroot = nginx.docroot_from_domain(domain) target_file = docroot + 'wp-admin/wp-autologin-' + passname + '.php' site_url = get_site_url(sys_user, docroot) # Set environment whoami = os.geteuid() os.seteuid(getpwnam(sys_user).pw_uid) with open(settings.get('install_path') + 'etc/wp-autologin.php', 'r') as template: with open(target_file, 'w') as php_file: for line in template: line = line.replace('PASSWORDD', passcode, 10000) php_file.write(line) # Reset environment os.seteuid(whoami) print('Go to: ' + site_url + 'wp-admin/wp-autologin-' + passname + '.php?pass=' + passcode)
def create_and_install_dkim(domain): """ Create a new DKIM key pair and apply them to the DNS and mail system configuration for a domain. Args: domain - The domain to update with a new DKIM key pair """ if domain and not os.path.exists(bind.zone_folder + domain + '.db'): print('No zone file for ' + domain + ' found.') domain = False if not domain: domain = bind.select_main_domain('Select site to add DKIM to: ')[:-3] dkim_folder = settings.get('dkim_folder') if not os.path.exists(dkim_folder + domain + '.pub'): if not os.path.exists(dkim_folder): os.makedirs(dkim_folder) make_dkim_pair(domain) dkim = make_dkim_dns_value(domain) print('TXT Encoded DKIM: ' + dkim) DkimZoneUpdater(domain, dkim).run() bind.increment_soa(domain) print('Reloading nameserver to apply changes...') bind.reload() print('Done.')
def logrotate_file(domain): """ Get the logrotate configuration filename associated with a domain. Args: domain - The domain name """ return settings.get('install_path') + 'etc/logrotate.d/php-sites/' + domain
def template_path(name): """ Get the full path for a given nginx vhost template name. Args: name - The name (slug) of the template """ return settings.get('install_path') + 'etc/nginx-templates/' + name
def populate_config_args(self, log, command=False): if command == False: command = ['./configure'] command.append('--prefix=/opt/php-' + self.versions['sub']) command.append('--with-mysql-sock=' + settings.get('mysql_socket')) for pecl_builder in get_registered_pecl_builders(): command.append(pecl_builder.get_php_build_arg()) return super().populate_config_args(log, command)
def log_name(self): if not self.source_version: self.source_version = self.get_updated_version() name = settings.get('install_path') + 'var/log/build/' + self.slug if self.source_version and len(self.source_version) > 0: name += '-' + self.source_version name += '.log' return name
def get_updated_versions(force_refresh=False): """ Get the full version numbers of versions avaliable at php.net. Args: force_refresh - (optional) When set to True, do not use cached values """ vers = [] cache_dir = settings.get('install_path') + 'var/cache/' cache_file = cache_dir + 'php-versions' use_cache = force_refresh == False if os.path.exists(cache_file): max_age = settings.get_num('build_cache_age') mod_time = os.stat(cache_file).st_mtime age = time.time() - mod_time if age > max_age: use_cache = False else: if not os.path.exists(cache_dir): os.makedirs(cache_dir) use_cache = False if use_cache: return file_filter.get_trimmed_file_as_array(cache_file) else: request = requests.get('https://www.php.net/downloads.php') regex = re.compile(r'.*/distributions/php-([0-9\.]*)\.tar\.bz2.*') for line in request.text.splitlines(): match = regex.match(line) if match == None: continue match = match.group(1) if len(match) > 0: vers.append(match) if settings.get_bool('enable_php_legacy_versions'): vers.append(php71version) vers.append(php70version) vers.append(php56version) if settings.get_bool('enable_php_super_legacy_versions'): vers.append(php55version) vers.append(php54version) vers.append(php53version) vers.append(php52version) vers.append(php51version) vers.append(php50version) vers.append(php44version) vers.append(php43version) vers.append(php42version) vers.append(php41version) vers.append(php40version) vers.append(php30version) if settings.get_bool('enable_php_prerelease_version'): vers.extend(get_prerelease_version(vers)) with open(cache_file, 'w+') as cache_write: for v in vers: cache_write.write(v + "\n") return vers
def make_vhost(username, domain, template_name='php', template_fields=False): """ Create a new vhost file for a given domain. Args: username - The system username that stores the sitesfiles domain - The domain associated with the new vhost file template_name - The name of the nginx vhost template to use """ modsec = get_modsec_path(domain) home = user.home_dir(username) dash_domain = domain.replace('.', '-', 100) under_domain = domain.replace('.', '_', 100) vhost_path = get_vhost_path(domain) local_ip = settings.get('local_ip') public_ip = settings.get('public_ip') read_path = template_path(template_name) fields = get_vhost_headers(read_path)[0] if not template_fields: template_fields = [] for key, value in fields: value = input_util.prompt_value(key, value) template_fields.append([key, value]) if not os.path.exists(modsec_exception_dir): os.makedirs(modsec_exception_dir) if not os.path.exists(vhost_dir): os.makedirs(vhost_dir) with open(modsec, 'a+'): pass with open(read_path) as template: with open(vhost_path, 'w') as host: for line in template: line = line.replace('DOMAINNAMEE', domain, 10000) line = line.replace('USERNAMEE', username, 10000) line = line.replace('DASHDOMAINN', dash_domain, 10000) line = line.replace('UNDERDOMAINN', under_domain, 10000) line = line.replace('HOMEDIRR', home, 10000) line = line.replace('LOCALIPP', local_ip, 10000) line = line.replace('PUBLICIPP', public_ip, 10000) line = line.replace('MODSECC', modsec, 10000) for key, value in template_fields: line = line.replace(key, value, 10000) host.write(line) print('Created ' + vhost_path) reload()
def make_dkim_pair(domain): """ Generate a new DKIM public key and private key. Then give EXIM access to those files. Args: domain - generate the keys for this domain """ pem_file = settings.get('dkim_folder') + domain + '.pem' pub_file = settings.get('dkim_folder') + domain + '.pub' pwd_user = pwd.getpwnam(settings.get('exim_user')).pw_uid grp_group = grp.getgrnam(settings.get('exim_group')).gr_gid subprocess.run(['openssl', 'genrsa', '-out', pem_file, '2048']) subprocess.run( ['openssl', 'rsa', '-in', pem_file, '-pubout', '-out', pub_file]) os.chown(pem_file, pwd_user, grp_group) os.chown(pub_file, pwd_user, grp_group) os.chmod(pem_file, 0o640) os.chmod(pub_file, 0o640)
def get_mail_domains(): """Get an array of all domains registered in the mail system.""" domains = [] with open(settings.get('mail_domain_file')) as file: for line in file: segments = line.split(':') domain = segments[0].strip().lower() if domain != '*': domains.append(domain) return sorted(domains)
def get_distro(self): distro = settings.get('imap_distro') if distro == 'unset': detected_distro = detect_distro_code() if detected_distro: distro = detected_distro settings.set('imap_distro', distro) if distro == 'unset': distro = self.select_distro('Select your current operating system') settings.set('imap_distro', distro) return distro
def account_exists(account): """Check if an email address is registered in the email system.""" account = account.lower() + ':' length = len(account) found = False with open(settings.get('mail_shadow_file')) as shadow: for line in shadow: if line[:length].lower() == account: found = True break return found
def is_frozen(slug): """ Returns True if the slug is set to be restricted from updating. """ freeze_file = settings.get('install_path') + 'etc/build-freeze' if os.path.exists(freeze_file): with open(freeze_file) as frozen: for line in frozen: if slug == line.strip(): return True return False
def libs_path(): path = settings.get('openssl_libs') if path == 'unset': paths = ['/usr/local/ssl/lib', '/usr/local/lib64', '/usr/local/lib'] at = -1 while path == 'unset' or not os.path.exists(path + '/libssl.so.1.1'): at += 1 if at >= len(paths): return False path = paths[at] settings.set('openssl_libs', path) return path
def cleanup_old_versions(self, log): found = False found_version = False for logname in builder.find_old_build_elements( settings.get('install_path') + 'var/log/build/php-' + self.versions['sub'] + '.', '.log'): os.remove(logname) log.log("Removed old log file " + logname) for folder in builder.find_old_build_elements( '/usr/local/src/php-' + self.versions['sub'] + '.', '/'): shutil.rmtree(folder) log.log("Removed old source directory " + folder)
def clean(self, log): """ Clean build binaries for the software. Args: log - An open log file or null """ old_pwd = os.getcwd() target_dir = self.source_dir() if os.path.exists(target_dir): os.chdir(target_dir) log.run(['make', 'clean', '-l', settings.get('max_build_load')]) os.chdir(old_pwd)
def get_account_from_domain(domain): """ Get the username of the system user that stores email for the given domain. Args: domain - The domain to check """ domain = domain.lower() with open(settings.get('mail_domain_file')) as file: for line in file: segments = line.split(':') if segments[0].strip().lower() == domain: return segments[1].strip() return False
def get_local_deploy_domains(): """ Returns an array of domains that should have a copy of new certificates placed in ~/certs/ after renewal. """ local_deploy = [] local_filename = settings.get('install_path') + 'etc/local-cert-deploy' if os.path.exists(local_filename): with open(local_filename) as domain_list: for domain in domain_list: domain = domain.lower().strip() if not domain.startswith('#'): local_deploy.append(domain.lower().strip()) return local_deploy
def remove_local_deploy(domain): """ Unregister a domain to recieve copies of new certificates in the matching user's ~/cert directory. Args: domain - The domain to unregister """ domain = domain.strip() filename = settings.get('install_path') + 'etc/local-cert-deploy' return file_filter.RemoveExact(filename, domain, ignore_trim=True, ignore_case=True).run()
def template_list(hide_ssl=False): """ Get an array containing all of the nginx vhost template file names exluding path. Args: hide_ssl - Only show non-SSL templates """ templates = [] folder_name = settings.get('install_path') + 'etc/nginx-templates/' for temp in glob.glob(folder_name + '*'): if not hide_ssl or not temp.endswith('-ssl'): templates.append(temp[len(folder_name):]) return sorted(templates)
def get_public_dkim_key(domain): """ Get the contents of a public DKIM key with no line breaks. Args: domain - Get the public key for this domain """ key = '' with open(settings.get('dkim_folder') + domain + '.pub') as pub: for line in pub: if line.startswith('-----'): continue key += line.replace('\n', '').replace('\r', '') return key
def get_connection(): """ Cet a connected MySQL connection. """ root_needs_pass = settings.get_bool('db_root_requires_password') socket = settings.get('mysql_socket') if (root_needs_pass): bitpass = getpass('Enter the MySQL root password: '******'root', passwd=bitpass) else: return connector.connect(unix_socket=socket, user='******')
def _setpass(address): from libsw import email from getpass import getpass if address != False and not email.account_exists(address): print(address + ' does not exist on this server.') address = False if address == False: address = email.select_email() password = email.hash_password( getpass('Email Password: '******'Password Updated.') else: from libsw import settings print('Error. Password Not Updated. Check ' + settings.get('mail_shadow_file') + ' for syntax errors.')
def make_zone(domain): """ Create a new zone file for the given domain name. The new zone file is populated with values for the new domain. The index of zone files is not rebuilt to include the new zone and Bind is not restarted or reloaded. Args: domain - The primary domain of the zone file """ if not os.path.exists(zone_folder): os.mkdir(zone_folder) template = open(settings.get('install_path') + 'etc/zone-file', 'r') ns1 = settings.get('nameserver_one') ns2 = settings.get('nameserver_two') dns_authority = settings.get('dns_authority') ip = settings.get('public_ip') with open(zone_filename(domain), 'w') as zone: for line in template: line = line.replace('DOMAINNAMEE', domain, 10000) line = line.replace('NAMESERVER_ONEE', ns1, 10000) line = line.replace('NAMESERVER_TWOO', ns2, 10000) line = line.replace('DNS_AUTHORITYY', dns_authority, 10000) line = line.replace('PUBLIC_IPP', ip, 10000) zone.write(line)