App-Level Isolation in Web Server Config

App-Level Isolation in Web Server Config

January 17, 2025
đź“Ž
Clace is an open source platform for developing internal tools and deploying them across a team. Clace can auto-generate an UI for backend actions. Clace is the the first internal tools platform built for Hypermedia Driven Applications and first internal tools platform on the CNCF landscape.

Background

Clace is a platform for developing and deploying internal tools. Clace provides functionality usually handled separately by a web server and an application server. The web server part of Clace is built such that there is complete isolation between app-level routing rules. Creating a new app or updating an existing app cannot break other apps. This post goes into details about how this is done and why it is useful.

Web Server Overview

A Web Server is software that accepts HTTP/HTTPS requests and routes them appropriately. Web Servers provide features like URL rewrites, reverse proxying, header manipulation, static file serving, and WebSocket connection handling. Web Servers can accept connections on multiple ports. A common pattern is that all requests are received on port 80 (for HTTP) and 443 (for HTTPS) and routing is done based on request domain (from the Host HTTP header) and path (from the url). Apache, Nginx, and Caddy are popular web servers.

Updating Routing Rules

Most web servers use a config file for specifying the rules for request routing. The config file is generally a DSL which specifies the API rules. Some servers like Caddy support JSON input. Encapsulating or grouping together rules related to an app is supported, but this encapsulation is not enforced. For updating the rules, the approach is to update the file on disk and send a reload request to the web server. Even for API based updates, the approach is usually to send the whole config contents, partial updates are not supported.

The issue with this approach is that if there are multiple apps, updating the rules for one app can break other apps. There is no enforcement of isolation across apps. This results in app developers trying to avoid updating the web server, doing more in the application server when it would have been more efficiently done in the web server.

App-Level Isolation

The goal of app-level isolation is for developers to confidently update web server routing rules, being sure that a broken update will not impact other apps.

To enforce isolation across apps, web servers first need a concept of an app. There needs to be a way to say which rules are for which app. Each update has to be scoped to be for a particular app. If there are rules which conflict with another app, those rules need to be rejected.

Clace Approach

Clace has the concept of an app where each app is installed for a path within a domain. If app A is installed at appA.example.com:/, then that domain is completely dedicated for that app. An app installed at appB.example.com:/test owns the /test/* namespace for domain appB.example.com, no other app can use that namespace.

This approach has the benefit that each app gets a dedicated namespace. Within that namespace, the app can do whatever it wants, without interfering with other apps. At app installation time, a check is done whether the domain path being requested is available. If some other app is using that path, the new app installation is rejected. A SQLite database is used to store app metadata, so API driven app updates do not conflict with each other.

Within the app, rules are defined using Starlark. This avoids the need to learn a new DSL. The routing config is read at app initialization only and the actual handler logic can be expressed alongside the routing rules. For containerized apps, the routing rule specifies the revery proxy url. Reloading an app updates its config, without impacting other apps.

Conclusion

By enforcing app-level isolation in routing rules, Clace allows each app to manage its own domain and path namespace without risking conflicts or breakages. This approach encourages developers to utilize efficient web server–level routing features, confident that changes in one app won’t disrupt others. Some webserver routing use cases which are more complex cannot use this approach, but this is useful for app deployment scenarios.