Last year I wrote a post on using the CosmosDB Docker-based emulator with devcontainers and since then Iβve used that pattern many times to build applications, but there was one thing that kept bothering me, having to disable SSL for Node.js.
Sure, disabling SSL with NODE_TLS_REJECT_UNAUTHORIZED
wasnβt a huge pain, but it did feel like a dirty little workaround, it also hit a snag - dotnet projects.
I had the idea that I should add the CosmosDB emulator to the devcontainer used by FSharp.CosmosDb, as I kept deleting the Azure resource that I used between when I was working on it. But when Iβd set the account host to https://cosmos:8081
for the connection string, itβd fail to do queries as the self-signed certificate was rejected.
I guess itβs time to install the certificate.
The emulator provides the certificate at a well-known endpoint, which you can get using cURL
:
curl -k https://$ipaddr:8081/_explorer/emulator.pem > emulatorcert.crt
But when should we run that, and whatβs the IP of the Cosmos emulator container?
Installing the certificate
Because we need to wait until the containers have started, weβll use the postCreateCommand
in the devcontainer.json
file, and weβll have it call a bash script. Hereβs the bash script:
#!/usr/bin/env bash
set -euxo pipefail
ipAddress=https://$(docker inspect cosmos -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'):8081
# Try to get the emulator cert in a loop
until sudo curl -ksf "${ipAddress}/_explorer/emulator.pem" -o '/usr/local/share/ca-certificates/emulator.crt'; do
echo "Downloading cert from $ipAddress"
sleep 1
done
sudo update-ca-certificates
To get the IP of the emulator, weβll use docker inspect
and in the docker-compose
I set a name for the container, cosmos
, so that itβs a well-known name (we could make an assumption of the name, based off the way compose names containers, but this is safest), and we provide a template to grab the IP from the JSON response - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}
. This is combined with the protocol/port information to make a variable for the IP address to then download and install the certificate as described here.
Setting the connection info
With the certificate installed, it might be convenient to set the connection string information so that it can be used. Initially, I thought to use environment variables (since we have the IP as a bash variable) and load them with the Microsoft.Extensions.Configuration.EnvironmentVariables
NuGet package, so we can add a export ipAddress
to the end of the bash script (or maybe make the variable something easier to parse into the dotnet config system), but it turns out that you canβt export variables from postCreateCommand
s (see this issue).
Since that was off the table, an alternative solution would be to dump the info out as a file on disk. Hereβs the dotnet approach for my project, you just have to adapt the file (and its contents) for your project needs:
if [! -f ./samples/FSharp.CosmosDb.Samples/appsettings.Development.json]
then
echo '{ "Cosmos": { "EndPoint" : "'$ipAddress'" } }' >> ./samples/FSharp.CosmosDb.Samples/appsettings.Development.json
fi
Note: I have the Access Key for cosmos in the docker-compose
file, but you could also dump it out here if you prefer.
And with that, when the container starts, the connection to Cosmos is ready for your application to use.
Summary
In this post weβve seen how we can run the Docker CosmosDB emulator side-by-side with our app container using a VS Code devcontainer. The full definitions that I published for my project can be found here.
Now that Iβve figured out how to do this, Iβm going to be going back and retrofitting some other repos so that I donβt have to disable SSL validation for Node.js apps, making it more secure to run them locally.
Addendum
After writing this post and going back to some JavaScript/Node.js projects, I found that they were still failing with an invalid certificate and it turns out that if I'd read the docs fully I'd have know this. It seems that while dotnet applications running on Linux respect the certificate store, Node.js apps don't, so you need to explicitly add the certificate using the NODE_EXTRA_CA_CERTS
environment variable, so I've added "NODE_EXTRA_CA_CERTS": "/usr/local/share/ca-certificates/emulator.crt"
to the remoteEnv
section of the devcontainer.json
file... sigh.
Top comments (0)