-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Add CSRF protection to deck #13323
Add CSRF protection to deck #13323
Conversation
76bb84e
to
8415661
Compare
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.
It works now!
I think this substantially improves our security here. My primary (non-security) concern is that the CSRF handling is done separately for every page. It would be preferable if it instead of everyone having to do this manually, it were handled in the base template. That would make it infrastructure that can mostly be ignored by everything else, and also gets rid of the need to have the defaulting behaviour for the params in handleSimpleTemplate
.
Other than that, I'd like to see the ability to run with CSRF enabled in debug mode without needing to edit code, and I'm not a fan of us introducing new flags to do the same thing as the old ones - see the specific comments for details.
prow/cmd/deck/main.go
Outdated
@@ -139,6 +141,8 @@ func gatherOptions(fs *flag.FlagSet, args ...string) options { | |||
fs.StringVar(&o.templateFilesLocation, "template-files-location", "/template", "Path to the template files") | |||
fs.StringVar(&o.gcsCredentialsFile, "gcs-credentials-file", "", "Path to the GCS credentials file") | |||
fs.BoolVar(&o.rerunCreatesJob, "rerun-creates-job", false, "Change the re-run option in Deck to actually create the job. **WARNING:** Only use this with non-public deck instances, otherwise strangers can DOS your Prow instance") | |||
// csrfTokenFile, if specified, must point to a file containing a 32 byte token that will be used to protect against CSRF in POST requests | |||
fs.StringVar(&o.csrfTokenFile, "csrf-token", "", "Path to the file containing the CSRF 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.
Ideally we could reuse --cookie-secret
for this - it serves the same purpose. Unfortunately, we didn't require 32-byte token (and it looks like on our deployment it's currently 64 bytes). We might be able to just truncate and complain if it's less than 32 bytes, but that presents a backwards compatibility concern. As far as I can tell, we never previously expressed guidance on how long --cookie-secret
should be (or documented it at all?), which is itself bad.
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're eventually going to need to announce that we'll start requiring a CSRF token anyway. We could instead announce that --cookie-secret
must be at least 32 bytes (or some more specific length) and truncate it. What do you think?
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 we should use --cookie-secret
and do the following:
- if the value is more than 32 bytes, emit a warning that the value should be exactly 32 bytes and suggest truncating the existing value, then continue
- if the value is less than 32 bytes and
--rerun-creates-job
is not specified but--oauth-url
is, emit a warning that it should be exactly 32 bytes and continue - if the value is less than 32 bytes and
--rerun-creates-job
is specified, emit an error and refuse to start - if neither
--rerun-creates-job
nor--oauth-url
are specified, but--cookie-secret
is, and--cookie-secret
is not a 32-byte value, emit a warning and continue - if none of
--rerun-creates-job
,--oauth-url
, nor--cookie-secret
are specified, do nothing and proceed silently
Note that for some reason, --cookie-secret
is base64-encoded (and then k8s internally base64-encodes it again...), so all of the above is after decoding. Unfortunately, changing this would not be backwards compatible, so let's not.
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.
Done
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.
Are we sure that the entropy here is evenly distributed? If not then hashing the token might be preferable to truncating it. It would be unfortunate if the first 32 bytes tend to be the same, for example
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.
No way of knowing since AFAICT we never documented how to generate this (in our case, probably yes, but hard to tell for anyone else). Hashing is probably a good idea.
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.
Is there a specific library I should use for this?
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'd suggest the built-in crypto/sha256
, which will give you the 32 bytes you want.
bd6e4fb
to
701d31d
Compare
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.
This looks pretty good to me! Mostly just nits at this point.
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.
Since none of us are experts (or maybe its just me) at CSRF a short explanation (and maybe including links to more authoritative ones) explaining how this is support to work in the PR description and/or a README in prow/githuboauth and/or deck would be appreciated.
prow/cmd/deck/main.go
Outdated
} | ||
|
||
// if we allow direct reruns, we must protect against CSRF in all post requests using the cookie secret as a token | ||
if o.rerunCreatesJob { |
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.
This should always be enabled if it can be (i.e. if we have a sufficiently long secret). If we can't enable it, warn loudly and continue.
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.
What is the use of the --rerun-creates-job
flag now? It seems like we won't need it after this change
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.
It still controls whether you can rerun jobs via the UI or have to copy a provided kubectl command.
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.
Yes and we would very much like that flag to stay, because wer will eventually hopefully have two deck instances using the same backing Prow, a public and a private one. On the private one everyone should be allowed to use the rerun feature, on the public one obviously not. They will however use the same config.yaml
d8a2175
to
7569599
Compare
d2b91e7
to
4ef1605
Compare
Adds documentation for why we need CSRF protection and how the protection works.
if len(decodedSecret) > 32 { | ||
logrus.Warning("Cookie secret should be exactly 32 bytes. Consider truncating the existing cookie to that length") | ||
hash := sha256.Sum256(decodedSecret) | ||
csrfToken = hash[:] |
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.
Why the [:]
here?
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.
Sum256
returns a fixed length array, not a slice. It was a bit of a hassle making this work, and the [:]
was the best solution I could find
LGTM label has been added. Git tree hash: 4685b6ea5a342a3784c1841b15163c6c182c0a25
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: fejta, Katharine, mirandachrist The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
This adds CSRF protection to deck (using the gorilla csrf library) if
--rerun-creates-job
is set totrue
and a file path for the CSRF token is specified. We will eventually require a CSRF file path to be specified if--rerun-creates-job
is set totrue
, but for now we log a warning that this is insecure.In preparation for removing the
--rerun-creates-job
flag, we will later be making an announcement that specifying the CSRF file path will be mandatory soon./assign @cjwagner
/cc @fejta
/cc @Katharine