Introducing Osgood
Tags: javascript, opensource, node, security
Published: 2019-07-15 18:22:52

Services written today share a common flaw: Being over-privileged. Node.js applications, for example, are capable of executing child processes, sending network requests, writing to the filesystem, and sending signals to other processes. A typical application requires a small subset of these features, and as [malicious modules](https://medium.com/intrinsic/common-node-js-attack-vectors-the-dangers-of-malicious-modules-863ae949e7e8) gain in popularity, the risk of these often-unnecessary features being abused only increases.

At [Intrinsic](https://intrinsic.com), we've spent years building a product which applies the _Principle of Least Privilege_ to Node.js. Our core product allows our customers to provide a list of policies describing what the application is capable of doing. This includes things like which binaries can be executed, how the filesystem can be interacted with, and what URLs the application can request. If an I/O operation hasn't been whitelisted, then it fails.

Recently we asked ourselves: if a new platform was designed for building middleware / microservices, one which followed the _Principle of Least Privilege_, and provided enough functionality to securely replace the most common microservice use-cases, what would such a system look like?

[Osgood](https://github.com/IntrinsicLabs/osgood) became our attempt to build such a platform. However, we didn't want such a tool to be completely unfamiliar to developers. So, we reached for a pile of technology already familiar to many.

The platform is built using Rust, a language heralded for its safety, and JavaScript is run in V8, a ridiculously fast JavaScript engine.


## How does it work?

Osgood is available as a statically-linked binary that can be downloaded for Linux and MacOS. The binary can then be called with a path to a JavaScript application defining high level configuration, how to route incoming requests to different Osgood workers, and the security policies required by each worker.

![Osgood Screencast](https://thepracticaldev.s3.amazonaws.com/i/p9raiah7dr5s723gclo7.gif)

The binary can easily be run on a laptop for doing local development. Once the application is ready, the code, as well as the Osgood binary, can be uploaded to a production server.

Osgood is generally useful in situations where logic needs to be performed on a server, or some sort of secret needs to be kept from a client, or when the only required outbound I/O is via HTTP requests.

Osgood provides the necessary APIs to intuitively build CRUD applications backed with technologies like [CouchDB](https://couchdb.apache.org/) or [Elasticsearch](https://www.elastic.co/products/elasticsearch). Clients such as mobile apps and web browsers typically shouldn't be given unfettered access to a database, both for security purposes as well as preventing tight coupling. Using Osgood to maintain database credentials is a more secure approach, and transforming data into a common format helps avoid vendor lock-in.

Another use-case is providing an HTTP facade in front of existing backend API services. For example, if a mobile application wants to access data from two internal services, Osgood can make the two calls on behalf of the client and transform the resulting data. This also makes Osgood feasible as a GraphQL API.


## Guiding Principles

A few principles helped guide us while designing Osgood. Since we're a security company it's very important that the platform be **secure**. Nobody is going to want to use Osgood if it's slower than other technologies, so it needs to be **fast**. And finally, we wanted to bring the _Principle of Least Privilege_ into the hands of more programmers. This required us to make such an implementation extremely **simple**.


### Osgood Applications are Secure

Policies are defined within Osgood Application files using JavaScript functions. These functions use the same syntax as the [Intrinsic for Node.js](https://intrinsic.com/docs/latest/policy-outbound-http.html) HTTP policies.

Here's an example of a policy file which is able to interact with a few select GitHub APIs, as well as a CouchDB database:

```javascript
// app.js

// global configuration
app.interface = '0.0.0.0';
app.port = 3000;

app.get('/user/:username', './worker.js', (policy) => {
  policy.outboundHttp.allowGet('https://api.github.com/users/*/gists');
  policy.outboundHttp.allowGet('https://api.github.com/users/*/repos');

  policy.outboundHttp.allowGet('http://couchdb.local:5984/users/*');
  policy.outboundHttp.allowPut('http://couchdb.local:5984/users/*');
  policy.outboundHttp.allowPost('http://couchdb.local:5984/users');
  policy.outboundHttp.allowDelete('http://couchdb.local:5984/users/*');
});
```

Many backend databases expose functionality via HTTP-based APIs—think CouchDB and Elasticsearch. Many third-party services expose their APIs via HTTP as well—such as GitHub and Stripe. Needless to say a _lot_ of these middle-layer microservices can be built by exclusively communicating via HTTP.

### Osgood is Fast

With a simple `Hello, World!` benchmark, Osgood is able to serve around 40k requests per second.

```plain
$ wrk -c 100 -d 60 http://localhost:3000/hello
Running 1m test @ http://localhost:3000/hello
  2 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.61ms   13.36ms 219.02ms   97.42%
    Req/Sec    20.36k     3.17k   25.04k    91.00%
  2422992 requests in 1.00m, 265.74MB read
Requests/sec:  40360.32
Transfer/sec:      4.43MB
```

### Osgood is Simple

Osgood is available as a single statically-linked binary about 20MB in size. The only required argument for executing the binary is a single JavaScript file.

Let's take a look at a sample Osgood Application. This application represents a common task amongst microservices. The application accepts an input value in the request, makes multiple outbound HTTP requests using the value, transforms the resulting data in some manner, and responds with the combined dataset. This is the API facade pattern and is frequently used to reduce requests made by a client.

```javascript
// worker.js

export default async (_request, context) => {
  const username = context.params.username;

  const [gists_res, repos_res] = await Promise.all([
    fetch(`https://api.github.com/users/${username}/gists`),
    fetch(`https://api.github.com/users/${username}/repos`),
  ]);

  const [gists, repos] = await Promise.all([
    gists_res.json(),
    repos_res.json(),
  ]);

  return { username, gists, repos };
}
```

Once you [download a release](https://github.com/IntrinsicLabs/osgood/releases) you can then run this Osgood Application using the following command:

```shell
$ osgood app.js
```

* * *

In our next post we'll look at [Hosting a Static Site and Contact Form with Osgood](https://dev.to/tlhunter/hosting-a-static-site-and-contact-form-with-osgood-5c1g).