Skip to content

Conversation

@iliana
Copy link
Contributor

@iliana iliana commented Apr 17, 2024

This is the Nexus side to oxidecomputer/console#2142, which needs to land before this lands. See also https://github.com/oxidecomputer/product-assurance/issues/50.

content-security-policy tells a web browser what type of content, and from which origins, may be loaded on a page. The primary use is to help guard against cross-site scripting attacks and other kinds of novel attacks on web applications.

x-content-type-options: nosniff tells the web browser to disallow content sniffing that can cause a browser to decide that responses with non-executable content types (e.g. image/png) can in fact be used as executable content types (e.g. text/javascript). This needs to be set for all console assets.

x-frame-options: DENY disallows embedding the console within another page, which helps to prevent click-jacking attacks. (This is obsoleted by the frame-ancestors 'none' CSP directive, but no harm in adding it.)

content-security-policy only needs to be set for the console index page, but there's no harm in setting it for the console assets as well.

As part of this change I did some refactoring:

  • The common code between the asset function and the serve_console_index function are now in a single common function. This allows us to ship a gzip-compressed console index in the future.
  • Assets are now streamed instead of read completely into memory.
  • I removed the dependency on mime_guess; we only have a small list of file extensions we're willing to serve, so it doesn't make sense to compile a huge list of content types we'll never use into Nexus.

There may be other headers from https://owasp.org/www-project-secure-headers/ (see the Best Practices tab) that we want but these are probably the most urgent.

iliana added 5 commits April 16, 2024 09:21
this API is somewhat confusing, because i am not sure why one would need
to _replace_ the list of allowed header names. it seems better to ensure
that there is one place where every allowed response header is set.
@iliana iliana added this to the 8 milestone Apr 17, 2024
@iliana iliana marked this pull request as draft April 17, 2024 03:00

Ok(resp.body(file_contents.into())?)
let mut path = Utf8PathBuf::from("assets");
path.extend(path_params.into_inner().path);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find anywhere where we prevent the user from passing ".." in the path and walking back up the directory tree. How do we avoid that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I had assumed find_file does that, but rereading it I don't think it does (it guards against following symlinks). I will add checks to that function and write a test.

I'm debating whether it makes more sense to bail on any path segment that contains .., or if we should std::path::Path::canonicalize every path as it comes in and bail if it isn't inside static_dir.

Copy link
Contributor

@david-crespo david-crespo Apr 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also thought it did, and I thought we had tests for it. I don't a huge problem with taking the easy route and telling any .. to go away.

Copy link
Contributor Author

@iliana iliana Apr 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good news, Dropshot already does this. https://github.com/oxidecomputer/dropshot/blob/28c63c7f9d3b81729c0b0343640fb815877cdfab/dropshot/src/router.rs#L636-L687 (since v0.6.0, oxidecomputer/dropshot#118)

I'm going to add a test on the Omicron side to ensure that remains the case.

.header(http::header::CONTENT_TYPE, content_type)
.header(http::header::CACHE_CONTROL, "max-age=31536000, immutable"); // 1 year
let stream = FramedRead::new(file, BytesCodec::new());
let body = Body::wrap_stream(stream);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean we don't have to read the entire file into memory at once?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. That may have been leading to some request latency with the larger bundles, although not sure if it actually was in practice.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like probably not (this is colo, dogfood looks a bit different due to VPN I assume), but might as well do it the right way.

image

Copy link
Contributor

@david-crespo david-crespo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All good

@iliana
Copy link
Contributor Author

iliana commented Apr 23, 2024

#5065 pulls in the CSP changes on the console side, so moving out of draft.

@iliana iliana marked this pull request as ready for review April 23, 2024 20:40
@iliana iliana merged commit debbf7c into main Apr 25, 2024
@iliana iliana deleted the iliana/csp branch April 25, 2024 03:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants