Giter Site home page Giter Site logo

fly.rs's Introduction

superfly octokitty

Fly Edge Apps

Travis Status Gitter chat Minimum rustc 1.31

DNS Applications

This is a DNS application server. It executes JavaScript to respond to DNS requests, and provides libraries for caching, global data storage, and outbound DNS/HTTP requests.

Why would you want it?

You can use this project to build custom DNS services — both authoritative servers and resolvers. It's quicker and easier to do complicated DNS work with Fly than it is to build a DNS service from scratch, especially if you already know JavaScript.

The real power is in running other peoples' code, however. It's designed to be deployed around the world, run untrusted applications built by not-you and make DNS development accessible to more developers.

How it works

DNS application code runs in v8 isolates with strict memory limits. The runtime accepts requests, parses them, and hands structured data over to application code.

Installation

MacOS and Linux

Download the latest release for your platform, ungzip and put the binary somewhere

Windows

Not yet done. Relevant issue: #9

Usage

fly-dns --port 8053 relative/path/to/file.js

Examples

Simple proxy

// Handle an event for a DNS request
addEventListener("resolv", event => {
  event.respondWith( // this function responds to the DNS request event
    resolv( // the resolv function resolves DNS queries
      event.request.name // requested record name
    )
  )
})

Static response

addEventListener("resolv", event => {
  event.respondWith(function () { // can respond with a function
    return new DNSResponse([ // list of DNS answers
      {
        name: event.request.queries[0].name, // name of the DNS entry
        rrType: DNSRecordType.A, // record type
        ttl: 300, // time-to-live for the client
        data: {ip: "127.0.0.1"} // data for the record
      }
    ], { authoritative: true })
  })
})

Fly & Deno

The Fly runtime was originally derived from deno and shares some of the same message passing semantics. It has diverged quite a bit, but when possible we'll be contributing code back to deno.

There's an issue: #5

Development

Prerequisites

Setup

  • wget -qO- https://github.com/superfly/libv8/releases/download/7.2.502.13/v8-osx-x64.tar.gz | tar xvz -C libfly
  • cd v8env
    • yarn install
    • rollup -c
    • cd ..
  • cargo run --bin dns hello-world.js

Running tests

  1. Runtime tests:
cargo test
  1. Javascript tests
cargo run --bin fly test "v8env/tests/**/*.spec.js"

fly.rs's People

Contributors

afinch7 avatar jeromegn avatar michaeldwan avatar mrkurt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

fly.rs's Issues

fetch + serverName / SNI for https requests

In node land, we had some issues with fetch requests that weren't sending the right servername. This commit fixed it, I don't believe the Rust fetch implementation is doing this right yet: superfly/fly@de6aac5

superfly/fly is about to get an option to override servername in fetch that we'll need over here too.

Proposal: Programmatic "secrets" API

We need a way to encrypt secret strings securely in various environments.

Previously, the only way to encrypt secrets on fly was to use the fly secrets CLI and "inject" the secret names in your config. We stored each value encrypted via KMS and distributed everywhere.

Shortcomings

  • [Technical] Very slow: decrypting these values takes minimum 50ms (up to 1s!) via the AWS API. Tempting to cache the plaintext results, making this less secure.
  • [UX] Awkward workflow: we need the secrets before we can deploy an app. Adding secrets also count as a release. Therefore the first release has no code.
  • [UX] Error prone: users sometimes forget they need to update secrets since adding secrets is disconnected from producing code.
  • [UX] Awkward API: how we merge the secrets into the config is not very intuitive and requires a specific format.

Possible solution: Asymmetric public key cryptography

I'm a big fan of how Travis CI does it. I think it's the right tradeoff between convenience and security.

Using public key encryption allows putting encrypted strings directly in code while keeping the values secret.

Workflow example

  • CLI: fly encrypt [-f <filename>] [text] returns an encoded string (could be base64, but I took a liking to base58 recently)
  • Copy/paste this string anywhere in code, usually assigning it to a variable. Could be in a .json file of some kind that's imported.
  • Code: fly.decrypt(str) returns the plaintext value

In production, this means we generate a private & public key for their app (like travis does), encrypt it using AWS KMS (or similar) and store it for distribution. Our API can return the public key and the binary can encrypt the value (or we can encrypt from the API.) If the project is not hosted with fly.io, this can be setup as an environment variable (we need a trait for how these can be retrieved) or stored in a manner similar to ours. Hell, it could just use KMS directly with their own AWS access and secret keys if they don't mind the slow path and putting encrypted ciphertext blobs in their code.

Locally, this would be a different key living close to a $HOME directory or to the project. Maybe it's something developers within the same company can share so they benefit from the same values everywhere. Encrypted values can be checked in source control.

It should be possible to cycle the key in the case of a breach. This would be mitigated by encrypting private keys with AWS KMS and never distributing private keys. Not sure if private keys should be distributed to allow for the same public keys in every environment. Sounds a convenience with a security cost. If they can be cycled easily, that might be "ok".

resolv against specific nameserver addresses

Right now, resolv takes a query/string and resolves it against 8.8.8.8. It should let you specify a nameserver to use for resolution.

It's a little fetch-like, so the most consistent way to do this might be to have a signature like:

interface DNSRequestInit{
  nameserver: string
}

resolv(name: string | DNSRequest, init?: DNSRequestInit)

Slow data store instantiation

Creating a data store (via a runtime) is slow and blocking. This is because creating the database on instantiation is at least 1 network call which may or may not take a while.

This is of course worse in production since the database is distributed.

We need to look into creating the database lazily and then storing the state so we don't try to recreate it again when we know it's been created previously.

Graceful shutdown of HTTP server

There's an hyper server API for passing a oneshot that will trigger a graceful shutdown of inflight HTTP requests.

We've been seeing some downtime while restarting the server (even with blue/green deployment). I think a graceful shutdown (triggered by a SIGINT or something) would help there.

Global cache notifications reader + writer

Requirements:

  • Receive notifications (as a stream)
  • Write notifications (distributed)

For now, only redis is necessary here. Other cache stores are not distributed.

Pubsub for receiving and a simple connection pool for writing to the primary redis.

Handle Let's Encrypt http acme challenges

  • Respond to /.well-known/acme-challenge/[hash]
  • Store the content to respond with somewhere (fs, redis, etc.), abstracted as a trait
  • Pass the store as an option to the http server

Runtime lifecycle management

Runtime deaths (and pre-deaths) should be handled gracefully.

A runtime should be "replaced" seamlessly in the following scenarios:

  • Close to its heap hard limit (to avoid RangeErrors)
  • If it has stayed too long beyond the soft limit
  • Uncaught exception
  • Uncaught promise rejection

Example DNS app

We should have a reasonably useful example app to show people. An app that does these seems like a good start:

  • Defines a "zone" in JSON
  • Handles ALIAS lookups

Use the new cache schema for responseCache

Currently responseCache is copied from the node project. It uses multiple keys to store metadata. With the new cache schema, it's possible to store arbitrary metadata.

responseCache could use this new feature to store response headers and such as metadata and the body as the cache value, removing the need for multiple lookups.

fetch with a Request body

fetch should be able to send a body for a Request. That's currently not implemented, there's nothing blocking this, just needs to be added.

Requirements:

  • Streams the body (like all our other bodies)

Deno for the runtime

This project is heavily inspired by deno and in a lot of cases uses code from deno directly.

Talking with Ryan Dahl, we've determined deno should be embeddable in other rust projects. It would be nice to use deno directly since it's improving rapidly (performance, security, features) and bringing all its changes is hard.

There are some things we're "waiting" for before we can make the switch:

  • Deno as a rust crate (denoland/deno#695)
  • Memory limits (both max-old-space and allocations)
    • Inspecting the memory usage (V8's GetHeapStatistics + allocated space)
  • Uncaught exception and promise rejection callbacks (ability to only crash the runtime and not the whole process)
  • Custom operation handlers (including flatbuffers message)
    • Opt-in / opt-out for built-in operations
  • Custom snapshots
  • print callback with log "level" to handle logs differently

Use deno core crate?

One of the goals for deno v0.4 is a public crate for the core functionality. This might be a good direction to go with future development.

Moving forward towards execution with v8 modules.

I just finishing up my PR for a modular module resolver system #20. I've gotten familiar with the code base, and thought about what features I need in fly and what features might be useful to others. I have decided that my first big goal is move code execution over to the module system in v8. This should allow for more dynamic dependency resolution and analysis in a way that isn't as heavy(and insecure) as running typescript compiler/language services in each runtime. Moving import control out of the runtime will make import metadata like referer more trustworthy sources of information, so some form of security can be established for things like secrets from imports. In general I want to make dynamic module resolution and loading more production friendly.

I've come up with an approach that is designed around making flexible and extensible systems from simpler goals, so that I can be adding value to the project with each step along the way. Here is my list of smaller goals:

  • Implement Modular and flexible resolver system.
  • Implement c++ bindings for running, compiling, and receiving import callbacks for v8 Modules.
  • Design framework for service runtimes that run javascript native code as services, so things like the typescript compiler can be run as a shared service.
  • Module resolvers/loaders will need to talk to these service runtimes, so design and implement a system for asynchronous communication between runtimes(not from untrusted code/javascript directly for security).
  • Create a service runtime for the typescript compiler, and the respective tools/js libs for creating others.
  • Implement a SourceLoader for typescript code that uses these new systems.
  • Move execution from v8 Script to Module.

Note the first two are already done I started working on this while working on #20, but as I got a better feel for the scope of this goal I saw that It was far to much for one PR.

That about sums up my goals for the next month or so. I welcome any feedback/ideas since most of this is in no way final.

Validate arguments

We're relying on typescript to verify correct argument types, but we don't have those assertions in prod or with js apps. We should perform best effort type assertions at runtime before dispatching messages over the bridge so users get better error messages.

Allow promises to be handled after rejection

This causes an unhandled promise rejection error in fly.rs but is considered valid in other engines and MDN:

const handler = Promise.reject();
handler.then(console.log).catch(console.error);

Abstract file operations as a "store"

Similar to the Cache and Data stores, file operations should be abstracted as a FileStore of some kind.

Default should be querying the actual fs, but there is a case for storing files in different places (ie redis for small files, mongodb, etc.) Not that we recommend doing so...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.