diff options
Diffstat (limited to 'lib/puppet/network')
-rw-r--r-- | lib/puppet/network/http/handler.rb | 8 | ||||
-rw-r--r-- | lib/puppet/network/rest_authconfig.rb | 72 | ||||
-rw-r--r-- | lib/puppet/network/rest_authorization.rb | 49 |
3 files changed, 128 insertions, 1 deletions
diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 04ba14401..20234b2da 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -2,9 +2,11 @@ module Puppet::Network::HTTP end require 'puppet/network/http/api/v1' +require 'puppet/network/rest_authorization' module Puppet::Network::HTTP::Handler include Puppet::Network::HTTP::API::V1 + include Puppet::Network::RestAuthorization attr_reader :server, :handler @@ -38,7 +40,11 @@ module Puppet::Network::HTTP::Handler def process(request, response) indirection_request = uri2indirection(http_method(request), path(request), params(request)) - send("do_%s" % indirection_request.method, indirection_request, request, response) + if authorized?(indirection_request) + send("do_%s" % indirection_request.method, indirection_request, request, response) + else + return do_exception(response, "Request forbidden by configuration %s %s" % [indirection_request.indirection_name, indirection_request.key], 403) + end rescue Exception => e return do_exception(response, e) end diff --git a/lib/puppet/network/rest_authconfig.rb b/lib/puppet/network/rest_authconfig.rb new file mode 100644 index 000000000..58708e120 --- /dev/null +++ b/lib/puppet/network/rest_authconfig.rb @@ -0,0 +1,72 @@ +require 'puppet/network/authconfig' + +module Puppet + class Network::RestAuthConfig < Network::AuthConfig + + attr_accessor :rights + + DEFAULT_ACL = { + :facts => { :acl => "/facts", :method => [:save, :find] }, + :catalog => { :acl => "/catalog", :method => :find }, + # this one will allow all file access, and thus delegate + # to fileserver.conf + :file => { :acl => "/file" }, + :cert => { :acl => "/certificate", :method => :find }, + :reports => { :acl => "/report", :method => :save } + } + + def self.main + add_acl = @main.nil? + super + @main.insert_default_acl if add_acl and !@main.exists? + @main + end + + # check wether this request is allowed in our ACL + def allowed?(request) + read() + return @rights.allowed?(build_uri(request), request.node, request.ip, request.method) + end + + def initialize(file = nil, parsenow = true) + super(file || Puppet[:rest_authconfig], parsenow) + + # if we didn't read a file (ie it doesn't exist) + # make sure we can create some default rights + @rights ||= Puppet::Network::Rights.new + end + + def parse() + super() + insert_default_acl + end + + # force regular ACLs to be present + def insert_default_acl + DEFAULT_ACL.each do |name, acl| + unless rights[acl[:acl]] + Puppet.warning "Inserting default '#{acl[:acl]}' acl because none were found in '%s'" % ( @file || "no file configured") + mk_acl(acl[:acl], acl[:method]) + end + end + # queue an empty (ie deny all) right for every other path + # actually this is not strictly necessary as the rights system + # denies not explicitely allowed paths + rights.newright("/") unless rights["/"] + end + + def mk_acl(path, method = nil) + @rights.newright(path) + @rights.allow(path, "*") + + if method + method = [method] unless method.is_a?(Array) + method.each { |m| @rights.restrict_method(path, m) } + end + end + + def build_uri(request) + "/#{request.indirection_name}/#{request.key}" + end + end +end diff --git a/lib/puppet/network/rest_authorization.rb b/lib/puppet/network/rest_authorization.rb new file mode 100644 index 000000000..3278640fe --- /dev/null +++ b/lib/puppet/network/rest_authorization.rb @@ -0,0 +1,49 @@ +require 'puppet/network/client_request' +require 'puppet/network/rest_authconfig' + +module Puppet::Network + # Most of our subclassing is just so that we can get + # access to information from the request object, like + # the client name and IP address. + class InvalidClientRequest < Puppet::Error; end + module RestAuthorization + + # Create our config object if necessary. If there's no configuration file + # we install our defaults + def authconfig + unless defined? @authconfig + @authconfig = Puppet::Network::RestAuthConfig.main + end + + @authconfig + end + + # Verify that our client has access. We allow untrusted access to + # certificates terminus but no others. + def authorized?(request) + msg = "%s client %s access to %s [%s]" % + [ request.authenticated? ? "authenticated" : "unauthenticated", + (request.node.nil? ? request.ip : "#{request.node}(#{request.ip})"), + request.indirection_name, request.method ] + + if request.authenticated? + res = authenticated_authorized?(request, msg ) + else + res = unauthenticated_authorized?(request, msg) + end + Puppet.notice((res ? "Allowing " : "Denying ") + msg) + return res + end + + # delegate to our authorization file + def authenticated_authorized?(request, msg) + authconfig.allowed?(request) + end + + # allow only certificate requests when not authenticated + def unauthenticated_authorized?(request, msg) + request.indirection_name == :certificate or request.indirection_name == :certificate_request + end + end +end + |