Repository: ansible/ansible

Issue:
Windows 10/WSL: Ansible cannot read ansible.cfg from NTFS mounts
##### SUMMARY
Ansible 2.6.1 added https://github.com/ansible/ansible/pull/42070 which makes Ansible ignore `ansible.cfg` files in 777 directories. The problem is that all NTFS mounts (anything under `/mnt/c`) in WSL on Windows 10 are 777 because their permissions are managed by Windows.

Detecting a WSL install is pretty easy, I have been using `ansible_kernel.find('Microsoft') != -1` to detect WSL. Tested on Arch, Ubuntu and Kali. 

##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
lib/ansible/config/manager.py

##### ANSIBLE VERSION
```
ansible 2.6.1
  config file = None
  configured module search path = [u'/home/cbailey/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python2.7/dist-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 2.7.15 (default, May  1 2018, 05:55:50) [GCC 7.3.0]
```


##### CONFIGURATION
N/A

##### OS / ENVIRONMENT
Windows 10 + any WSL distro

##### STEPS TO REPRODUCE
1. [Install a WSL distro](https://docs.microsoft.com/en-us/windows/wsl/install-win10)
2. `sudo pip install ansible==2.6.1`
3. Put an `ansible.cfg` some where on your `C:\` drive
4. `cd` to Directory from WSL
5. Run any Ansible command

##### EXPECTED RESULTS
`ansible.cfg` files on Windows mounts in WSL are not ignored

##### ACTUAL RESULTS
`ansible.cfg` file is ignored with the following warning:
```
[WARNING] Ansible is in a world writable directory (/mnt/c/**), ignoring it as an ansible.cfg source.
```



Discussion:
Files identified in the description:
* [lib/ansible/cli/config.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/cli/config.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
Files identified in the description:
* [lib/ansible/config/manager.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/config/manager.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
Detecting that the system is WSL would still leave a vulnerability.  We'd have to detect that the file isn't world writable or in a world writable directory despite the permissions saying that it is.

We also don't support Ansible on Windows hosts so we can't guarantee a fix will be created for this.
I absolutely understand the need for security, but there should at least be an option some how to disable that. Maybe something that *has* to be in the `/etc/ansible/ansible.cfg` specifically or something similar that makes it harder to flip on. 

There are other use cases where it would not just be a Windows 10 NTFS mount to WSL that this would occur. If you have an NTFS mount or an Azure File Share mount on a locked down Linux build box, the mount would be 777, but it does not mean it is insecure to trust files from that location.

OpenSSH does not stop you from enabling `root` password login, but it does tell you it is a bad idea. Most security guidelines are relative to what the purpose is and the context around it. 
Maybe you could allow people to whitelist directories from this check via an environment variable?  Something like `ANSIBLE_TRUSTED_CONFIGPATHS` or something?  If the config is inside a path in that variable, then skip the permissions check?

I am not that good at python, but maybe something like this?

    diff --git a/lib/ansible/config/manager.py b/lib/ansible/config/manager.py
    index 48cf2cba3a..b356c84b9e 100644
    --- a/lib/ansible/config/manager.py
    +++ b/lib/ansible/config/manager.py
    @@ -146,6 +146,10 @@ def find_ini_config_file(warnings=None):
        ''' Load INI Config File order(first found is used): ENV, CWD, HOME, /etc/ansible '''
        # FIXME: eventually deprecate ini configs
    
    +    trusted_paths = os.getenv("ANSIBLE_TRUSTED_CONFIGPATHS",None)
    +    if isinstance(trusted_paths, string_types):
    +        trusted_paths=list(filter(None,trusted_paths.split(':')))
    +
        path0 = os.getenv("ANSIBLE_CONFIG", None)
        if path0 is not None:
            path0 = unfrackpath(path0, follow=False)
    @@ -154,7 +158,9 @@ def find_ini_config_file(warnings=None):
        try:
            path1 = os.getcwd()
            perms1 = os.stat(path1)
    -        if perms1.st_mode & stat.S_IWOTH:
    +        if trusted_paths and [i for i in trusted_paths if path1.startswith(i)]:
    +            path1 += "/ansible.cfg"
    +        elif perms1.st_mode & stat.S_IWOTH:
                if warnings is not None:
                    warnings.add("Ansible is in a world writable directory (%s), ignoring it as an ansible.cfg source." % to_text(path1))
                path1 = None

Then I can just set `export ANSIBLE_TRUSTED_CONFIGPATHS=/mnt/c/Users/` in my shell profile?
You can set the `ANSIBLE_CONFIG` variable to point to your world writable ansible.cfg and it currently works.
Yep, it's only current working directory that we're making this test on.
It prevents surprises like exploring your file tree and then running
ansible in /tmp and picking up a malicious ansible.cfg

On Thu, Jul 5, 2018, 11:23 PM Ramin Baradari <notifications@github.com>
wrote:

> You can set the ANSIBLE_CONFIG variable to point to your world writable
> ansible.cfg and it currently works.
>
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub
> <https://github.com/ansible/ansible/issues/42388#issuecomment-402938591>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AAMxWr8Z2LerEPWWmwZu1BR5RT9qgdnTks5uDwJ5gaJpZM4VEmwB>
> .
>

i am using vagrant on windows to run ansible playbooks.

Its still failing even after setting ansible_config value.

[root@controller ]# echo $ANSIBLE_CONFIG
/home/vagrant/projects/DevOps-Projects/

[root@controller mapr-ansible-paysafe]# ls -lh ansible.cfg
-rwxrwxrwx. 1 vagrant vagrant 73 Jul  9 07:43 ansible.cfg

[root@controller ]# ansible -m ping all -i environment/XXXX/cluster.ini -v
 **[WARNING] Ansible is in a world writable directory (/home/vagrant/projects/DevOps-Projects), ignoring it as an ansible.cfg source**.
No config file found; using defaults

Temporary Solution
`Install Only ansible 2.6.0`

The `ANSIBLE_CONFIG` env var should point to a file, not a directory.
I've stumbled upon the same issue, and I don't want a global cfg for all my projects, I need a project-relative configuration. So how can I fix this for me? 
> I've stumbled upon the same issue, and I don't want a global cfg for all my projects, I need a project-relative configuration. So how can I fix this for me?

`pip install ansible==2.6.0` until the devs decide how they want to handle this. 
Problems on Vagrant shared folder too, it is 0777 only mode for folders in this case and can't be changed.
Please, add check that CWD folder == folder containing playbook file, and then do not check for world writable. Or maybe try to find ansible.cfg in the same folder as playbook file? Would be helpful for vagrant on windows users.
I think  I found a solution for 2.6.1 and so on...

Create this file in your wsl: /etc/wsl.conf

Content:

```
[automount]
enabled = true
mountFsTab = false
root = /mnt/
options = "metadata,umask=22,fmask=11"

[network]
generateHosts = true
generateResolvConf = true
```

After that all /mnt/c/foo will have different folder permissions (not 777 any more) and you will be able to use chmod.
It requires you to have the latest WSL as far as I know.
[wsl.conf docs](https://docs.microsoft.com/en-us/windows/wsl/wsl-config)
Okay, the comment in https://github.com/ansible/ansible/issues/42388#issuecomment-403926971 is the equivalent of advice we'd tell people mounting an ntfs or vfat filesystem under Unix so I think that is the correct solution here.

For people using ANSIBLE_CONFIG (correctly... as agaffney notes, it needs to point to a file not a directory), please check whether the config file is *actually* being ignored or it's just spitting out an extra warning.

From looking at the code, I think that the config file is read when ANSIBLE_CONFIG is used correctly.  If that happens to also be the current working directory, then the warning message is spit out in *addition* to the config being used because it is listed in ANSIBLE_CONFIG.  If that is indeed the symptom being seen, it should be easy to fix it to only emit the warning if the only reason for the config to be used is because it's in the current working directory.
```diff --git a/lib/ansible/config/manager.py b/lib/ansible/config/manager.py
index c308c3810d..36641d9e01 100644
--- a/lib/ansible/config/manager.py
+++ b/lib/ansible/config/manager.py
@@ -150,6 +150,8 @@ def find_ini_config_file(warnings=None):
     ''' Load INI Config File order(first found is used): ENV, CWD, HOME, /etc/ansible '''
     # FIXME: eventually deprecate ini configs

+    warn_cwd_public = False
+
     path0 = os.getenv("ANSIBLE_CONFIG", None)
     if path0 is not None:
         path0 = unfrackpath(path0, follow=False)
@@ -159,8 +161,7 @@ def find_ini_config_file(warnings=None):
         path1 = os.getcwd()
         perms1 = os.stat(path1)
         if perms1.st_mode & stat.S_IWOTH:
-            if warnings is not None:
-                warnings.add("Ansible is in a world writable directory (%s), ignoring it as an ansible.cfg source." % to_text(path1))
+            warn_cwd_public = True
             path1 = None
         else:
             path1 += "/ansible.cfg"
@@ -175,6 +176,8 @@ def find_ini_config_file(warnings=None):
     else:
         path = None

+    if warn_cwd_public and warnings is not None and path != os.getcwd() + "/ansible.cfg":
+        warnings.add("Ansible is in a world writable directory (%s), skipped from possible ansible.cfg sources list." % to_text(path1))
     return path```
something like this should be more precise 
For me the best solution is to change the permission on an wsl mounted nfs filesystem with Metadata option turned on. To do that  follow this procedure here: https://blogs.msdn.microsoft.com/commandline/2018/01/12/chmod-chown-wsl-improvements.

Basically do the following for your ntfs mount to enable "chmod":

```
sudo umount /mnt/c
sudo mount -t drvfs C:/mnt/c -o metadata
```

And then do the chmod on the folder and everything is fine again. After the metadata was attached to the folder it stays there. should be wsl default for my humble opinion.  
Solution for Vagrant, edit Vagrantfile and add `mount_options`:

```ruby
config.vm.synced_folder "../my-folder", "/home/vagrant/my-folder",  mount_options: ["dmode=775"]
```
This is a nice example why ansible is a high upkeep module to build your project on.

I have prepared a vagrant+virtualbox+ansible project 3 years ago. Since then every 5 months ansible breaks. I have to implement workarounds to KEEP IT WORKING THE SAME WAY. So I can't tell the (few) users of the system that "this works", because every so often it just breaks. This was working 3 weeks ago, and it is not working now. And it's not a mayor version change, just a minor one, so breaking changes are not an option.

(see "touch" not implemented for years, apt module not having autoremove but has warnings implemented that I should not call "apt" as a command, stat.md5 removed without notice, vault_password.txt location was not allowed to set in ansible.cfg (feature request closed, then years later implemented) etc...)

You need to use workarounds every few weeks to get the same result as before.

I was hoping for a more mature project management from Redhat and ansible 2.0, but let this be a warning for the newcomers, that ansible needs upkeep.
Could you please write this warning out to **STDERR** instead of **STDOUT**?

```
[WARNING] Ansible is in a world writable directory...
```

I'm seeing this message when I'm decrypting a variables file to STDOUT and I'm going to have to hack some special code node to watch for and strip this line out.
Oh ha, this seems to work

```
export ANSIBLE_CONFIG=./ansible.cfg
```

Throw that in any vagrant/docker/wsl setups
All these workarounds are great to know, but I think it is ridiculous assumption, and to have no possible workaround with inline ansible-playbook flags. I am now spending my day updating 40 build jobs with hacky workarounds, for breaking changes that were introduced in a patch version change! And all these builds run in short lived containers in which a single automated user has access to, so this dangerous world writable security hole you are protecting me from is completely invalid assumption.
Quick update: we're not ignoring this- we're looking at a few different ideas to preserve the security fix of not reading config from an unprotected cwd. Trust me, what shipped in response to the recent CVEs around this was a very measured approach compared to some of the draconian measures being floated. 

That said, I'm sure we can come up with a refinement or workaround to support these "world-writable-but-not-really" cases. 

@thegreatjerboa Can you be more specific about your setup? I'm curious why you're implicitly reading ansible.cfg from cwd rather than a fixed location (which should work fine with world-writable- if not, it's a bug).
@jefflill I'm working on a proposal to fix the more general problem there (that almost all warnings and errors, as well as verbose debugging type information) are going to stdout instead of stderr.  unfortunately, I keep getting pulled onto other problems...
@nitzmahone it is same-dir-as-playbook. We have a git repo per playbook that gets run on checkin. The ansible.cfg and playbook are the top level of the repo, which is also the CWD when it runs.
playbook dir is not looked at for ansible.cfg
```
export ANSIBLE_CONFIG=./ansible.cfg
```
is **NOT** working for me, still getting
```
 [WARNING] Ansible is in a world writable directory (/mnt/c/Users/soar/projectdir), ignoring it as an ansible.cfg source.
```
with `ansible 2.6.2`
As I asked before... Does it actually pick up the config file despite
printing that warning?

The logic was printing the warning even if it was used due to
ANSIBLE_CONFIG.￼

#43583 fixes the warning and should be merged later today to devel (backports to follow)

Resolving patch:
diff --git a/lib/ansible/config/manager.py b/lib/ansible/config/manager.py
--- a/lib/ansible/config/manager.py
+++ b/lib/ansible/config/manager.py
@@ -5,6 +5,7 @@
 __metaclass__ = type
 
 import os
+import os.path
 import sys
 import stat
 import tempfile
@@ -144,31 +145,59 @@ def find_ini_config_file(warnings=None):
     ''' Load INI Config File order(first found is used): ENV, CWD, HOME, /etc/ansible '''
     # FIXME: eventually deprecate ini configs
 
-    path0 = os.getenv("ANSIBLE_CONFIG", None)
-    if path0 is not None:
-        path0 = unfrackpath(path0, follow=False)
-        if os.path.isdir(path0):
-            path0 += "/ansible.cfg"
+    if warnings is None:
+        # Note: In this case, warnings does nothing
+        warnings = set()
+
+    # A value that can never be a valid path so that we can tell if ANSIBLE_CONFIG was set later
+    # We can't use None because we could set path to None.
+    SENTINEL = object
+
+    potential_paths = []
+
+    # Environment setting
+    path_from_env = os.getenv("ANSIBLE_CONFIG", SENTINEL)
+    if path_from_env is not SENTINEL:
+        path_from_env = unfrackpath(path_from_env, follow=False)
+        if os.path.isdir(path_from_env):
+            path_from_env = os.path.join(path_from_env, "ansible.cfg")
+        potential_paths.append(path_from_env)
+
+    # Current working directory
+    warn_cmd_public = False
     try:
-        path1 = os.getcwd()
-        perms1 = os.stat(path1)
-        if perms1.st_mode & stat.S_IWOTH:
-            if warnings is not None:
-                warnings.add("Ansible is in a world writable directory (%s), ignoring it as an ansible.cfg source." % to_text(path1))
-            path1 = None
+        cwd = os.getcwd()
+        perms = os.stat(cwd)
+        if perms.st_mode & stat.S_IWOTH:
+            warn_cmd_public = True
         else:
-            path1 += "/ansible.cfg"
+            potential_paths.append(os.path.join(cwd, "ansible.cfg"))
     except OSError:
-        path1 = None
-    path2 = unfrackpath("~/.ansible.cfg", follow=False)
-    path3 = "/etc/ansible/ansible.cfg"
+        # If we can't access cwd, we'll simply skip it as a possible config source
+        pass
+
+    # Per user location
+    potential_paths.append(unfrackpath("~/.ansible.cfg", follow=False))
 
-    for path in [path0, path1, path2, path3]:
-        if path is not None and os.path.exists(path):
+    # System location
+    potential_paths.append("/etc/ansible/ansible.cfg")
+
+    for path in potential_paths:
+        if os.path.exists(path):
             break
     else:
         path = None
 
+    # Emit a warning if all the following are true:
+    # * We did not use a config from ANSIBLE_CONFIG
+    # * There's an ansible.cfg in the current working directory that we skipped
+    if path_from_env != path and warn_cmd_public:
+        warnings.add(u"Ansible is being run in a world writable directory (%s),"
+                     u" ignoring it as an ansible.cfg source."
+                     u" For more information see"
+                     u" https://docs.ansible.com/ansible/devel/reference_appendices/config.html#cfg-in-world-writable-dir"
+                     % to_text(cwd))
+
     return path
 
 


Test patch:
diff --git a/test/units/config/manager/__init__.py b/test/units/config/manager/__init__.py
new file mode 100644
diff --git a/test/units/config/manager/test_find_ini_config_file.py b/test/units/config/manager/test_find_ini_config_file.py
new file mode 100644
--- /dev/null
+++ b/test/units/config/manager/test_find_ini_config_file.py
@@ -0,0 +1,221 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2017, Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# Make coding more python3-ish
+from __future__ import (absolute_import, division)
+__metaclass__ = type
+
+import os
+import os.path
+import stat
+
+import pytest
+
+from ansible.config.manager import find_ini_config_file
+
+
+real_exists = os.path.exists
+real_isdir = os.path.isdir
+
+working_dir = os.path.dirname(__file__)
+cfg_in_cwd = os.path.join(working_dir, 'ansible.cfg')
+
+cfg_dir = os.path.join(working_dir, 'data')
+cfg_file = os.path.join(cfg_dir, 'ansible.cfg')
+alt_cfg_file = os.path.join(cfg_dir, 'test.cfg')
+cfg_in_homedir = os.path.expanduser('~/.ansible.cfg')
+
+
+@pytest.fixture
+def setup_env(request):
+    cur_config = os.environ.get('ANSIBLE_CONFIG', None)
+    cfg_path = request.param[0]
+
+    if cfg_path is None and cur_config:
+        del os.environ['ANSIBLE_CONFIG']
+    else:
+        os.environ['ANSIBLE_CONFIG'] = request.param[0]
+
+    yield
+
+    if cur_config is None and cfg_path:
+        del os.environ['ANSIBLE_CONFIG']
+    else:
+        os.environ['ANSIBLE_CONFIG'] = cur_config
+
+
+@pytest.fixture
+def setup_existing_files(request, monkeypatch):
+    def _os_path_exists(path):
+        if path in (request.param[0]):
+            return True
+        else:
+            return False
+
+    # Enable user and system dirs so that we know cwd takes precedence
+    monkeypatch.setattr("os.path.exists", _os_path_exists)
+    monkeypatch.setattr("os.getcwd", lambda: os.path.dirname(cfg_dir))
+    monkeypatch.setattr("os.path.isdir", lambda path: True if path == cfg_dir else real_isdir(path))
+
+
+class TestFindIniFile:
+    # This tells us to run twice, once with a file specified and once with a directory
+    @pytest.mark.parametrize('setup_env, expected', (([alt_cfg_file], alt_cfg_file), ([cfg_dir], cfg_file)), indirect=['setup_env'])
+    # This just passes the list of files that exist to the fixture
+    @pytest.mark.parametrize('setup_existing_files',
+                             [[('/etc/ansible/ansible.cfg', cfg_in_homedir, cfg_in_cwd, alt_cfg_file, cfg_file)]],
+                             indirect=['setup_existing_files'])
+    def test_env_has_cfg_file(self, setup_env, setup_existing_files, expected):
+        """ANSIBLE_CONFIG is specified, use it"""
+        warnings = set()
+        assert find_ini_config_file(warnings) == expected
+        assert warnings == set()
+
+    @pytest.mark.parametrize('setup_env', ([alt_cfg_file], [cfg_dir]), indirect=['setup_env'])
+    @pytest.mark.parametrize('setup_existing_files',
+                             [[('/etc/ansible/ansible.cfg', cfg_in_homedir, cfg_in_cwd)]],
+                             indirect=['setup_existing_files'])
+    def test_env_has_no_cfg_file(self, setup_env, setup_existing_files):
+        """ANSIBLE_CONFIG is specified but the file does not exist"""
+
+        warnings = set()
+        # since the cfg file specified by ANSIBLE_CONFIG doesn't exist, the one at cwd that does
+        # exist should be returned
+        assert find_ini_config_file(warnings) == cfg_in_cwd
+        assert warnings == set()
+
+    # ANSIBLE_CONFIG not specified
+    @pytest.mark.parametrize('setup_env', [[None]], indirect=['setup_env'])
+    # All config files are present
+    @pytest.mark.parametrize('setup_existing_files',
+                             [[('/etc/ansible/ansible.cfg', cfg_in_homedir, cfg_in_cwd, cfg_file, alt_cfg_file)]],
+                             indirect=['setup_existing_files'])
+    def test_ini_in_cwd(self, setup_env, setup_existing_files):
+        """ANSIBLE_CONFIG not specified.  Use the cwd cfg"""
+        warnings = set()
+        assert find_ini_config_file(warnings) == cfg_in_cwd
+        assert warnings == set()
+
+    # ANSIBLE_CONFIG not specified
+    @pytest.mark.parametrize('setup_env', [[None]], indirect=['setup_env'])
+    # No config in cwd
+    @pytest.mark.parametrize('setup_existing_files',
+                             [[('/etc/ansible/ansible.cfg', cfg_in_homedir, cfg_file, alt_cfg_file)]],
+                             indirect=['setup_existing_files'])
+    def test_ini_in_homedir(self, setup_env, setup_existing_files):
+        """First config found is in the homedir"""
+        warnings = set()
+        assert find_ini_config_file(warnings) == cfg_in_homedir
+        assert warnings == set()
+
+    # ANSIBLE_CONFIG not specified
+    @pytest.mark.parametrize('setup_env', [[None]], indirect=['setup_env'])
+    # No config in cwd
+    @pytest.mark.parametrize('setup_existing_files', [[('/etc/ansible/ansible.cfg', cfg_file, alt_cfg_file)]], indirect=['setup_existing_files'])
+    def test_ini_in_systemdir(self, setup_env, setup_existing_files):
+        """First config found is the system config"""
+        warnings = set()
+        assert find_ini_config_file(warnings) == '/etc/ansible/ansible.cfg'
+        assert warnings == set()
+
+    # ANSIBLE_CONFIG not specified
+    @pytest.mark.parametrize('setup_env', [[None]], indirect=['setup_env'])
+    # No config in cwd
+    @pytest.mark.parametrize('setup_existing_files',
+                             [[('/etc/ansible/ansible.cfg', cfg_in_homedir, cfg_file, alt_cfg_file)]],
+                             indirect=['setup_existing_files'])
+    def test_cwd_does_not_exist(self, setup_env, setup_existing_files, monkeypatch):
+        """Smoketest current working directory doesn't exist"""
+        def _os_stat(path):
+            raise OSError('%s does not exist' % path)
+        monkeypatch.setattr('os.stat', _os_stat)
+
+        warnings = set()
+        assert find_ini_config_file(warnings) == cfg_in_homedir
+        assert warnings == set()
+
+    @pytest.mark.parametrize('setup_env', [[None]], indirect=['setup_env'])
+    # No config in cwd
+    @pytest.mark.parametrize('setup_existing_files', [[list()]], indirect=['setup_existing_files'])
+    def test_no_config(self, setup_env, setup_existing_files):
+        """No config present, no config found"""
+        warnings = set()
+        assert find_ini_config_file(warnings) is None
+        assert warnings == set()
+
+    # ANSIBLE_CONFIG not specified
+    @pytest.mark.parametrize('setup_env', [[None]], indirect=['setup_env'])
+    # All config files are present
+    @pytest.mark.parametrize('setup_existing_files',
+                             [[('/etc/ansible/ansible.cfg', cfg_in_homedir, cfg_in_cwd, cfg_file, alt_cfg_file)]],
+                             indirect=['setup_existing_files'])
+    def test_cwd_warning_on_writable(self, setup_env, setup_existing_files, monkeypatch):
+        """If the cwd is writable, warn and skip it """
+        real_stat = os.stat
+
+        def _os_stat(path):
+            if path == working_dir:
+                from posix import stat_result
+                stat_info = list(real_stat(path))
+                stat_info[stat.ST_MODE] |= stat.S_IWOTH
+                return stat_result(stat_info)
+            else:
+                return real_stat(path)
+
+        monkeypatch.setattr('os.stat', _os_stat)
+
+        warnings = set()
+        assert find_ini_config_file(warnings) == cfg_in_homedir
+        assert len(warnings) == 1
+        warning = warnings.pop()
+        assert u'Ansible is being run in a world writable directory' in warning
+        assert u'ignoring it as an ansible.cfg source' in warning
+
+    # ANSIBLE_CONFIG is sepcified
+    @pytest.mark.parametrize('setup_env, expected', (([alt_cfg_file], alt_cfg_file), ([cfg_in_cwd], cfg_in_cwd)), indirect=['setup_env'])
+    # All config files are present
+    @pytest.mark.parametrize('setup_existing_files',
+                             [[('/etc/ansible/ansible.cfg', cfg_in_homedir, cfg_in_cwd, cfg_file, alt_cfg_file)]],
+                             indirect=['setup_existing_files'])
+    def test_no_warning_on_writable_if_env_used(self, setup_env, setup_existing_files, monkeypatch, expected):
+        """If the cwd is writable but ANSIBLE_CONFIG was used, no warning should be issued"""
+        real_stat = os.stat
+
+        def _os_stat(path):
+            if path == working_dir:
+                from posix import stat_result
+                stat_info = list(real_stat(path))
+                stat_info[stat.ST_MODE] |= stat.S_IWOTH
+                return stat_result(stat_info)
+            else:
+                return real_stat(path)
+
+        monkeypatch.setattr('os.stat', _os_stat)
+
+        warnings = set()
+        assert find_ini_config_file(warnings) == expected
+        assert warnings == set()
+
+    # ANSIBLE_CONFIG not specified
+    @pytest.mark.parametrize('setup_env', [[None]], indirect=['setup_env'])
+    # All config files are present
+    @pytest.mark.parametrize('setup_existing_files',
+                             [[('/etc/ansible/ansible.cfg', cfg_in_homedir, cfg_in_cwd, cfg_file, alt_cfg_file)]],
+                             indirect=['setup_existing_files'])
+    def test_cwd_warning_on_writable_no_warning_set(self, setup_env, setup_existing_files, monkeypatch):
+        """Smoketest that the function succeeds even though no warning set was passed in"""
+        real_stat = os.stat
+
+        def _os_stat(path):
+            if path == working_dir:
+                from posix import stat_result
+                stat_info = list(real_stat(path))
+                stat_info[stat.ST_MODE] |= stat.S_IWOTH
+                return stat_result(stat_info)
+            else:
+                return real_stat(path)
+
+        monkeypatch.setattr('os.stat', _os_stat)
+
+        assert find_ini_config_file() == cfg_in_homedir
diff --git a/test/units/config/test_manager.py b/test/units/config/test_manager.py
--- a/test/units/config/test_manager.py
+++ b/test/units/config/test_manager.py
@@ -3,6 +3,7 @@
 __metaclass__ = type
 
 import os
+import os.path
 
 from ansible.compat.tests import unittest
 
@@ -50,12 +51,6 @@ def test_ensure_type_float(self):
         self.assertIsInstance(ensure_type('0.10', 'float'), float)
         self.assertIsInstance(ensure_type(0.2, 'float'), float)
 
-    def test_find_ini_file(self):
-        cur_config = os.environ['ANSIBLE_CONFIG']
-        os.environ['ANSIBLE_CONFIG'] = cfg_file
-        self.assertEquals(cfg_file, find_ini_config_file())
-        os.environ['ANSIBLE_CONFIG'] = cur_config
-
     def test_resolve_path(self):
         self.assertEquals(os.path.join(curdir, 'test.yml'), resolve_path('./test.yml', cfg_file))