Created
May 14, 2022 12:00
-
-
Save vsajip/3c6d6d269d9f406953bf34b450b033f4 to your computer and use it in GitHub Desktop.
For Opalstack, configure Nginx and Apache proxy-port "private stack" applications to proxy to a static site with or without PHP.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
# | |
# Copyright (C) 2022 Red Dove Consultants Limited. | |
# | |
# License: Apache 2.0, see https://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Gratefully based on work by Ryan Sanden and Sean Fulmer at | |
# | |
# https://github.com/rsanden/userspace-fpm-installer | |
# | |
import argparse | |
import functools | |
import io | |
import logging | |
import os | |
import re | |
import sys | |
DEBUGGING = 'PY_DEBUG' in os.environ | |
logger = logging.getLogger(__name__) | |
APACHE_CONF = ''' | |
LoadModule mpm_event_module /usr/lib64/httpd/modules/mod_mpm_event.so | |
LoadModule unixd_module /usr/lib64/httpd/modules/mod_unixd.so | |
LoadModule authz_core_module /usr/lib64/httpd/modules/mod_authz_core.so | |
LoadModule proxy_module /usr/lib64/httpd/modules/mod_proxy.so | |
LoadModule proxy_fcgi_module /usr/lib64/httpd/modules/mod_proxy_fcgi.so | |
LoadModule mime_module /usr/lib64/httpd/modules/mod_mime.so | |
LoadModule dir_module /usr/lib64/httpd/modules/mod_dir.so | |
LoadModule alias_module /usr/lib64/httpd/modules/mod_alias.so | |
LoadModule autoindex_module /usr/lib64/httpd/modules/mod_autoindex.so | |
LoadModule log_config_module /usr/lib64/httpd/modules/mod_log_config.so | |
LoadModule rewrite_module /usr/lib64/httpd/modules/mod_rewrite.so | |
#LoadModule authz_host_module /usr/lib64/httpd/modules/mod_authz_host.so | |
#LoadModule authz_groupfile_module /usr/lib64/httpd/modules/mod_authz_groupfile.so | |
#LoadModule authz_owner_module /usr/lib64/httpd/modules/mod_authz_owner.so | |
#LoadModule authz_user_module /usr/lib64/httpd/modules/mod_authz_user.so | |
#LoadModule proxy_connect_module /usr/lib64/httpd/modules/mod_proxy_connect.so | |
#LoadModule proxy_http_module /usr/lib64/httpd/modules/mod_proxy_http.so | |
#LoadModule proxy_ftp_module /usr/lib64/httpd/modules/mod_proxy_ftp.so | |
#LoadModule remoteip_module /usr/lib64/httpd/modules/mod_remoteip.so | |
#LoadModule dav_module /usr/lib64/httpd/modules/mod_dav.so | |
#LoadModule dav_fs_module /usr/lib64/httpd/modules/mod_dav_fs.so | |
#LoadModule auth_basic_module /usr/lib64/httpd/modules/mod_auth_basic.so | |
#LoadModule auth_digest_module /usr/lib64/httpd/modules/mod_auth_digest.so | |
#LoadModule authn_file_module /usr/lib64/httpd/modules/mod_authn_file.so | |
#LoadModule cgid_module /usr/lib64/httpd/modules/mod_cgid.so | |
#LoadModule deflate_module /usr/lib64/httpd/modules/mod_deflate.so | |
#LoadModule setenvif_module /usr/lib64/httpd/modules/mod_setenvif.so | |
#LoadModule headers_module /usr/lib64/httpd/modules/mod_headers.so | |
#LoadModule include_module /usr/lib64/httpd/modules/mod_include.so | |
#LoadModule expires_module /usr/lib64/httpd/modules/mod_expires.so | |
#LoadModule env_module /usr/lib64/httpd/modules/mod_env.so | |
#LoadModule actions_module /usr/lib64/httpd/modules/mod_actions.so | |
#LoadModule negotiation_module /usr/lib64/httpd/modules/mod_negotiation.so | |
#LoadModule speling_module /usr/lib64/httpd/modules/mod_speling.so | |
#LoadModule access_compat_module /usr/lib64/httpd/modules/mod_access_compat.so | |
ServerName 127.0.0.1:%(port)s | |
ServerRoot %(prefix)s | |
DefaultRuntimeDir %(prefix)s/var/run | |
Listen 127.0.0.1:%(port)s | |
KeepAliveTimeout 3 | |
KeepAlive Off | |
MaxConnectionsPerChild 5000 | |
Timeout 60 | |
PidFile %(prefix)s/var/run/httpd.pid | |
TypesConfig /etc/mime.types | |
LogLevel warn | |
LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined | |
CustomLog %(prefix)s/log/apache_%(appnane)s_access.log combined | |
ErrorLog %(prefix)s/log/apache_%(appname)s_error.log | |
<VirtualHost 127.0.0.1:%(post)s> | |
ServerName %(domain)s | |
#ServerAlias www.%(domain)s | |
DocumentRoot %(targappdir)s | |
DirectoryIndex index.html index.htm index.cgi index.php | |
ProxyPreserveHost on | |
AddDefaultCharset utf-8 | |
#if php | |
<Proxy "unix:/%(prefix)s/var/run/php-fpm.sock"> | |
</Proxy> | |
#endif | |
Include /etc/httpd/conf.modules.d/autoindex.conf | |
<Directory %(targappdir)s> | |
AllowOverride all | |
<FilesMatch \.ht(access|passwd)> | |
Require all denied | |
</FilesMatch> | |
<FilesMatch (\.user\.ini|php\.ini)> | |
Require all denied | |
</FilesMatch> | |
#if php | |
<FilesMatch \.php$> | |
SetHandler "proxy:unix:%(prefix)s/var/run/php-fpm.sock|fcgi://localhost" | |
</FilesMatch> | |
#endif | |
</Directory> | |
</VirtualHost> | |
'''.strip() | |
NGINX_CONF = ''' | |
pid %(prefix)s/var/run/nginx.pid; | |
error_log %(prefix)s/log/nginx_%(appname)s_error.log; | |
events {} | |
http { | |
include /etc/nginx/mime.types; | |
default_type application/octet-stream; | |
client_body_temp_path %(prefix)s/tmp/client_body; | |
fastcgi_temp_path %(prefix)s/tmp/fastcgi_temp; | |
proxy_temp_path %(prefix)s/tmp/proxy_temp; | |
scgi_temp_path %(prefix)s/tmp/scgi_temp; | |
uwsgi_temp_path %(prefix)s/tmp/uwsgi_temp; | |
log_format main '$http_x_forwarded_for - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"'; | |
access_log %(prefix)s/log/nginx_%(appname)s_access.log main; | |
server { | |
listen %(port)s; | |
server_name %(domain)s; | |
index index.php index.html index.htm; | |
location / { | |
try_files $uri $uri/ //index.php?$args; | |
alias %(targappdir)s/; | |
#if php | |
location ~ \.php$ { | |
fastcgi_index index.php; | |
fastcgi_intercept_errors on; | |
include /etc/nginx/fastcgi_params; | |
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; | |
fastcgi_pass unix:%(prefix)s/var/run/php-fpm.sock; | |
} | |
#endif | |
} | |
} | |
} | |
'''.strip() | |
FPM_CONF = ''' | |
[global] | |
pid = %(prefix)s/var/run/php-fpm.pid | |
[www] | |
listen = %(prefix)s/var/run/php-fpm.sock | |
pm = dynamic | |
pm.max_children = 5 | |
pm.start_servers = 2 | |
pm.min_spare_servers = 1 | |
pm.max_spare_servers = 3 | |
'''.strip() | |
APACHE_START = ''' | |
#!/bin/bash | |
PREFIX="%(prefix)s" | |
#if php | |
$PREFIX/bin/php-fpm --prefix "$PREFIX" | |
#endif | |
$PREFIX/bin/httpd -d "$PREFIX" | |
'''.strip() | |
APACHE_STOP = ''' | |
#!/bin/bash | |
PREFIX="%(prefix)s" | |
#if php | |
kill $(cat "$PREFIX/var/run/php-fpm.pid") &> /dev/null | |
#endif | |
kill $(cat "$PREFIX/var/run/httpd.pid") &> /dev/null | |
'''.strip() | |
NGINX_START = ''' | |
#!/bin/bash | |
PREFIX="%(prefix)s" | |
#if php | |
$PREFIX/bin/php-fpm --prefix "$PREFIX" | |
#endif | |
$PREFIX/bin/nginx -c "$PREFIX/conf/nginx.conf" -p "$PREFIX" -e "$PREFIX/log/nginx_%(appname)s_error.log" 2>/dev/null | |
'''.strip() | |
NGINX_STOP = ''' | |
#!/bin/bash | |
PREFIX="%(prefix)s" | |
#if php | |
kill $(cat "$PREFIX/var/run/php-fpm.pid") &> /dev/null | |
#endif | |
kill $(cat "$PREFIX/var/run/nginx.pid") &> /dev/null | |
'''.strip() | |
RESTART = ''' | |
#!/bin/bash | |
PREFIX="%(prefix)s" | |
"$PREFIX/bin/stop" | |
sleep 3 | |
"$PREFIX/bin/start" | |
'''.strip() | |
SERVER_TYPES = ('apache', 'nginx') | |
PHP_VERSIONS = { | |
'5.6': '/opt/remi/php56/root/usr/sbin/php-fpm', | |
'7.3': '/usr/sbin/php-fpm', | |
'7.4': '/opt/remi/php74/root/usr/sbin/php-fpm', | |
'8.0': '/opt/remi/php80/root/usr/sbin/php-fpm', | |
'8.1': '/opt/remi/php81/root/usr/sbin/php-fpm', | |
} | |
PHP_COND = re.compile(r'(#if php\n(.*)#endif)\n', re.MULTILINE | re.DOTALL) | |
def create_dirs(options): | |
prefix = os.path.expanduser('~/apps/%s' % options.appname) | |
dirs = ('bin', 'conf', 'etc', 'log', 'tmp', 'var/run') | |
if options.php: | |
dirs += ('lib',) | |
for d in dirs: | |
p = os.path.join(prefix, d) | |
if not os.path.exists(p): | |
if options.dry_run: | |
print('mkdir -p %s' % p) | |
else: | |
os.makedirs(p) | |
def get_context(options): | |
return { | |
'appname': options.appname, | |
'prefix': os.path.expanduser('~/apps/%s' % options.appname), | |
'port': options.port, | |
'domain': options.domain, | |
'targappdir': os.path.expanduser('~/apps/%s' % options.target), | |
} | |
def process_output(options, s, p): | |
if options.dry_run: | |
print('\ncat << "EOF" > %s' % p) | |
print('%s\nEOF' % s) | |
else: | |
with io.open(p, 'w', encoding='utf-8') as f: | |
f.write(s) | |
def generate_php_conf(options): | |
context = get_context(options) | |
d = os.path.join(context['prefix'], 'lib') | |
fn = os.path.join(d, 'php.ini') | |
if options.dry_run: | |
print('touch %s' % fn) | |
else: | |
with io.open(fn, 'w', encoding='utf-8'): | |
pass | |
s = FPM_CONF % context | |
p = os.path.join(context['prefix'], 'etc', 'php-fpm.conf') | |
process_output(options, s, p) | |
def conditional_replacer(want_php, m): | |
if want_php: | |
return m.groups()[1] | |
else: | |
return '' | |
replacer = None | |
def generate_apache_conf(options): | |
context = get_context(options) | |
p = os.path.join(context['prefix'], 'conf', 'httpd.conf') | |
s = PHP_COND.sub(replacer, APACHE_CONF % context) | |
process_output(options, s, p) | |
def generate_nginx_conf(options): | |
context = get_context(options) | |
p = os.path.join(context['prefix'], 'conf', 'nginx.conf') | |
s = PHP_COND.sub(replacer, NGINX_CONF % context) | |
process_output(options, s, p) | |
def generate_scripts(options): | |
if options.server == 'apache': | |
templates = (APACHE_START, APACHE_STOP) | |
else: | |
templates = (NGINX_START, NGINX_STOP) | |
context = get_context(options) | |
for i, t in enumerate(templates): | |
sname = 'start' if i == 0 else 'stop' | |
p = os.path.expanduser('~/apps/%s/bin/%s' % (options.appname, sname)) | |
# import pdb; pdb.set_trace() | |
s = PHP_COND.sub(replacer, t % context) | |
process_output(options, s, p) | |
if options.dry_run: | |
print('chmod 755 %s' % p) | |
else: | |
os.chmod(p, 0o755) | |
s = RESTART % context | |
p = os.path.expanduser('~/apps/%s/bin/restart' % options.appname) | |
process_output(options, s, p) | |
if options.dry_run: | |
print('chmod 755 %s' % p) | |
else: | |
os.chmod(p, 0o755) | |
def make_link(options, src, dst): | |
if os.path.islink(dst): | |
return | |
if options.dry_run: | |
print('ln -s "%s" "%s"' % (src, dst)) | |
else: | |
os.symlink(src, dst) | |
def process(options): | |
global replacer | |
replacer = functools.partial(conditional_replacer, options.php) | |
create_dirs(options) | |
d = os.path.expanduser('~/apps/%s/bin' % options.appname) | |
fn = 'httpd' if options.server == 'apache' else 'nginx' | |
p = os.path.join(d, fn) | |
make_link(options, '/usr/sbin/%s' % fn, p) | |
if options.php: | |
php = PHP_VERSIONS[options.php] | |
p = os.path.join(d, 'php-fpm') | |
make_link(options, php, p) | |
generate_php_conf(options) | |
if options.server == 'apache': | |
generate_apache_conf(options) | |
else: | |
generate_nginx_conf(options) | |
generate_scripts(options) | |
def check_port(s): | |
if not s.isdigit(): | |
raise argparse.ArgumentTypeError('%r is not a valid port value' % s) | |
result = int(s) | |
if result < 1024: | |
raise argparse.ArgumentTypeError('%r is not a valid port value (too low)' % result) | |
return result | |
def main(): | |
fn = os.path.basename(__file__) | |
fn = os.path.splitext(fn)[0] | |
adhf = argparse.ArgumentDefaultsHelpFormatter | |
ap = argparse.ArgumentParser(formatter_class=adhf, prog=fn, | |
description='Set up a private stack on Opalstack for Apache/nginx/PHP') | |
aa = ap.add_argument | |
aa('appname', metavar='APPNAME', | |
help='Name of the proxy-port application to use for the private stack') | |
aa('port', metavar='PORT', type=check_port, help='The port for the proxy-port application') | |
aa('target', metavar='TARGET', | |
help='Name of the target application (can be PHP or purely static)') | |
aa('domain', metavar='DOMAIN', | |
help='Name of the domain for the private stack') | |
aa('--server', default='nginx', choices=SERVER_TYPES, help='Server type for private stack') | |
aa('--php', choices=sorted(PHP_VERSIONS), help='PHP version for PHP-FPM') | |
aa('--dry-run','-n', default=False, action='store_true', help='Do a dry run only (prints equivalent Bash commands, which you can edit to customize)') | |
options = ap.parse_args() | |
process(options) | |
if __name__ == '__main__': | |
try: | |
rc = main() | |
except KeyboardInterrupt: | |
rc = 2 | |
except Exception as e: | |
if DEBUGGING: | |
s = ' %s:' % type(e).__name__ | |
else: | |
s = '' | |
sys.stderr.write('Failed:%s %s\n' % (s, e)) | |
if DEBUGGING: import traceback; traceback.print_exc() | |
rc = 1 | |
sys.exit(rc) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment