Sandbox for Nginx

Once I was chatting with a friend about how great it would be if there was such a sandbox site where you could just copy-paste the Nginx config and test it. And I realized that this is not so difficult to do, so I plunged into the topic and did: nginx-playground.wizardzines.com

screenshot

Next, let’s talk about how everything works here, because in the process I encountered some curious nuances and problems with non-obvious solutions.

How to use

You need to set up the Nginx configuration and also write a curl or http command that will send an HTTP request to this web server you configured.

You press “Run” at the top right and you get the answer:

Why is this sandbox needed?

I noticed that such sandboxes help me learn: it is very useful to be able to quickly and safely experiment and test different approaches to solving problems, while without fear of breaking something because of a mistake.

And Nginx is extremely finicky in setting, and therefore, I think that there should definitely be such a tool for it.

Here are three other similar projects I did earlier:

  • SQL sandbox – allows you to execute arbitrary SQL queries on small data to SQLite (uses sql.js).
  • CSS examples – using CodePen showcases some amazing CSS styling examples you can play with.
  • DNS lookup – allows you to perform any DNS queries and see the answers.

And a few other great sandboxes developed by other people:

  • CodePen – for CSS / JS / HTML.
  • RegExr – for regular expressions.
  • DB Fiddle – for SQL (MySQL, PostgreSQL and SQLite).
  • Nginx location match tester – simulates the algorithm for iterating over location directives in the Nginx config (written in TypeScript).

We do it quickly, without complications

The site consists of:

  1. a static frontend (and using Vue.js and Tailwind, my typical frontend kit)
  2. a backend written in Go with a single API method that does only one thing – starts Nginx with the given config.

The plan is simple, because it is beautiful: you just need to write one API method, and also the front that calls it. The tool is just as simple DNS lookup… I really like this approach, I think I and other projects will do the same.

Let’s talk about what the backend code does when it receives a request from the front.

What happens when a request comes in

This is what the Go backend does when you press “Run” note with code):

  1. write the config to a temporary file;
  2. create a new network namespace (ip netns add $RANDOM_NAMESPACE_NAME);
  3. launch go-httpbin on port 7777 so people can use it as the next hop in their Nginx configurations (for reverse proxy debugging);
  4. start Nginx;
  5. wait 100 milliseconds – let Nginx start, and if it doesn’t work, return a log with errors to the client;
  6. we execute the command specified by the user (we first make sure that the command starts with curl or http);
  7. return the result of the command;
  8. PROFIT!

Security Questions

The main idea of ​​the tool is to give the user the ability to try absolutely any Nginx configuration. Against this background, it is easy to imagine how many bad things can be done on the server. I wanted to make the tool free for users without spending too much money on hosting. Ideally, use one server to serve all requests.

Such nuances often stopped me on the way to launching similar projects, but specifically with this, everything should have worked out.

Security Approach: A Little Bit of Isolation and YOLO (!)

Here’s what I came to about safety after discussing with a friend:

  1. You need to place the frontend separately from the backend – somewhere in the CDN (if the backend is hacked, then at least no one will place any malware on the front).
  2. Do not use the database, only the localStorage of the browser (it is impossible to hack the database if it does not exist).
  3. Isolate Nginx in its own network namespace, prohibit access to the global network.
  4. Use a free plan on fly.io: weak servers, but isolated virtual machines that I can kill and recreate at least every hour if I want.
  5. Ask people to be sweethearts in the FAQ section (this is “YOLO” :))

The worst thing that can happen with all these restrictions:

  • someone will be able to access someone else’s Nginx config if two people use the site at the same time;
  • will replace the backend so that subsequent clients will receive all sorts of nasty things as a server response instead of the expected error log or the result of running curl;
  • some sly guy will try to mine bitcoin on this tiny virtual server (1 shared CPU, 256 MB RAM).

I don’t think the above looks too bad in light of the potential benefits of the site. However, perhaps I am missing something even more important. For example, someone has already shown that you can steal the contents of a file /etc/passwd (it was interesting, but there was nothing important in it anyway).

It would be possible to run Nginx in a container (instead of isolating only using the network namespace), but this will hit performance hard. And without this it doesn’t work very quickly.

Speaking of performance.

Something about performance

As mentioned earlier, the backend runs on fairly weak hardware (1 shared CPU, 256 MB RAM). In this regard, I have the following to say:

  1. The frontend is located on the CDN, so the backend load starts only when the user clicks the “Run” button. This alone makes life much easier for the server.
  2. According to the logs, requests take about 400 milliseconds. Not bad!
  3. The project now lives on a server located in Toronto. I believe that for users remote from Toronto, things are even slower. So you can deploy more of these small servers in other parts of the world.
  4. I used a clone written in Go httpbin instead of the original (written in Python), because this version seemed lighter and faster.
  5. Performance on the frontend side is slightly lame: styles and scripts are split into multiple files. I didn’t want to put it all together as a team npm buildbecause I am not very good at JavaScript, and therefore worried that in case of problems in the scripts, this would be an additional obstacle before fixing bugs and updating – it would just be lazy.
  6. Added a small GIF with a flying rocket – the user sees it while the request is being executed.

The stupidest performance error was caused by sending SIGKILL… This stopped only the main process, but the worker processes continued to work, and at some point the system ran out of free memory. When I changed everything so that the signal was sent SIGTERM, the situation has improved.

Design

Externally repeats design JSFiddle and CodePen.

In particular, the JSFiddle has something cool: they calculate the height of the stage as calc(100vh - 60px)and the header is set to height 60px… I wouldn’t have guessed it myself, but it works well.

I use CodeMirror as a syntax highlighting editor because that’s what JSFiddle and CodePen use. Setting it up was easy enough. In addition, there is syntax highlighting for Nginx and shell configs. Everything you can dream of 🙂

There was a headache with this CodeMirror just because I was going to use a ready-made package vue-codemirrorbut there were compatibility issues with Vue 3. I decided it wasn’t necessary and just wrote my own tiny integration that updates Vue when the text field is updated.

You can take a look at the code in the file script.js, there is not much of it.

To do more: more examples of configs

I still do not understand what exactly these configuration examples should be, but it would not be superfluous to add a couple of different typical configs as starting ones.

It was easier than it seemed

This tool was much easier to make than I thought. And this success prompts to make even more similar sandboxes for other programs, although I did not choose the next one. HAProxy seems like a good candidate.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *