Skip to content
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

Fixes #24631 - httpboot module #605

Merged
merged 1 commit into from Aug 24, 2018

Conversation

@lzap
Copy link
Member

commented Aug 16, 2018

New module for nicer UEFI HTTP BOOT implementation:

theforeman/foreman#5950

Please pay extra attention to security during review. I tested with paths like '/EFI/../../../etc/passwd' or similar. Directory listing is not allowed. Symlinks are followed by design and they are followed even outside of TFTP directory. Not sure if this is a problem, we can limit that tho if needed.

@stbenjam
Copy link
Member

left a comment

I haven't tried end-to-end yet, but the proxy module looks good itself. I couldn't get it to do any nefarious traversals using the common tricks, sinatra seems pretty smart.


get "/*" do
file = Pathname.new(params[:splat].first).cleanpath
real_file = Pathname.new(File.join(Proxy::TFTP::Plugin.settings.tftproot, file)).realpath

This comment has been minimized.

Copy link
@stbenjam

stbenjam Aug 16, 2018

Member

Should this be Proxy::Httpboot::Plugin.settings.root_dir instead?

This comment has been minimized.

Copy link
@stbenjam

stbenjam Aug 16, 2018

Member

Also, if you get /EFI or a non-existent file, we return 500. It'd be nice if we return the right http error codes, 404, 503, etc.

This comment has been minimized.

Copy link
@lzap

lzap Aug 17, 2018

Author Member

Good catch, yeah absolutely 404. Changing.

This comment has been minimized.

Copy link
@lzap

lzap Aug 17, 2018

Author Member

So I am implementing extra verbose checks just to be sure.

@timogoebel
Copy link
Member

left a comment

Thanks. Note, that tests are missing.

helpers ::Proxy::Helpers

get "/*" do
file = Pathname.new(params[:splat].first).cleanpath

This comment has been minimized.

Copy link
@timogoebel

timogoebel Aug 16, 2018

Member

Is this safe to prevent ../../../etc/shadow ?

This comment has been minimized.

Copy link
@stbenjam

stbenjam Aug 16, 2018

Member

Sinatra filters it out, among other tricky ways to do path traversal.

This comment has been minimized.

Copy link
@lzap

lzap Aug 17, 2018

Author Member

Yep I tested this, but I was wondering if we should allow following symlinks outside of TFTP root directory.

I can prevent from both possible security issues by comparing absolute file path with root directory - if it's not the same then we have an error. You know what, let me do that, just for case. I don't want my name on a CVE like this :-)

This comment has been minimized.

Copy link
@lzap

lzap Aug 17, 2018

Author Member

I am doing this, we can't be sure if e.g. older version of Sinatra does this.

@lzap lzap force-pushed the lzap:httpboot-24631 branch from 277367b to ecf2662 Aug 17, 2018

@lzap

This comment has been minimized.

Copy link
Member Author

commented Aug 17, 2018

I have amended the last commit, thank you for review.

@stbenjam

This comment has been minimized.

Copy link
Member

commented Aug 21, 2018

Looks good, but a few notes. First, smart Proxy is returning a Server header with no value:

[vagrant@centos7-devel ~]$ curl -I http://192.168.73.1:7000/httpboot/grub2/grubx64.efi
HTTP/1.1 200 OK 
Content-Type: application/octet-stream
Server: 
Last-Modified: Tue, 21 Aug 2018 20:59:12 GMT
Content-Length: 1107968
X-Content-Type-Options: nosniff
Date: Tue, 21 Aug 2018 21:06:26 GMT
Connection: Keep-Alive

This is not valid although most things seem happy to accept it except the edk2 firmware which causes it to freeze. That took a better part of the afternoon to figure out why my vm was locking up.

Can you add that to the smart proxy somewhere?

@stbenjam

This comment has been minimized.

Copy link
Member

commented Aug 21, 2018

The other thing is how grub2 doesn't handle relative paths (https://bugzilla.redhat.com/show_bug.cgi?id=1616395). I have a proposed fix to grub2, but even then relative paths (e.g. boot/centos7-vmlinuz or whatever) will be relative to the EFI image, e.g. the grub2 folder in our default set up. Users will need a symlink /var/lib/tftpboot/grub2/boot -> /var/lib/tftpboot/boot (which the installer can do unless you have another idea).

@lzap lzap force-pushed the lzap:httpboot-24631 branch from ecf2662 to 7afcd9b Aug 23, 2018

@lzap

This comment has been minimized.

Copy link
Member Author

commented Aug 23, 2018

Thanks rebased, it was intentionally set to blank but I explained why we add this back: https://projects.theforeman.org/issues/14394

@stbenjam
Copy link
Member

left a comment

Tested, works great

---
# Enable publishing of a given directory under /EFI and /httpboot paths.
# Directory listing is not possible, symlinks are followed but not outside
# of the rood directory specified in this file.

This comment has been minimized.

Copy link
@timogoebel

timogoebel Aug 24, 2018

Member

Nit: typoroot

@@ -50,7 +50,7 @@ def http_app(http_port, plugins = http_plugins)
:DoNotListen => true,
:Port => http_port, # only being used to correctly log http port being used
:Logger => ::Proxy::LogBuffer::Decorator.instance,
:ServerSoftware => '',
:ServerSoftware => "foreman-proxy/#{Proxy::VERSION}",

This comment has been minimized.

Copy link
@timogoebel

timogoebel Aug 24, 2018

Member

It's common security practice to not publish the software versions. Do we need this?

This comment has been minimized.

Copy link
@lzap

lzap Aug 24, 2018

Author Member

It's a common practice in the enterprise, not in the open source world I'd say. But to the point - we do advertise the same via /features endpoint anyway. And we must not remove this - Foreman now checks foreman version and shows you a warning for compatibility.

If this is a major concern, please file a RM issue to encrypt the version or something like that. But I guess all we can do is obscurity not security in this case.

This comment has been minimized.

Copy link
@timogoebel

timogoebel Aug 24, 2018

Member

Ok, fair enough.

This comment has been minimized.

Copy link
@ekohl

ekohl Aug 24, 2018

Member

I would like to see it as a separate commit. It's not obvious that this is part of the httpboot and I wouldn't look at this commit in git log if I was looking for the change.

# Directory listing is not possible, symlinks are followed but not outside
# of the rood directory specified in this file.

# Enables the module, make sure to enable TFTP module as well to allow

This comment has been minimized.

Copy link
@timogoebel

timogoebel Aug 24, 2018

Member

You can use requires in the plugin spec to make sure the tftp module is loaded.

This comment has been minimized.

Copy link
@lzap

lzap Aug 24, 2018

Author Member

Oh cool this one was exactly what I was looking for. Thanks.

@lzap lzap force-pushed the lzap:httpboot-24631 branch from 7afcd9b to 8b84238 Aug 24, 2018

@lzap

This comment has been minimized.

Copy link
Member Author

commented Aug 24, 2018

Thanks amended the two things.

@timogoebel timogoebel merged commit 8af0137 into theforeman:develop Aug 24, 2018

3 checks passed

core Build finished. 5299 tests run, 0 skipped, 0 failed.
Details
prprocessor Commit message style is correct
Details
rubocop Build finished. No test results found.
Details
@timogoebel

This comment has been minimized.

Copy link
Member

commented Aug 24, 2018

Thanks, @lzap and @stbenjam.

@@ -50,7 +50,7 @@ def http_app(http_port, plugins = http_plugins)
:DoNotListen => true,
:Port => http_port, # only being used to correctly log http port being used
:Logger => ::Proxy::LogBuffer::Decorator.instance,
:ServerSoftware => '',
:ServerSoftware => "foreman-proxy/#{Proxy::VERSION}",

This comment has been minimized.

Copy link
@ekohl

ekohl Aug 24, 2018

Member

I would like to see it as a separate commit. It's not obvious that this is part of the httpboot and I wouldn't look at this commit in git log if I was looking for the change.

require 'httpboot/httpboot_plugin'
require 'httpboot/httpboot_api'

ENV['RACK_ENV'] = 'test'

This comment has been minimized.

Copy link
@ekohl

ekohl Aug 24, 2018

Member

It feels weird to set an env var here. Is this needed?

This comment has been minimized.

Copy link
@lzap

lzap Aug 27, 2018

Author Member

Copy pasted from other file, thought it's required.

@lzap

This comment has been minimized.

Copy link
Member Author

commented Aug 27, 2018

I did not want to do this in a separate commit, this might be backported (as it's new module and there cannot be regressions). The feature does not work end-to-end without this.

@lzap lzap deleted the lzap:httpboot-24631 branch Aug 27, 2018

@ekohl

This comment has been minimized.

Copy link
Member

commented Aug 27, 2018

How does the code not work without it? Does the client reject empty server strings?

@stbenjam

This comment has been minimized.

Copy link
Member

commented Aug 27, 2018

How does the code not work without it? Does the client reject empty server strings?

It would work fine without it (or at least I would guess so), but Sinatra sends Server: as the header. This breaks the UEFI firmware that Qemu uses: https://bugzilla.tianocore.org/show_bug.cgi?id=1102.

I didn't look at the code, but I would guess the HTTP client does a strchr on space or something.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
5 participants
You can’t perform that action at this time.