Source – opensource.com
I struggled with writing the title for this post, and I worry that it comes across as clickbait. If you’ve come to read this because it looked like clickbait, then sorry.1 I hope you’ll stay anyway: there are lots of fascinating2 points and many3 footnotes. What I didn’t mean to suggest is that microservices cause security problems—though like any component, of course, they can—but that microservices are appropriate objects of interest to those involved with security. I’d go further than that: I think they are an excellent architectural construct for those concerned with security.
And why is that? Well, for those of us with a systems security bent, the world is an interesting place at the moment. We’re seeing a growth in distributed systems, as bandwidth is cheap and latency low. Add to this the ease of deploying to the cloud, and more architects are beginning to realise that they can break up applications, not just into multiple layers, but also into multiple components within the layer. Load balancers, of course, help with this when the various components in a layer are performing the same job, but the ability to expose different services as small components has led to a growth in the design, implementation, and deployment of microservices.
So, what exactly is a microservice? I quite like Wikipedia’s definition, though it’s interesting that security isn’t mentioned there.4 One of the points that I like about microservices is that, when well-designed, they conform to the first two points of Peter H. Salus’ description of the Unix philosophy:
- Write programs that do one thing and do it well.
- Write programs to work together.
- Write programs to handle text streams, because that is a universal interface.
The last of the three is slightly less relevant, because the Unix philosophy is generally used to refer to standalone applications, which often have a command instantiation. It does, however, encapsulate one of the basic requirements of microservices: that they must have well-defined interfaces.
By “well-defined,” I don’t just mean a description of any externally accessible APIs’ methods, but also of the normal operation of the microservice: inputs and outputs—and, if there are any, side-effects. As I described in a previous post, “5 traits of good systems architecture,” data and entity descriptions are crucial if you’re going to be able to design a system. Here, in our description of microservices, we get to see why these are so important, because, for me, the key defining feature of a microservices architecture is decomposability. And if you’re going to decompose5 your architecture, you need to be very, very clear which “bits” (components) are going to do what.
And here’s where security starts to come in. A clear description of what a particular component should be doing allows you to:
- Check your design
- Ensure that your implementation meets the description
- Come up with reusable unit tests to check functionality
- Track mistakes in implementation and correct them
- Test for unexpected outcomes
- Monitor for misbehaviour
- Audit actual behaviour for future scrutiny
Now, are all these things possible in a larger architecture? Yes, they are. But they become increasingly difficult where entities are chained together or combined in more complex configurations. Ensuring correct implementation and behaviour is much, much easier when you’ve got smaller pieces to work together. And deriving complex systems behaviours—and misbehaviours—is much more difficult if you can’t be sure that the individual components are doing what they ought to be.
It doesn’t stop here, however. As I’ve mentioned on many previous occasions, writing good security code is difficult.7 Proving that it does what it should do is even more difficult. There is every reason, therefore, to restrict code that has particular security requirements—password checking, encryption, cryptographic key management, authorisation, etc.—to small, well-defined blocks. You can then do all the things that I’ve mentioned above to try to make sure it’s done correctly.
And yet there’s more. We all know that not everybody is great at writing security-related code. By decomposing your architecture such that all security-sensitive code is restricted to well-defined components, you get the chance to put your best security people on that and restrict the danger that J. Random Coder8 will put something in that bypasses or downgrades a key security control.
It can also act as an opportunity for learning: It’s always good to be able to point to a design/implementation/test/monitoring tuple and say: “That’s how it should be done. Hear, read, mark, learn, and inwardly digest.9”
Should you go about decomposing all of your legacy applications into microservices? Probably not. But given all the benefits you can accrue, you might consider starting with your security functions.