-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add TinyGo support; Add Cloudflare Workers rendezvous server example
Requires the TinyGo fork found at https://github.com/ben-krieger/tinygo/tree/reflect-clear-and-more Signed-off-by: Ben Krieger <[email protected]>
- Loading branch information
1 parent
9b17722
commit 4d523b0
Showing
11 changed files
with
511 additions
and
91 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
// SPDX-FileCopyrightText: (C) 2024 Intel Corporation | ||
// SPDX-License-Identifier: Apache 2.0 | ||
|
||
// Package main implements a Rendezvous Server which can be compiled with | ||
// TinyGo and run on Cloudflare Workers within the free tier (under reasonable | ||
// load). | ||
// | ||
// Create a Cloudflare Worker with the name "rv" and start a new repo with the | ||
// following configuration: | ||
// | ||
// wrangler.toml: | ||
// | ||
// name = "rv" | ||
// main = "./build/worker.mjs" | ||
// | ||
// [build] | ||
// command = """ | ||
// go run github.com/syumai/workers/cmd/[email protected] -mode tinygo && | ||
// tinygo build -o ./build/app.wasm -target wasm -no-debug ./main.go | ||
// """ | ||
// | ||
// [observability] | ||
// enabled = true | ||
// | ||
// [[ d1_databases ]] | ||
// binding = "RendezvousDB" | ||
// database_name = "rv" | ||
// database_id = "COPY_YOUR_UUID_HERE" | ||
// | ||
// Upon creating a D1 database instance with name "rv" and updating | ||
// wrangler.toml, execute the following schema.sql file using: | ||
// | ||
// $ wrangler d1 execute rv --remote --file=./schema.sql | ||
// | ||
// schema.sql: | ||
// | ||
// PRAGMA foreign_keys = ON; | ||
// CREATE TABLE IF NOT EXISTS sessions | ||
// ( id BLOB PRIMARY KEY | ||
// , protocol INTEGER NOT NULL | ||
// ); | ||
// CREATE TABLE IF NOT EXISTS to0_sessions | ||
// ( session BLOB UNIQUE NOT NULL | ||
// , nonce BLOB | ||
// , FOREIGN KEY(session) REFERENCES sessions(id) ON DELETE CASCADE | ||
// ); | ||
// CREATE TABLE IF NOT EXISTS to1_sessions | ||
// ( session BLOB UNIQUE NOT NULL | ||
// , nonce BLOB | ||
// , alg INTEGER | ||
// , FOREIGN KEY(session) REFERENCES sessions(id) ON DELETE CASCADE | ||
// ); | ||
// CREATE TABLE IF NOT EXISTS rv_blobs | ||
// ( guid BLOB PRIMARY KEY | ||
// , rv BLOB NOT NULL | ||
// , voucher BLOB NOT NULL | ||
// , exp INTEGER NOT NULL | ||
// ); | ||
// CREATE TABLE IF NOT EXISTS trusted_emails | ||
// ( email TEXT PRIMARY KEY | ||
// ); | ||
// CREATE TABLE IF NOT EXISTS trusted_owners | ||
// ( pkix BLOB PRIMARY KEY | ||
// , email TEXT NOT NULL | ||
// , FOREIGN KEY(email) REFERENCES trusted_emails(email) ON DELETE CASCADE | ||
// ); | ||
// | ||
// Add users by executing SQL with "wrangler d1 execute rv --remote" to add to | ||
// the trusted_emails table. Then, add owner keys for users in the | ||
// trusted_owners table. | ||
// | ||
// Setting up Cloudflare cron jobs to remove expired rendezvous blobs is left | ||
// as an exercise for the implementer. | ||
package main | ||
|
||
import ( | ||
"context" | ||
"crypto/x509" | ||
"database/sql" | ||
"errors" | ||
"fmt" | ||
"log/slog" | ||
"net/http" | ||
"os" | ||
|
||
"github.com/syumai/workers" | ||
_ "github.com/syumai/workers/cloudflare/d1" | ||
|
||
"github.com/fido-device-onboard/go-fdo" | ||
fdo_http "github.com/fido-device-onboard/go-fdo/http" | ||
"github.com/fido-device-onboard/go-fdo/sqlite" | ||
) | ||
|
||
const oneWeekInSeconds uint32 = 7 * 24 * 60 * 60 | ||
|
||
func main() { | ||
db, err := sql.Open("d1", "RendezvousDB") | ||
if err != nil { | ||
slog.Error("d1 connect", "error", err) | ||
os.Exit(1) | ||
} | ||
state := sqlite.New(db) | ||
|
||
// If building with Go instead of TinyGo, use: | ||
// (*http.ServeMux).Handle("POST /fdo/101/{msg}", ...) | ||
handler := http.NewServeMux() | ||
handler.Handle("/fdo/101/", &fdo_http.Handler{ | ||
TO0Responder: &fdo.TO0Server{ | ||
Session: state, | ||
RVBlobs: state, | ||
AcceptVoucher: func(ctx context.Context, ov fdo.Voucher) (accept bool, err error) { | ||
owner, err := ov.OwnerPublicKey() | ||
if err != nil { | ||
return false, fmt.Errorf("error getting voucher owner key: %w", err) | ||
} | ||
der, err := x509.MarshalPKIXPublicKey(owner) | ||
if err != nil { | ||
return false, fmt.Errorf("error marshaling voucher owner key: %w", err) | ||
} | ||
return trustedOwner(ctx, db, der) | ||
}, | ||
NegotiateTTL: func(requestedSeconds uint32, ov fdo.Voucher) (waitSeconds uint32) { | ||
return min(requestedSeconds, oneWeekInSeconds) | ||
}, | ||
}, | ||
TO1Responder: &fdo.TO1Server{ | ||
Session: state, | ||
RVBlobs: state, | ||
}, | ||
}) | ||
workers.Serve(handler) | ||
} | ||
|
||
func trustedOwner(ctx context.Context, db *sql.DB, pkixKey []byte) (bool, error) { | ||
var email string | ||
row := db.QueryRowContext(ctx, `SELECT email FROM trusted_owners WHERE pkix = ?`, pkixKey) | ||
if err := row.Scan(&email); errors.Is(err, sql.ErrNoRows) { | ||
return false, nil | ||
} else if err != nil { | ||
return false, err | ||
} | ||
slog.Info("accepting voucher", "user", email) | ||
|
||
return true, nil | ||
} |
This file contains 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
This file contains 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
This file contains 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
Oops, something went wrong.