Skip to content

Instantly share code, notes, and snippets.

@rezan
Created October 20, 2017 19:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rezan/9277b78dfd8d912d1fa1bf87849d50ac to your computer and use it in GitHub Desktop.
Save rezan/9277b78dfd8d912d1fa1bf87849d50ac to your computer and use it in GitHub Desktop.
Edge Logic: Securing Varnish with per user JSON data
// 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