diff options
author | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2006-04-21 19:14:59 +0000 |
---|---|---|
committer | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2006-04-21 19:14:59 +0000 |
commit | a0b4553d3ad64d45c393443d84f0b02298e582b5 (patch) | |
tree | 55b5ff23edc624f7b5b9d988b64466930f095d72 /lib/puppet/server | |
parent | 63cdc6cf6fa2c680173532cbbf989ee97c664e83 (diff) | |
download | puppet-a0b4553d3ad64d45c393443d84f0b02298e582b5.tar.gz puppet-a0b4553d3ad64d45c393443d84f0b02298e582b5.tar.xz puppet-a0b4553d3ad64d45c393443d84f0b02298e582b5.zip |
Final commit before 0.16.0
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@1129 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib/puppet/server')
-rw-r--r-- | lib/puppet/server/authconfig.rb | 166 | ||||
-rwxr-xr-x | lib/puppet/server/authstore.rb | 2 | ||||
-rwxr-xr-x | lib/puppet/server/pelement.rb | 37 | ||||
-rwxr-xr-x | lib/puppet/server/rights.rb | 27 | ||||
-rw-r--r-- | lib/puppet/server/servlet.rb | 25 |
5 files changed, 243 insertions, 14 deletions
diff --git a/lib/puppet/server/authconfig.rb b/lib/puppet/server/authconfig.rb new file mode 100644 index 000000000..05901a207 --- /dev/null +++ b/lib/puppet/server/authconfig.rb @@ -0,0 +1,166 @@ +require 'puppet/parsedfile' +require 'puppet/server/rights' + +module Puppet +class Server + +class ConfigurationError < Puppet::Error; end + +class AuthConfig < Puppet::ParsedFile + Puppet.config.setdefaults(:puppet, + :authconfig => [ "$confdir/namespaceauth.conf", + "The configuration file that defines the rights to the different + namespaces and methods. This can be used as a coarse-grained + authorization system for both ``puppetd`` and ``puppetmasterd``." + ] + ) + + # Just proxy the setting methods to our rights stuff + [:allow, :deny].each do |method| + define_method(method) do |*args| + @rights.send(method, *args) + end + end + + # Here we add a little bit of semantics. They can set auth on a whole namespace + # or on just a single method in the namespace. + def allowed?(name, host, ip) + namespace, method = name.to_s.split(".") + unless namespace and method + raise ArgumentError, "Invalid method name %s" % name + end + + name = name.intern if name.is_a? String + namespace = namespace.intern + method = method.intern + + if @rights.include?(name) + return @rights[name].allowed?(host, ip) + elsif @rights.include?(namespace) + return @rights[namespace].allowed?(host, ip) + else + return false + end + end + + # Does the file exist? Puppetmasterd does not require it, but + # puppetd does. + def exists? + FileTest.exists?(@file) + end + + def initialize(file = nil, parsenow = true) + @file ||= Puppet[:authconfig] + return unless self.exists? + super(file) + @rights = Rights.new + @configstamp = @configtimeout = @configstatted = nil + + if parsenow + read() + end + end + + # Read the configuration file. + def read + return unless FileTest.exists?(@file) + + if @configstamp + if @configtimeout and @configstatted + if Time.now - @configstatted > @configtimeout + @configstatted = Time.now + tmp = File.stat(@file).ctime + + if tmp == @configstamp + return + end + else + return + end + end + end + + parse() + + @configstamp = File.stat(@file).ctime + @configstatted = Time.now + end + + private + + def parse + newrights = Puppet::Server::Rights.new + begin + File.open(@file) { |f| + right = nil + count = 1 + f.each { |line| + case line + when /^\s*#/: next # skip comments + when /^\s*$/: next # skip blank lines + when /\[([\w.]+)\]/: # "namespace" or "namespace.method" + name = $1 + if newrights.include?(name) + raise FileServerError, "%s is already set at %s" % + [newrights[name], name] + end + newrights.newright(name) + right = newrights[name] + when /^\s*(\w+)\s+(.+)$/: + var = $1 + value = $2 + case var + when "allow": + value.split(/\s*,\s*/).each { |val| + begin + right.info "allowing %s access" % val + right.allow(val) + rescue AuthStoreError => detail + raise ConfigurationError, "%s at line %s of %s" % + [detail.to_s, count, @config] + end + } + when "deny": + value.split(/\s*,\s*/).each { |val| + begin + right.info "denying %s access" % val + right.deny(val) + rescue AuthStoreError => detail + raise ConfigurationError, "%s at line %s of %s" % + [detail.to_s, count, @config] + end + } + else + raise ConfigurationError, + "Invalid argument '%s' at line %s" % [var, count] + end + else + raise ConfigurationError, "Invalid line %s: %s" % [count, line] + end + count += 1 + } + } + rescue Errno::EACCES => detail + Puppet.err "Configuration error: Cannot read %s; cannot serve" % @file + #raise Puppet::Error, "Cannot read %s" % @config + rescue Errno::ENOENT => detail + Puppet.err "Configuration error: '%s' does not exit; cannot serve" % + @file + #raise Puppet::Error, "%s does not exit" % @config + #rescue FileServerError => detail + # Puppet.err "FileServer error: %s" % detail + end + + # Verify each of the rights are valid. + # We let the check raise an error, so that it can raise an error + # pointing to the specific problem. + newrights.each { |name, right| + right.valid? + } + @rights = newrights + end +end +end +end + +# $Id$ diff --git a/lib/puppet/server/authstore.rb b/lib/puppet/server/authstore.rb index 70f518f7c..0573a2d36 100755 --- a/lib/puppet/server/authstore.rb +++ b/lib/puppet/server/authstore.rb @@ -15,7 +15,7 @@ class Server [:name, [:hostname, :domain]] ] - Puppet::Util.logmethods(self, false) + Puppet::Util.logmethods(self, true) def allow(pattern) # a simple way to allow anyone at all to connect diff --git a/lib/puppet/server/pelement.rb b/lib/puppet/server/pelement.rb index 9799a36af..b7fe35f7c 100755 --- a/lib/puppet/server/pelement.rb +++ b/lib/puppet/server/pelement.rb @@ -8,10 +8,45 @@ class Server::PElement < Server::Handler attr_accessor :local @interface = XMLRPC::Service::Interface.new("pelementserver") { |iface| + iface.add_method("string apply(string, string)") iface.add_method("string describe(string, string, array, array)") iface.add_method("string list(string, array, string)") } + # Apply a TransBucket as a transaction. + def apply(bucket, format = "yaml", client = nil, clientip = nil) + unless @local + begin + case format + when "yaml": + tmp = YAML::load(CGI.unescape(bucket)) + bucket = tmp + else + raise Puppet::Error, "Unsupported format '%s'" % format + end + rescue => detail + raise Puppet::Error, "Could not load YAML TransBucket: %s" % detail + end + end + + component = bucket.to_type + + # Create a client, but specify the remote machine as the server + # because the class requires it, even though it's unused + client = Puppet::Client::MasterClient.new(:Server => client||"localhost") + + # Set the objects + client.objects = component + + # And then apply the configuration. This way we're reusing all + # the code in there. It should probably just be separated out, though. + transaction = client.apply + + # It'd be nice to return some kind of report, but... at this point + # we have no such facility. + return "success" + end + # Describe a given object. This returns the 'is' values for every state # available on the object type. def describe(type, name, retrieve = nil, ignore = [], format = "yaml", client = nil, clientip = nil) @@ -58,7 +93,7 @@ class Server::PElement < Server::Handler str = nil case format when "yaml": - str = YAML.dump(trans) + str = CGI.escape(YAML::dump(trans)) else raise XMLRPC::FaultException.new( 1, "Unavailable config format %s" % format diff --git a/lib/puppet/server/rights.rb b/lib/puppet/server/rights.rb index cd4b4b978..0ed12a122 100755 --- a/lib/puppet/server/rights.rb +++ b/lib/puppet/server/rights.rb @@ -4,11 +4,13 @@ require 'puppet/server/authstore' module Puppet class Server # Define a set of rights and who has access to them. - class Rights + class Rights < Hash # We basically just proxy directly to our rights. Each Right stores # its own auth abilities. [:allow, :allowed?, :deny].each do |method| define_method(method) do |name, *args| + name = name.intern if name.is_a? String + if obj = right(name) obj.send(method, *args) else @@ -17,18 +19,19 @@ class Server end end - def initialize - @rights = {} + def [](name) + name = name.intern if name.is_a? String + super(name) end # Define a new right to which access can be provided. def newright(name) name = name.intern if name.is_a? String shortname = Right.shortname(name) - if @rights.include? shortname + if self.include? name raise ArgumentError, "Right '%s' is already defined" % name else - @rights[shortname] = Right.new(name, shortname) + self[name] = Right.new(name, shortname) end end @@ -36,13 +39,16 @@ class Server # Retrieve a right by name. def right(name) - @rights[Right.shortname(name)] + name = name.intern if name.is_a? String + self[name] end # A right. class Right < AuthStore attr_accessor :name, :shortname + Puppet::Util.logmethods(self, true) + def self.shortname(name) name.to_s[0..0] end @@ -55,6 +61,15 @@ class Server end super() end + + def to_s + "access[%s]" % @name + end + + # There's no real check to do at this point + def valid? + true + end end end end diff --git a/lib/puppet/server/servlet.rb b/lib/puppet/server/servlet.rb index f0b32efb0..dd34fcd03 100644 --- a/lib/puppet/server/servlet.rb +++ b/lib/puppet/server/servlet.rb @@ -34,15 +34,29 @@ class Server end # Verify that our client has access. We allow untrusted access to - # puppetca methods but none others. + # puppetca methods but no others. def authorize(request, method) namespace = method.sub(/\..+/, '') client = request.peeraddr[2] ip = request.peeraddr[3] if request.client_cert - Servlet.log "Allowing %s(%s) trusted access to %s" % - [client, ip, method] - return true + if @puppetserver.authconfig.exists? + return @puppetserver.authconfig.allowed?(method, client, ip) + else + # This is pretty hackish, but... + # This means we can't actually test this method at this point. + # The next release of Puppet will almost definitely require + # this file to exist or will default to denying all access. + if Puppet.name == "puppetmasterd" or defined? Test::Unit::TestCase + Servlet.log "Allowing %s(%s) trusted access to %s" % + [client, ip, method] + return true + else + Servlet.log "Denying %s(%s) trusted access to %s on %s" % + [client, ip, method, Puppet.name] + return false + end + end else if method =~ /^puppetca\./ Puppet.notice "Allowing %s(%s) untrusted access to CA methods" % @@ -69,8 +83,7 @@ class Server end def initialize(server, handlers) - #Puppet.info server.inspect - + @puppetserver = server # the servlet base class does not consume any arguments # and its BasicServer base class only accepts a 'class_delim' # option which won't change in Puppet at all |