summaryrefslogtreecommitdiffstats
path: root/lib/puppet/server
diff options
context:
space:
mode:
authorluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-04-21 19:14:59 +0000
committerluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-04-21 19:14:59 +0000
commita0b4553d3ad64d45c393443d84f0b02298e582b5 (patch)
tree55b5ff23edc624f7b5b9d988b64466930f095d72 /lib/puppet/server
parent63cdc6cf6fa2c680173532cbbf989ee97c664e83 (diff)
downloadpuppet-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.rb166
-rwxr-xr-xlib/puppet/server/authstore.rb2
-rwxr-xr-xlib/puppet/server/pelement.rb37
-rwxr-xr-xlib/puppet/server/rights.rb27
-rw-r--r--lib/puppet/server/servlet.rb25
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