import json import os import io import sys def forward( input_json, output_conf, ): with io.open( input_json, 'r' ) as f: forward_nginx = json.load(f) with io.open( output_conf, 'w' ) as f: names = [o['app_name'] for o in forward_nginx] if not '' in names: forward_nginx.append( dict( app_name='', redirect_url='https://product-development-service.blogspot.com', ) ) sections = dict() for entry in forward_nginx: location_path = None if entry['app_name'] != '': location_path = '/%s/' % entry['app_name'] else: location_path = '/' if 'server_name' in entry: server_name = entry['server_name'] else: server_name = 'default_server' if ( not server_name in sections ): sections[server_name] = [] if 'client_max_body_size' in entry: client_max_body_size = entry['client_max_body_size'] else: client_max_body_size = '50M' assert isinstance(client_max_body_size, str) sections[server_name].append( r''' client_max_body_size %s; ''' % client_max_body_size ) location_get = lambda location_body, location_path2, prefix=None,: ( r''' location {location} { {location_body} } '''.replace( '{location_body}', location_body, ).replace( '{location}', '%s %s' % ( ( '^~' if prefix is None else prefix ), location_path2 ), ) ) if 'target_endpoint' in entry: location_body_get = lambda target_endpoint: \ r''' proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $t1; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_redirect off; proxy_buffering off; proxy_pass {target_endpoint}; '''.replace( '{target_endpoint}', target_endpoint, ) if 'fallback_endpoint' in entry: fallback_name = '@fallback-%s' % os.urandom(10).hex() sections[server_name].append( location_get( location_body_get(entry['target_endpoint']) + r''' proxy_intercept_errors on; error_page 502 =200 {fallback_name}; '''.replace( '{fallback_name}', fallback_name, ), location_path, ) ) sections[server_name].append( location_get( location_body_get(entry['fallback_endpoint']), fallback_name, '', ) ) else: sections[server_name].append( location_get( location_body_get(entry['target_endpoint']), location_path, ) ) elif 'redirect_url' in entry: sections[server_name].append( location_get( r''' return 302 {redirect_url}$request_uri; '''.replace( '{redirect_url}', entry['redirect_url'], ), location_path, ) ) else: raise NotImplementedError servers = [] for server_name, current_sections in sections.items(): servers.append( r''' server { set $t1 $remote_addr; if ($http_x_forwarded_for) { set $t1 $http_x_forwarded_for; } server_name {server_name}; listen 80 {default_server}; #client_max_body_size 50M; {sections_config} } '''.replace( '{sections_config}', '\n'.join(current_sections) ).replace( '{server_name}', ( '_' if server_name == 'default_server' else server_name ), ).replace( '{default_server}', ( '' if not server_name == 'default_server' else server_name ) ) ) f.write(r''' events { multi_accept on; worker_connections 64; } http { log_format main '[$time_local][$remote_addr:$remote_port, $http_x_forwarded_for, $t1, $http_host]' '[$request_length,$bytes_sent,$request_time]' '[$status][$request]' '[$http_user_agent][$http_referer]'; access_log /dev/null combined; access_log /dev/stderr main; gzip on; server_tokens off; {servers_config} map $http_upgrade $connection_upgrade { default upgrade; '' close; } } '''.replace( '{servers_config}', '\n'.join(servers) )) def ssl(input_json, output_conf): with io.open( input_json, 'r' ) as f: ssl_nginx = json.load(f) servers = [] if 'stream_server' in ssl_nginx: upstream_servers = [] server_names = [] if 'by_server_name' in ssl_nginx['stream_server']: for k, v in ssl_nginx['stream_server']['by_server_name'].items(): upstream_servers.append( 'upstream %s { server %s; }' % ( v['upstream_name'], v['url'], ) ) server_names.append( '"%s" %s;' % ( v['server_name'], v['upstream_name'], ) ) if 'ssh' in ssl_nginx['stream_server']: ssh_section = 'upstream ssh { server {ssh}; }'.replace( '{ssh}', ssl_nginx['stream_server']['ssh'], ) else: ssh_section = '' ssl_port = 444 stream_server = r''' stream { upstream web { server 127.0.0.1:444; } {upstream_servers} {ssh_section} map $ssl_preread_protocol $upstream_protocol { default ssh; "TLSv1.2" $upstream_server_name; "TLSv1.3" $upstream_server_name; } map $ssl_preread_server_name $upstream_server_name { default web; {server_names} } # SSH and SSL on the same port server { listen 443; ssl_preread on; proxy_pass $upstream_protocol; } } '''.replace( '{upstream_servers}', ''.join([ ' ' + o + '\n' for o in upstream_servers ]), ).replace( '{ssh_section}', ssh_section, ).replace( '{server_names}', ''.join([ ' ' + o + '\n' for o in server_names ]), ) else: stream_server = '' ssl_port = 443 if 'default_server' in ssl_nginx: server = ssl_nginx['default_server'] servers.append( r''' server { set $t1 $remote_addr; if ($http_x_forwarded_for) { set $t1 $http_x_forwarded_for; } listen {ssl_port} ssl default_server; server_name _; client_max_body_size {client_max_body_size}; ssl_certificate {signed_chain_cert}; ssl_certificate_key {domain_key}; return 444; } '''.replace( '{signed_chain_cert}', server['signed_chain_cert'], ).replace( '{client_max_body_size}', server['client_max_body_size'], ).replace( '{domain_key}', server['domain_key'], ).replace( '{ssl_port}', '%d' % ssl_port, ) ) for server in ssl_nginx['servers']: servers.append( r''' server { set $t1 $remote_addr; if ($http_x_forwarded_for) { set $t1 $http_x_forwarded_for; } listen 80; server_name {server_names}; client_max_body_size {client_max_body_size}; location ~ ^/.well-known/acme-challenge/ { alias /var/www/; try_files $uri =404; } location ~ { #return 444; return 301 https://$host$request_uri; } } server { set $t1 $remote_addr; if ($http_x_forwarded_for) { set $t1 $http_x_forwarded_for; } listen {ssl_port} ssl; server_name {server_names}; client_max_body_size {client_max_body_size}; ssl_certificate {signed_chain_cert}; ssl_certificate_key {domain_key}; location ^~ / { proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_redirect off; proxy_buffering off; proxy_pass http://app:80; } } '''.replace( '{server_names}', ' '.join(server['server_names']) ).replace( '{signed_chain_cert}', server['signed_chain_cert'], ).replace( '{client_max_body_size}', server['client_max_body_size'], ).replace( '{domain_key}', server['domain_key'], ).replace( '{ssl_port}', '%d' % ssl_port, ) ) with io.open( output_conf, 'w' ) as f: f.write( r''' load_module "modules/ngx_stream_module.so"; events { multi_accept on; worker_connections 64; } {stream_server} http { log_format main '[$time_local][$remote_addr:$remote_port, $http_x_forwarded_for, $t1, $http_host]' '[$request_length,$bytes_sent,$request_time]' '[$status][$request]' '[$http_user_agent][$http_referer]'; access_log /dev/null combined; access_log /dev/stderr main; gzip on; server_tokens off; {servers} map $http_upgrade $connection_upgrade { default upgrade; '' close; } } '''\ .replace('{servers}', '\n'.join(servers)) \ .replace('{stream_server}', stream_server) ) if __name__ == '__main__': if len(sys.argv) >= 2 and sys.argv[1] == 'ssl': ssl(*sys.argv[2:]) else: forward(*sys.argv[1:])