This topic describes how Pivotal Web Services secures the containers that host application instances on Linux. For an overview of other PWS security features, see the PWS Security topic.
- Container Mechanics provides an overview of container isolation.
- Inbound and Outbound Traffic from PWS provides an overview of container networking and describes how PWS administrators customize container network traffic rules for their deployment.
- Container Security describes how PWS secures containers by running application instances in unprivileged containers and by hardening them.
Each instance of an app deployed to PWS runs within its own self-contained environment, a Garden container. This container isolates processes, memory, and the filesystem using operating system features and the characteristics of the virtual and physical infrastructure where PWS is deployed.
PWS achieves container isolation by namespacing kernel resources that would otherwise be shared. The intended level of isolation is set to prevent multiple containers that are present on the same host from detecting each other. Every container includes a private root filesystem, which includes a Process ID (PID), namespace, network namespace, and mount namespace.
PWS creates container filesystems using the Garden Rootfs (GrootFS) tool. It stacks the following using OverlayFS:
- A read-only base filesystem: This filesystem has the minimal set of operating system packages and Garden-specific modifications common to all containers. Containers can share the same read-only base filesystem because all writes are applied to the read-write layer.
- A container-specific read-write layer: This layer is unique to each container and its size is limited by XFS project quotas. The quotas prevent the read-write layer from overflowing into unallocated space.
Resource control is managed using Linux control groups. Associating each container with its own cgroup or job object limits the amount of memory that the container may use. Linux cgroups also require the container to use a fair share of CPU compared to the relative CPU share of other containers.
Note: PWS does not support a RedHat Enterprise Linux OS stemcell. This is due to an inherent security issue with the way RedHat handles user namespacing and container isolation.
Each container is placed in its own cgroup. Cgroups make each container use a fair share of CPU relative to the other containers. This prevents oversubscription on the host VM where one or more containers hog the CPU and leave no computing resources to the others.
The way cgroups allocate CPU time is based on shares. CPU shares do not work as direct percentages of total CPU usage. Instead, a share is relative in a given time window to the shares held by the other containers. The total amount of CPU that can be overall divided among the cgroups is what is left by other processes that may run in the host VM.
Generally, cgroups offers two possibilities for limiting the CPU usage: CPU affinity and CPU bandwidth, the latter of which is used in PWS.
CPU affinity: It consists of binding a cgroup to certain CPU cores. The actual amount of CPU cycles that can be consumed by the cgroup is thus limited to what is available on the bound CPU cores.
CPU bandwidth: Sets the weight of a cgroup with the process scheduler. The process scheduler divides the available CPU cycles among cgroups depending on the shares held by each cgroup, relative to the shares held by the others.
For example, consider two cgroups, one holding two shares and one holding four. Assuming the process scheduler gets to administer 60 CPU cycles, the first cgroup with two shares will get one third of those available CPU cycles, as it holds one third of the overall shares. Similarly, the second cgroup will get 40 cycles, as it holds two thirds of the collective shares.
The calculation of the CPU usage based on the percentage of the total CPU power
available is quite sophisticated and is performed regularly as the CPU demands
of the various containers fluctuates. Specifically, the percentage of CPU cycles
a cgroup gets can be calculated by dividing the
cpu.shares it holds by the sum
cpu.shares of all the cgroups that are currently doing CPU activity:
process_cpu_share_percent = cpu.shares / sum_cpu_shares * 100
In PWS, cgroup shares range from 10 to 1024, with 1024 being the default.
The actual amount of shares a cgroup gets can be read from the
cpu.shares file of the cgroup configurations pseudo-file-system
available in the container at
The amount of shares given to an applications cgroup depends on the amount of memory the application declares to need in the manifest. PWS scales the number of allocated shares linearly with the amount of memory, with an app instance requesting 8G of memory getting the upper limit of 1024 shares.
process_cpu.shares = min( 1024*(application_memory / 8 GB), 1024)
The next example helps to illustrate this better. Consider three processes. P1, P2 and P3, which are assigned
cpu.shares of 5, 20 and 30, respectively.
P1 is active, while P2 and P3 require no CPU. Hence, P1 may use the whole CPU. When P2 joins in and is doing some actual work (e.g. a request comes in), the CPU share between P1 and P2 will be calculated as follows:
- P1 -> 5 / (5+20) = 0.2 = 20%
- P2 -> 20 / (5+20) = 0.8 = 80%
- P3 (idle)
At some point process P3 joins in as well. Then the distribution will be recalculated again:
- P1 -> 5 / (5+20+30) = 0.0909 = ~9%
- P2 -> 20 / (5+20+30) = 0.363 = ~36%
- P3 -> 30 / (5+20+30) = 0.545 = ~55%
Should P1 become idle, the following recalculation between P2 and P3 takes place:
- P1 (idle)
- P2 -> 20 / (20+30) = 0.4 = 40%
- P3 -> 30 / (20+30) = 0.6 = 60%
If P3 finishes or becomes idle then P2 can consume all the CPU as another recalculation will be performed.
Summary: The cgroup shares are the minimum guaranteed CPU share that the process can get. This limitation becomes effective only when processes on the same host compete for resources.
A host VM has a single IP address. If you configure the deployment with the cluster on a VLAN, as recommended, then all traffic goes through the following levels of network address translation, as shown in the diagram below.
Inbound requests flow from the load balancer through the router to the host cell, then into the application container. The router determines which application instance receives each request.
Outbound traffic flows from the application container to the cell, then to the gateway on the cell’s virtual network interface. Depending on your IaaS, this gateway may be a NAT to external networks.
Administrators configure rules to govern container network traffic. This is how containers send traffic outside of PWS and receive traffic from outside, the Internet. These rules can prevent system access from external networks and between internal components and determine if apps can establish connections over the virtual network interface.
Administrators configure these rules at two levels:
- Application Security Groups (ASGs) apply network traffic rules at the container level.
- Container-to-Container networking policies determine app-to-app communication. Within PWS, apps can communicate directly with each other, but the containers are isolated from outside PWS.
PWS secures containers through the following measures:
- Running application instances in unprivileged containers by default
- Hardening containers by limiting functionality and access rights
- Only allowing outbound connections to public addresses from application containers.
Garden has two container types: unprivileged and privileged. Currently, PWS runs all application instances and staging tasks in unprivileged containers by default. This measure increases security by eliminating the threat of root escalation inside the container.
PWS mitigates against container breakout and denial of service attacks in the following ways:
- PWS uses the full set of Linux namespaces (IPC, Network, Mount, PID, User, UTS) to provide isolation between containers running on the same host. The User namespace is not used for privileged containers.
- In unprivileged containers, CF maps UID/GID 0 (root) inside the container user namespace to a different UID/GID on the host to prevent an app from inheriting UID/GID 0 on the host if it breaks out of the container.
- PWS uses the same UID/GID for all containers.
- PWS maps all UIDs except UID 0 to themselves. PWS maps UID 0 inside the container namespace to
MAX_UID-1outside of the container namespace.
- Container Root does not grant Host Root permissions.
- PWS mounts
/sysas read-only inside containers.
- PWS disallows
dmesgaccess for unprivileged users and all users in unprivileged containers.
- PWS uses
chrootwhen importing docker images from docker registries.
- PWS establishes a container-specific overlay filesystem mount. PWS uses
pivot_rootto move the root filesystem into this overlay, in order to isolate the container from the host system’s filesystem.
- PWS does not call any binary or script inside the container filesystem, in order to eliminate any dependencies on scripts and binaries inside the root filesystem.
- PWS avoids side-loading binaries in the container through bind mounts or other methods. Instead, it re-executes the same binary by reading it from
/proc/self/exewhenever it needs to run a binary in a container.
- PWS establishes a virtual Ethernet pair for each container for network traffic. See the Container Network Traffic section above for more information. The virtual Ethernet pair has the following features:
- One interface in the pair is inside the container’s network namespace, and is the only non-loopback interface accessible inside the container.
- The other interface remains in the host network namespace and is bridged to the container-side interface.
- Egress whitelist rules are applied to these interfaces according to Application Security Groups (ASGs) configured by the administrator.
- First-packet logging rules may also be enabled on TCP whitelist rules.
- DNAT rules are established on the host to enable traffic ingress from the host interface to whitelisted ports on the container-side interface.
- PWS applies disk quotas using container-specific XFS quotas with the specified disk-quota capacity.
- PWS applies a total memory usage quota through the memory cgroup and destroys the container if the memory usage exceeds the quota.
- PWS applies a fair-use limit to CPU usage for processes inside the container through the
- PWS allows administrators to rate limit the maximum bandwidth consumed by single application containers, configuring
burstproperties on the
- PWS limits access to devices using cgroups but explicitly whitelists the following safe device nodes:
- PWS drops the following Linux capabilities for all container processes. Every dropped capability limits the actions the root user can perform.
CAP_SYS_ADMIN(for unprivileged containers)