Skip to content

Instantly share code, notes, and snippets.

@FND
Last active June 4, 2019 11:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save FND/80d591c9a9762376869556787c623883 to your computer and use it in GitHub Desktop.
Save FND/80d591c9a9762376869556787c623883 to your computer and use it in GitHub Desktop.
SSI with nginx
# http://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length = 80
indent_style = tab
indent_size = 4
[*.{md,py}]
indent_style = space
[COMMIT_EDITMSG]
trim_trailing_whitespace = false
max_line_length = 72
indent_style = space
#!/usr/bin/env python3
import sys
HOST = "localhost"
PORT = int(sys.argv[1])
ROOT_URI = "/"
AUTH_URI = "/auth"
NAV_URI = "/nav"
FRAGMENT_PREFIX = "/_fragment"
COOKIE_FIELD = "token"
JWT = ("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9" +
".eyJmb28iOiJiYXIifQ" +
".VAoRL1IU0nOguxURF2ZcKR0SGKE1gCbqwyh8u2MLAyY")
FRAGMENT_TYPE = "text/html; fragment=true"
DOCUMENT = """<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>%(title)s</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
nav {
margin-left: 1em;
border: 1px solid #DCA;
padding: 0.5em;
background-color: #FEFFCD;
}
nav p,
nav ul {
margin: 0;
}
</style>
</head>
<body>
<h1>%(title)s</h1>
<p>welcome, %(username)s</p>
<pre>%(request_details)s</pre>
<a href="%(auth_uri)s">toggle authentication</a>
<!--# include virtual="%(embed_uri)s" -->
<p>EOF</p>
</body>
</html>
"""
FRAGMENT = """
<nav>
<p>browsing as %(username)s</p>
<pre>%(request_details)s</pre>
<ul>
<li>foo</li>
<li>bar</li>
<li>baz</li>
</ul>
</nav>
"""
def handler(environ, start_response):
method = environ["REQUEST_METHOD"]
uri = environ.get("PATH_INFO", "")
accept = environ.get("HTTP_ACCEPT")
cookie = environ.get("HTTP_COOKIE")
user = environ.get("HTTP_X_USER")
if uri == AUTH_URI:
origin = environ.get("HTTP_REFERER", ROOT_URI)
value = ("%s=void; expires=Thu, 01 Jan 1970 00:00:00 GMT" % COOKIE_FIELD
if cookie else "%s=%s" % (COOKIE_FIELD, JWT))
start_response("302 Found",
[("Set-Cookie", value), ("Location", origin)])
return ""
start_response("200 OK", [("Content-Type", "text/html"),
("Cache-Control", "no-cache, must-revalidate, max-age=0")])
embed_uri = FRAGMENT_PREFIX + NAV_URI
data = {
"request_details": "\n".join([
"🔗 %s" % uri,
"⛓️ %s" % embed_uri,
"📄 %s" % accept,
"🍪 %s" % (cookie or "N/A"),
"👤 %s" % (user or "N/A")
]),
"username": "admin" if user else "anonymous"
}
html = FRAGMENT % data if accept == FRAGMENT_TYPE else DOCUMENT % {
**data,
"title": "Hello World",
"auth_uri": AUTH_URI,
"embed_uri": embed_uri
}
return uft8(html)
def uft8(*args):
return (txt.encode("utf-8") for txt in args)
if __name__ == "__main__":
from wsgiref.simple_server import make_server
srv = make_server(HOST, PORT, handler)
print("→ http://%s:%s" % (HOST, PORT))
srv.serve_forever()
local authr = {}
local jwt = require "resty.jwt"
local cjson = require "cjson"
local ngx = ngx
-- TODO: use environment variables
local auth_uri = "/auth"
local cookie_field = "token"
local jwt_key = "lua-resty-jwt"
function authr.access_handler()
if ngx.var.uri == auth_uri then -- FIXME: hacky workaround; brittle?
return
end
-- retrieve JWT from cookie, otherwise abort
local cookie = ngx.var["cookie_" .. cookie_field]
if not cookie then
return ngx.redirect(auth_uri)
end
-- retrieve payload from JWT, otherwise abort
-- TODO:
-- * ensure `is_not_expired`
-- * LRU cache?
local token = jwt:verify(jwt_key, cookie)
if not token.valid or not token.verified then
return ngx.redirect(auth_uri)
end
ngx.req.set_header("X-USER", cjson.encode(token.payload))
-- NB: ideally we'd avoid exposing these (not all) cookies to upstream
-- applications, but it's not worth the parsing hassle
end
return authr
FROM openresty/openresty:stretch-fat
RUN opm get cdbattags/lua-resty-jwt
COPY ./nginx.conf /usr/local/openresty/nginx/conf/nginx.conf
COPY ./nginx.default.conf /etc/nginx/conf.d/default.conf
COPY ./authr.lua /usr/local/openresty/lualib/authr.lua
worker_processes 1;
events {
worker_connections 1024;
}
http {
init_by_lua_block {
authr = require "authr"
}
server {
location /_fragment/ {
rewrite ^/_fragment/(.*)$ /$1 break;
proxy_pass http://host.docker.internal:9999;
proxy_set_header Host $host;
proxy_set_header Accept "text/html; fragment=true";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
ssi on;
access_by_lua_block {
authr.access_handler()
}
proxy_pass http://host.docker.internal:9999;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
server {
listen 80;
server_name localhost;
}
#!/usr/bin/env bash
host_port="8080"
vm_port="80" # cf. nginx.conf
app_port="9999" # cf. nginx.conf
echo "----"
echo "starting application and reverse proxy → http://localhost:$host_port"
echo "----"
./app "$app_port" &
app_pid="$!"
docker build -t nginxy ./
docker run -p "$host_port:$vm_port" nginxy
kill $app_pid # it appears Python ignores SIGINT ¯\_(ツ)_/¯
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment