strugee.net

Posts categorized as "development"

Default-secure systems

So recently I presented on operational security and then started in on the nightmare that is HTTPS deployment. And like I did with language-level security (I still need to write part 2 of that post), I thought to myself, this is so difficult. Why isn't there something that will do this for me? That's what my latest project is.

Here's the tl;dr:

type(app)
=> Django/Express.js/etc. app
secure_system(app)
=> Docker image

Or in other words, you'll be able to take an existing web app that you've written, run it through this system, and it will spit out a complete, reasonably-secure system image.

Let's step back.

The status quo

Currently, when a developer wants to run a web app, they can either use something like Heroku, which is fully manged, or a VM from DigitalOcean or Amazon EC2 or something. There are a variety of reasons you might not want to use Heroku, but the only other option is a VM - and with a VM, you get a bare system where you have to set up everything from scratch. Lots of developers just don't have the operational experience to do this properly or securely, but it's not like they can go and get an operations team to do it for them. So they end up with systems that may have active security problems as well as little to no defense-in-depth mitigations for when security inevitably fails. Security is just another operational concern the developer has no time and no expertise to deal with, so it just doesn't happen. The developer spins up a VM, gets it to where it "works" and then moves on. This is not good enough.

I don't want to create a false dichotomy, though: this is not the developer's fault. Everyone has conflicting priorities and it's unreasonable to expect the developer to spend lots of time learning to administrate systems so that they can then spend even more time, you know, administering systems. The problem is that there just isn't enough options available - we have to provide something better.

A middle ground

This is what my project is about: creating a middle ground between fully-managed deployment platforms and barebones, setup-from-scratch VMs.

This project rests on the idea that operational security (at least, in a single-server, single-admin context) flows from consistency, least privilege, and proactive, defense-in-depth security measures. Here are a couple core design goals:

  1. Meet developers where they are. Configuration management like Puppet is a great way to enforce consistency, but it adds a level of indirection and is just another thing that people running hobbyist projects don't have time to learn.
  2. Tight integration with apps - this excludes more obscure types of web applications, but gives us a better footing to set up a solid deployment environment. It also may let us integrate more tightly with things like Content Security Policy in the future.
  3. Support virtual hosting. The ability to run multiple apps while paying for a single VM is a compelling reason people go with VMs over e.g. Heroku - we won't be helping anyone if we leave this out.
  4. Upgrades are optional. Any system image created by this project will present a system that is organized and can be maintained and modified by hand without breaking everything.
  5. Upgrades are possible. Tight app framework integration will aid with putting data into well-known places that can be backed up and migrated to a new image generated by a newer version of this system.
  6. Not designed for "real" production environments. Any project that has a dedicated operations person should not be using this; they should be rolling their own custom environments with something like Puppet. Accordingly, there won't be compromises in security in favor of flexibility - it's designed to cover 75% of cases "pretty well", which is still better than the status quo for smaller projects (almost 100% of cases don't have any security at all).

I'd also like to highlight one really important decision: the output is complete system images. Probably at first this will mean Docker containers but this could easily be turned into VM images. This is a critical part of the design because it allows us to make broad, sweeping changes - for example, preferring system components written in memory-safe languages, replacing OpenSSL with LibreSSL, or creating systemd unit files that lock down service runtime environments to reduce the impact of a compromise. These improvements aren't possible unless we control the whole system. And because upgrades are optional but possible, the developer can get security improvements by "just" upgrading a component that they use, in the same way that they'd upgrade a library or something, as opposed to security being a continuous process they have to worry about. Again, obviously not perfect - but much better than the status quo.

I hope to have a MVP out Real Soon Now™. But in the meantime, if you have thoughts, feel free to reach out.


How I passed 2k GitHub contributions

So the other day I logged into GitHub and saw something crazy:

Screenshot of my GitHub contribution graph showing 2,054 contributions over the past year

How in the hell did I end up with that many contributions? Well, I think I know why - it happened because of a couple related, small habits I have. I thought I'd share them with everyone else as a sort of trivia - maybe you can adopt these habits too! (Although hopefully because you think they're good ideas, not because you just want to make yourself look cool on GitHub.)

Fix typos

If projects are freedom-respecting, that means that anyone is welcome to contribute if they're able to offer something valuable. How many times have you seen a typo in some docs? GitHub makes it super, super easy to fix these (just click the pencil icon in the upper-right of GitHub file views), and they're a fantastic way to contribute a little back to projects. Even if you aren't reading docs on GitHub, it often doesn't take that much time to find where they're hosted (which is frequently GitHub anyway). Really good docs will even have a link to their source right on the generated page.

So whenever I see a typo, I send a Pull Request. It's become an automatic response to seeing something that needs fixing, and is pretty routine for me nowadays.

Oftentimes there are changes that aren't as easy as a typo, but are still super easy to fix. Take this Homebrew PR - I authored it on a tablet and it took me under 3 minutes to make the suggested change[1]. That's not a lot of effort, and it'll help lots of people looking for the Homebrew change log. The next time you find yourself thinking "it kinda sucks that..." ask yourself - can I fix this myself? And if you can, go for it! Even if you screw up, I promise most projects will want to help you out instead of making fun of you[2] - as someone who runs several freedom-respecting projects, I know that getting new contributors is very, very valuable and because of that I'll do a lot to make contributing a good experience. I would encourage everyone to, as Mike McQuaid puts it in that Homebrew PR, "be the change you want to see in the world."

[1]: of course, that's partly because I'm pretty good at git and grok things like rebase -i. But even if that's not you and it would take you longer, it sounds like a great opportunity to improve your git skills to me!

[2]: I feel obligated to point out that not every project is like this - there are some projects that have a terrible culture (*coughcough*Linuxkernel*cough*). Screw them. They're not that common in my experience and it's their loss, anyway. Don't let them deter you from trying to improve things outside of their bubble of suck.

File issues

I just talked about making small changes whenever you see something you can improve with a couple minutes' time. But what if you're in a hurry, or it would take longer?

Filing issues is a super easy way to solve this. Get into the habit of filing issues for everything that irks you[3] - you're not allowed to say "such-and-such a project sucks because it's buggy!" when you haven't told the developers about the bug you're experiencing!

Filing issues also takes very little time and is super helpful to the developer. Probably a lot of the people who read this blog are software developers, so I'm betting you can understand how great it is to receive an issue that gives you lots of details that you can then use to fix a bug or a design problem. So why not give the gift of issues? Just make sure to search for duplicates! (This accounts for a significant amount of those 2,000 contributions on my GitHub profile. Over the course of my time on GitHub, I've filed 619 issues, 438 of which were on other people's projects. Of course, that's just on GitHub - I also do this frequently outside of GitHub.)

Again, every time you find yourself saying, "why doesn't it..." or "I wish it..." or "it's so annoying when...", let the developer know! You'll help them make their project better.

[3]: this is not to suggest that you file ridiculous issues that don't really say anything or just complain. But a focused bug report describing something concrete that's problematic is awesome.

Publish by default

This is the last and biggest habit I have that I think led to those 2k contributions: I publish everything by default, no matter what. It doesn't matter how bad or hacky or ugly I think a software project is, I just publish it. Because honestly, why not?

Keeping projects public doesn't get in anyone's way. It's not like someone will look at your GitHub and be annoyed that you give so much software to the world. And there's even a chance that your hacky script may actually be useful to someone. Even if I think a project is "bad" I'll still publish it because the reality is that no one is going to go around GitHub specifically trying to find people to make fun of.

Publishing projects also gives me incentive to clean them up - write a README, write tests, and keep a change log (if relevant). My standards are higher because I know all my work is all in the open - not because I'm afraid of other people telling me I'm doing it wrong, but because I want to create high quality code that has a better chance of being useful to the wider community.

In summary

All of these habits are related. The tl;dr is this: engage by default. Get used to being involved instead of shrugging off a problem and moving on. Put your work out there for other people to see. The open source/freedom-respcting software community belongs to everyone - and that means we can all contribute to improving it. We just have to start.


From static to Stratic - part 1

So a couple days ago I published generator-stratic@1.0.0-beta.1 to npm. Since Stratic is now officially in beta, I thought I'd write up a guide to converting a regular, static site to a Stratic-powered blog.

Each step in this blog post (part 1 of 2[?]) will take you closer to having a fully-functional blog, but because of Stratic's decoupled design, you can work through them at your own pace. Each step will leave you with a functional environment (i.e. nothing will be "broken" such that you can't work on your website anymore).

You can see the steps in this post in action at straticjs/static-to-stratic. Each commit corresponds to a step in this post.

Let's get started!

Initial setup

The site we'll be converting is currently pretty simple. It has an index.html and a projects.html. Each of these includes /css/main.css and /js/main.js. Also, they both have a navigation section and a footer that are duplicated across each page. Each time Alyssa P. Hacker - the website's owner - makes a change to these (for example to fix the copyright year in the footer), she has to change both HTML files. The best way for her to add a new page will be to copy an existing HTML file and then change it. This is a little unideal.

Alyssa tracks her website on GitHub (in the example repository mentioned above). Here are links for the index.html and the projects.html we'll be working with.

Here's a visual of the project layout:

% tree .
.
├── css
│   └── main.css
├── index.html
├── js
│   └── main.js
└── projects.html

2 directories, 4 files

When Alyssa needs to preview her website, she manually runs http-server ..

Since Alyssa uses GitHub she publishes her website on GitHub Pages, so her website is in the master branch of her git repository. (Here we're assuming that the repository is called aphacker.github.io or something, instead of static-to-stratic.)

In addition to adding blog support, we'll improve Alyssa's website by reducing duplication while still allowing her to publish to GitHub Pages.

Step 1 - adding gulp

Before we do anything else, we need to add a build system. Stratic is designed to work with [gulpjs][], so that's the one we'll be using.

Adding gulp is super easy. First, we need to create a package.json, so we do npm init:

% npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (static-to-stratic)
version: (1.0.0)
description: Personal website of Alyssa P. Hacker
entry point: (index.js)
test command:
git repository: (https://github.com/straticjs/static-to-stratic.git)
keywords:
author: Alyssa P. Hacker <alyssaphacker@example.net>
license: (ISC) AGPL-3.0+
About to write to /Users/alex/Development/static-to-stratic/package.json:

{
  "name": "static-to-stratic",
  "version": "1.0.0",
  "description": "Personal website of Alyssa P. Hacker",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/straticjs/static-to-stratic.git"
  },
  "author": "Alyssa P. Hacker <alyssaphacker@example.net>",
  "license": "AGPL-3.0+",
  "bugs": {
    "url": "https://github.com/straticjs/static-to-stratic/issues"
  },
  "homepage": "https://github.com/straticjs/static-to-stratic#readme"
}


Is this ok? (yes) yes

A couple things to note here: in general, the defaults are fine to accept. We've provided a description and an author, but these are optional since this isn't actually going to be published on the npm registry. They're just kind of nice to have.

The same goes for the license, which in this case is the Affero GPL 3.0 or above - however, as the copyright holder you are of course free to choose whatever license you want. (Or no license, although I'd discourage you from doing that.)

Once we have a package.json, we can go ahead and install gulp and another module we'll need, ecstatic:

% npm install --save-dev gulp ecstatic

If you haven't used gulp previously, you'll also need to install gulp-cli:

% npm install -g gulp-cli

At this point, we'll need to move some files around. Now that we have a build system, we can organize our repository however we want instead of putting stuff exactly where we want it in production.

You can do this however you want. The organization that you'll find most projects using, though, is to put stuff in a src directory. Let's make that right now.

% mkdir src
% git mv *.html src
% git mv css src/styles
% git mv js src/scripts

Finally, create a file named gulpfile.js and put the following in it:

var gulp = require('gulp'),
    http = require('http'),
    ecstatic = require('ecstatic');

gulp.task('build:html', function() {
    gulp.src('src/*.html')
        .pipe(gulp.dest('dist'));
});

gulp.task('build:css', function() {
    gulp.src('src/styles/*')
        .pipe(gulp.dest('dist/css'));
});

gulp.task('build:js', function() {
    gulp.src('src/scripts/*')
        .pipe(gulp.dest('dist/js'));
});

gulp.task('watch', ['build'], function() {
    gulp.watch('src/*.html', ['build:html']);
    gulp.watch('src/styles/*', ['build:css']);
    gulp.watch('src/scripts/*', ['build:js']);
});

gulp.task('serve', ['watch'], function() {
        http.createServer(
                ecstatic({ root: __dirname + '/dist' })
        ).listen(8080);
});

gulp.task('build', ['build:html', 'build:css', 'build:js']);

gulp.task('default', ['serve']);

This gives us a pretty good starting point. This gulpfile defines a couple tasks that simply copy source files into dist. The watch task watches for changes and rebuilds when they occur, and the serve task starts up a server, replacing Alyssa's usage of http-server. This provides exactly the same workflow as before: Alyssa runs one command and then she can look at her site on localhost:8080. You can use different task names if you want (for example, html instead of build:html, etc.), but these are what generator-stratic gives you.

However, there's one problem: Alyssa can't deploy her site anymore. If she pushed like this, visitors would have to visit e.g. https://aphacker.github.io/src/projects instead of https://aphacker.github.io/projects! That's no good.

In order to rectify this, we'll create a new git branch, src. src will contain the source files, and we'll put the final, built site in master, which is what's served by GitHub Pages. So:

% git checkout -b src
% git push --set-upstream origin src

Great. Now, we need to add something to put the built files (i.e. the contents of dist) in master. We'll use the gh-pages module for this. First install it and a dependency we'll need:

% npm install --save-dev gh-pages gulp-util

Next, make it available in the gulpfile by adding a line at the end of require() statements:

var gulp = require('gulp'),
    http = require('http'),
    ecstatic = require('ecstatic');

And finally, add a deploy task somewhere in the gulpfile:

gulp.task('deploy', ['build'], function(done) {
    ghpages.publish(path.join(__dirname, 'dist'), { logger: gutil.log, branch: 'master' }, done);
});

Now whenever Alyssa wants to deploy a new version of her website, she just runs gulp deploy and it'll be taken care of for her. (ProTip™: change the default branch to src on GitHub. That way visitors and new clones see the source files, not the build files generated by a program.)

The very last thing we need to do is add a .gitignore file since we're installing Node modules and have a build directory now. We'll just use GitHub's, adding a line for dist/ at the end:

% curl https://raw.githubusercontent.com/github/gitignore/master/Node.gitignore > .gitignore
% echo "\ndist/\n" >> .gitignore

Now we've got a functionally equivalent development setup based on gulp. Success!

Step 2: converting HTML to Pug

The next step is to convert the HTML to Pug. Pug (formerly known as Jade) is a language that compiles to HTML. It lets you do useful things like inherit from a common layout as well as looping over JavaScript variables. If you're not familiar with Pug, you should go take a look at its syntax now.

The easiest way to do this conversion is to get a program to do it for you. Here's the one I used way back when; you may be able to find a better one. The generated Pug will be valid but not the prettiest - you may want to wait to clean it up since we're going to do some work to reduce the duplication soon.

Once you've got the converted Pug, you should rename the relevant HTML file to have a .pug extention, then replace the contents with the Pug. Do this for each HTML file.

The last step here is to make gulp build the Pug. Install gulp-pug:

% npm install --save-dev gulp-pug

Then add pug = require('gulp-pug') to the end of the var declaration at the top of your gulpfile. Finally, change your html task to look like this:

gulp.task('build:html', function() {
    gulp.src('src/*.pug')
        .pipe(pug({pretty: true}))
        .pipe(gulp.dest('dist'));
});

We'll also need to fix the watch task so it has:

gulp.watch('src/*.pug', ['build:html']);

which will watch Pug files instead of HTML files.

That's it! Alyssa's site is now building with Pug instead of HTML.

Step 3: splitting out the layout

Pug's looping and flow control constructs will be very useful to us later on, but we can get some immediate productivity wins by splitting out the site layout so it's not duplicated across every Pug file.

There's one tricky thing about this: the navigation is mostly the same between pages, but not quite - the page the user is currently on shouldn't be a link. We'll solve this by using a block directive for each link. That way, we can override just what needs to be changed, while introducing no duplication.

You'll have to figure out exactly what parts of your personal layout make sense to be split out. In Alyssa's case, there are three main things that are mostly or fully duplicated across pages:

  1. The navigation bar
  2. The footer
  3. Invisible metadata and script/style includes

These are what we'll split out. First, we'll make a copy of index.pug and put it in src/includes/layout.pug. (Again, you can organize your files however you want - but in projects generated by generator-stratic, utility Pug files go in src/includes.) Next, edit out the page-specific content and replace them with block directives. Finally, edit each navigation bar item so it has its own block directive, leaving the old code as the default for the block directive.

Here's what this looks like when we do this to Alyssa's site:

doctype html
html
  head
    meta(charset='UTF-8')
    link(href='/css/main.css', rel='stylesheet', type='text/css')
    block head
  body
    block heading
    nav
      ul
        block nav-homepage
          li
            a(href='/') Homepage
        block nav-projects
          li
            a(href='/projects') Projects

    block body

    footer
      p &copy; Copyright 2016 Alyssa P. Hacker.
    script(src='/js/main.js', type='text/javascript')

Note how I've replaced the h1 element (the contents of which vary per-page) with block heading, I've added a block head directive so we can specify the title per-page, I've made a block for each navigation link so we can override them if we want to individually (otherwise they'll have the default of being a link), and I've added block body for the main content. I've also cleaned out a bunch of the cruft the automatic converter put in there.

Now, we can edit index.pug so that it inherits from layout.pug - we'll use the extends keyword for this. Then we just fill in the content we want using block. Here's what this looks like after we're finished with Alyssa's site:

extends includes/layout.pug

block head
  title Alyssa P. Hacker's homepage

block heading
  h1 Alyssa P. Hacker's homepage

block nav-homepage
  li Homepage

block body
  p This is the homepage of Alyssa P. Hacker. You can check out the projects I've worked on #[a(href='/projects') here].

You'll note that I've cleaned out some cruft here, too. We have one last thing to fix: if we change the layout, nothing will get rebuilt. We can fix this by changing the watch task again so that the line for watching Pug files reads:

gulp.watch(['src/*.pug', 'src/includes/*.pug'], ['build:html']);

Sweet! index.pug is way shorter than what we had before and includes just the content now. We can do the same thing to projects.pug. Then Alyssa can, for example, correct the copyright year in layout.pug - i.e., once - and that change will go into both index.html and projects.html. I've gone ahead and made the change for her.

To give a high-level overview, here's what Alyssa's site looks like now:

% tree -I node_modules .
.
├── dist
│   ├── css
│   │   └── main.css
│   ├── index.html
│   ├── js
│   │   └── main.js
│   └── projects.html
├── gulpfile.js
├── package.json
└── src
    ├── includes
    │   └── layout.pug
    ├── index.pug
    ├── projects.pug
    ├── scripts
    │   └── main.js
    └── styles
        └── main.css

7 directories, 11 files

Next time...

This post is long enough already, so I'll stop here. We've converted Alyssa's site to have a really solid base, so next time we'll build on top of this work to add superpowered blog features, powered by Stratic.

Now go apply this to your own site!


RSS and pagination on strugee.net

RSS and pagination are now enabled on strugee.net's blog, thanks to stratic-indexes-to-rss and stratic-paginate-indexes respectively.

The pagination code is already pretty solid although there's always room for improvement - thanks to Ajay Tungare for pairing with me at the Recurse Center and helping me catch the bug! However, I'm not particularly confident in the RSS code, since RSS is actually somewhat tricky to properly handle. Because of that, I would seriously appreciate it if people tried adding the RSS feeds to their readers and seeing if anything breaks. If so, let me know!

Cheers!


New project! thanksmaintainer.com

So I've been thinking about a new project recently. It's called thanksmaintainer.com, and it helps you say, "thanks, maintainer!"

Basically this idea came about from this GitHub thread where some user comes and disparages the Gulp project for being "not actively developed" and complains that it's taking a long time to release despite the fact that a) Gulp 4 is being actively developed and b) the Gulp maintainers owe this ungrateful, presumptive person nothing. As @contra says:

@rbatllet 4.0 is being worked - if you took more than five seconds to look you'd see it in other repos (for example: #1604). This repo has almost no code in it, it's a wrapper around the other modules so the contribution graph should be completely flat except for doc updates and major releases. We've responded to these tickets so many times - we have lives too.

I've had to move across the country 3 times since the tweet you linked - had my business unexpectedly fail (startups!), had relationships fail, deaths, travel, poverty, etc. - where the fuck am I supposed to find time to work on this while I'm trying to pay rent and put food in my stomach? The attitude people have towards open source authors is disgusting - I don't owe you anything, nobody does. Unless you've done something for the open source community (hint: you haven't!) then you aren't entitled to shit. If you want something to happen then help make it happen, otherwise log off.

I read this comment and thought, that freaking sucks. I feel like there's a problem in the open source and freedom-respecting software community where consumers demand stuff from project maintainers like the maintainers owe them something or they deserve some feature/bugfix/etc. This is obviously complete and utter bullshit - most maintainers work on their projects in their spare time - and it can start to feel like maintaining a project is a waste of time because all you get from it is a drove of angry entitled haters.

thanksmaintainer.com is designed to solve this problem. Here's how you use it; it's very simple:

  1. You visit thanksmaintainer.com
  2. You sign in with GitHub
  3. You find projects that you use and are grateful for
  4. You click the "thanks, maintainer!" button

thanksmaintainer.com will keep track of all the good vibes people are sending towards projects. When it reaches a certain threshold, the website will (probably) find a way to contact the maintainers and let them know how many people's lives they've made better. Not sure how this last bit will work yet, or if it'll even work like that at all. But the core idea is there - hopefully it'll be an easy way to give some love back to maintainers who may really need it.

Some design notes: it'll only work with GitHub to begin with, but eventually I hope to expand support to e.g. GitLab. Also, the fact that it's focused on projects and not maintainers is intentional - being thankful for a person instead of a project isn't as meaningful because you're not specifying why you're thankful. In contrast, the project-centric design lets you say, "these particular lines of code impacted my life in a meaningful way." Plus, a lot of projects have more than one maintainer, and it seems foolish to only thank some of the project maintainers, and not others.

I would love to hear feedback on this idea. So, if you have any thoughts, get in touch with me and let me know.

Stay happy <3


~