by Richard Taylor : 2020-03-12
As soon as you start deploying an API into a running system you notice a few "must have" features that make life easier and probably a few blunders that you hope no-one notices (but they do).
The Electric Cat API as it stands absolutely has to be HTTPS not HTTP. Did you notice why in the previous posts? Our authorisation model is based on passing tokens in the request headers. If we use HTTP then anyone sniffing the network can see those tokens and use them to make requests as if they were the user who legitimately obtained them, until the token expires.
Fortunately setting up HTTPS is easy these days. All you need to do is generate a key and a certificate, then use them in your API server.
In this commit you can see how simple it is. You can also see how easy it is to accidentally commit the key and certificate to source control... a classic schoolboy error. If you manage to do this then immediately remove them, make new ones and update your .gitignore file to stop it happening again.
I can never remember the options for openssl so I put the command to generate the key and certificate into a script:
bin/new_cert.sh
When you run this it will ask you a few questions. Mostly you can answer
with whatever you like; except for the "server" name: this must match the
server name in the URL. So if you expect to use https://localhost
then the certificate needs "localhost" as the server name.
The key and certificate act as the private and public parts of your server's encryption system. The key is never shared, because it is private. The certificate is shared widely, because it is public. However, a stand-alone, or self-signed, certificate can be subjected to a man-in-the-middle attack. So for production use you need to get your certificate signed by a trusted third party; then clients know that it really is your certificate.
For test purposes you wont want to get all your certificates signed. Especially if you publish your keys to github by mistake and immediately have to junk them!
But you still want your tests to verify that the server has a valid certificate. So you can do this by adding the server certificate to your local trust store manually; or in the case of the python requests library we are using for our API tests, we can add it to the certificate authority bundle using an environment variable in the test runner as follows:
export REQUESTS_CA_BUNDLE=$TOP/config/cert.pem
Change all the test URLS from http
to https
and bingo! We have an API working securely over HTTPS.
When you are spinning up a bunch of services, how do you know that they are
ready? If one service can only start after another is ready you need to know
when to start the second one. To that end, many tools expect a service to have
a /health
endpoint which returns a 200 code when it is ready.
GET /health
UP
Given a service, how do you know what endpoints it supports? This is a slightly tricky question. Maybe your security policy says that you should only make that information visible to registered users. One option is to make your base URL give out the bare minimum information:
GET /
The Electric Cat - REST API
All endpoints other than "/" require
a valid Authorization header.
You can find a full list of endpoints at /help
Meeeow!
You could also include information about how to register!
Then your /help
endpoint lists the available endpoints:
GET /help
The Electric Cat
All endpoints require a valid Authorization header.
GET
/help
/health
/metrics
/v1/items/{item identifier}
/v1/items?{query string}
/v1/links/{link identifier}
/v1/links?{query string}
POST
/v1/items
/v1/links
PUT
/v1/items/{item identifier}
/v1/links/{link identifier}
DELETE
/v1/links/{link identifier}
Notice how the documentation is extracted from comments in the code itself. Whilst not essential, it is a great way to keep the code and documentation in sync with each other.
The third piece in the Observability toolkit is metrics. Once your service is running how is it doing? Is the pattern of usage today the same as it was yesterday, or last week, or last month? Has there been a sudden spike in failed access attempts?
These questions can all be delegated to third-party monitoring tools if your service provides them with the raw data in a format they understand. Fortunately there is now a widely used defacto standard for this - prometheus.
GET /metrics
# HELP http_responses The total number of HTTP responses.
# TYPE http_responses counter
http_responses{code="200"} 121
http_responses{code="400"} 75
http_responses{code="404"} 24
http_responses{code="401"} 36
Even just these simple counters, sampled every few minutes, can give a lot of insights into the recent and historic behaviour of our service.
For security on today's networks HTTPS is a must have, not a luxury for the paranoid.
And it's not paranoid to want to know what your service is up to after you have deployed it. So observability is important too: health, help and metrics can assist with that; and maybe even automate it to a large degree.