Feature #1875
A REST Authorization file is needed
| Status: | Closed | Start: | 01/19/2009 | |
|---|---|---|---|---|
| Priority: | High | Due date: | ||
| Assignee: | % Done: | 0% |
||
| Category: | network | |||
| Target version: | 0.25.0 | |||
| Affected version: | 0.24.7 | Branch: | ||
| Keywords: | ||||
| Votes: | 0 |
Description
In the switch from XMLRPC to REST, we’ve exposed the lack of decent authorization in the network interfaces.
Note that authorization is distinct from authentication, which is handled via certificates.
Once a host is authenticated, we need some way of knowing what services or aspects of services the host has access to. Current 0.24.x systems provide multiple configuration files that cover some of this, notably fileserver.conf. For arbitrary authorization, there is namespaceauth.conf.
We need something more generic that relies on our switch to REST, likely providing authorization against URIs, possibly with some kind of mapping to services.
E.g., we might have something like:
path / deny * path /file allow authenticated path /certificate allow unauthenticated path /file/$host deny * allow $host # or something like that
Clearly I haven’t exactly thought this all the way through.
The main thing we need is something mirrors our existing default authentication – defer to fileserver.conf for files, allow unauthenticated users to upload certificate requests and download certificates, and allow authenticated users to download catalogs.
It’d be nice to be able to fold fileserver.conf into this file eventually, I think, but I wouldn’t prioritize that.
Note that there are three fields to any authentication attempt: path/uri, authentication status, http verb. Any configuration file would need to be able to specify each of these in a given authorization specification.
It’d be best of all if we could find someone else’s solution to this problem and crib from them.
Related issues
| related to Puppet - Feature #2178: Strict hostname checking should require matching the IP r... | Accepted | 04/21/2009 |
History
Updated by Luke Kanies over 1 year ago
- Assignee changed from Paul Nasrat to Brice Figureau
Brice has actually taken this over.
Updated by Luke Kanies over 1 year ago
- Status changed from Accepted to Ready for Testing
Sounds like this is almost ready, just a couple of small changes left.
Updated by Luke Kanies over 1 year ago
In testing this, I think that it makes more sense to have something like a default configuration for authenticated connections, and then a different default configuration for unauthenticated connections.
From what I can tell of the current configuration, any unauthenticated client can save certificates to the server, which I think is a pretty significant security hole. The only things that should be possible for unauth’d clients is to save CSRs and retrieve certificates.
Updated by Brice Figureau over 1 year ago
luke wrote:
In testing this, I think that it makes more sense to have something like a default configuration for authenticated connections, and then a different default configuration for unauthenticated connections.
From what I can tell of the current configuration, any unauthenticated client can save certificates to the server, which I think is a pretty significant security hole. The only things that should be possible for unauth’d clients is to save CSRs and retrieve certificates.
So your suggestion would be:
to be able to restrict an ACL to an authenticated or unauthenticated request in the auth.conf file
to let the unauthenticated case use the same code as the authenticated case
to have the default set of rule define rules for authenticated and unauthenticated cases
Is that right?
Updated by Brice Figureau over 1 year ago
luke wrote:
In testing this, I think that it makes more sense to have something like a default configuration for authenticated connections, and then a different default configuration for unauthenticated connections.
From what I can tell of the current configuration, any unauthenticated client can save certificates to the server, which I think is a pretty significant security hole. The only things that should be possible for unauth’d clients is to save CSRs and retrieve certificates.
I have an implementation (and posted it for review to the puppet-dev list) in my tickets/1875 github branch: http://github.com/masterzen/puppet/tree/tickets/1875
Updated by Brice Figureau over 1 year ago
masterzen wrote:
luke wrote:
In testing this, I think that it makes more sense to have something like a default configuration for authenticated connections, and then a different default configuration for unauthenticated connections.
From what I can tell of the current configuration, any unauthenticated client can save certificates to the server, which I think is a pretty significant security hole. The only things that should be possible for unauth’d clients is to save CSRs and retrieve certificates.
I have an implementation (and posted it for review to the puppet-dev list) in my tickets/1875 github branch: http://github.com/masterzen/puppet/tree/tickets/1875
Latest (and hopefully) final implementation is in the tickets/master/1875 branch of my gh repository: http://github.com/masterzen/puppet/tree/tickets/master/1875
Note: it is rebased on top of lak’s tickets/master/2149, so to be applied after 2149.
Updated by Luke Kanies over 1 year ago
- Status changed from Ready for Testing to Code Insufficient
I can’t seem to find an easy way to test this, but my client is apparently asking for the catalog for an unqualified host, and I’m getting an exception:
Users/luke/puppet/lib/puppet/network/authstore.rb:332:in `parse' /Users/luke/puppet/lib/puppet/network/authstore.rb:193:in `pattern=' /Users/luke/puppet/lib/puppet/network/authstore.rb:228:in `interpolate' /Users/luke/puppet/lib/puppet/network/authstore.rb:77:in `interpolate' /Users/luke/puppet/lib/puppet/network/authstore.rb:76:in `collect' /Users/luke/puppet/lib/puppet/network/authstore.rb:76:in `interpolate' /Users/luke/puppet/lib/puppet/network/rights.rb:188:in `allowed?' /Users/luke/puppet/lib/puppet/network/rights.rb:50:in `fail_on_deny' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/httputils.rb:236:in `find' /Users/luke/puppet/lib/puppet/network/rights.rb:43:in `each' /Users/luke/puppet/lib/puppet/network/rights.rb:43:in `find' /Users/luke/puppet/lib/puppet/network/rights.rb:43:in `fail_on_deny' /Users/luke/puppet/lib/puppet/network/rest_authconfig.rb:36:in `allowed?' /Users/luke/puppet/lib/puppet/network/rest_authorization.rb:21:in `check_authorization' /Users/luke/puppet/lib/puppet/network/http/handler.rb:46:in `process' /Users/luke/puppet/lib/puppet/network/http/webrick/rest.rb:23:in `service' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/httpserver.rb:104:in `service' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/httpserver.rb:65:in `run' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:173:in `start_thread' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:162:in `start' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:162:in `start_thread' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:95:in `start' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:92:in `each' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:92:in `start' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:23:in `start' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:82:in `start' /Users/luke/puppet/lib/puppet/network/http/webrick.rb:40:in `listen' /Users/luke/puppet/lib/puppet/network/http/webrick.rb:40:in `initialize' /Users/luke/puppet/lib/puppet/network/http/webrick.rb:40:in `new' /Users/luke/puppet/lib/puppet/network/http/webrick.rb:40:in `listen' /Users/luke/puppet/lib/puppet/network/http/webrick.rb:37:in `synchronize' /Users/luke/puppet/lib/puppet/network/http/webrick.rb:37:in `listen' /Users/luke/puppet/lib/puppet/network/server.rb:128:in `listen' /Users/luke/puppet/lib/puppet/network/server.rb:143:in `start' /Users/luke/puppet/lib/puppet/daemon.rb:128:in `start' /Users/luke/puppet/lib/puppet/application/puppetmasterd.rb:87:in `main' /Users/luke/puppet/lib/puppet/application.rb:226:in `send' /Users/luke/puppet/lib/puppet/application.rb:226:in `run_command' /Users/luke/puppet/lib/puppet/application.rb:217:in `run' sbin/puppetmasterd:66 err: Could not check authorization: Invalid pattern phage
Updated by Luke Kanies over 1 year ago
(Hit submit too soon.)
Note that I added an extra wrapper around the auth check to make this error read a bit better.
The only thing I’ve firmly established is that the problem occurs when my host (whose certname is other.madstop.com) asks for /catalog/phage (phage being its real name).
Updated by Brice Figureau over 1 year ago
luke wrote:
(Hit submit too soon.)
Note that I added an extra wrapper around the auth check to make this error read a bit better.
The only thing I’ve firmly established is that the problem occurs when my host (whose certname is other.madstop.com) asks for /catalog/phage (phage being its real name).
That’s why I asked you a couple of days ago on #puppet why you used Facter.value(“hostname”) for catalog retrieval, and Puppet[:certname] for facts retrieval, because I was hitting the same issue. The real cause of the problem is that the underlying authstore requires fully qualified names, but the requests to catalog indirectors sends only the hostname, so in turns we’re trying to create a dynamic temporary allow(“hostname”), which is forbidden.
Now that I think about it, it looks like a bug, as hostnames can clearly not be unique among the various nodes connecting to a master (ie host1.domain1.com and host2.domain2.com would get the same request, and maybe the same catalog… after looking to the code, in fact no it works, because what matters is the request determined node which hopefully uses the certname).
So it’s just the catalog request that is wrong (the logic is OK), we should move from /catalog/hostname to /catalog/fqdn or /catalog/certname.
What do you think? If you’re OK, I’ll queue a patch to fix this issue.
Updated by Brice Figureau over 1 year ago
masterzen wrote:
luke wrote:
(Hit submit too soon.)
Note that I added an extra wrapper around the auth check to make this error read a bit better.
The only thing I’ve firmly established is that the problem occurs when my host (whose certname is other.madstop.com) asks for /catalog/phage (phage being its real name).
That’s why I asked you a couple of days ago on #puppet why you used Facter.value(“hostname”) for catalog retrieval, and Puppet[:certname] for facts retrieval, because I was hitting the same issue. The real cause of the problem is that the underlying authstore requires fully qualified names, but the requests to catalog indirectors sends only the hostname, so in turns we’re trying to create a dynamic temporary allow(“hostname”), which is forbidden.
Now that I think about it, it looks like a bug, as hostnames can clearly not be unique among the various nodes connecting to a master (ie host1.domain1.com and host2.domain2.com would get the same request, and maybe the same catalog… after looking to the code, in fact no it works, because what matters is the request determined node which hopefully uses the certname).
So it’s just the catalog request that is wrong (the logic is OK), we should move from /catalog/hostname to /catalog/fqdn or /catalog/certname.
What do you think? If you’re OK, I’ll queue a patch to fix this issue.
I updated my tickets/master/1875 branch with this fix included.
Updated by Luke Kanies over 1 year ago
- Status changed from Code Insufficient to Ready for Checkin
- Assignee changed from Brice Figureau to James Turnbull
It looks like this doesn’t allow blanket statements like ‘allow ’ or ‘deny ’.
I think that’s an existing limitation, though, so I’m comfortable merging as is and fixing that later.
I ran through a basic list of tests and everything seems to work, so I’m comfortable checking in as is.
Updated by James Turnbull over 1 year ago
- Status changed from Ready for Checkin to Code Insufficient
- Assignee changed from James Turnbull to Brice Figureau
Can you rebase tickets/master/1875 on current HEAD please.
Updated by Brice Figureau over 1 year ago
luke wrote:
It looks like this doesn’t allow blanket statements like ‘allow ’ or ‘deny ’.
I think that’s an existing limitation, though, so I’m comfortable merging as is and fixing that later.
‘allow *’ works fine. But deny * is not supported. This is because of the way authstore is working (ie it sorts allow rules before deny rules and finishes with an implit deny all).
So basically, it has no sense to write: allow something deny *
and a lone: deny * is equivalent to nothing.
If we want something which would allow more complex scenarios, we need something ala Apache, with a directive to specify in which order ACE should be evaluated: order allow, deny allow this deny * Then deny would be applied after the allow.
I thought about this, but in the end I think your system works, except we are tempted to add some “deny ” as catch-all rules, which is forbidden. And not that an “allow ” is treated before any other rules, so that even if you have some denies after that they can’t be reached.
If you think we need to change this, then let’s open a ticket, I’ll be happy to work on this.
I ran through a basic list of tests and everything seems to work, so I’m comfortable checking in as is.
Thanks, I’ll rebase to latest HEAD tonight.
Updated by Brice Figureau over 1 year ago
- Status changed from Code Insufficient to Ready for Checkin
I rebased against the latest master, you can apply. Thanks,
Updated by James Turnbull over 1 year ago
- Status changed from Ready for Checkin to Closed
Pushed in branch master.