Repository: ansible/ansible

Issue:
Provide list of current Linux capabilities
##### Issue Type:

Feature idea
##### Ansible Version:

Current development
##### Environment:

Linux environments, Docker, LXC, OpenVZ, virtual machines, containers.
##### Summary:

Ansible should provide a list of currently present Linux capabilities(7). This can be especially useful in a restricted environment, for example a Docker/LXC container, to check if certain actions are allowed before executing them. Examples:
- missing `CAP_SYS_TIME` means that we cannot change system clock, so installing `ntpd` is not needed;
- missing `CAP_NET_ADMIN` means that we cannot manage network interfaces or firewall, so tasks/roles related to these can be confidently skipped;
##### Steps To Reproduce:

Current capabilities can be easily checked with:

```
sudo /sbin/capsh --print
```

command, available from `libcap2-bin` package (on Debian-based systems). Here's a Bash script that parses the output of that command and returns JSON-formatted list of capabilities. A separate variable indicates if capabilities are enabled or not (full `root` access).

```
#!/bin/bash
output="{"
# Get current capabilities
line=$(capsh --print | grep -E '^Current:\s+=')
line=${line##* }

# '=ep' shows up on systems that don't have capabilities enabled
# and indicates root, unrestricted privileges
if [ "${line}" = "=ep" ]; then
    output="${output}\"enabled\": \"false\","
else
    output="${output}\"enabled\": \"true\","
fi

IFS=, caps=${line}
output="${output} \"list\": ["
first=1
for cap in ${caps} ; do
    [ -z "${first}" ] && output="${output}, "
    first=
    output="${output}\"${cap}\""
done
output="${output}]"
output="${output}}"IFS=

echo ${output}
```
##### Expected Results:

In Ansible facts, if `libcap2-bin` package is present on remote host, there should be a separate variable `ansible_system_capabilities_enforced`, which indicates that capabilities are enabled or disabled, and a list of current capabilities in a separate list, `ansible_system_capabilities`.
##### Actual Results:

Currently there's no such functionality present.



Discussion:

Resolving patch:
diff --git a/lib/ansible/module_utils/facts.py b/lib/ansible/module_utils/facts.py
--- a/lib/ansible/module_utils/facts.py
+++ b/lib/ansible/module_utils/facts.py
@@ -173,6 +173,7 @@ def __init__(self, module, load_on_init=True):
             self.get_cmdline()
             self.get_public_ssh_host_keys()
             self.get_selinux_facts()
+            self.get_caps_facts()
             self.get_fips_facts()
             self.get_pkg_mgr_facts()
             self.get_service_mgr_facts()
@@ -184,6 +185,7 @@ def __init__(self, module, load_on_init=True):
             self.get_dns_facts()
             self.get_python_facts()
 
+
     def populate(self):
         return self.facts
 
@@ -697,6 +699,25 @@ def get_selinux_facts(self):
             except OSError:
                 self.facts['selinux']['type'] = 'unknown'
 
+    def get_caps_facts(self):
+        capsh_path = module.get_bin_path('capsh')
+        if capsh_path:
+            rc, out, err = module.run_command([capsh_path, "--print"])
+            enforced_caps = []
+            enforced = 'NA'
+            for line in out.split('\n'):
+                if len(line) < 1:
+                    continue
+                if line.startswith('Current:'):
+                    if line.split(':')[1].strip() == '=ep':
+                        enforced = 'False'
+                    else:
+                        enforced = 'True'
+                        enforced_caps = [i.strip() for i in line.split('=')[1].split(',')]
+
+            self.facts['system_capabilities_enforced'] = enforced
+            self.facts['system_capabilities'] = enforced_caps
+
 
     def get_fips_facts(self):
         self.facts['fips'] = False


Test patch: