Created
October 20, 2017 19:42
-
-
Save rezan/9277b78dfd8d912d1fa1bf87849d50ac to your computer and use it in GitHub Desktop.
Edge Logic: Securing Varnish with per user JSON data
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
// Edge Logic: Securing Varnish with per user JSON data | |
// https://info.varnish-software.com/blog/securing-varnish-plus-with-user-json-data | |
vcl 4.0; | |
backend default | |
{ | |
.host = "content.company.com"; | |
.port = "80"; | |
} | |
import cookieplus; | |
import edgestash; | |
import goto; | |
import http; | |
import json; | |
import kvstore; | |
import xkey; | |
// INIT | |
sub vcl_init | |
{ | |
// Init a new kvstore in slot 0 | |
kvstore.init(0, 25000); | |
// Add an entry for _guest | |
kvstore.set(0, "_guest", {" | |
{ | |
"type": "guest", | |
"access": 0 | |
} | |
"}); | |
// Goto backend for api | |
new api = goto.dns_director("api.company.com"); | |
} | |
// RECV | |
sub vcl_recv | |
{ | |
// Get the auth-id from a session cookie, default to _guest | |
set req.http.X-auth-id = cookieplus.get("sessid", "_guest"); | |
// api logic | |
if (req.http.Host == "api.company.com") { | |
set req.backend_hint = api.backend(); | |
set req.http.X-auth-type = "api"; | |
// Never cache hit if the request came locally (vmod_http) | |
if (client.ip == "127.0.0.1") { | |
set req.hash_always_miss = true; | |
} | |
// Cache GET/HEAD | |
if (req.method == "GET" || req.method == "HEAD") { | |
return(hash); | |
// Purge support | |
} else if (req.method == "PURGE") { | |
kvstore.delete(0, req.http.X-auth-id); | |
set req.http.X-purged = xkey.purge(req.http.X-auth-id); | |
return(synth(200, "PURGED " + req.http.X-purged)); | |
} | |
return(pass); | |
} | |
// Load the user's JSON data | |
json.parse(kvstore.get(0, req.http.X-auth-id, "")); | |
if (!json.is_valid() || json.get("type", "") == "") { | |
call pull_user_json; | |
} | |
set req.http.X-auth-type = json.get("type", "guest"); | |
set req.http.X-auth-access = json.get("access", "0"); | |
// Continue on with access control using req.http.X-auth-* | |
if (req.method == "GET" || req.method == "HEAD") { | |
return(hash); | |
} | |
return(pass); | |
} | |
// HASH | |
sub vcl_hash | |
{ | |
// Add X-auth-type to the hash | |
// This is better done with a Vary response | |
hash_data(req.http.X-auth-type); | |
} | |
// PULL JSON | |
sub pull_user_json | |
{ | |
// Make an HTTP request back to Varnish | |
http.init(0); | |
http.req_copy_headers(0); | |
http.req_set_method(0, "HEAD"); | |
http.req_set_url(0, http.varnish_url("/json/auth")); | |
http.req_set_header(0, "Host", "api.company.com"); | |
http.req_send(0); | |
http.resp_wait(0); | |
// Load the JSON | |
json.parse(kvstore.get(0, req.http.X-auth-id, "")); | |
} | |
// JSON | |
sub vcl_hash | |
{ | |
if (req.http.Host == "api.company.com") { | |
hash_data(req.http.X-auth-id); | |
} | |
} | |
sub vcl_backend_response | |
{ | |
// Stage the JSON in Edgestash | |
if (bereq.http.Host == "api.company.com") { | |
edgestash.index_json(); | |
// Add auth-id as a purge key | |
set beresp.http.xkey = bereq.http.X-auth-id; | |
} | |
} | |
sub vcl_deliver | |
{ | |
// Pull the JSON from Edgestash, save into kvstore with TTL | |
if (req.http.Host == "api.company.com") { | |
json.parse_resp_index(); | |
kvstore.set(0, req.http.X-auth-id, | |
json.get(".", kvstore.get(0, "_guest", "")), 1h); | |
} | |
} | |
// PULL JSON ASYNC | |
sub vcl_deliver | |
{ | |
// This request resulted in an authorized view | |
// Notify the backend and get new authorization (async) | |
if (req.http.X-auth-used == "true") { | |
call pull_user_json_async; | |
} | |
} | |
sub pull_user_json_async | |
{ | |
// Make an HTTP request back thru to Varnish | |
http.init(1); | |
http.req_copy_headers(1); | |
http.req_set_method(1, "HEAD"); | |
http.req_set_url(1, http.varnish_url("/json/auth?content=" + | |
req.http.X-auth-location)); | |
http.req_set_header(1, "Host", "api.company.com"); | |
http.req_send_and_finish(1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment