The Aporeto Trireme project provides the mechanism end-to-end authentication and authorization for containers and Linux services. In this series of posts, we discuss several features of Trireme and how they can be used to secure a cloud infrastructure.
Trireme associates a contextual identity with every application and uses this identity to transparently insert authentication and authorization in any communication between applications. Through this mechanism, it achieves detailed access control in any Linux environment without the need for firewall rules, ACLs, or a complex infrastructure. The end-to-end authorization approach makes it especially useful in multi-cloud or multi-availability-zone environments, where dealing with network primitives is operationally challenging.
Although we have documented in detail how Trireme works in Docker and Kubernetes environments, the library is designed to work with any Linux service. In this blog we describe how Trireme can be used with Linux processes and what are the underlying mechanisms that make this possible.
TL;DR – Show Me How
In order to illustrate how to use Trireme with Linux processes we will use the Trireme example with default settings.
- Download and build trireme-example from https://github.com/aporeto-inc/trireme-example. Follow the instructions in the Readme file and make sure all the dependencies are installed in your system.
- Start trireme-example in one window
% sudo trireme-example daemon --hybridThe above command will start the Trireme daemon supporting both Linux Processes and docker containers and basic PSK authentication. You will be able to see all the Trireme messages in the window.
- In a different window start an nginx server protected by Trireme
% sudo trireme-example run --ports=80 --label=app=web nginxNote, that we use sudo for this since nginx requires root access by default. The additional parameters are:
- Nginx will be listening on port 80
- The label app=web will be attached to the nginx service.
- Try to access the nginx server with a standard curl command
% curl https://127.0.0.1You will see that the curl command fails to connect to the nginx server, even though the server is running and listening on port 80.
- Run a curl command with a Trireme identity so that it can actually access the server:
% trireme-example run --label=app=web curl -- https://127.0.0.1In this case your curl command will succeed.
- Let’s run now a bash shell in the Trireme context
% trireme-example run --label=app=web /bin/bashWe essentially started just a standard bash shell within the Trireme context and protected by Trireme. In this case our bash shell can actually access the nginx server. A simple curl will succeed.
What did we do?
We started the Trireme daemon and started an nginx server protected by Trireme. By default only authorized traffic will be able to reach this nginx server and authorization is controlled through identities. There is no need for firewall ACLs or other networking based rules. Even processes in the same host will be unable to reach the nginx server, unless they are also protected with Trireme and they are explicitly authorized.
Deep Dive – Trireme and Linux Processes
In this section we will describe how Trireme introduces the transparent authorization for every Linux Process. This has several use cases that we will illustrate in some subsequent blogs. Some examples are:
- Separate Linux users in different authorization contexts and allow them only specific access to network resources.
- Isolate important applications like databases from the network and restrict access to authorized sources only.
- Isolate the sshd daemon and allow access only to management tool like Ansible to ssh into the machine.
- Restrict communication between processes based on libraries, checksums on executables, SELinux labels or other structures.
The Forgotten cgroup : net_cls
For several reasons the Linux kernel does not have an easy method to differentiate traffic based on the source or destination process. However, it has a very important facility that is not very well documented, but very useful. One of the control groups (cgroup) is known as net_cls. When a process is associated with this cgroup, the Linux kernel will mark all packets initiated by a process with a mark that is set in the configuration of the cgroup. For example, in a standard Ubuntu distribution you can see the mark in /sys/fs/cgroup/net_cls/net_cls.classid. The default value is 0, indicating that there is no mark placed on the packets.
In the case of Trireme, you will find a trireme directory under this controller in /sys/fs/cgroup/net_cls/trireme and Trireme will create a sub-directory there for every protected process. Let’s assume that the nginx process in the example above had a process ID of 100. Then Trireme will create the directory /sys/fs/cgroup/net_cls/trireme/100 and it will populate the net_cls.classid file with a corresponding mark value.
Once Trireme does that, all packets out of the nginx server or any of its children will be marked with the same mark. We can now apply the Trireme ACLs that capture Syn/SynAck traffic only on packets with this mark. As a result, we can apply policy to a specific Linux processes and not just a container.
Of course we need some more work. Eventually the process will die and we need to clean up. Fortunately, there is a kernel facility for that. The file /sys/fs/cgroup/net_cls/trireme/100/notify_on_release is populated with 1, meaning that the kernel should notify a release agent that there are no more processes associated with the particular cgroup. The file /sys/fs/cgroup/net_cls/release_agent identifies the binary that should be executed when such an event happens and Trireme automatically populates these files with the right values.
We can now put all the parts together.
- The trireme-example command sends an RPC message to the Trireme daemon requesting to run a process in a protected
- The daemon resolves the policy for the requested command and populates the right fields in the net_cls file structures.
- The daemon then does a simple exec and releases control to the requested process.
- When the process dies, the release_agent notifies the Trireme daemon about the event, and the Trireme daemon cleans up the state.
Metadata Extractor and Linux Processes
One of the most powerful features of the Trireme approach is the metadata extractor. In the above example we used a simple method where the labels that are forming the identity are provided by the user. However, these are not the only attributes that Trireme identifies and it can be extended to actually associate any attribute with this identity model.
By default the metadata extractor will capture the following information:
- Md5 checksum of the binary that is executed.
- Library dependencies of the binary.
- User ID.
- Group ID.
- Executable path
The result is that a user can define an authorization policy across any of these attributes. For example, you can define a policy that only a binary with a specific checksum can access a database. Or, that only a process with a given User ID can access an application. Or, that users with sudo capability can never talk to the Internet.
One can enhance this metadata extractor with additional sources. For example in an AWS environment, the AMI ID, VPC ID, subnet, SELinux/AppArmor labels and so on. The possibilities are unlimited.
By associating custom and system metadata with a process, Trireme allows you to create a “contextual identity” for every Linux process. You can then use this identity to control access over the network without worrying about IP addresses and port numbers. It is the identity that matters.