Commit Graph

25 Commits

Author SHA1 Message Date
Clark Boylan 2846fe2e0d Replace python imp module with importlib
The imp module has been deprecated for quite some time. The importlib
module has been present since python 3.6 and replaces the imp module.
This is important because python3.12 removes the imp module entirely and
any code using imp needs to be rewritten to be python3.12 compatible.

The rewrite here comes from the suggestions in an upstream discussion
about this transition [0].

[0] https://discuss.python.org/t/how-do-i-migrate-from-imp/27885

Change-Id: Iaa742c71bbb208225726c2b8fa5af111f48667c9
2024-01-10 13:09:39 -08:00
James E. Blair 096e47dc43 Fix console log streaming with duplicated roles
If a role is applied to a host more than once (via either play
roles or include_roles, but not via an include_role loop), it will
have the same task UUID from ansible which means Zuul's command
plugin will write the streaming output to the same filename, and
the log streaming will request the same file.  That means the file
might look this after the second invocation:

2022-05-19 17:06:23.673625 | one
2022-05-19 17:06:23.673781 | [Zuul] Task exit code: 0
2022-05-19 17:06:29.226463 | two
2022-05-19 17:06:29.226605 | [Zuul] Task exit code: 0

But since we stop reading the log after "Task exit code", the user
would see "one" twice, and never see "two".

Here are some potential fixes for this that don't work:

* Accessing the task vars from zuul_stream to store any additional
  information: the callback plugins are not given the task vars.
* Setting the log id on the task args in zuul_stream instead of
  command: the same Task object is used for each host and therefore
  the command module might see the task object after it has been
  further modified (in other words, nothing host-specific can be
  set on the task object).
* Setting an even more unique uuid than Task._uuid on the Task
  object in zuul_stream and using that in the command module instead
  of Task._uuid: in some rare cases, the actual task Python object
  may be different between the callback and command plugin, yet still
  have the same _uuid; therefore the new attribute would be missing.

Instead, a global variable is used in order to transfer data between
zuul_stream and command.  This variable holds a counter for each
task+host combination.  Most of the time it will be 1, but if we run
the same task on the same host again, it will increment.  Since Ansible
will not run more than one task on a host simultaneously, so there is
no race between the counter being incremented in zuul_stream and used
in command.

Because Ansible is re-invoked for each playbook, the memory usage is
not a concern.

There may be a fork between zuul_stream and command, but that's fine
as long as we treat it as read-only in the command plugin.  It will
have the data for our current task+host from the most recent zuul_stream
callback invocation.

This change also includes a somewhat unrelated change to the test
infrastructure.  Because we were not setting the log stream port on
the executor in tests, we were actually relying on the "real" OpenDev
Zuul starting zuul_console on the test nodes rather than the
zuul_console we set up for each specific Ansible version from the tests.
This corrects that and uses the correct zuul_console port, so that if we
make any changes to zuul_console in the future, we will test the
changed version, not the one from the Zuul which actually runs the
tox-remote job.

Change-Id: Ia656db5f3dade52c8dbd0505b24049fe0fff67a5
2022-05-21 08:42:03 -07:00
James E. Blair 381398273e Remove unused functions from zuul.ansible.paths
These functions are no longer used.

Change-Id: I5c405b87c36a741311eb0fb563e5351617ef5e1f
2022-04-13 14:17:04 -07:00
Monty Taylor d07bc25fc2
Remove restriction on add_host
There's actually not anything unsafe about add_host. Doing CD from Zuul
requires being able to add hosts, so relax the restriction.

Change-Id: I7a5992808773722f3b81890fb4193da202cfea68
2018-09-06 03:33:19 +07:00
Zuul 5b406d8196 Merge "Revert "Temporarily override Ansible linear strategy"" 2018-06-14 18:36:25 +00:00
Zuul 89c0e26d92 Merge "Move zuul_log_id injection to command action plugin" 2018-06-14 18:36:22 +00:00
Zuul 1b49d0b346 Merge "Allow zuul_return in untrusted jobs" 2018-06-14 12:51:23 +00:00
Tobias Henkel 686f500cac
Revert "Temporarily override Ansible linear strategy"
After reworking the injection of zuul_log_id this is no longer
necessary.

This reverts commit dec1900178.
This reverts commit d6f1fd13fa.

Change-Id: I2c491e0f643638ef57ef86f505974fb012939569
2018-06-14 12:27:59 +02:00
Tobias Henkel 72dd0e82c2
Move zuul_log_id injection to command action plugin
The log streaming callback is not being called in the same way
in Ansible 2.5 as it was in 2.3.  In particular, in some cases
different Task objects are used for different hosts.  This,
combined with the fact that the callback is only called once for
a given task means that in these cases we are unable to supply
the zuul_log_id to the Task object for the second host on a task.

This can be resolved by injecting the zuul_log_id within the command
action plugin based on the task uuid directly.

Change-Id: I7ff35263c52d93aeabe915532230964994c30850
2018-06-14 12:27:59 +02:00
Paul Belanger 3316507181 Allow zuul_return in untrusted jobs
Whitelist zuul_return to allow untrusted jobs to run the task on the
executor (localhost). Otherwise, only trusted jobs are only able to
use it.

Change-Id: I768394251d7a2ee102883694bfc93845254e8514
Signed-off-by: Paul Belanger <pabelanger@redhat.com>
2018-06-13 15:48:23 -07:00
James E. Blair dec1900178 Temporarily override Ansible linear strategy (2/2)
The log streaming callback is not being called in the same way
in Ansible 2.5 as it was in 2.3.  In particular, in some cases
different Task objects are used for different hosts.  This,
combined with the fact that the callback is only called once for
a given task means that in these cases we are unable to supply
the zuul_log_id to the Task object for the second host on a task.

To correct this, a local copy of the linear strategy plugin is
added, with the change that for every host-task, it calls either
the normal on_task_start callback, or a new zuul_task_start
callback.  This ensures that we are able to set up log streaming
on every host-task.

We plan to move to a different system for establishing log streaming
soon so that we don't have to keep carrying this patched plugin.

Story: 2002528
Task: 22067

Change-Id: Ifd17d799fc28174df5194a6cd09e0e25f3ea75ac
Co-Authored-By: Tobias Henkel <tobias.henkel@bmw-carit.de>
Co-Authored-By: Clark Boylan <clark.boylan@gmail.com>
2018-06-13 15:38:36 -07:00
James E. Blair d0a3567221 Check out more appropriate branches of role and playbook repos
Currently when a job adds a zuul role repo to a playbook, we only
use the master branch of the role repo, unless the role repo
appears in the dependency chain for the change under test.

That means that if the role repo appears in required-projects,
but not as a dependency, then we use the master branch instead of
what was specified in required-projects.  That doesn't seem to make
much sense and is likely an oversight.  We attempt to use the
prepared repos where possible (ie, the requested branches match
and the playbook is not trusted).  However, the current check for
that only looks at 'items', that is, the dependency chain.  Instead,
we should look at 'projects', which includes not only the projects
which appear in 'items', but also those that appear in
required-projects.

The same check is performed for playbooks, and therefore is also
updated.

Also, in the case where a role repo doesn't appear in either the
dependency chain or in required-projects, we were hard-coded to
check out the 'master' branch.  Instead, re-use some of the logic
used when preparing required-projects to attempt to find the best
branch to check out.  We will try the job override branch first,
then the zuul branch, then the project default branch.

All playbook project repos are now prepared outside of the work dir,
even in cases where their projects also appear in the work dir.  If
the playbook is untrusted, then the repo is cloned into the "untrusted/"
jobdir directory (with speculative changes applied).  To account for
this, the "allow_trusted" flag in the ansible safe path checker is
updated to allow access to both "trusted/" and "untrusted/" paths.

Change-Id: If95a9b0aaff982040cd4e6e957f9588b26ef7935
2018-04-05 10:41:51 -07:00
James E. Blair b4385b2d93 Allow some plugins to read from playbook dir
Currently the lookup plugins are only permitted to read data
from the work dir.  Some of them expect to read files based on
the CWD which Ansible sets to be the playbook directory.  In the
case that the lookup plugins are being run by a playbook in a
project which is in the dependency chain, that will work fine.
But if the playbook project is not in the dependency chain, it
is checked out into the "trusted/" directory in the jobdir, which
is outside of the work dir.  In this case, the lookup plugin will
fail.

Alter the lookup plugins to permit access to the "trusted/"
directory as well as "work/".  This is safe because these plugins
only read files, not write.  And the only content in the "trusted/"
directory is git repository checkouts which the user already has
access to.

Also allow the include_vars and synchronize action modules access
to read files from these directories.

The current tests are sufficient to show that current behavior is
not broken by this change.  A followup change moves all playbook
project checkouts outside of the work directory, so that case will
be tested then.

Change-Id: Ie2e5b0d1c099d4f9cf59c1e67fb0603d7c5f757b
2018-04-05 10:40:51 -07:00
Tobias Henkel 5763b8e4d7
Add missing localhost delegation checks to some modules
Currently we don't check some modules for delegation to
localhost. This would make it possible to overwrite any data which is
writable within the bwrap context. Further the script module allows
arbitrary code execution when delegated to localhost.

The following modules are affected:
* assemble: add safe path check
* copy: add safe path check
* patch: add safe path check
* script: block completely
* template: add safe path check
* unarchive: add tests, fixed by safe path check of copy

Change-Id: I2360219f50e6a28bb134468ead08ec72148ad192
Story: 2001681
2018-03-22 20:42:01 +01:00
Tobias Henkel e54fcde58a
Fix safe path check for directories containing symlinks
Currently it is possible to bypass safe path checks by utilising
modules that can operate on directories instead of files like
assemble. This can be done by putting symlinks into a directory the
module is allowed to access.

This can be fixed by walking the whole sub tree and checking the paths
instead of just checking the path itself.

Change-Id: Iaa4efcf0737e47429339e9afd66eecf4e38fd8ea
2018-03-14 20:55:43 +01:00
Tobias Henkel 1214b104d1
Allow trusted for find_needle
In ansible find_needle is used for finding source files. This must be
allowed also for roles from trusted repos.

Change-Id: I0491bc08ba1869849a562bd5047253e60c40c7d7
2018-03-12 21:59:09 +01:00
Tobias Henkel 53147dd648
Fix safe path checks
Currently the safe path checks mostly just work on the pure given
arguments. However ansible searches in several paths for the file and
uses this then. Thus the safe path check can validate a non existing
file (as it doesn't obey the modules search path) while the module
finds the file and follows any symlinks.

The solution for this is not to try to determine the correct paths
ourselves but hooking into the search routine (_find_needle) the
modules use for finding their local files. Using this approach we have
the correct paths and can validate them properly.

Change-Id: I2b3dbcfcfcf8a82e4e6df286cc3287aaa7fa2790
2018-03-12 16:00:26 +01:00
Monty Taylor 950c6b1e2d
Fix path exclusions
The current code checks to see that the destination path shares a prefix
with os.path.curdir. However, os.path.curdir is set to the directory
containing the playbook, not the root of the workdir, which means we're
not excluding things in the trusted dir like we'd like to be doing.

We already set HOME to the root of thew workdir, so we can just switch
the check from os.path.curdir to os.path.expanduser('~') and achieve the
original intent.

Change-Id: Ifac41f74f3306fe74b522c910867f9a5375bd61e
2017-10-05 15:47:52 -05:00
Monty Taylor cee3fc7dd1
Pass the correct object to local module check
The normal action plugin was passing module_name when the code path
actually needed the module object itself.

Change-Id: Idb8968a7020ffb159d0d016b5bfab6bd88219c70
2017-08-31 14:04:53 -05:00
Monty Taylor 788a40e75c
Prevent execution of locally overridden core modules
We greylist some modules in our action plugin blocking allowing them to
execute local code as long as it falls within safe constraints. Due to
the way ansible module loading works, a user could attack this by
creating a module in a local role or adjacent to a playbook that has the
same name as one of the modules we allow limited local execution. If
they did that it would allow them to execute arbitrary python code on
the executor.

Find the path of the module that will be executed in these cases and if
it is not within the ansible.modules package, disallow it. There are no
circumstances in which this is ok.

Change-Id: I7499e6b1091d745984ca36179de2793827c9f98f
2017-08-29 10:50:53 -05:00
Monty Taylor d08b4ce374
Start blocking lookup plugins on insecure jobs
Some of the lookup plugins access files on the executor host. Obviously
that's not what we want, so block them like we block action plugins.

password.py is banned, although it could be filtered. However, the
upstream code is fairly intense and slated for refactoring - so let's
wait until someone gets upset about it.

Change-Id: I6260b4658619a972b588c8bfba40ec33557bf2f6
2017-04-06 13:43:50 -05:00
Monty Taylor 7ec6a1b7fb
Fully expand path when testing it
People can use symlinks because they can. Expand them. Also, don't block
relative paths because we're expanding to absolute.

Change-Id: I483b5abbbeb962761d604dc5e7d6b64492dfd83d
2017-02-23 11:56:47 -05:00
Monty Taylor 1954d3528b
Add working dir to error message
Change-Id: Ia576c0160b0f909d199d13f355d50c1697947382
2017-02-23 11:48:50 -05:00
James E. Blair 4e5b0dedb9 Fix infinite recursion on action module import
Ansible imports our module with the name ansible.plugins.action.normal
(regardless of its actual path).  This line would then import the original
module, also under the same name, so it would overwrite ours.  Correct
this by importing the original with a unique name.  It's not actually
used anywhere anyway.

Change-Id: I5dd26e2f89399f991cebc23839a36e7aff47056c
2017-02-22 14:32:05 -05:00
Monty Taylor c231d939ea Add action plugins to restrict untrusted execution
There are actions undertaken by action plugins in normal ansible that
allow for executing code on the host that ansible is executing on. We do
not want to allow that for untrusted code, so add a set of action
plugins that override the upstream ones and simply return errors.

Additionally, we can trap for attempts to execute local commands in the
normal action plugin by looking at remote_addr, connection and
delegate_to.

Change-Id: I57dbe5648a9dc6ec9147c8698ad46c4fa1326e5a
2017-02-15 16:12:40 -08:00