Repository: ansible/ansible

Issue:
jenkins_plugin "params" argument is insecure
##### ISSUE TYPE

 - Bug Report

##### COMPONENT NAME
lib/ansible/modules/web_infrastructure/jenkins_plugin.py

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
devel 2.4 2.3
```

##### CONFIGURATION
N/A

##### OS / ENVIRONMENT
N/A

##### SUMMARY

It was noticed that using the jenkins_plugin with username and password would log the password on the remote host.  After some digging I discovered that in addition to the normal url_username and url_password arguments for fetch_url() the jenkins_plugin module also has a params argument where arbitrary ansible module arguments can be given.   This parameter should go away as it circumvents all the normal argument checking, validation, and normalization.

For url_password, this is bad as using param to send in the url_password instead of the specific url_password argument allows the url_password to be logged.  This can be a security problem.

I also found the source of the user's use of param instead of url_username and url_password: the module documentation has an example of using param that has that in it.

##### STEPS TO FIX

* I am going to immediately create and merge a PR to remove the param example and replace it with using the url_username and url_password arguments directly.
* The params argument should also be removed (as it bypasses the no_log setting on url_password, allowing the password to be logged by mistake).  Additional parameters that the module uses should be explicitly stated in the argument_spec instead.  (I took a look at the module and it does not appear that params is passed directly to the jenkins server, instead, specific keys are always plucked out of the params.  So there should be no problem removing params.)


Discussion:
cc @jtyr
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Changed the documentation and also raised an error if params has url_password.  I think that will take care of the security aspect of the bug.  We can remove the param field in devel to finish off this ticket.
@abadger I see the security risk when password is exposed in logs but I don't like to remove this usability feature completely. Wouldn't be possible to define which fields should be obfuscated from the logs on the `AnsibleModule` level? Something like this:

```python
module = AnsibleModule(
    argument_spec=dict(
        ...
        params=dict(type='dict', nolog=True),
        ...
    )
)
```
The core team discussed that argument and decided that they didn't think
that was a good idea.  You can put it on the public irc meeting agenda,
though.

I have added it on the agenda for Thursday ;o)
Just thinking about it again and we should not do `nolog` on the whole `params`. We want to obfuscate only the particular key of the `params` which feels like something not really standard thing to do on the `AnsibleModule` level.

By "_We can remove the param field in devel to finish off this ticket_" you meant to remove the whole `param` option from the module? Restricting the `params` not to hold password options is OK. But removing it all together is not good.
Yes, that is what the core team decided they wanted.


Resolving patch:
diff --git a/lib/ansible/modules/packaging/os/yum_repository.py b/lib/ansible/modules/packaging/os/yum_repository.py
--- a/lib/ansible/modules/packaging/os/yum_repository.py
+++ b/lib/ansible/modules/packaging/os/yum_repository.py
@@ -241,12 +241,6 @@
       - Unique repository ID.
       - This parameter is only required if I(state) is set to C(present) or
         C(absent).
-  params:
-    required: false
-    default: null
-    description:
-      - Option used to allow the user to overwrite any of the other options.
-        To remove an option, set the value of the option to C(null).
   password:
     required: false
     default: null
@@ -391,6 +385,8 @@
   - The repo file will be automatically deleted if it contains no repository.
   - When removing a repository, beware that the metadata cache may still remain
     on disk until you run C(yum clean all). Use a notification handler for this.
+  - "The C(params) parameter was removed in Ansible 2.5 due to circumventing Ansible's parameter
+    handling"
 '''
 
 EXAMPLES = '''
@@ -699,11 +695,11 @@ def main():
         supports_check_mode=True,
     )
 
-    # Update module parameters by user's parameters if defined
-    if 'params' in module.params and isinstance(module.params['params'], dict):
-        module.params.update(module.params['params'])
-        # Remove the params
-        module.params.pop('params', None)
+    # Params was removed
+    # https://meetbot.fedoraproject.org/ansible-meeting/2017-09-28/ansible_dev_meeting.2017-09-28-15.00.log.html
+    if module.params['params']:
+        module.fail_json(msg="The params option to yum_repository was removed in Ansible 2.5"
+                         "since it circumvents Ansible's option handling")
 
     name = module.params['name']
     state = module.params['state']
diff --git a/lib/ansible/modules/web_infrastructure/jenkins_plugin.py b/lib/ansible/modules/web_infrastructure/jenkins_plugin.py
--- a/lib/ansible/modules/web_infrastructure/jenkins_plugin.py
+++ b/lib/ansible/modules/web_infrastructure/jenkins_plugin.py
@@ -46,14 +46,6 @@
     default: jenkins
     description:
       - Name of the Jenkins user on the OS.
-  params:
-    required: false
-    default: null
-    description:
-      - Option used to allow the user to overwrite any of the other options. To
-        remove an option, set the value of the option to C(null).
-      - Changed in 2.5.0, 2.4.1, 2.3.3 to raise an error if C(url_password) is specified in params.
-        Use the actual C(url_password) argument instead.
   state:
     required: false
     choices: [absent, present, pinned, unpinned, enabled, disabled, latest]
@@ -120,6 +112,8 @@
   - It is not possible to run the module remotely by changing the I(url)
     parameter to point to the Jenkins server. The module must be used on the
     host where Jenkins runs as it needs direct access to the plugin files.
+  - "The C(params) option was removed in Ansible 2.5 due to circumventing Ansible's
+    option handling"
 '''
 
 EXAMPLES = '''
@@ -762,16 +756,11 @@ def main():
         supports_check_mode=True,
     )
 
-    # Update module parameters by user's parameters if defined
-    if 'params' in module.params and isinstance(module.params['params'], dict):
-        if 'url_password' in module.params['params']:
-            # The params argument should be removed eventually.  Until then, raise an error if
-            # url_password is specified there as it can lead to the password being logged
-            module.fail_json(msg='Do not specify url_password in params as it may get logged')
-
-        module.params.update(module.params['params'])
-        # Remove the params
-        module.params.pop('params', None)
+    # Params was removed
+    # https://meetbot.fedoraproject.org/ansible-meeting/2017-09-28/ansible_dev_meeting.2017-09-28-15.00.log.html
+    if module.params['params']:
+        module.fail_json(msg="The params option to jenkins_plugin was removed in Ansible 2.5"
+                         "since it circumvents Ansible's option handling")
 
     # Force basic authentication
     module.params['force_basic_auth'] = True


Test patch: