Commit c08a6567 authored by Rafael Monnerat's avatar Rafael Monnerat

apache-frontend: Jupyter Notebook and evensource support with NGINX

  Introduce NGINX on the same partition of apache to handle websocket\
  and eventsource types.

  The NGINX will run on another port and it would require a second ip at
  the machine for enable it.

  This configuration is a working version with fully https support, but
  some additional adjustments might be required.
parent 909fe8a1
......@@ -53,7 +53,7 @@ mode = 0644
[template-apache-frontend]
recipe = slapos.recipe.template
url = ${:_profile_base_location_}/instance-apache-frontend.cfg
md5sum = 3e52cdd1fba381cdb98b438589d1c4ab
md5sum = 6d437f8a3836484d42bf9bf8d747e201
output = ${buildout:directory}/template-apache-frontend.cfg
mode = 0644
......@@ -66,7 +66,7 @@ mode = 0644
[template-slave-list]
recipe = slapos.recipe.build:download
url = ${:_profile_base_location_}/templates/apache-custom-slave-list.cfg.in
md5sum = a79c5d53baa335b89deeb8ce0b88b645
md5sum = 6828096d9ec4333b8c72a2e2ab768ea0
mode = 640
[template-slave-configuration]
......@@ -90,7 +90,7 @@ mode = 640
[template-custom-slave-list]
recipe = slapos.recipe.build:download
url = ${:_profile_base_location_}/templates/apache-default-slave-list.cfg.in
md5sum = 9362384cd80727987b34c7746a6de196
md5sum = 5252c0db72b57ce6debb9d4fb4e706a1
mode = 640
[template-not-found-html]
......@@ -157,6 +157,25 @@ filename = storage.config.jinja2
download-only = true
mode = 0644
# NGINX Configuration
[template-nginx-configuration]
recipe = slapos.recipe.template
url = ${:_profile_base_location_}/templates/nginx.cfg.in
md5sum = 18633ce55e53340efa1ba7693aac4152
output = ${buildout:directory}/template-nginx.cfg.in
mode = 0644
[template-nginx-eventsource-slave-virtualhost]
recipe = slapos.recipe.build:download
url = ${:_profile_base_location_}/templates/nginx-eventsource-slave.conf.in
md5sum = a5186f666acb2f040ede04c91e60408f
mode = 0644
[template-nginx-notebook-slave-virtualhost]
recipe = slapos.recipe.build:download
url = ${:_profile_base_location_}/templates/nginx-notebook-slave.conf.in
md5sum = 82d74a7f2aceb2b4a7acc6259291b7f2
mode = 0644
# Migrated from KVM recipe
[http-proxy]
......
......@@ -8,9 +8,11 @@ parts =
ca-frontend
certificate-authority
logrotate-entry-apache
logrotate-entry-nginx
apache-frontend
switch-apache-softwaretype
frontend-apache-graceful
frontend-nginx-graceful
dynamic-template-default-vh
not-found-html
promise-frontend-apache-configuration
......@@ -19,7 +21,13 @@ parts =
promise-apache-frontend-v6-https
promise-apache-frontend-v6-http
promise-apache-frontend-cached
promise-apache-frontend-ssl-cached
promise-apache-frontend-ssl-cached
promise-nginx-frontend-v4-https
promise-nginx-frontend-v4-http
promise-nginx-frontend-v6-https
promise-nginx-frontend-v6-http
promise-nginx-configuration
trafficserver-launcher
trafficserver-reload
......@@ -30,6 +38,10 @@ parts =
trafficserver-storage-config
trafficserver-promise-listen-port
## Nginx
nginx-frontend
## Monitor for apache
monitor-base
monitor-ats-cache-stats-wrapper
......@@ -67,6 +79,7 @@ crontabs = $${:etc}/crontabs
cronstamps = $${:etc}/cronstamps
ca-dir = $${:srv}/ssl
varnginx = $${:var}/nginx
[switch-apache-softwaretype]
recipe = slapos.cookbook:softwaretype
......@@ -90,6 +103,8 @@ configuration.domain = example.org
configuration.public-ipv4 =
configuration.port = 4443
configuration.plain_http_port = 8080
configuration.plain_nginx_port = 8081
configuration.nginx_port = 9443
configuration.server-admin = admin@example.com
configuration.apache_custom_https = ""
configuration.apache_custom_http = ""
......@@ -139,27 +154,35 @@ filename = custom-personal-instance-slave-list.cfg
extensions = jinja2.ext.do
extra-context =
key apache_configuration_directory apache-directory:slave-configuration
key nginx_configuration_directory apache-directory:nginx-slave-configuration
key apache_cached_configuration_directory apache-directory:slave-with-cache-configuration
key slave_with_cache_configuration_directory apache-directory:slave-with-cache-configuration
key cached_port apache-configuration:cache-through-port
key ssl_cached_port apache-configuration:ssl-cache-through-port
key http_port instance-parameter:configuration.plain_http_port
key https_port instance-parameter:configuration.port
key nginx_http_port instance-parameter:configuration.plain_nginx_port
key nginx_https_port instance-parameter:configuration.nginx_port
key public_ipv4 instance-parameter:configuration.public-ipv4
key slave_instance_list instance-parameter:slave-instance-list
key extra_slave_instance_list instance-parameter:configuration.extra_slave_instance_list
key custom_ssl_directory apache-directory:vh-ssl
key apache_log_directory apache-directory:slave-log
key local_ipv4 instance-parameter:ipv4-random
key global_ipv6 slap-network-information:global-ipv6
key cache_port apache-configuration:cache-port
key varnginx directory:varnginx
raw empty_template ${template-empty:target}
raw template_custom_slave_configuration ${template-slave-configuration:target}
raw template_default_slave_configuration ${template-default-slave-virtualhost:target}
raw template_cached_slave_configuration ${template-cached-slave-virtualhost:target}
raw template_eventsource_slave_configuration ${template-nginx-eventsource-slave-virtualhost:target}
raw template_notebook_slave_configuration ${template-nginx-notebook-slave-virtualhost:target}
raw software_type single-custom-personal
section logrotate_dict logrotate
section frontend_configuration frontend-configuration
section apache_configuration apache-configuration
section nginx_configuration nginx-configuration
key monitor_base_url monitor-instance-parameter:monitor-base-url
[dynamic-virtualhost-template-slave]
......@@ -228,6 +251,7 @@ cache = $${directory:var}/cache
mod-ssl = $${:cache}/httpd_mod_ssl
vh-ssl = $${:slave-configuration}/ssl
slave-log = $${directory:log}/httpd
nginx-slave-configuration = $${directory:etc}/nginx-slave-conf.d/
[apache-configuration]
frontend-configuration = $${directory:etc}/apache_frontend.conf
......@@ -335,14 +359,14 @@ sharedscripts = true
notifempty = true
create = true
[logrotate-entry-apache-cached]
[logrotate-entry-nginx]
<= logrotate
recipe = slapos.cookbook:logrotate.d
name = apache-cached
log = $${apache-configuration:cache-error-log} $${apache-configuration:cache-access-log}
name = apache-nginx
log = $${nginx-configuration:error_log} $${nginx-configuration:access_log}
frequency = daily
rotatep-num = 30
post = $${apache-configuration:cached-graceful-command}
post = $${nginx-configuration:nginx-graceful-command}
sharedscripts = true
notifempty = true
create = true
......@@ -555,3 +579,72 @@ path = $${directory:promise}/re6st-connectivity
url = $${instance-parameter:configuration.re6st-verification-url}
dash_path = ${dash:location}/bin/dash
curl_path = ${curl:location}/bin/curl
#######################
# Nginx
#
[nginx-frontend]
recipe = slapos.cookbook:wrapper
command-line = ${nginx-push-stream:location}/sbin/nginx -c $${nginx-configuration:output}
wrapper-path = $${directory:service}/frontend_nginx
[nginx-configuration]
recipe = slapos.recipe.template
url = ${template-nginx-configuration:output}
output = $${directory:etc}/nginx.cfg
mode = 0600
access_log = $${directory:log}/nginx-access.log
error_log = $${directory:log}/nginx-error.log
ip = $${slap-network-information:global-ipv6}
local_ip = $${slap-network-information:local-ipv4}
port = $${instance-parameter:configuration.nginx_port}
plain_port = $${instance-parameter:configuration.plain_nginx_port}
worker_processes = 4
worker_connections = 1024
slave-configuration-directory = $${apache-directory:nginx-slave-configuration}
pid-file = $${directory:run}/nginx.pid
nginx-graceful-command = $${:nginx-configuration-verification}; if [ $? -eq 0 ]; then kill -HUP $(cat $${:pid-file}); fi
nginx-configuration-verification = ${nginx-push-stream:location}/sbin/nginx -t -c $${nginx-configuration:output}
[frontend-nginx-graceful]
< = jinja2-template-base
template = ${template-wrapper:output}
rendered = $${directory:etc-run}/frontend-nginx-safe-graceful
mode = 0700
extra-context =
key content nginx-configuration:nginx-graceful-command
[promise-nginx-configuration]
< = jinja2-template-base
template = ${template-wrapper:output}
rendered = $${directory:promise}/nginx-configuration-promise
mode = 0700
extra-context =
key content nginx-configuration:nginx-configuration-verification
[promise-nginx-frontend-v4-https]
recipe = slapos.cookbook:check_port_listening
path = $${directory:promise}/nginx_frontend_ipv4_https
hostname = $${instance-parameter:ipv4-random}
port = $${instance-parameter:configuration.nginx_port}
[promise-nginx-frontend-v4-http]
recipe = slapos.cookbook:check_port_listening
path = $${directory:promise}/nginx_frontend_ipv4_http
hostname = $${instance-parameter:ipv4-random}
port = $${instance-parameter:configuration.plain_nginx_port}
[promise-nginx-frontend-v6-https]
recipe = slapos.cookbook:check_port_listening
path = $${directory:promise}/nginx_frontend_ipv6_https
hostname = $${instance-parameter:ipv6-random}
port = $${instance-parameter:configuration.nginx_port}
[promise-nginx-frontend-v6-http]
recipe = slapos.cookbook:check_port_listening
path = $${directory:promise}/nginx_frontend_ipv6_http
hostname = $${instance-parameter:ipv6-random}
port = $${instance-parameter:configuration.plain_nginx_port}
......@@ -34,7 +34,7 @@
"description": "Type of slave. If redirect, the slave will redirect to the given url. If zope, the rewrite rules will be compatible with Virtual Host Monster",
"type": "string",
"default": "",
"enum": ["", "zope", "redirect"]
"enum": ["", "zope", "redirect", "notebook", "eventsource"]
},
"path": {
......
......@@ -5,6 +5,7 @@
{% set cache_access = "http://%s:%s" % (local_ipv4, cache_port) -%}
{% set ssl_cache_access = "http://%s:%s/HTTPS" % (local_ipv4, cache_port) -%}
{% set TRUE_VALUES = ['y', 'yes', '1', 'true'] -%}
{% set NGINX_TYPE_LIST = ['eventsource', 'notebook'] -%}
{% set generic_instance_parameter_dict = {'cache_access': cache_access,} -%}
{% set slave_log_dict = {} -%}
{% if extra_slave_instance_list -%}
......@@ -38,6 +39,7 @@ crl = {{ custom_ssl_directory }}/crl/
{% for slave_instance in slave_instance_list -%}
{# # Do all set and do upper, so it makes easy to read the file later #}
{% set slave_reference = slave_instance.get('slave_reference') -%}
{% set slave_type = slave_instance.get('type', '') -%}
{% set slave_section_title = 'dynamic-template-slave-instance-%s' % slave_reference -%}
{% set slave_parameter_dict = generic_instance_parameter_dict.copy() -%}
{% set slave_publish_dict = {} -%}
......@@ -72,7 +74,7 @@ crl = {{ custom_ssl_directory }}/crl/
{% do slave_instance.__setitem__('custom_domain', "%s.%s" % (slave_instance.get('slave_reference').replace("-", "").lower(), slapparameter_dict.get('domain'))) -%}
{% endif -%}
{% set enable_cache = (('' ~ slave_instance.get('enable_cache', '')).lower() in TRUE_VALUES and slave_instance.get('type', '') != 'redirect') -%}
{% set enable_cache = (('' ~ slave_instance.get('enable_cache', '')).lower() in TRUE_VALUES and slave_type != 'redirect') -%}
{% if enable_cache and 'url' in slave_instance -%}
{% if 'domain' in slave_instance -%}
{% do slave_instance.__setitem__('custom_domain', slave_instance.get('domain')) -%}
......@@ -192,8 +194,19 @@ apache_custom_https = {{ dumps(apache_custom_https) }}
[{{ slave_section_title }}]
< = jinja2-template-base
{% if slave_instance.has_key('apache_custom_http') %}
{% if slave_type in NGINX_TYPE_LIST %}
rendered = {{ nginx_configuration_directory }}/${:filename}
{% else %}
rendered = {{ apache_configuration_directory }}/${:filename}
{% endif %}
{% if apache_custom_http %}
template = {{ template_custom_slave_configuration }}
{% elif slave_type == 'eventsource' %}
template = {{ template_eventsource_slave_configuration }}
{% elif slave_type == 'notebook' %}
template = {{ template_notebook_slave_configuration }}
{% else %}
template = {{ template_default_slave_configuration }}
{% endif %}
......@@ -202,6 +215,10 @@ filename = {{ '%s.conf' % slave_reference }}
extra-context =
raw https_port {{ https_port }}
raw http_port {{ http_port }}
raw global_ipv6 {{ global_ipv6 }}
raw local_ipv4 {{ local_ipv4 }}
raw nginx_http_port {{ nginx_http_port }}
raw nginx_https_port {{ nginx_https_port }}
section slave_parameter {{ slave_configuration_section_name }}
{{ '\n' }}
......
{% set url = slave_parameter.get('url') -%}
{% set https_url = slave_parameter.get('https-url', url) -%}
{% if url.startswith("http://") or url.startswith("https://") -%}
{% set upstream = url.split("/")[2] -%}
{% set https_upstream = https_url.split("/")[2] -%}
{% set protocol = url.split("/")[0] -%}
{% set https_protocol = https_url.split("/")[0] -%}
{% set proxy_pass = '%s//%s' % (protocol, slave_parameter.get('slave_reference')) -%}
{% set https_proxy_pass = '%s//https_%s' % (protocol, slave_parameter.get('slave_reference')) -%}
{%- set ssl_configuration_list = [('ssl_certificate', 'path_to_ssl_crt'),
('ssl_certificate_key', 'path_to_ssl_key')] -%}
upstream {{ slave_parameter.get('slave_reference') }} {
server {{ upstream }};
}
upstream https_{{ slave_parameter.get('slave_reference') }} {
server {{ https_upstream }};
}
server {
listen [{{ global_ipv6 }}]:{{ nginx_http_port }};
listen {{ local_ipv4 }}:{{ nginx_http_port }};
server_name {{ slave_parameter.get('custom_domain') }};
error_log {{ slave_parameter.get('error_log') }} error;
access_log {{ slave_parameter.get('access_log') }} custom;
location /pub {
push_stream_publisher;
push_stream_channels_path $arg_id;
# store messages in memory
push_stream_store_messages off;
# Message size limit
# client_max_body_size MUST be equal to client_body_buffer_size or
# you will be sorry.
client_max_body_size 16k;
client_body_buffer_size 16k;
}
location ~ /sub/(.*) {
# activate subscriber mode for this location
add_header "Access-Control-Allow-Origin" "*";
add_header 'Access-Control-Allow-Credentials' 'false';
add_header 'Access-Control-Allow-Methods' 'GET, HEAD, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since';
push_stream_subscriber eventsource;
# positional channel path
push_stream_channels_path $1;
# content-type
default_type "text/event-stream; charset=utf-8";
}
}
server {
listen [{{ global_ipv6 }}]:{{ nginx_https_port }} ssl;
listen {{ local_ipv4 }}:{{ nginx_https_port }} ssl;
server_name {{ slave_parameter.get('custom_domain') }};
error_log {{ slave_parameter.get('error_log') }} error;
access_log {{ slave_parameter.get('access_log') }} custom;
ssl on;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:HIGH:!aNULL:!MD5';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
{% for key, value in ssl_configuration_list -%}
{% if value in slave_parameter -%}
{{ ' %s' % key }} {{ slave_parameter.get(value) }};
{% endif -%}
{% endfor %}
location /pub {
push_stream_publisher;
push_stream_channels_path $arg_id;
# store messages in memory
push_stream_store_messages off;
# Message size limit
# client_max_body_size MUST be equal to client_body_buffer_size or
# you will be sorry.
client_max_body_size 16k;
client_body_buffer_size 16k;
}
location ~ /sub/(.*) {
# activate subscriber mode for this location
add_header "Access-Control-Allow-Origin" "*";
add_header 'Access-Control-Allow-Credentials' 'false';
add_header 'Access-Control-Allow-Methods' 'GET, HEAD, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since';
push_stream_subscriber eventsource;
# positional channel path
push_stream_channels_path $1;
# content-type
default_type "text/event-stream; charset=utf-8";
}
}
{% endif -%}
{% set url = slave_parameter.get('url') -%}
{% set https_url = slave_parameter.get('https-url', url) -%}
{% if url.startswith("http://") or url.startswith("https://") -%}
{% set upstream = url.split("/")[2] -%}
{% set https_upstream = https_url.split("/")[2] -%}
{% set protocol = url.split("/")[0] -%}
{% set https_protocol = https_url.split("/")[0] -%}
{% set proxy_pass = '%s//%s' % (protocol, slave_parameter.get('slave_reference')) -%}
{% set https_proxy_pass = '%s//https_%s' % (protocol, slave_parameter.get('slave_reference')) -%}
{%- set ssl_configuration_list = [('ssl_certificate', 'path_to_ssl_crt'),
('ssl_certificate_key', 'path_to_ssl_key')] -%}
upstream {{ slave_parameter.get('slave_reference') }} {
server {{ upstream }};
}
upstream https_{{ slave_parameter.get('slave_reference') }} {
server {{ https_upstream }};
}
server {
listen [{{ global_ipv6 }}]:{{ nginx_http_port }};
listen {{ local_ipv4 }}:{{ nginx_http_port }};
server_name {{ slave_parameter.get('custom_domain') }};
error_log {{ slave_parameter.get('error_log') }} error;
access_log {{ slave_parameter.get('access_log') }} custom;
location / {
proxy_pass {{ proxy_pass }};
proxy_set_header Host $host;
}
location ~ /api/kernels/ {
proxy_pass {{ proxy_pass }};
proxy_set_header Host $host;
# websocket support
proxy_http_version 1.1;
proxy_set_header Upgrade "websocket";
proxy_set_header Connection "Upgrade";
proxy_read_timeout 86400;
}
location ~ /terminals/ {
proxy_pass {{ proxy_pass }};
proxy_set_header Host $host;
# websocket support
proxy_http_version 1.1;
proxy_set_header Upgrade "websocket";
proxy_set_header Connection "Upgrade";
proxy_read_timeout 86400;
}
}
server {
listen [{{ global_ipv6 }}]:{{ nginx_https_port }} ssl;
listen {{ local_ipv4 }}:{{ nginx_https_port }} ssl;
server_name {{ slave_parameter.get('custom_domain') }};
error_log {{ slave_parameter.get('error_log') }} error;
access_log {{ slave_parameter.get('access_log') }} custom;
ssl on;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:HIGH:!aNULL:!MD5';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
{% for key, value in ssl_configuration_list -%}
{% if value in slave_parameter -%}
{{ ' %s' % key }} {{ slave_parameter.get(value) }};
{% endif -%}
{% endfor %}
location / {
proxy_pass {{ https_proxy_pass }};
proxy_set_header Host $host;
}
location ~ /api/kernels/ {
proxy_pass {{ https_proxy_pass }};
proxy_set_header Host $host;
# websocket support
proxy_http_version 1.1;
proxy_set_header Upgrade "websocket";
proxy_set_header Connection "Upgrade";
proxy_read_timeout 86400;
}
location ~ /terminals/ {
proxy_pass {{ https_proxy_pass }};
proxy_set_header Host $host;
# websocket support
proxy_http_version 1.1;
proxy_set_header Upgrade "websocket";
proxy_set_header Connection "Upgrade";
proxy_read_timeout 86400;
}
}
{% endif -%}
daemon off; # run in the foreground so supervisord can look after it
worker_processes $${nginx-configuration:worker_processes};
pid $${nginx-configuration:pid-file};
events {
worker_connections $${nginx-configuration:worker_connections};
# multi_accept on;
}
error_log $${nginx-configuration:error_log};
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off;
error_log $${nginx-configuration:error_log};
log_format custom '$remote_addr - $remote_user $time_local $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_time';
access_log $${nginx-configuration:access_log} custom;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
default_type application/octet-stream;
ssl_certificate $${ca-frontend:cert-file};
ssl_certificate_key $${ca-frontend:key-file};
##
# Gzip Settings
##
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
##
# Push stream Settings
##
push_stream_shared_memory_size 32m;
fastcgi_temp_path $${directory:varnginx} 1 2;
uwsgi_temp_path $${directory:varnginx} 1 2;
scgi_temp_path $${directory:varnginx} 1 2;
client_body_temp_path $${directory:varnginx} 1 2;
proxy_temp_path $${directory:varnginx} 1 2;
include $${nginx-configuration:slave-configuration-directory}/*.conf;
server {
listen [$${nginx-configuration:ip}]:$${nginx-configuration:plain_port};
listen $${nginx-configuration:local_ip}:$${nginx-configuration:plain_port};
## Serve an error 204 (No Content) for favicon.ico
location = /favicon.ico {
return 204;
}
location / {
root $${apache-directory:document-root};
index notfound.html;
}
}
server {
listen [$${nginx-configuration:ip}]:$${nginx-configuration:port} ssl;
listen $${nginx-configuration:local_ip}:$${nginx-configuration:port} ssl;
ssl on;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:HIGH:!aNULL:!MD5';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
## Serve an error 204 (No Content) for favicon.ico
location = /favicon.ico {
return 204;
}
location / {
root $${apache-directory:document-root};
index notfound.html;
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment