Service exposing implementation details ======================================= Not in scope ------------ It is not in the scope of this specification to determine mapping to a public DNS or other directory service. Implementation of ``expose`` and ``unexpose`` subcommands --------------------------------------------------------- Two new user commands will be added:: ensemble expose ensemble unexpose These commands will set and remove a flag znode, **/services//exposed**, respectively. Hook command additions ---------------------- Two new hook commands are added for opening and closing ports. They may be executed within any formula hook:: open-port port[/protocol] close-port port[/protocol] These commands will store in the ZK tree, under **/units//ports**, the desired opened port information as serialized to JSON. For example, executing ``open-port 80`` would be serialized as follows:: {"open": [{"port": 80, "proto": "tcp"}, ...]} This format accommodates tracking other ancillary information for exposing services. New ``exposed`` and ``unexposed`` service hooks ----------------------------------------------- The ``exposed`` service hook runs upon a service being exposed with the ``ensemble expose`` command. As part of the unit workflow, it is scheduled to run upon the existence of **/services//exposed** and the service unit being in the ``started`` state. Likewise, the ``unexposed`` service hook runs upon the removal of a **/services//exposed** flag znode. ``ensemble status`` display of opened ports ------------------------------------------- If a service has been exposed, then the ensemble status output is suitably augmented. For the YAML serialization, for each exposed service, the ``exposed`` key is added, with the value of ``yes``. For each service unit of an exposed service with opened ports, the ``open-ports`` key is added, with its value a sequence of ``port/proto`` strings. Provisioning agent implementation --------------------------------- The provisioning agent currently is the only place within Ensemble that can take global actions with respect to the provider. Consequently, provisioning is currently responsible for the current, if simple EC2 security group management (with the policy of open all ports, seen in the code `ensemble.providers.ec2.launch.EC2LaunchMachine`). The provisioning agent will watch for the existence of **/services//exposed**, and if so watch the service units settings **/units//ports** and make changes in the firewall settings through the provider. For the EC2 provider, this will be done through security groups (see below). Later we will revisit to let a machine agent do this in the context of iptables, so as to get out of the 500 security group limit for EC2, enable multiple service units per machine, be generic with other providers, and to provide future support for internal firewall config. EC2 provider implementation --------------------------- Prior to the launch of a new machine instance, a unique EC2 security group is added. The machine instance is then assigned to this group at launch. Likewise, terminating the machine will result in the EC2 provider deleting the security group for the machine. Given this model of a security group per machine, with one service unit per machine, exposing and unexposing ports for a service unit corresponds to EC2's support for authorization and revocation of ports per security group. In particular, EC2 supports a source address of ``0.0.0.0/0`` that corresponds to exposing the port to the world. To make this concrete, consider the example of exposing the ``my-wordpress`` service. Once the command ``open-port 80`` has been run on a given service unit of ``my-wordpress``, then for the corresponding machine instance, the equivalent of this EC2 command is run:: ec2-authorize $MACHINE_SECURITY_GROUP -P tcp -p 80 -s 0.0.0.0/0 Any additional service units of ``my-wordpress``, if they run ``open-port 80``, will likewise invoke the equivalent of the above command, for the corresponding machine security groups. If ``my-wordpress`` is unexposed, a ``my-wordpress`` service unit is removed, the ``my-wordpress`` service is destroyed, or the ``close-port`` command is run for a service unit, then the equivalent of the following EC2 command is run, for all applicable machines:: ec2-revoke $MACHINE_SECURITY_GROUP -P tcp -p 80 -s 0.0.0.0/0 Although this section showed the equivalent EC2 commands for simplicity, txaws will be used for the actual implementation. Implementation plan ------------------- The following functionality needs to be added. This should divisible into separate, small branches: * Add dummy provider support for port authorization/revocation (to support unit testing). * Add ``open-port`` and ``close-port`` hook commands. * Add the new ``ensemble expose`` and ``ensemble unexpose`` subcommands, which set and unset the exposed flag for a service. * Modify ``ensemble status`` so that it displays the exposed ports for service units and whether a service is exposed or not. * Modify the provisioning agent that watches the exposed service setting, and if so, watches the service units settings (**/units//ports**) and make changes in the firewall settings through the provider. * Add a provider implementation for EC2: change launch code (`provider.start_machine`) to put newly created instances in a unique EC2 security group and implement actual authorize/revoke support. * Modify the canonical WordPress formula to demonstrate exposing. Related bugs ------------ This bug is subsumed by this work; however, it offers a different approach: * https://bugs.launchpad.net/ensemble/+bug/712134 - Utilize ec2 security groups with formula defined access ports