Repository: ansible/ansible-modules-extras

Issue:
Bug: Unsafe call in lxc_container
The grep below may not work as intended. The grep will blindly match any line is the passwd file that contains the result of $(whoami) and is unsafe. Consider matching the user id, using getent, or something safer that what is used here.

```
# This is used to attach to a running container and execute commands from
# within the container on the host.  This will provide local access to a
# container without using SSH.  The template will attempt to work within the
# home directory of the user that was attached to the container and source
# that users environment variables by default.
ATTACH_TEMPLATE = """#!/usr/bin/env bash
pushd "$(grep $(whoami) /etc/passwd | awk -F':' '{print $6}')"
    if [[ -f ".bashrc" ]];then
        source .bashrc
    fi
popd
```



Discussion:

Resolving patch:
diff --git a/cloud/lxc/lxc_container.py b/cloud/lxc/lxc_container.py
--- a/cloud/lxc/lxc_container.py
+++ b/cloud/lxc/lxc_container.py
@@ -398,7 +398,7 @@
 # home directory of the user that was attached to the container and source
 # that users environment variables by default.
 ATTACH_TEMPLATE = """#!/usr/bin/env bash
-pushd "$(grep $(whoami) /etc/passwd | awk -F':' '{print $6}')"
+pushd "$(getent passwd $(whoami)|cut -f6 -d':')"
     if [[ -f ".bashrc" ]];then
         source .bashrc
     fi
diff --git a/system/gluster_volume.py b/system/gluster_volume.py
--- a/system/gluster_volume.py
+++ b/system/gluster_volume.py
@@ -38,27 +38,33 @@
         use started/stopped to control it's availability.
   cluster:
     required: false
+    default: null
     description:
       - List of hosts to use for probing and brick setup
   host:
     required: false
+    default: null
     description:
       - Override local hostname (for peer probing purposes)
   replicas:
     required: false
+    default: null
     description:
       - Replica count for volume
   stripes:
     required: false
+    default: null
     description:
       - Stripe count for volume
   transport:
     required: false
     choices: [ 'tcp', 'rdma', 'tcp,rdma' ]
+    default: 'tcp'
     description:
       - Transport type for volume
   brick:
     required: false
+    default: null
     description:
       - Brick path on servers
   start_on_create:
@@ -69,22 +75,27 @@
   rebalance:
     choices: [ 'yes', 'no']
     required: false
+    default: 'no'
     description:
       - Controls whether the cluster is rebalanced after changes
   directory:
     required: false
+    default: null
     description:
       - Directory for limit-usage
   options:
     required: false
+    default: null
     description:
       - A dictionary/hash with options/settings for the volume
   quota:
     required: false
+    default: null
     description:
       - Quota value for limit-usage (be sure to use 10.0MB instead of 10MB, see quota list)
   force:
     required: false
+    default: null
     description:
       - If brick is being created in the root partition, module will fail.
         Set force to true to override this behaviour
@@ -119,165 +130,167 @@
 import time
 import socket
 
-def main():
-
 
-    def run_gluster(gargs, **kwargs):
-        args = [glusterbin]
-        args.extend(gargs)
+def run_gluster(gargs, **kwargs):
+    args = [glusterbin]
+    args.extend(gargs)
+    try:
         rc, out, err = module.run_command(args, **kwargs)
         if rc != 0:
             module.fail_json(msg='error running gluster (%s) command (rc=%d): %s' % (' '.join(args), rc, out if out != '' else err))
-        return out
-
-    def run_gluster_nofail(gargs, **kwargs):
-        args = [glusterbin]
-        args.extend(gargs)
-        rc, out, err = module.run_command(args, **kwargs)
-        if rc != 0:
-            return None
-        return out
-
-    def run_gluster_yes(gargs):
-        args = [glusterbin]
-        args.extend(gargs)
-        rc, out, err = module.run_command(args, data='y\n')
-        if rc != 0:
-            module.fail_json(msg='error running gluster (%s) command (rc=%d): %s' % (' '.join(args), rc, out if out != '' else err))
-        return out
-
-    def get_peers():
-        out = run_gluster([ 'peer', 'status'])
-        i = 0
-        peers = {}
-        hostname = None
-        uuid = None
-        state = None
-        for row in out.split('\n'):
-            if ': ' in row:
-                key, value = row.split(': ')
-                if key.lower() == 'hostname':
-                    hostname = value
-                if key.lower() == 'uuid':
-                    uuid = value
-                if key.lower() == 'state':
-                    state = value
-                    peers[hostname] = [ uuid, state ]
-        return peers
-
-    def get_volumes():
-        out = run_gluster([ 'volume', 'info' ])
-
-        volumes = {}
-        volume = {}
-        for row in out.split('\n'):
-            if ': ' in row:
-                key, value = row.split(': ')
-                if key.lower() == 'volume name':
-                    volume['name'] = value
+    except Exception, e:
+        module.fail_json(msg='error running gluster (%s) command: %s' % (' '.join(args), str(e))
+    return out
+
+def run_gluster_nofail(gargs, **kwargs):
+    args = [glusterbin]
+    args.extend(gargs)
+    rc, out, err = module.run_command(args, **kwargs)
+    if rc != 0:
+        return None
+    return out
+
+def run_gluster_yes(gargs):
+    args = [glusterbin]
+    args.extend(gargs)
+    rc, out, err = module.run_command(args, data='y\n')
+    if rc != 0:
+        module.fail_json(msg='error running gluster (%s) command (rc=%d): %s' % (' '.join(args), rc, out if out != '' else err))
+    return out
+
+def get_peers():
+    out = run_gluster([ 'peer', 'status'])
+    i = 0
+    peers = {}
+    hostname = None
+    uuid = None
+    state = None
+    for row in out.split('\n'):
+        if ': ' in row:
+            key, value = row.split(': ')
+            if key.lower() == 'hostname':
+                hostname = value
+            if key.lower() == 'uuid':
+                uuid = value
+            if key.lower() == 'state':
+                state = value
+                peers[hostname] = [ uuid, state ]
+    return peers
+
+def get_volumes():
+    out = run_gluster([ 'volume', 'info' ])
+
+    volumes = {}
+    volume = {}
+    for row in out.split('\n'):
+        if ': ' in row:
+            key, value = row.split(': ')
+            if key.lower() == 'volume name':
+                volume['name'] = value
+                volume['options'] = {}
+                volume['quota'] = False
+            if key.lower() == 'volume id':
+                volume['id'] = value
+            if key.lower() == 'status':
+                volume['status'] = value
+            if key.lower() == 'transport-type':
+                volume['transport'] = value
+            if key.lower() != 'bricks' and key.lower()[:5] == 'brick':
+                if not 'bricks' in volume:
+                    volume['bricks'] = []
+                volume['bricks'].append(value)
+            # Volume options
+            if '.' in key:
+                if not 'options' in volume:
                     volume['options'] = {}
-                    volume['quota'] = False
-                if key.lower() == 'volume id':
-                    volume['id'] = value
-                if key.lower() == 'status':
-                    volume['status'] = value
-                if key.lower() == 'transport-type':
-                    volume['transport'] = value
-                if key.lower() != 'bricks' and key.lower()[:5] == 'brick':
-                    if not 'bricks' in volume:
-                        volume['bricks'] = []
-                    volume['bricks'].append(value)
-                # Volume options
-                if '.' in key:
-                    if not 'options' in volume:
-                        volume['options'] = {}
-                    volume['options'][key] = value
-                    if key == 'features.quota' and value == 'on':
-                        volume['quota'] = True
-            else:
-                if row.lower() != 'bricks:' and row.lower() != 'options reconfigured:':
-                    if len(volume) > 0:
-                        volumes[volume['name']] = volume
-                    volume = {}
-        return volumes
-
-    def get_quotas(name, nofail):
-        quotas = {}
-        if nofail:
-            out = run_gluster_nofail([ 'volume', 'quota', name, 'list' ])
-            if not out:
-                return quotas
+                volume['options'][key] = value
+                if key == 'features.quota' and value == 'on':
+                    volume['quota'] = True
         else:
-            out = run_gluster([ 'volume', 'quota', name, 'list' ])
-        for row in out.split('\n'):
-            if row[:1] == '/':
-                q = re.split('\s+', row)
-                quotas[q[0]] = q[1]
-        return quotas
-
-    def wait_for_peer(host):
-        for x in range(0, 4):
-            peers = get_peers()
-            if host in peers and peers[host][1].lower().find('peer in cluster') != -1:
-                return True
-            time.sleep(1)
-        return False
-
-    def probe(host):
-        run_gluster([ 'peer', 'probe', host ])
-        if not wait_for_peer(host):
-            module.fail_json(msg='failed to probe peer %s' % host)
-        changed = True
-
-    def probe_all_peers(hosts, peers, myhostname):
-        for host in hosts:
-            if host not in peers:
-                # dont probe ourselves
-                if myhostname != host:
-                    probe(host)
-
-    def create_volume(name, stripe, replica, transport, hosts, brick, force):
-        args = [ 'volume', 'create' ]
-        args.append(name)
-        if stripe:
-            args.append('stripe')
-            args.append(str(stripe))
-        if replica:
-            args.append('replica')
-            args.append(str(replica))
-        args.append('transport')
-        args.append(transport)
-        for host in hosts:
-            args.append(('%s:%s' % (host, brick)))
-        if force:
-            args.append('force')
-        run_gluster(args)
-
-    def start_volume(name):
-        run_gluster([ 'volume', 'start', name ])
-
-    def stop_volume(name):
-        run_gluster_yes([ 'volume', 'stop', name ])
-
-    def set_volume_option(name, option, parameter):
-        run_gluster([ 'volume', 'set', name, option, parameter ])
-
-    def add_brick(name, brick, force):
-        args = [ 'volume', 'add-brick', name, brick ]
-        if force:
-            args.append('force')
-        run_gluster(args)
-
-    def rebalance(name):
-        run_gluster(['volume', 'rebalance', name, 'start'])
-
-    def enable_quota(name):
-        run_gluster([ 'volume', 'quota', name, 'enable' ])
-
-    def set_quota(name, directory, value):
-            run_gluster([ 'volume', 'quota', name, 'limit-usage', directory, value ])
+            if row.lower() != 'bricks:' and row.lower() != 'options reconfigured:':
+                if len(volume) > 0:
+                    volumes[volume['name']] = volume
+                volume = {}
+    return volumes
 
+def get_quotas(name, nofail):
+    quotas = {}
+    if nofail:
+        out = run_gluster_nofail([ 'volume', 'quota', name, 'list' ])
+        if not out:
+            return quotas
+    else:
+        out = run_gluster([ 'volume', 'quota', name, 'list' ])
+    for row in out.split('\n'):
+        if row[:1] == '/':
+            q = re.split('\s+', row)
+            quotas[q[0]] = q[1]
+    return quotas
+
+def wait_for_peer(host):
+    for x in range(0, 4):
+        peers = get_peers()
+        if host in peers and peers[host][1].lower().find('peer in cluster') != -1:
+            return True
+        time.sleep(1)
+    return False
+
+def probe(host):
+    run_gluster([ 'peer', 'probe', host ])
+    if not wait_for_peer(host):
+        module.fail_json(msg='failed to probe peer %s' % host)
+    changed = True
+
+def probe_all_peers(hosts, peers, myhostname):
+    for host in hosts:
+        if host not in peers:
+            # dont probe ourselves
+            if myhostname != host:
+                probe(host)
+
+def create_volume(name, stripe, replica, transport, hosts, brick, force):
+    args = [ 'volume', 'create' ]
+    args.append(name)
+    if stripe:
+        args.append('stripe')
+        args.append(str(stripe))
+    if replica:
+        args.append('replica')
+        args.append(str(replica))
+    args.append('transport')
+    args.append(transport)
+    for host in hosts:
+        args.append(('%s:%s' % (host, brick)))
+    if force:
+        args.append('force')
+    run_gluster(args)
+
+def start_volume(name):
+    run_gluster([ 'volume', 'start', name ])
+
+def stop_volume(name):
+    run_gluster_yes([ 'volume', 'stop', name ])
+
+def set_volume_option(name, option, parameter):
+    run_gluster([ 'volume', 'set', name, option, parameter ])
+
+def add_brick(name, brick, force):
+    args = [ 'volume', 'add-brick', name, brick ]
+    if force:
+        args.append('force')
+    run_gluster(args)
+
+def do_rebalance(name):
+    run_gluster(['volume', 'rebalance', name, 'start'])
+
+def enable_quota(name):
+    run_gluster([ 'volume', 'quota', name, 'enable' ])
+
+def set_quota(name, directory, value):
+        run_gluster([ 'volume', 'quota', name, 'limit-usage', directory, value ])
 
+
+def main():
     ### MAIN ###
 
     module = AnsibleModule(
@@ -403,7 +416,7 @@ def set_quota(name, directory, value):
     if changed:
         volumes = get_volumes()
         if rebalance:
-            rebalance(volume_name)
+            do_rebalance(volume_name)
 
     facts = {}
     facts['glusterfs'] = { 'peers': peers, 'volumes': volumes, 'quotas': quotas }


Test patch: