def setup_daemon(): cfg = psite.get_cfg() dprog = psite.get_option("daemon") if dprog is None: return dname = re.sub(r'[.].*$', '', dprog) service_name = "{}-{}.service".format(cfg['siteid'], dname) unit = "" unit += "[Unit]\n" unit += "Description={}\n".format(dprog) unit += "\n" unit += "[Service]\n" unit += "User=root\n" unit += "Type=simple\n" unit += "ExecStart={}/{} run\n".format(cfg['src_dir'], dprog) unit += "WorkingDirectory={}\n".format(cfg['src_dir']) unit += "\n" unit += "[Install]\n" unit += "WantedBy=multi-user.target\n" with open("TMP.service", "w") as outf: outf.write(unit) uname = "/etc/systemd/system/{}".format(service_name) old = psite.slurp_file(uname) if old != unit: print( "sudo sh -c 'cp TMP.service {} && systemctl daemon-reload'".format( uname))
def make_virtual_host(ssl_flag, port): cfg = psite.get_cfg() conf = "" if port != 80 and port != 443: conf += "Listen {}\n".format(port) conf += "<VirtualHost *:{}>\n".format(port) conf += " ServerName {}\n".format(cfg['external_name']) conf += " ServerAlias www.{}\n".format(cfg['external_name']) if (ssl_flag): conf += add_ssl_engine() conf += " php_flag display_errors on\n" conf += " DocumentRoot {}\n".format(cfg['document_root']) conf += " SetEnv APP_ROOT {}\n".format(cfg['src_dir']) conf += " SetEnv PSITE_DIR {}\n".format(cfg['psite_dir']) conf += " SetEnv PSITE_PHP {}/psite.php\n".format(cfg['psite_dir']) conf += " <Directory {}>\n".format(cfg['document_root']) conf += add_valhtml() conf += add_nocache() conf += " </Directory>\n" if psite.get_option("flat", 0) == 0: conf += " DirectoryIndex disabled\n" else: conf += " DirectoryIndex index.php\n" conf += add_rewrites(ssl_flag) if psite.get_option("auth", 0) == 1: conf += setup_auth() conf += "</VirtualHost>\n" return conf
def install(site_name_arg=None, conf_key_arg=None): cfg = psite.get_cfg() setup_siteid(site_name_arg, conf_key_arg) cfg['db'] = psite.get_option("db", "") cfg['dbname'] = psite.get_option("db_dbname", cfg['siteid']) setup_name_and_ports() if psite.get_option('skip_ssl', 0) == 1: cfg['ssl_enabled'] = False else: setup_ssl() setup_dirs() setup_urls() if psite.get_option("skip_apache", 0) == 0: setup_apache() else: setup_htaccess() setup_cron() setup_daemon() # setup_tunnel() print(cfg['plain_url']) if cfg['ssl_url'] != "": print(cfg['ssl_url']) print(cfg['local_url']) psite.write_json("cfg.json", cfg) do_credentials()
def setup_name_and_ports(): cfg = psite.get_cfg() val = psite.get_option("external_name") if val is not None: cfg['external_name'] = val cfg['plain_port'] = 80 cfg['ssl_port'] = 443 cfg['port_base'] = 8000 else: nat_info = re.split("\\s+", psite.slurp_file("/etc/apache2/NAT_INFO")) if len(nat_info) >= 2: cfg['nat_name'] = nat_info[0] cfg['port_base'] = int(nat_info[1]) else: cfg['nat_name'] = "localhost" cfg['port_base'] = 8000 cfg['external_name'] = cfg['nat_name'] if 'plain_port' not in cfg: cfg['plain_port'] = get_free_port() if 'wss_port' not in cfg: port = psite.get_option("wss_port") if port is None: port = get_free_port() cfg['wss_port'] = port
def add_rewrites(ssl_flag): cfg = psite.get_cfg() conf = "" conf += " RewriteEngine on\n" # static files for lets encrypt conf += " RewriteCond %{REQUEST_URI} /.well-known/.*\n" conf += " RewriteRule ^(.*) /var/www/html/$1 [L]\n" conf += "\n" if cfg['ssl_enabled'] and not ssl_flag: # this is the http version of an ssl site ... redirect to https conf += " RewriteRule ^/(.*) {}$1 [R]\n".format(cfg['main_url']) conf += "\n" return conf # send www.example.com to example.comf conf += (" RewriteCond %{{HTTP_HOST}} www.{}\n".format( cfg['external_name'])) conf += " RewriteRule ^/(.*) {}$1 [R]\n".format(cfg['main_url']) conf += "\n" if (psite.get_option("flat", 0) == 0 or psite.get_option("app_php", 0) != 0): # send all non-static files to app.php conf += (" RewriteCond {}%{{REQUEST_FILENAME}} !-f\n".format( cfg['document_root'])) conf += " RewriteRule .* {}/app.php\n".format(cfg['src_dir']) return conf
def add_ssl_engine(): cfg = psite.get_cfg() conf = "" conf += " SSLEngine on\n" conf += " SSLCertificateFile {}\n".format(cfg['crt_file']) conf += " SSLCertificateKeyFile {}\n".format(cfg['key_file']) if 'chain_file' in cfg: conf += " SSLCertificateChainFile {}\n".format(cfg['chain_file']) return conf
def try_cert(dns_name): cfg = psite.get_cfg() names = make_cert_filenames(dns_name) if os.path.exists(names['crt']) and os.path.exists(names['key']): cfg['crt_file'] = names['crt'] cfg['key_file'] = names['key'] if os.path.exists(names['chain']): cfg['chain_file'] = names['chain'] return True return False
def setup_cron(): cfg = psite.get_cfg() hour = random.randrange(3, 6) minute = random.randrange(0, 60) cron = ("# created by: psite install\n" "{} {} * * * . $HOME/.bash_profile && cd {} && {}/psite do-cron\n" ).format(minute, hour, cfg['src_dir'], cfg['psite_dir']) with open("TMP.crontab", "w") as outf: outf.write(cron)
def mysql_table_exists(table): cfg = psite.get_cfg() db = get_db() cur = db['cursor'] cur.execute("select 0" " from information_schema.tables" " where table_schema = %s" " and table_name = %s", (cfg['dbname'], table)) return fetch() is not None
def cmd_sql(): cfg = psite.get_cfg() if cfg["db"] == "mysql": cmd = [] cmd.append("mysql") if psite.get_option("db_host") is not None: cmd.append("--login-path={}".format(cfg['siteid'])) cmd.append(cfg['dbname']) print(" ".join(cmd))
def do_backup(): cfg = psite.get_cfg() backups_dir = "{}/backups".format(cfg['aux_dir']) if not os.path.exists(backups_dir): os.mkdir(backups_dir, 0o775) ts = time.strftime("%Y%m%dT%H%M%S") basename = "{}-{}.sql".format(cfg['siteid'], ts) gzname = "{}.gz".format(basename) if cfg["db"] == "mysql": cmd = [] cmd.append("mysqldump") if psite.get_option("db_host") is not None: cmd.append("--login-path={}".format(cfg['siteid'])) cmd.append("--single-transaction") cmd.append("--add-drop-table") cmd.append("--result-file") cmd.append("{}/{}".format(backups_dir, basename)) cmd.append(cfg['dbname']) print(" ".join(cmd)) if subprocess.call(cmd) != 0: print("db dump error") sys.exit(1) cmd = "gzip --force {}/{}".format(backups_dir, basename) if os.system(cmd) != 0: print("db dump error during compression") sys.exit(1) elif cfg["db"] == "postgres": cmd = ("pg_dump" " --dbname={}" " --file={}/{}" " --no-owner" " --no-acl" " --compress=6" " --lock-wait-timeout=60000").format( cfg['dbname'], backups_dir, gzname) if os.system(cmd) != 0: print("db dump error") sys.exit(1) latest = "{}/latest.gz".format(backups_dir) if os.path.exists(latest): os.remove(latest) os.symlink(gzname, latest) print(latest)
def setup_tunnel(): cfg = psite.get_cfg() tunnel_host = psite.get_option("tunnel_host") tunnel_sshd_port = psite.get_option("tunnel_sshd_port") tunnel_port = psite.get_option("tunnel_port") if tunnel_host is None or tunnel_sshd_port is None or tunnel_port is None: return service_name = "{}-tunnel.service".format(cfg['siteid']) keyfile = "tunnel-key" if not os.path.isfile(keyfile): print("you must obtain", keyfile, "to set up the ssh tunnel") return tun = "" tun += "/usr/bin/ssh" tun += " -NTC" tun += " -o ServerAliveInterval=60" tun += " -o ExitOnForwardFailure=yes" tun += " -o StrictHostKeyChecking=no" tun += " -R {}:localhost:22".format(tunnel_port) tun += " -i {}".format(keyfile) tun += " -p {}".format(tunnel_sshd_port) tun += " tunnel@{}".format(tunnel_host) unit = "" unit += "[Unit]\n" unit += "Description={} ssh tunnel\n".format(cfg['siteid']) unit += "StartLimitIntervalSec=0\n" unit += "\n" unit += "[Service]\n" unit += "User={}\n".format(getpass.getuser()) unit += "Type=simple\n" unit += "ExecStart={}\n".format(tun) unit += "WorkingDirectory={}\n".format(cfg['src_dir']) unit += "Restart=always\n" unit += "RestartSec=30\n" unit += "\n" unit += "[Install]\n" unit += "WantedBy=multi-user.target\n" with open("TMP.tunnel", "w") as outf: outf.write(unit) uname = "/etc/systemd/system/{}".format(service_name) old = psite.slurp_file(uname) if old != unit: print(("sudo sh -c 'cp TMP.tunnel {} &&" " systemctl daemon-reload'").format(uname))
def make_apache_conf(): cfg = psite.get_cfg() conf = "" conf += "<Directory {}>\n".format(cfg['src_dir']) conf += " Options Indexes FollowSymLinks Includes ExecCGI\n" conf += " Require all granted\n" conf += " Allow from all\n" conf += "</Directory>\n" conf += make_virtual_host(False, cfg['plain_port']) if cfg['ssl_enabled']: conf += make_virtual_host(True, cfg['ssl_port']) return conf
def setup_auth(): cfg = psite.get_cfg() conf = '' conf += f' DBDriver pgsql\n' dbname = cfg['dbname'] conf += f' DBDParams "dbname={dbname} user=apache"\n' conf += f' DBDPrepareSQL "delete from session where key = %s" deletesession\n' conf += f' DBDPrepareSQL "update session set value = %s, expiry = %lld, key = %s where key = %s" updatesession\n' conf += f' DBDPrepareSQL "insert into session (value, expiry, key) values (%s, %lld, %s)" insertsession\n' conf += f' DBDPrepareSQL "select value from session where key = %s and (expiry = 0 or expiry > %lld)" selectsession\n' conf += f' DBDPrepareSQL "delete from session where expiry != 0 and expiry < %lld" cleansession\n' conf += '\n' conf += f' Session On \n' conf += f' SessionEnv On \n' conf += f' SessionDBDCookieName session path=/\n' conf += f' <Location "/xyzzy">\n' conf += f' SetHandler form-login-handler\n' conf += f' AuthFormProvider dbd\n' conf += f' AuthType form\n' conf += f' AuthName "realm"\n' conf += f' AuthFormFakeBasicAuth On\n' conf += f' AuthDBDUserPWQuery "SELECT password, user_name FROM authn WHERE user_name = %s"\n' conf += f' Require valid-user\n' conf += f' </Location>\n' conf += f' <Location "/logout">\n' conf += f' SetHandler form-logout-handler\n' conf += f' AuthFormLogoutLocation /\n' conf += f' AuthName "realm"\n' conf += f' </Location>\n' conf += f'\n' conf += f' <Location "/a">\n' conf += f' AuthFormProvider dbd\n' conf += f' AuthType form\n' conf += f' AuthName "realm"\n' conf += f' AuthFormFakeBasicAuth On\n' conf += f' AuthDBDUserPWQuery "SELECT password, user_name FROM authn WHERE user_name = %s"\n' conf += f' AuthFormLoginSuccessLocation /welcome.php\n' conf += f' Require valid-user\n' conf += f' </Location>\n' conf += f'\n' return conf
def setup_dirs(): cfg = psite.get_cfg() cfg['psite_dir'] = os.path.dirname(__file__) cfg['src_dir'] = os.getcwd() aux_dir = psite.get_option("aux_dir") if aux_dir is None: aux_dir = "/var/{}".format(cfg['siteid']) if not os.path.exists(aux_dir): print("sudo sh -c 'mkdir -pm2775 {0};chown www-data.www-data {0}'". format(aux_dir)) else: if not os.path.exists(aux_dir): print("mkdir -pm777 {}".format(aux_dir)) cfg['aux_dir'] = aux_dir
def setup_siteid(site_name_arg, conf_key_arg): cfg = psite.get_cfg() if 'site_name' not in cfg: if site_name_arg is None: print("must specify site_name for first call to install-site") sys.exit(1) else: cfg['site_name'] = site_name_arg if 'conf_key' not in cfg: if conf_key_arg is None: cfg['conf_key'] = getpass.getuser() else: cfg['conf_key'] = conf_key_arg cfg['siteid'] = "{}-{}".format(cfg['site_name'], cfg['conf_key'])
def setup_urls(): cfg = psite.get_cfg() cfg['plain_url'] = make_url("http", cfg['external_name'], cfg['plain_port']) if cfg['ssl_enabled']: cfg['ssl_url'] = make_url("https", cfg['external_name'], cfg['ssl_port']) cfg['main_url'] = cfg['ssl_url'] else: cfg['ssl_url'] = "" cfg['main_url'] = cfg['plain_url'] cfg['wss_url'] = make_url("wss", cfg['external_name'], cfg['wss_port']) cfg['local_url'] = re.sub(r'/[-_a-z0-9]+', "/local", cfg['main_url'])
def restore(): cfg = psite.get_cfg() print("restore") if len(sys.argv) < 3: print("usage: psite restore filename") sys.exit(1) filename = sys.argv[2] auth = "" if psite.get_option("db_host") is not None: auth = " --login-path={}".format(cfg['siteid']) cmd = "mysql {} -Nrse 'create database `{}`'".format(auth, cfg['dbname']) print(cmd) cmd = "gunzip < {} | mysql {} {}".format(filename, auth, cfg['dbname']) print(cmd)
def find_certs(): cfg = psite.get_cfg() cfg.pop("crt_file", None) cfg.pop("key_file", None) cfg.pop("chain_file", None) if try_cert(cfg['external_name']): return True wname = "wildcard." + cfg['external_name'] if try_cert(wname): return True wname = re.sub("^[^.]*", "wildcard", cfg['external_name']) if try_cert(wname): return True return False
def setup_ssl(): cfg = psite.get_cfg() ssl_port = psite.get_option("ssl_port", "") if ssl_port != "": ssl_port = int(ssl_port) if ssl_port == 0: cfg['ssl_enabled'] = False else: cfg['ssl_enabled'] = True cfg['ssl_port'] = ssl_port return if not find_certs(): cfg['ssl_enabled'] = False return cfg['ssl_enabled'] = True if cfg.get('ssl_port', 0) == 0: cfg['ssl_port'] = get_free_port()
def setup_apache(): cfg = psite.get_cfg() if psite.get_option("flat", 0) == 0: cfg['document_root'] = "{}/static".format(cfg['src_dir']) else: cfg['document_root'] = cfg['src_dir'] conf = make_apache_conf() with open("TMP.conf", "w") as outf: outf.write(conf) av_name = "/etc/apache2/sites-available/{}.conf".format(cfg['siteid']) old = psite.slurp_file(av_name) if old != conf: print( "sudo sh -c 'cp TMP.conf {}; apache2ctl graceful'".format(av_name)) en_name = "/etc/apache2/sites-enabled/{}.conf".format(cfg['siteid']) if not os.path.exists(en_name): print("sudo a2ensite {}".format(cfg['siteid']))
def get_db(): global db if db is not None: return db cfg = psite.get_cfg() db = dict(db=cfg["db"]) if cfg["db"] == "sqlite3": import sqlite3 filename = "{}/{}.db".format(cfg['aux_dir'], cfg['dbname']) db['conn'] = sqlite3.connect(filename) make_writable_for_server(filename) db['cursor'] = db['conn'].cursor() db['table_exists'] = sqlite3_table_exists db['column_exists'] = sqlite3_column_exists db['commit'] = sqlite3_commit return db elif cfg["db"] == "postgres": # apt-get install python3-psycopg2 import psycopg2 dsn = "postgresql://apache@/{}".format(cfg['dbname']) try: db['conn'] = psycopg2.connect(dsn) except(psycopg2.OperationalError): print("can't connect to database, maybe do:") print("createdb -O apache {}".format(cfg['dbname'])) raise sys.exit(1) db['cursor'] = db['conn'].cursor() db['table_exists'] = postgres_table_exists db['column_exists'] = postgres_column_exists db['commit'] = postgres_commit return db elif cfg["db"] == "mysql": # apt-get install python3-mysqldb import MySQLdb try: params = {} params['db'] = cfg['dbname'] if psite.get_option("db_host") is not None: params['host'] = psite.get_option("db_host") params['user'] = psite.get_option("db_user") file = "{}/psite_db_passwd".format(cfg['aux_dir']) pw = psite.slurp_file(file).strip() params['password'] = pw else: # get unix_socket name: mysqladmin variables | grep sock params['unix_socket'] = '/var/run/mysqld/mysqld.sock' db['conn'] = MySQLdb.connect(**params) except(MySQLdb.OperationalError): print("") print("*******") print("can't connect to database, maybe do:") print("mysql -Nrse 'create database `{}`'".format(cfg['dbname'])) print("*******") print("") print("") raise sys.exit(1) db['cursor'] = db['conn'].cursor() db['table_exists'] = mysql_table_exists db['column_exists'] = mysql_column_exists db['commit'] = mysql_commit return db else: print("get_db failed") sys.exit(1)
def setup_htaccess(): cfg = psite.get_cfg() with open(".htaccess", "w") as outf: outf.write("SetEnv PSITE_PHP {}/psite.php\n".format(cfg['psite_dir'])) outf.write("SetEnv APP_ROOT {}\n".format(cfg['src_dir']))
def get_free_port(): cfg = psite.get_cfg() port_base = cfg['port_base'] return random.randrange(port_base, port_base + 1000)