![The Wasm Component Model and idiomatic codegen](/content/images/size/w632h355/2024/12/obsercured-sun.jpg)
The Wasm Component Model and idiomatic codegen
Idiomatic code generation for Go using the Wasm Component Model. Compiling for different languages always has tradeoffs, which is why using standards helps everyone.
Configuring OrbStack for local SSL certificates without needing to manage custom self-signed certs.
At Arcjet, we use OrbStack to manage our local development environment. We run various containers in production, so we use Docker Compose to mirror those services locally. This includes our low-latency security decision API used by our security as code SDKs, the dashboard webapp, website, docs, and backend processing components.
OrbStack is a feature-rich alternative to (but compatible with) Docker Desktop. It has a significantly better UI, is much more performant, and comes with powerful features like Custom Domains and Automatic HTTPS. These allow us to mirror our production environment very closely, particularly ensuring we have full SSL configured locally so we can properly test things like security headers.
Recently, OrbStack 1.9 made improvements so SSL certificates are now trusted between containers. I decided to spend some time improving our development experience by replacing the self-signed certificates we previously used between containers with this new OrbStack functionality.
The first change I made was removing a custom HTTP transport workaround to allow the local certificates between our services written in Go. This was very straightforward and no other changes were needed—the Go services trusted the certificate and seamlessly communicated via HTTPS. This allowed me to remove the self-signed certificates generated by mkcert and remove a setup step for our development environment.
However, whenever I tried to make the same changes for our Node.js services, they would fail with an error: self-signed certificate in certificate chain
. This didn’t make sense to me because OrbStack claimed that these certificates were trusted between containers and the error went away in Go code with the 1.9 release.
In scouring the Node.js documentation, I discovered the --use-openssl-ca
command-line flag. The documentation explicitly calls out:
The bundled CA store, as supplied by Node.js, is a snapshot of Mozilla CA store that is fixed at release time. It is identical on all supported platforms.
This means that Node.js bundles a static snapshot of Mozilla’s Certificate Authority which it uses by default to validate certificates. Once I understood this, it makes sense that each Node.js service still flagged the OrbStack certificates would be untrusted—they aren’t included in Mozilla’s CA!
Luckily, Node.js accepts the --use-openssl-ca
flag to opt-out of the bundled CA and instead rely on OpenSSL for CA. The documentation even explains that changes to the CA require this flag:
Using OpenSSL store allows for external modifications of the store. For most Linux and BSD distributions, this store is maintained by the distribution maintainers and system administrators. OpenSSL CA store location is dependent on configuration of the OpenSSL library but this can be altered at runtime using environment variables.
We want this to apply to any instance of the node
command run inside our containers without needing to specify the flag each time. We can apply this globally by setting the NODE_OPTIONS
environment variable in our container. I set this in our docker-compose.yml
file:
node-service:
build:
context: .
dockerfile: services/node-service/Dockerfile
environment:
- NODE_OPTIONS="--use-openssl-ca"
Even with this option set, communication from our Node.js services was still failing. It turns out that OrbStack mounts the root certificate at /usr/local/share/ca-certificates/orbstack-root.crt
but the OpenSSL CA doesn’t know about it.
To make Node.js aware of the root certificate, we need to set the SSL_CERT_FILE
environment variable. I updated our docker-compose.yml
file with this variable:
node-service:
build:
context: .
dockerfile: services/node-service/Dockerfile
environment:
- NODE_OPTIONS="--use-openssl-ca"
- SSL_CERT_FILE=/usr/local/share/ca-certificates/orbstack-root.crt
Perhaps we could apply some other commands to avoid setting the SSL_CERT_FILE
variable, but I wasn’t sure which magic incantation to apply.
Be aware of your base containers when applying this technique. Using a *-slim
container will fail with ERR_SSL_WRONG_VERSION_NUMBER
because the OpenSSL headers are removed to slim down the container. Instead, use the full container image or reinstall the OpenSSL headers that were removed.
With all of the above applied, we have seamless HTTPS communication in our entire development stack—our browser communicates over HTTPS with our applications which communicate over HTTPS to various other services.
Development certificates have always frustrated me, so I am delighted that none of this required manually generating a certificate or committing a development certificate into the repository. Finally, we have a streamlined onboarding process for our development environment with full HTTPS support!
Recently, Vite fixed a CVE that would allow a malicious page to interact with the dev server. Their solution (more or less) was to disallow anything other than the localhost domain from communicating with the dev server.
This is a problem with the OrbStack Custom Domains feature because you are accessing the Vite dev server via a domain such as https://modest_bhaskara.orb.local instead of localhost
. You can solve this by setting your custom domain in server.allowedHosts
in your vite.config.js
file:
export default {
server: {
allowedHosts: ["modest_bhaskara.orb.local"]
}
}
Unfortunately, OrbStack doesn’t provide this value inside the container. If it were available as an environment variable, we could specify it as a generalized process.env
property access. However, the custom domain names are based on the container name or customizable with labels, so we know what the domain will be for each container.
It’s great to see these regular improvements to OrbStack, which is a core part of our development setup. It means we can remove dev-only workarounds so our development environment is as close as possible to mirroring production.
Idiomatic code generation for Go using the Wasm Component Model. Compiling for different languages always has tradeoffs, which is why using standards helps everyone.
Framework switching, custom sidebar, custom table of contents, improved SEO, and a better user experience. How we customized Astro Starlight for the Arcjet docs.
Using Go + Gin to reimplement our backend REST API. How we built the golden API: performance & scalability, comprehensive docs, security, authentication, and testability.
Get the full posts by email every week.