Skip to content

Commit

Permalink
feat(lambda): add AWS Lambda capability and example (#9)
Browse files Browse the repository at this point in the history
* Add AWS Lambda capability with example

* Update year
  • Loading branch information
SafeEval authored Aug 19, 2024
1 parent 4ab11a5 commit 57d9a2d
Show file tree
Hide file tree
Showing 38 changed files with 2,339 additions and 29 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
##########################################################

examples/docker-compose/ui-app/config.json

examples/aws-lambda-authorizer/bootstrap

##########################################################
# Golang
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 Divergent Codes LLC
Copyright (c) 2024 Divergent Codes LLC

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
7 changes: 2 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,10 @@ modcheck:
build:
./scripts/build.sh

# Local installation of existing built executable.
_build_install:
# Local installation of executable.
install:
./scripts/install.sh

# Local build and installation of executable.
install: build _build_install

# Build point-in-time snapshot release.
snapshot:
./scripts/snapshot.sh
Expand Down
132 changes: 122 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,38 @@ Exploring the feasability and performance of a JWT blocklist.

## About

JWT Block is a blocklist & auth proxy service for JWTs, to support immediate termination of access, since access tokens cannot truly be revoked.
JWT Block is a blocklist & forward auth proxy service for JWTs, to support
immediatetermination of access, since access tokens cannot truly be revoked.

It is a standalone binary that requires a Redis instance to store the blocklist.

It can be run as a web service or an AWS Lambda authorizer.

## Installation

Download the [binary release](https://github.com/DivergentCodes/jwtblock/releases) for your platform,
and place it in the executable path.
### Build From Source

1. Have a functional Golang development environment.
2. Build and install: `make && make install`

### Binary Release

JWT Block is also available as [a Docker image](https://hub.docker.com/r/divergentcodes/jwtblock).
1. Download the [binary release](https://github.com/DivergentCodes/jwtblock/releases)
for your platform
2. Place the `jwtblock` binary in an executable path.

### Docker

JWT Block is available as [a Docker image](https://hub.docker.com/r/divergentcodes/jwtblock).

```
docker run -it --rm divergentcodes/jwtblock:latest
docker run --rm -it divergentcodes/jwtblock:latest
```


## Usage

### CLI

```
JWT Block is a blocklist & auth proxy service for JWTs, to support immediate termination of access, since access tokens cannot truly be revoked.
Expand All @@ -38,6 +52,7 @@ Available Commands:
flush Empty the blocklist
help Help about any command
list List blocked JWT hashes
openapi Generate OpenAPI specs for jwtblock
serve Serve the web API
status Get status of the blocklist
unblock Unblock a JWT
Expand All @@ -47,23 +62,120 @@ Flags:
--config string config file (default is ./jwtblock.yaml)
--debug Enable debug mode
-h, --help help for jwtblock
--json Use JSON output
--json Use JSON log output
-q, --quiet Quiet CLI output
--redis-dbnum int Redis DB number
--redis-host string Redis host (default "localhost")
--redis-noverify Skip Redis TLS certificate verification
--redis-pass string Redis password
--redis-port int Redis port (default 6379)
--redis-tls Connect to Redis over TLS (default true)
--redis-tls Connect to Redis over TLS
--redis-user string Redis username
--verbose Verbose CLI output
Use "jwtblock [command] --help" for more information about a command.
```

## Configuration
### API

The web service listens on port `4474/tcp` by default. It has two primary
API endpoints, one for adding tokens to the blocklist (e.g. "logout") and
one to check whether a token is in the blocklist.

- `POST /blocklist/block`
- `GET /blocklist/check`

Both endpoints parse the token from the `Authorization` header as a
bearer token. No other parameters are needed.

Start the web service with `jwtblock serve`.

OpenAPI specs can be generated with `jwtblock openapi`.

### AWS Lambda

> [!TIP]
> Check the full [AWS Lambda example](./examples/aws-lambda-authorizer/README.md).
JWT Block can run as an AWS Lambda function that will handle the following events:
- API Gateway AWS Proxy: handle HTTP requests from API Gateway to block a token.
- API Gateway Authorizer: make authentication decisions for API Gateway, similar to "forward auth" proxies.

There are a few ways to configure JWT Block (in order of precedence):
### Configuration

There are multiple ways to configure JWT Block (in order of precedence):
- CLI argument flags.
- Environment variables.
- Configuration file.

## Demo

> [!TIP]
> The easiest way to try JWT Block is to spin up the [Docker Compose example](./examples/docker-compose/README.md).
Run a Redis instance and then start the web service with the `serve` subcommand.

```sh
$ docker run -d --rm -p6379:6379 redis:alpine
402f9668087f59fa085f6bcf5f40db441291f74b6399023a17654575d6d1dc95

$ jwtblock serve
JWT Block 0.0.1-DEV-SNAPSHOT-c7d8e29 created by Jack Sullivan <[email protected]>

Serving the jwtblock web API on :4474
{"level":"info","message":"Serving web API","func":"HandleRequests","host":"","port":4474}
```

Send requests to the service to block a token and verify that it is blocked.

```sh
export JWT="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNzIyNzIyODg4LCJleHAiOjE3MjI3MjY0ODh9.jPZiGRRudxPAku-FBiWHrxyn95Zj01Pm6ZiUw097fcE"

$ curl -s -X GET http://jwtblock.localhost:4474/blocklist/check \
-H "Authorization: Bearer $JWT" \
| jq
{
"message": "JWT is allowed",
"blocked": false,
"block_ttl_sec": -1,
"block_ttl_str": "",
"error": false
}

$ curl -s -X POST http://jwtblock.localhost:4474/blocklist/block \
-H "Authorization: Bearer $JWT" \
| jq
{
"message": "Token blocked",
"error": false
}

$ curl -s -X GET http://jwtblock.localhost:4474/blocklist/check \
-H "Authorization: Bearer $JWT" \
| jq
{
"message": "JWT is blocked",
"blocked": true,
"block_ttl_sec": 3463,
"block_ttl_str": "57m43s",
"error": false
}
```

The blocklist can be managed with the CLI.

```sh
$ jwtblock --quiet status
Blocklist size: 1

$ jwtblock --quiet list
{"level":"info","message":"Listed token hashes in the blocklist","size":1}
0: b8a5471d47b724b277d4861db071ae817556655abd9f31ce7cfa8b055cf9e397

$ jwtblock --quiet flush
{"level":"info","message":"Flushed the blocklist","count":1,"result":{"message":"OK","count":1,"error":false}}
Flushed 1 tokens from the blocklist

$ jwtblock --quiet status
Blocklist size: 0
```
62 changes: 62 additions & 0 deletions examples/aws-lambda-authorizer/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions examples/aws-lambda-authorizer/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Control the AWS Lambda Authorizor example.

all: build deploy

build:
cd ../../; GOOS="linux" GOARCH="amd64" bash ./scripts/build.sh

deploy:
cp "../../dist/jwtblock_linux_amd64_v1/jwtblock" bootstrap
terraform apply

stop:
echo "not implemented"
32 changes: 32 additions & 0 deletions examples/aws-lambda-authorizer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# JWT Block Example: AWS Lambda Authorizer

This example demonstrates using JWT Block as a Lambda authorizer.
It uses Terraform to provision an AWS environment:
- Dedicated VPC and private subnets.
- API Gateway that sends web requests to the Lambda if a custom authorizer approves.
- Lambda function with the JWT Block binary.
- Redis ElastiCache cluster for the blocklist.

The Lambda function acts as both a web API endpoint (`POST /block`)
and a [request-based](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html#api-gateway-lambda-authorizer-choose) Lambda authorizer for the API Gateway.

A Lambda authorizer takes the caller's identity as the input and returns
an IAM policy as the output. The API Gateway then evaluates the returned policy to allow or deny the request.

## Usage

Set the AWS profile to use as an environment variable and run `make`.
This will build JWT Block, deploy it to AWS via Terraform, and
provision all of the other necessary infrastructure for the example
(e.g. Redis ElastiCache).

```sh
export AWS_PROFILE=myaccount
make
```


## Resources

- [Use API Gateway Lambda authorizers](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html) (Amazon)
- [Control access to HTTP APIs with AWS Lambda authorizers](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html) (Amazon)
22 changes: 22 additions & 0 deletions examples/aws-lambda-authorizer/apigateway_http.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
###########################################################
# API Gateway (HTTP)
###########################################################

resource "aws_apigatewayv2_api" "main" {
name = "${var.project_name}-apigw-http"
description = "APIGW v2 (HTTP) for Lambdas"
protocol_type = "HTTP"

cors_configuration {
allow_origins = [local.ui_origin]
allow_methods = ["GET", "POST", "OPTIONS"]
allow_headers = ["Authorization", "Content-Type"]
max_age = 3600 # Cache duration for preflight requests
}
}

resource "aws_apigatewayv2_stage" "main" {
api_id = aws_apigatewayv2_api.main.id
name = "main"
auto_deploy = true
}
Loading

0 comments on commit 57d9a2d

Please sign in to comment.