diff options
| author | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2007-02-08 01:39:39 +0000 |
|---|---|---|
| committer | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2007-02-08 01:39:39 +0000 |
| commit | 7e07e3dc843798bdbc7a03428ca054adaff2fb72 (patch) | |
| tree | 34d0f9f8c2ee11bdc281e6e4d18cad444253fe36 /lib/puppet/server | |
| parent | 6d8068eddd0d29ec53f62557eb53f6ebb8e40591 (diff) | |
| download | puppet-7e07e3dc843798bdbc7a03428ca054adaff2fb72.tar.gz puppet-7e07e3dc843798bdbc7a03428ca054adaff2fb72.tar.xz puppet-7e07e3dc843798bdbc7a03428ca054adaff2fb72.zip | |
Moving all of the client and server code into a single network/ directory. In other words, more code structure cleanup.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@2179 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib/puppet/server')
| -rw-r--r-- | lib/puppet/server/authconfig.rb | 177 | ||||
| -rwxr-xr-x | lib/puppet/server/authstore.rb | 229 | ||||
| -rw-r--r-- | lib/puppet/server/ca.rb | 155 | ||||
| -rwxr-xr-x | lib/puppet/server/filebucket.rb | 169 | ||||
| -rwxr-xr-x | lib/puppet/server/fileserver.rb | 591 | ||||
| -rwxr-xr-x | lib/puppet/server/logger.rb | 54 | ||||
| -rw-r--r-- | lib/puppet/server/master.rb | 214 | ||||
| -rwxr-xr-x | lib/puppet/server/report.rb | 176 | ||||
| -rwxr-xr-x | lib/puppet/server/resource.rb | 191 | ||||
| -rwxr-xr-x | lib/puppet/server/rights.rb | 78 | ||||
| -rwxr-xr-x | lib/puppet/server/runner.rb | 64 | ||||
| -rw-r--r-- | lib/puppet/server/servlet.rb | 277 |
12 files changed, 0 insertions, 2375 deletions
diff --git a/lib/puppet/server/authconfig.rb b/lib/puppet/server/authconfig.rb deleted file mode 100644 index d43371a77..000000000 --- a/lib/puppet/server/authconfig.rb +++ /dev/null @@ -1,177 +0,0 @@ -require 'puppet/util/loadedfile' -require 'puppet/server/rights' - -module Puppet -class Server - -class ConfigurationError < Puppet::Error; end - -class AuthConfig < Puppet::Util::LoadedFile - 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 - - read() - - 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] - - unless @file - raise Puppet::DevError, "No authconfig file defined" - end - return unless self.exists? - super(@file) - @rights = Rights.new - @configstamp = @configstatted = nil - @configtimeout = 60 - - 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 - else - Puppet.notice "%s vs %s" % [tmp, @configstamp] - end - else - return - end - else - Puppet.notice "%s and %s" % [@configtimeout, @configstatted] - 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 deleted file mode 100755 index b0f63b68a..000000000 --- a/lib/puppet/server/authstore.rb +++ /dev/null @@ -1,229 +0,0 @@ -# standard module for determining whether a given hostname or IP has access to -# the requested resource - -require 'ipaddr' - -module Puppet -class Server - class AuthStoreError < Puppet::Error; end - class AuthorizationError < Puppet::Error; end - - class AuthStore - # This has to be an array, not a hash, else it loses its ordering. - ORDER = [ - [:ip, [:ip]], - [:name, [:hostname, :domain]] - ] - - Puppet::Util.logmethods(self, true) - - def allow(pattern) - # a simple way to allow anyone at all to connect - if pattern == "*" - @globalallow = true - else - store(pattern, @allow) - end - end - - def allowed?(name, ip) - if name or ip - # This is probably unnecessary, and can cause some weirdnesses in - # cases where we're operating over localhost but don't have a real - # IP defined. - unless name and ip - raise Puppet::DevError, "Name and IP must be passed to 'allowed?'" - end - # else, we're networked and such - else - # we're local - return true - end - - # yay insecure overrides - if @globalallow - return true - end - - value = nil - ORDER.each { |nametype, array| - if nametype == :ip - value = IPAddr.new(ip) - else - value = name.split(".").reverse - end - - - array.each { |type| - [[@deny, false], [@allow, true]].each { |ary| - hash, retval = ary - if hash.include?(type) - hash[type].each { |pattern| - if match?(nametype, value, pattern) - return retval - end - } - end - } - } - } - - self.info "defaulting to no access for %s" % name - # default to false - return false - end - - def deny(pattern) - store(pattern, @deny) - end - - def initialize - @globalallow = nil - @allow = Hash.new { |hash, key| - hash[key] = [] - } - @deny = Hash.new { |hash, key| - hash[key] = [] - } - end - - private - - def match?(nametype, value, pattern) - if value == pattern # simplest shortcut - return true - end - - case nametype - when :ip: matchip?(value, pattern) - when :name: matchname?(value, pattern) - else - raise Puppet::DevError, "Invalid match type %s" % nametype - end - end - - def matchip?(value, pattern) - # we're just using builtin stuff for this, thankfully - if pattern.include?(value) - return true - else - return false - end - end - - def matchname?(value, pattern) - # yay, horribly inefficient - if pattern[-1] != '*' # the pattern has no metachars and is not equal - # thus, no match - #Puppet.info "%s is not equal with no * in %s" % [value, pattern] - return false - else - # we know the last field of the pattern is '*' - # if everything up to that doesn't match, we're definitely false - if pattern[0..-2] != value[0..pattern.length-2] - #Puppet.notice "subpatterns didn't match; %s vs %s" % - # [pattern[0..-2], value[0..pattern.length-2]] - return false - end - - case value.length <=> pattern.length - when -1: # value is shorter than pattern - if pattern.length - value.length == 1 - # only ever allowed when the value is the domain of a - # splatted pattern - #Puppet.info "allowing splatted domain %s" % [value] - return true - else - return false - end - when 0: # value is the same length as pattern - if pattern[-1] == "*" - #Puppet.notice "same length with *" - return true - else - return false - end - when 1: # value is longer than pattern - # at this point we've already verified that everything up to - # the '*' in the pattern matches, so we are true - return true - end - end - end - - def store(pattern, hash) - type, value = type(pattern) - - if type and value - # this won't work once we get beyond simple stuff... - hash[type] << value - else - raise AuthStoreError, "Invalid pattern %s" % pattern - end - end - - def type(pattern) - type = value = nil - case pattern - when /^(\d+\.){3}\d+$/: - type = :ip - begin - value = IPAddr.new(pattern) - rescue ArgumentError => detail - raise AuthStoreError, "Invalid IP address pattern %s" % pattern - end - when /^(\d+\.){3}\d+\/(\d+)$/: - mask = Integer($2) - if mask < 1 or mask > 32 - raise AuthStoreError, "Invalid IP mask %s" % mask - end - type = :ip - begin - value = IPAddr.new(pattern) - rescue ArgumentError => detail - raise AuthStoreError, "Invalid IP address pattern %s" % pattern - end - when /^(\d+\.){1,3}\*$/: # an ip address with a '*' at the end - type = :ip - match = $1 - match.sub!(".", '') - ary = pattern.split(".") - - mask = case ary.index(match) - when 0: 8 - when 1: 16 - when 2: 24 - else - raise AuthStoreError, "Invalid IP pattern %s" % pattern - end - - ary.pop - while ary.length < 4 - ary.push("0") - end - - begin - value = IPAddr.new(ary.join(".") + "/" + mask.to_s) - rescue ArgumentError => detail - raise AuthStoreError, "Invalid IP address pattern %s" % pattern - end - when /^[\d.]+$/: # necessary so incomplete IP addresses can't look - # like hostnames - raise AuthStoreError, "Invalid IP address pattern %s" % pattern - when /^([a-zA-Z][-\w]*\.)+[-\w]+$/: # a full hostname - type = :hostname - value = pattern.split(".").reverse - when /^\*(\.([a-zA-Z][-\w]*)){1,}$/: - type = :domain - value = pattern.split(".").reverse - else - raise AuthStoreError, "Invalid pattern %s" % pattern - end - - return [type, value] - end - end -end -end -# -# $Id$ diff --git a/lib/puppet/server/ca.rb b/lib/puppet/server/ca.rb deleted file mode 100644 index 10fafc940..000000000 --- a/lib/puppet/server/ca.rb +++ /dev/null @@ -1,155 +0,0 @@ -require 'openssl' -require 'puppet' -require 'puppet/sslcertificates' -require 'xmlrpc/server' - -# Much of this was taken from QuickCert: -# http://segment7.net/projects/ruby/QuickCert/ - -module Puppet -class Server - class CAError < Puppet::Error; end - class CA < Handler - attr_reader :ca - - @interface = XMLRPC::Service::Interface.new("puppetca") { |iface| - iface.add_method("array getcert(csr)") - } - - def autosign - if defined? @autosign - @autosign - else - Puppet[:autosign] - end - end - - # FIXME autosign? should probably accept both hostnames and IP addresses - def autosign?(hostname) - # simple values are easy - if autosign == true or autosign == false - return autosign - end - - # we only otherwise know how to handle files - unless autosign =~ /^\// - raise Puppet::Error, "Invalid autosign value %s" % - autosign.inspect - end - - unless FileTest.exists?(autosign) - unless defined? @@warnedonautosign - @@warnedonautosign = true - Puppet.info "Autosign is enabled but %s is missing" % autosign - end - return false - end - auth = Puppet::Server::AuthStore.new - File.open(autosign) { |f| - f.each { |line| - next if line =~ /^\s*#/ - next if line =~ /^\s*$/ - auth.allow(line.chomp) - } - } - - # for now, just cheat and pass a fake IP address to allowed? - return auth.allowed?(hostname, "127.1.1.1") - end - - def initialize(hash = {}) - Puppet.config.use(:puppet, :certificates, :ca) - if hash.include? :autosign - @autosign = hash[:autosign] - end - - @ca = Puppet::SSLCertificates::CA.new(hash) - end - - # our client sends us a csr, and we either store it for later signing, - # or we sign it right away - def getcert(csrtext, client = nil, clientip = nil) - csr = OpenSSL::X509::Request.new(csrtext) - - # Use the hostname from the CSR, not from the network. - subject = csr.subject - - nameary = subject.to_a.find { |ary| - ary[0] == "CN" - } - - if nameary.nil? - Puppet.err( - "Invalid certificate request: could not retrieve server name" - ) - return "invalid" - end - - hostname = nameary[1] - - unless @ca - Puppet.notice "Host %s asked for signing from non-CA master" % hostname - return "" - end - - # We used to save the public key, but it's basically unnecessary - # and it mucks with the permissions requirements. - # save_pk(hostname, csr.public_key) - - certfile = File.join(Puppet[:certdir], [hostname, "pem"].join(".")) - - # first check to see if we already have a signed cert for the host - cert, cacert = ca.getclientcert(hostname) - if cert and cacert - Puppet.info "Retrieving existing certificate for %s" % hostname - #Puppet.info "Cert: %s; Cacert: %s" % [cert.class, cacert.class] - return [cert.to_pem, cacert.to_pem] - elsif @ca - if self.autosign?(hostname) or client.nil? - if client.nil? - Puppet.info "Signing certificate for CA server" - end - # okay, we don't have a signed cert - # if we're a CA and autosign is turned on, then go ahead and sign - # the csr and return the results - Puppet.info "Signing certificate for %s" % hostname - cert, cacert = @ca.sign(csr) - #Puppet.info "Cert: %s; Cacert: %s" % [cert.class, cacert.class] - return [cert.to_pem, cacert.to_pem] - else # just write out the csr for later signing - if @ca.getclientcsr(hostname) - Puppet.info "Not replacing existing request from %s" % hostname - else - Puppet.notice "Host %s has a waiting certificate request" % - hostname - @ca.storeclientcsr(csr) - end - return ["", ""] - end - else - raise "huh?" - end - end - - private - - # Save the public key. - def save_pk(hostname, public_key) - pkeyfile = File.join(Puppet[:publickeydir], [hostname, "pem"].join('.')) - - if FileTest.exists?(pkeyfile) - currentkey = File.open(pkeyfile) { |k| k.read } - unless currentkey == public_key.to_s - raise Puppet::Error, "public keys for %s differ" % hostname - end - else - File.open(pkeyfile, "w", 0644) { |f| - f.print public_key.to_s - } - end - end - end -end -end - -# $Id$ diff --git a/lib/puppet/server/filebucket.rb b/lib/puppet/server/filebucket.rb deleted file mode 100755 index 56d994366..000000000 --- a/lib/puppet/server/filebucket.rb +++ /dev/null @@ -1,169 +0,0 @@ -#-------------------- -# accept and serve files - - -require 'webrick' -require 'xmlrpc/server' -require 'xmlrpc/client' -require 'facter' -require 'digest/md5' -require 'puppet/external/base64' - -module Puppet -class Server - class BucketError < RuntimeError; end - class FileBucket < Handler - Puppet.config.setdefaults("puppetmasterd", - :bucketdir => { - :default => "$vardir/bucket", - :mode => 0750, - :owner => "$user", - :group => "$group", - :desc => "Where FileBucket files are stored." - } - ) - - Puppet.config.setdefaults("filebucket", - :clientbucketdir => { - :default => "$vardir/clientbucket", - :mode => 0750, - :desc => "Where FileBucket files are stored locally." - } - ) - @interface = XMLRPC::Service::Interface.new("puppetbucket") { |iface| - iface.add_method("string addfile(string, string)") - iface.add_method("string getfile(string)") - } - - Puppet::Util.logmethods(self, true) - attr_reader :name, :path - - # this doesn't work for relative paths - def FileBucket.paths(base,md5) - return [ - File.join(base, md5), - File.join(base, md5, "contents"), - File.join(base, md5, "paths") - ] - end - - def initialize(hash) - if hash.include?(:ConflictCheck) - @conflictchk = hash[:ConflictCheck] - hash.delete(:ConflictCheck) - else - @conflictchk = true - end - - if hash.include?(:Path) - @path = hash[:Path] - hash.delete(:Path) - else - if defined? Puppet - @path = Puppet[:bucketdir] - else - @path = File.expand_path("~/.filebucket") - end - end - - Puppet.config.use(:filebucket) - - @name = "Filebucket[#{@path}]" - end - - # accept a file from a client - def addfile(contents, path, client = nil, clientip = nil) - if client - contents = Base64.decode64(contents) - end - md5 = Digest::MD5.hexdigest(contents) - - bpath, bfile, pathpath = FileBucket.paths(@path,md5) - - # if it's a new directory... - if Puppet.recmkdir(bpath) - msg = "Adding %s(%s)" % [path, md5] - msg += " from #{client}" if client - self.info msg - # ...then just create the file - File.open(bfile, File::WRONLY|File::CREAT, 0440) { |of| - of.print contents - } - else # if the dir already existed... - # ...we need to verify that the contents match the existing file - if @conflictchk - unless FileTest.exists?(bfile) - raise(BucketError, - "No file at %s for sum %s" % [bfile,md5], caller) - end - - curfile = File.read(bfile) - - # If the contents don't match, then we've found a conflict. - # Unlikely, but quite bad. - if curfile != contents - raise(BucketError, - "Got passed new contents for sum %s" % md5, caller) - else - msg = "Got duplicate %s(%s)" % [path, md5] - msg += " from #{client}" if client - self.info msg - end - end - end - - contents = "" - - # in either case, add the passed path to the list of paths - paths = nil - addpath = false - if FileTest.exists?(pathpath) - File.open(pathpath) { |of| - paths = of.readlines.collect { |l| l.chomp } - } - - # unless our path is already there... - unless paths.include?(path) - addpath = true - end - else - addpath = true - end - - # if it's a new file, or if our path isn't in the file yet, add it - if addpath - File.open(pathpath, File::WRONLY|File::CREAT|File::APPEND) { |of| - of.puts path - } - end - - return md5 - end - - def getfile(md5, client = nil, clientip = nil) - bpath, bfile, bpaths = FileBucket.paths(@path,md5) - - unless FileTest.exists?(bfile) - return false - end - - contents = nil - File.open(bfile) { |of| - contents = of.read - } - - if client - return Base64.encode64(contents) - else - return contents - end - end - - def to_s - self.name - end - end -end -end -# -# $Id$ diff --git a/lib/puppet/server/fileserver.rb b/lib/puppet/server/fileserver.rb deleted file mode 100755 index 3ea44d785..000000000 --- a/lib/puppet/server/fileserver.rb +++ /dev/null @@ -1,591 +0,0 @@ -require 'puppet' -require 'webrick/httpstatus' -require 'cgi' -require 'delegate' - -module Puppet -class FileServerError < Puppet::Error; end -class Server - class FileServer < Handler - attr_accessor :local - - Puppet.setdefaults("fileserver", - :fileserverconfig => ["$confdir/fileserver.conf", - "Where the fileserver configuration is stored."]) - - CHECKPARAMS = [:mode, :type, :owner, :group, :checksum] - - @interface = XMLRPC::Service::Interface.new("fileserver") { |iface| - iface.add_method("string describe(string, string)") - iface.add_method("string list(string, string, boolean, array)") - iface.add_method("string retrieve(string, string)") - } - - # Describe a given file. This returns all of the manageable aspects - # of that file. - def describe(url, links = :ignore, client = nil, clientip = nil) - links = links.intern if links.is_a? String - - if links == :manage - raise Puppet::FileServerError, "Cannot currently copy links" - end - - mount, path = convert(url, client, clientip) - - if client - mount.debug "Describing %s for %s" % [url, client] - end - - obj = nil - unless obj = mount.check(path, links) - return "" - end - - desc = [] - CHECKPARAMS.each { |check| - if property = obj.property(check) - unless property.is - mount.debug "Manually retrieving info for %s" % check - property.retrieve - end - desc << property.is - else - if check == "checksum" and obj.property(:type).is == "file" - mount.notice "File %s does not have data for %s" % - [obj.name, check] - end - desc << nil - end - } - - return desc.join("\t") - end - - # Create a new fileserving module. - def initialize(hash = {}) - @mounts = {} - @files = {} - - if hash[:Local] - @local = hash[:Local] - else - @local = false - end - - if hash[:Config] == false - @noreadconfig = true - else - @config = Puppet::Util::LoadedFile.new( - hash[:Config] || Puppet[:fileserverconfig] - ) - @noreadconfig = false - end - - if hash.include?(:Mount) - @passedconfig = true - unless hash[:Mount].is_a?(Hash) - raise Puppet::DevError, "Invalid mount hash %s" % - hash[:Mount].inspect - end - - hash[:Mount].each { |dir, name| - if FileTest.exists?(dir) - self.mount(dir, name) - end - } - else - @passedconfig = false - readconfig(false) # don't check the file the first time. - end - end - - # List a specific directory's contents. - def list(url, links = :ignore, recurse = false, ignore = false, client = nil, clientip = nil) - mount, path = convert(url, client, clientip) - - if client - mount.debug "Listing %s for %s" % [url, client] - end - - obj = nil - unless FileTest.exists?(path) - return "" - end - - # We pass two paths here, but reclist internally changes one - # of the arguments when called internally. - desc = reclist(mount, path, path, recurse, ignore) - - if desc.length == 0 - mount.notice "Got no information on //%s/%s" % - [mount, path] - return "" - end - - desc.collect { |sub| - sub.join("\t") - }.join("\n") - end - - def local? - self.local - end - - # Mount a new directory with a name. - def mount(path, name) - if @mounts.include?(name) - if @mounts[name] != path - raise FileServerError, "%s is already mounted at %s" % - [@mounts[name].path, name] - else - # it's already mounted; no problem - return - end - end - - # Let the mounts do their own error-checking. - @mounts[name] = Mount.new(name, path) - @mounts[name].info "Mounted %s" % path - - return @mounts[name] - end - - # Retrieve a file from the local disk and pass it to the remote - # client. - def retrieve(url, links = :ignore, client = nil, clientip = nil) - links = links.intern if links.is_a? String - - mount, path = convert(url, client, clientip) - - if client - mount.info "Sending %s to %s" % [url, client] - end - - unless FileTest.exists?(path) - return "" - end - - links = links.intern if links.is_a? String - - if links == :ignore and FileTest.symlink?(path) - return "" - end - - str = nil - if links == :manage - raise Puppet::Error, "Cannot copy links yet." - else - str = File.read(path) - end - - if @local - return str - else - return CGI.escape(str) - end - end - - def umount(name) - @mounts.delete(name) if @mounts.include? name - end - - private - - def authcheck(file, mount, client, clientip) - # If we're local, don't bother passing in information. - if local? - client = nil - clientip = nil - end - unless mount.allowed?(client, clientip) - mount.warning "%s cannot access %s" % - [client, file] - raise Puppet::Server::AuthorizationError, "Cannot access %s" % mount - end - end - - def convert(url, client, clientip) - readconfig - - url = URI.unescape(url) - - mount, stub = splitpath(url, client) - - authcheck(url, mount, client, clientip) - - path = nil - unless path = mount.subdir(stub, client) - mount.notice "Could not find subdirectory %s" % - "//%s/%s" % [mount, stub] - return "" - end - - return mount, path - end - - # Deal with ignore parameters. - def handleignore(children, path, ignore) - ignore.each { |ignore| - Dir.glob(File.join(path,ignore), File::FNM_DOTMATCH) { |match| - children.delete(File.basename(match)) - } - } - return children - end - - # Read the configuration file. - def readconfig(check = true) - return if @noreadconfig - - if check and ! @config.changed? - return - end - - newmounts = {} - begin - File.open(@config.file) { |f| - mount = nil - count = 1 - f.each { |line| - case line - when /^\s*#/: next # skip comments - when /^\s*$/: next # skip blank lines - when /\[(\w+)\]/: - name = $1 - if newmounts.include?(name) - raise FileServerError, "%s is already mounted at %s" % - [newmounts[name], name], count, @config.file - end - mount = Mount.new(name) - newmounts[name] = mount - when /^\s*(\w+)\s+(.+)$/: - var = $1 - value = $2 - case var - when "path": - begin - mount.path = value - rescue FileServerError => detail - Puppet.err "Removing mount %s: %s" % - [mount.name, detail] - newmounts.delete(mount.name) - end - when "allow": - value.split(/\s*,\s*/).each { |val| - begin - mount.info "allowing %s access" % val - mount.allow(val) - rescue AuthStoreError => detail - raise FileServerError.new(detail.to_s, - count, @config.file) - end - } - when "deny": - value.split(/\s*,\s*/).each { |val| - begin - mount.info "denying %s access" % val - mount.deny(val) - rescue AuthStoreError => detail - raise FileServerError.new(detail.to_s, - count, @config.file) - end - } - else - raise FileServerError.new("Invalid argument '%s'" % var, - count, @config.file) - end - else - raise FileServerError.new("Invalid line '%s'" % line.chomp, - count, @config.file) - end - count += 1 - } - } - rescue Errno::EACCES => detail - Puppet.err "FileServer error: Cannot read %s; cannot serve" % @config - #raise Puppet::Error, "Cannot read %s" % @config - rescue Errno::ENOENT => detail - Puppet.err "FileServer error: '%s' does not exist; cannot serve" % - @config - #raise Puppet::Error, "%s does not exit" % @config - #rescue FileServerError => detail - # Puppet.err "FileServer error: %s" % detail - end - - # Verify each of the mounts are valid. - # We let the check raise an error, so that it can raise an error - # pointing to the specific problem. - newmounts.each { |name, mount| - unless mount.valid? - raise FileServerError, "No path specified for mount %s" % - name - end - } - @mounts = newmounts - end - - # Recursively list the directory. FIXME This should be using - # puppet objects, not directly listing. - def reclist(mount, root, path, recurse, ignore) - # Take out the root of the path. - name = path.sub(root, '') - if name == "" - name = "/" - end - - if name == path - raise FileServerError, "Could not match %s in %s" % - [root, path] - end - - desc = [name] - ftype = File.stat(path).ftype - - desc << ftype - if recurse.is_a?(Integer) - recurse -= 1 - end - - ary = [desc] - if recurse == true or (recurse.is_a?(Integer) and recurse > -1) - if ftype == "directory" - children = Dir.entries(path) - if ignore - children = handleignore(children, path, ignore) - end - children.each { |child| - next if child =~ /^\.\.?$/ - reclist(mount, root, File.join(path, child), recurse, ignore).each { |cobj| - ary << cobj - } - } - end - end - - return ary.reject { |c| c.nil? } - end - - # Split the path into the separate mount point and path. - def splitpath(dir, client) - # the dir is based on one of the mounts - # so first retrieve the mount path - mount = nil - path = nil - if dir =~ %r{/(\w+)/?} - tmp = $1 - path = dir.sub(%r{/#{tmp}/?}, '') - - unless mount = @mounts[tmp] - raise FileServerError, "Fileserver module '%s' not mounted" % tmp - end - else - raise FileServerError, "Fileserver error: Invalid path '%s'" % dir - end - - if path == "" - path = nil - else - # Remove any double slashes that might have occurred - path = URI.unescape(path.gsub(/\/\//, "/")) - end - - return mount, path - end - - def to_s - "fileserver" - end - - # A simple class for wrapping mount points. Instances of this class - # don't know about the enclosing object; they're mainly just used for - # authorization. - class Mount < AuthStore - attr_reader :name - - Puppet::Util.logmethods(self, true) - - # Run 'retrieve' on a file. This gets the actual parameters, so - # we can pass them to the client. - def check(dir, links) - unless FileTest.exists?(dir) - self.notice "File source %s does not exist" % dir - return nil - end - - obj = fileobj(dir, links) - - # FIXME we should really have a timeout here -- we don't - # want to actually check on every connection, maybe no more - # than every 60 seconds or something. It'd be nice if we - # could use the builtin scheduling to do this. - - # Retrieval is enough here, because we don't want to cache - # any information in the state file, and we don't want to generate - # any state changes or anything. We don't even need to sync - # the checksum, because we're always going to hit the disk - # directly. - obj.retrieve - - return obj - end - - # Create a map for a specific client. - def clientmap(client) - { - "h" => client.sub(/\..*$/, ""), - "H" => client, - "d" => client.sub(/[^.]+\./, "") # domain name - } - end - - # Replace % patterns as appropriate. - def expand(path, client = nil) - # This map should probably be moved into a method. - map = nil - - if client - map = clientmap(client) - else - Puppet.notice "No client; expanding '%s' with local host" % - path - # Else, use the local information - map = localmap() - end - path.gsub(/%(.)/) do |v| - key = $1 - if key == "%" - "%" - else - map[key] || v - end - end - end - - # Do we have any patterns in our path, yo? - def expandable? - if defined? @expandable - @expandable - else - false - end - end - - # Create out object. It must have a name. - def initialize(name, path = nil) - unless name =~ %r{^\w+$} - raise FileServerError, "Invalid name format '%s'" % name - end - @name = name - - if path - self.path = path - else - @path = nil - end - - super() - end - - def fileobj(path, links) - obj = nil - if obj = Puppet.type(:file)[path] - # This can only happen in local fileserving, but it's an - # important one. It'd be nice if we didn't just set - # the check params every time, but I'm not sure it's worth - # the effort. - obj[:check] = CHECKPARAMS - else - obj = Puppet.type(:file).create( - :name => path, - :check => CHECKPARAMS - ) - end - - if links == :manage - links = :follow - end - - # This, ah, might be completely redundant - unless obj[:links] == links - obj[:links] = links - end - - return obj - end - - # Cache this manufactured map, since if it's used it's likely - # to get used a lot. - def localmap - unless defined? @@localmap - @@localmap = { - "h" => Facter.value("hostname"), - "H" => [Facter.value("hostname"), - Facter.value("domain")].join("."), - "d" => Facter.value("domain") - } - end - @@localmap - end - - # Return the path as appropriate, expanding as necessary. - def path(client = nil) - if expandable? - return expand(@path, client) - else - return @path - end - end - - # Set the path. - def path=(path) - # FIXME: For now, just don't validate paths with replacement - # patterns in them. - if path =~ /%./ - # Mark that we're expandable. - @expandable = true - else - unless FileTest.exists?(path) - raise FileServerError, "%s does not exist" % path - end - unless FileTest.directory?(path) - raise FileServerError, "%s is not a directory" % path - end - unless FileTest.readable?(path) - raise FileServerError, "%s is not readable" % path - end - @expandable = false - end - @path = path - end - - # Retrieve a specific directory relative to a mount point. - # If they pass in a client, then expand as necessary. - def subdir(dir = nil, client = nil) - basedir = self.path(client) - - dirname = if dir - File.join(basedir, dir.split("/").join(File::SEPARATOR)) - else - basedir - end - - dirname - end - - def to_s - "mount[#{@name}]" - end - - # Verify our configuration is valid. This should really check to - # make sure at least someone will be allowed, but, eh. - def valid? - return false unless @path - - return true - end - end - end -end -end - -# $Id$ diff --git a/lib/puppet/server/logger.rb b/lib/puppet/server/logger.rb deleted file mode 100755 index aa3521573..000000000 --- a/lib/puppet/server/logger.rb +++ /dev/null @@ -1,54 +0,0 @@ -require 'yaml' - -module Puppet -class Server - class LoggerError < RuntimeError; end - - # Receive logs from remote hosts. - class Logger < Handler - @interface = XMLRPC::Service::Interface.new("puppetlogger") { |iface| - iface.add_method("void addlog(string)") - } - - # accept a log message from a client, and route it accordingly - def addlog(message, client = nil, clientip = nil) - unless message - raise Puppet::DevError, "Did not receive message" - end - - Puppet.info message.inspect - # if the client is set, then we're not local - if client - begin - message = YAML.load(CGI.unescape(message)) - #message = message - rescue => detail - raise XMLRPC::FaultException.new( - 1, "Could not unYAML log message from %s" % client - ) - end - end - - unless message - raise Puppet::DevError, "Could not resurrect message" - end - - # Mark it as remote, so it's not sent to syslog - message.remote = true - - if client - if ! message.source or message.source == "Puppet" - message.source = client - end - end - - Puppet::Util::Log.newmessage(message) - - # This is necessary or XMLRPC gets all pukey - return "" - end - end -end -end - -# $Id$ diff --git a/lib/puppet/server/master.rb b/lib/puppet/server/master.rb deleted file mode 100644 index cda6027d0..000000000 --- a/lib/puppet/server/master.rb +++ /dev/null @@ -1,214 +0,0 @@ -require 'openssl' -require 'puppet' -require 'puppet/parser/interpreter' -require 'puppet/sslcertificates' -require 'xmlrpc/server' -require 'yaml' - -module Puppet -class Server - class MasterError < Puppet::Error; end - class Master < Handler - include Puppet::Util - - attr_accessor :ast, :local - attr_reader :ca - - @interface = XMLRPC::Service::Interface.new("puppetmaster") { |iface| - iface.add_method("string getconfig(string)") - iface.add_method("int freshness()") - } - - # FIXME At some point, this should be autodocumenting. - def addfacts(facts) - # Add our server version to the fact list - facts["serverversion"] = Puppet.version.to_s - - # And then add the server name and IP - {"servername" => "hostname", - "serverip" => "ipaddress" - }.each do |var, fact| - if obj = Facter[fact] - facts[var] = obj.value - else - Puppet.warning "Could not retrieve fact %s" % fact - end - end - end - - # Manipulate the client name as appropriate. - def clientname(name, ip, facts) - # Always use the hostname from Facter. - client = facts["hostname"] - clientip = facts["ipaddress"] - if Puppet[:node_name] == 'cert' - if name - client = name - end - if ip - clientip = ip - end - end - - return client, clientip - end - - # Tell a client whether there's a fresh config for it - def freshness(client = nil, clientip = nil) - if Puppet.features.rails? and Puppet[:storeconfigs] - host = Puppet::Rails::Host.find_or_create_by_name(client) - host.last_freshcheck = Time.now - if clientip and (! host.ip or host.ip == "") - host.ip = clientip - end - host.save - end - if defined? @interpreter - return @interpreter.parsedate - else - return 0 - end - end - - def initialize(hash = {}) - args = {} - - # Allow specification of a code snippet or of a file - if code = hash[:Code] - args[:Code] = code - else - args[:Manifest] = hash[:Manifest] || Puppet[:manifest] - end - - if hash[:Local] - @local = hash[:Local] - else - @local = false - end - - args[:Local] = @local - - if hash.include?(:CA) and hash[:CA] - @ca = Puppet::SSLCertificates::CA.new() - else - @ca = nil - end - - Puppet.debug("Creating interpreter") - - if hash.include?(:UseNodes) - args[:UseNodes] = hash[:UseNodes] - elsif @local - args[:UseNodes] = false - end - - # This is only used by the cfengine module, or if --loadclasses was - # specified in +puppet+. - if hash.include?(:Classes) - args[:Classes] = hash[:Classes] - end - - @interpreter = Puppet::Parser::Interpreter.new(args) - end - - def getconfig(facts, format = "marshal", client = nil, clientip = nil) - if @local - # we don't need to do anything, since we should already - # have raw objects - Puppet.debug "Our client is local" - else - Puppet.debug "Our client is remote" - - # XXX this should definitely be done in the protocol, somehow - case format - when "marshal": - Puppet.warning "You should upgrade your client. 'Marshal' will not be supported much longer." - begin - facts = Marshal::load(CGI.unescape(facts)) - rescue => detail - raise XMLRPC::FaultException.new( - 1, "Could not rebuild facts" - ) - end - when "yaml": - begin - facts = YAML.load(CGI.unescape(facts)) - rescue => detail - raise XMLRPC::FaultException.new( - 1, "Could not rebuild facts" - ) - end - else - raise XMLRPC::FaultException.new( - 1, "Unavailable config format %s" % format - ) - end - end - - client, clientip = clientname(client, clientip, facts) - - # Add any server-side facts to our server. - addfacts(facts) - - retobjects = nil - - # This is hackish, but there's no "silence" option for benchmarks - # right now - if @local - #begin - retobjects = @interpreter.run(client, facts) - #rescue Puppet::Error => detail - # Puppet.err detail - # raise XMLRPC::FaultException.new( - # 1, detail.to_s - # ) - #rescue => detail - # Puppet.err detail.to_s - # return "" - #end - else - benchmark(:notice, "Compiled configuration for %s" % client) do - begin - retobjects = @interpreter.run(client, facts) - rescue Puppet::Error => detail - Puppet.err detail - raise XMLRPC::FaultException.new( - 1, detail.to_s - ) - rescue => detail - Puppet.err detail.to_s - return "" - end - end - end - - if @local - return retobjects - else - str = nil - case format - when "marshal": - str = Marshal::dump(retobjects) - when "yaml": - str = YAML.dump(retobjects) - else - raise XMLRPC::FaultException.new( - 1, "Unavailable config format %s" % format - ) - end - return CGI.escape(str) - end - end - - def local? - if defined? @local and @local - return true - else - return false - end - end - end -end -end - -# $Id$ diff --git a/lib/puppet/server/report.rb b/lib/puppet/server/report.rb deleted file mode 100755 index 4298f8ee6..000000000 --- a/lib/puppet/server/report.rb +++ /dev/null @@ -1,176 +0,0 @@ -module Puppet -class Server - # A simple server for triggering a new run on a Puppet client. - class Report < Handler - class << self - include Puppet::Util::ClassGen - end - - module ReportBase - include Puppet::Util::Docs - attr_writer :useyaml - - def useyaml? - if defined? @useyaml - @useyaml - else - false - end - end - end - - @interface = XMLRPC::Service::Interface.new("puppetreports") { |iface| - iface.add_method("string report(array)") - } - - Puppet.setdefaults(:reporting, - :reports => ["store", - "The list of reports to generate. All reports are looked for - in puppet/reports/<name>.rb, and multiple report names should be - comma-separated (whitespace is okay)." - ], - :reportdir => {:default => "$vardir/reports", - :mode => 0750, - :owner => "$user", - :group => "$group", - :desc => "The directory in which to store reports - received from the client. Each client gets a separate - subdirectory."} - ) - - @reports = {} - @reportloader = Puppet::Util::Autoload.new(self, "puppet/reports") - - class << self - attr_reader :hooks - end - - # Add a new report type. - def self.newreport(name, options = {}, &block) - name = symbolize(name) - - mod = genmodule(name, :extend => ReportBase, :hash => @reports, :block => block) - - if options[:useyaml] - mod.useyaml = true - end - - mod.send(:define_method, :report_name) do - name - end - end - - # Load a report. - def self.report(name) - name = name.intern if name.is_a? String - unless @reports.include? name - if @reportloader.load(name) - unless @reports.include? name - Puppet.warning( - "Loaded report file for %s but report was not defined" % - name - ) - return nil - end - else - return nil - end - end - @reports[symbolize(name)] - end - - def self.reportdocs - docs = "" - - # Use this method so they all get loaded - reports.sort { |a,b| a.to_s <=> b.to_s }.each do |name| - mod = self.report(name) - docs += "## %s\n\n" % name - - docs += Puppet::Util::Docs.scrub(mod.doc) + "\n\n" - end - - docs - end - - def self.reports - @reportloader.loadall - @reports.keys - end - - def initialize(*args) - super - Puppet.config.use(:reporting) - Puppet.config.use(:metrics) - end - - # Accept a report from a client. - def report(report, client = nil, clientip = nil) - # Unescape the report - unless @local - report = CGI.unescape(report) - end - - begin - process(report) - rescue => detail - Puppet.err "Could not process report %s: %s" % [$1, detail] - if Puppet[:trace] - puts detail.backtrace - end - end - end - - private - - # Process the report using all of the existing hooks. - def process(yaml) - return if Puppet[:reports] == "none" - - # First convert the report to real objects - begin - report = YAML.load(yaml) - rescue => detail - Puppet.warning "Could not load report: %s" % detail - return - end - - # Used for those reports that accept yaml - client = report.host - - reports().each do |name| - if mod = self.class.report(name) - Puppet.info "Processing report %s for %s" % [name, client] - - # We have to use a dup because we're including a module in the - # report. - newrep = report.dup - begin - newrep.extend(mod) - if mod.useyaml? - newrep.process(yaml) - else - newrep.process - end - rescue => detail - if Puppet[:trace] - puts detail.backtrace - end - Puppet.err "Report %s failed: %s" % - [name, detail] - end - else - Puppet.warning "No report named '%s'" % name - end - end - end - - # Handle the parsing of the reports attribute. - def reports - Puppet[:reports].gsub(/(^\s+)|(\s+$)/, '').split(/\s*,\s*/) - end - end -end -end - -# $Id$ diff --git a/lib/puppet/server/resource.rb b/lib/puppet/server/resource.rb deleted file mode 100755 index d2bad52f3..000000000 --- a/lib/puppet/server/resource.rb +++ /dev/null @@ -1,191 +0,0 @@ -require 'puppet' -require 'puppet/server' - -module Puppet - -# Serve Puppet elements. Useful for querying, copying, and, um, other stuff. -class Server::Resource < Server::Handler - attr_accessor :local - - @interface = XMLRPC::Service::Interface.new("resource") { |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": - bucket = YAML::load(Base64.decode64(bucket)) - 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 - - # And then clean up - component.remove - - # 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 property - # available on the object type. - def describe(type, name, retrieve = nil, ignore = [], format = "yaml", client = nil, clientip = nil) - Puppet.info "Describing %s[%s]" % [type.to_s.capitalize, name] - @local = true unless client - typeklass = nil - unless typeklass = Puppet.type(type) - raise Puppet::Error, "Puppet type %s is unsupported" % type - end - - obj = nil - - retrieve ||= :all - ignore ||= [] - - if obj = typeklass[name] - obj[:check] = retrieve - else - begin - obj = typeklass.create(:name => name, :check => retrieve) - rescue Puppet::Error => detail - raise Puppet::Error, "%s[%s] could not be created: %s" % - [type, name, detail] - end - end - - unless obj - raise XMLRPC::FaultException.new( - 1, "Could not create %s[%s]" % [type, name] - ) - end - - trans = obj.to_trans - - # Now get rid of any attributes they specifically don't want - ignore.each do |st| - if trans.include? st - trans.delete(st) - end - end - - # And get rid of any attributes that are nil - trans.each do |attr, value| - if value.nil? - trans.delete(attr) - end - end - - unless @local - case format - when "yaml": - trans = Base64.encode64(YAML::dump(trans)) - else - raise XMLRPC::FaultException.new( - 1, "Unavailable config format %s" % format - ) - end - end - - return trans - end - - # Create a new fileserving module. - def initialize(hash = {}) - if hash[:Local] - @local = hash[:Local] - else - @local = false - end - end - - # List all of the elements of a given type. - def list(type, ignore = [], base = nil, format = "yaml", client = nil, clientip = nil) - @local = true unless client - typeklass = nil - unless typeklass = Puppet.type(type) - raise Puppet::Error, "Puppet type %s is unsupported" % type - end - - # They can pass in false - ignore ||= [] - ignore = [ignore] unless ignore.is_a? Array - bucket = TransBucket.new - bucket.type = typeklass.name - - typeklass.list.each do |obj| - next if ignore.include? obj.name - - object = TransObject.new(obj.name, typeklass.name) - bucket << object - end - - unless @local - case format - when "yaml": - begin - bucket = Base64.encode64(YAML::dump(bucket)) - rescue => detail - Puppet.err detail - raise XMLRPC::FaultException.new( - 1, detail.to_s - ) - end - else - raise XMLRPC::FaultException.new( - 1, "Unavailable config format %s" % format - ) - end - end - - return bucket - end - - private - - def authcheck(file, mount, client, clientip) - unless mount.allowed?(client, clientip) - mount.warning "%s cannot access %s" % - [client, file] - raise Puppet::Server::AuthorizationError, "Cannot access %s" % mount - end - end - - # Deal with ignore parameters. - def handleignore(children, path, ignore) - ignore.each { |ignore| - Dir.glob(File.join(path,ignore), File::FNM_DOTMATCH) { |match| - children.delete(File.basename(match)) - } - } - return children - end - - def to_s - "resource" - end -end -end - -# $Id$ diff --git a/lib/puppet/server/rights.rb b/lib/puppet/server/rights.rb deleted file mode 100755 index 0ed12a122..000000000 --- a/lib/puppet/server/rights.rb +++ /dev/null @@ -1,78 +0,0 @@ -require 'ipaddr' -require 'puppet/server/authstore' - -module Puppet -class Server - # Define a set of rights and who has access to them. - 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 - raise ArgumentError, "Unknown right '%s'" % name - end - end - end - - 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 self.include? name - raise ArgumentError, "Right '%s' is already defined" % name - else - self[name] = Right.new(name, shortname) - end - end - - private - - # Retrieve a right by name. - def right(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 - - def initialize(name, shortname = nil) - @name = name - @shortname = shortname - unless @shortname - @shortname = Right.shortname(name) - 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 -end -# -# $Id$ diff --git a/lib/puppet/server/runner.rb b/lib/puppet/server/runner.rb deleted file mode 100755 index 46fd7a7ae..000000000 --- a/lib/puppet/server/runner.rb +++ /dev/null @@ -1,64 +0,0 @@ -module Puppet -class Server - class MissingMasterError < RuntimeError # Cannot find the master client - end - # A simple server for triggering a new run on a Puppet client. - class Runner < Handler - @interface = XMLRPC::Service::Interface.new("puppetrunner") { |iface| - iface.add_method("string run(string, string)") - } - - # Run the client configuration right now, optionally specifying - # tags and whether to ignore schedules - def run(tags = nil, ignoreschedules = false, fg = true, client = nil, clientip = nil) - # We need to retrieve the client - master = Puppet::Client::MasterClient.instance - - unless master - raise MissingMasterError, "Could not find the master client" - end - - if Puppet::Util::Pidlock.new(Puppet[:puppetdlockfile]).locked? - Puppet.notice "Could not trigger run; already running" - return "running" - end - - if tags == "" - tags = nil - end - - if ignoreschedules == "" - ignoreschedules == nil - end - - msg = "" - if client - msg = "%s(%s) " % [client, clientip] - end - msg += "triggered run" % - if tags - msg += " with tags %s" % tags - end - - if ignoreschedules - msg += " without schedules" - end - - Puppet.notice msg - - # And then we need to tell it to run, with this extra info. - if fg - master.run(tags, ignoreschedules) - else - Puppet.newthread do - master.run(tags, ignoreschedules) - end - end - - return "success" - end - end -end -end - -# $Id$ diff --git a/lib/puppet/server/servlet.rb b/lib/puppet/server/servlet.rb deleted file mode 100644 index 81219ef44..000000000 --- a/lib/puppet/server/servlet.rb +++ /dev/null @@ -1,277 +0,0 @@ -require 'xmlrpc/server' - -module Puppet -class Server - class ServletError < RuntimeError; end - class Servlet < XMLRPC::WEBrickServlet - ERR_UNAUTHORIZED = 30 - - attr_accessor :request - - # this is just a duplicate of the normal method; it's here for - # debugging when i need it - def self.get_instance(server, *options) - self.new(server, *options) - end - - # This is a hackish way to avoid an auth message every time we have a - # normal operation - def self.log(msg) - unless defined? @logs - @logs = {} - end - if @logs.include?(msg) - @logs[msg] += 1 - else - Puppet.info msg - @logs[msg] = 1 - end - end - - def add_handler(interface, handler) - @loadedhandlers << interface.prefix - super - end - - # Verify that our client has access. We allow untrusted access to - # puppetca methods but no others. - def authorize(request, method) - namespace = method.sub(/\..+/, '') - client = request.peeraddr[2] - if defined? @client and @client - client = @client - end - ip = request.peeraddr[3] - if request.client_cert - if @puppetserver.authconfig.exists? - allowed = @puppetserver.authconfig.allowed?(method, client, ip) - - if allowed - Puppet.debug "Allowing %s(%s) trusted access to %s" % - [client, ip, method] - return true - else - Puppet.debug "Denying %s(%s) trusted access to %s" % - [client, ip, method] - return false - end - 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 - Puppet.debug "Allowing %s(%s) trusted access to %s" % - [client, ip, method] - return true - else - Puppet.debug "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" % - [client, ip] - else - Puppet.err "Unauthenticated client %s(%s) cannot call %s" % - [client, ip, method] - return false - end - end - end - - def available?(method) - namespace = method.sub(/\..+/, '') - client = request.peeraddr[2] - ip = request.peeraddr[3] - if @loadedhandlers.include?(namespace) - return true - else - Puppet.warning "Client %s(%s) requested unavailable functionality %s" % - [client, ip, namespace] - return false - end - end - - def initialize(server, handlers) - @puppetserver = server - @notified = {} - # 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 - # thus, we don't need to pass any args to our base class, - # and we can consume them all ourselves - super() - - @loadedhandlers = [] - handlers.each { |handler| - #Puppet.debug "adding handler for %s" % handler.class - self.add_handler(handler.class.interface, handler) - } - - # Initialize these to nil, but they will get set to values - # by the 'service' method. These have to instance variables - # because I don't have a clear line from the service method to - # the service hook. - @request = nil - @client = nil - @clientip = nil - - self.set_service_hook { |obj, *args| - if @client and @clientip - args.push(@client, @clientip) - end - begin - obj.call(*args) - rescue XMLRPC::FaultException - raise - rescue Puppet::Server::AuthorizationError => detail - #Puppet.warning obj.inspect - #Puppet.warning args.inspect - Puppet.err "Permission denied: %s" % detail.to_s - raise XMLRPC::FaultException.new( - 1, detail.to_s - ) - rescue Puppet::Error => detail - #Puppet.warning obj.inspect - #Puppet.warning args.inspect - if Puppet[:trace] - puts detail.backtrace - end - Puppet.err detail.to_s - error = XMLRPC::FaultException.new( - 1, detail.to_s - ) - error.set_backtrace detail.backtrace - raise error - rescue => detail - #Puppet.warning obj.inspect - #Puppet.warning args.inspect - if Puppet[:trace] - puts detail.backtrace - end - Puppet.err "Could not call: %s" % detail.to_s - error = XMLRPC::FaultException.new(1, detail.to_s) - error.set_backtrace detail.backtrace - raise error - end - } - end - - # Handle the actual request. This does some basic collection of - # data, and then just calls the parent method. - def service(request, response) - @request = request - - # The only way that @client can be nil is if the request is local. - if peer = request.peeraddr - @client = peer[2] - @clientip = peer[3] - else - raise XMLRPC::FaultException.new( - ERR_UNCAUGHT_EXCEPTION, - "Could not retrieve client information" - ) - end - - # If they have a certificate (which will almost always be true) - # then we get the hostname from the cert, instead of via IP - # info - if cert = request.client_cert - nameary = cert.subject.to_a.find { |ary| - ary[0] == "CN" - } - - if nameary.nil? - Puppet.warning "Could not retrieve server name from cert" - else - unless @client == nameary[1] - Puppet.debug "Overriding %s with cert name %s" % - [@client, nameary[1]] - @client = nameary[1] - end - end - end - begin - super - rescue => detail - Puppet.err "Could not service request: %s: %s" % - [detail.class, detail] - end - @client = nil - @clientip = nil - @request = nil - end - - private - - # this is pretty much just a copy of the original method but with more - # feedback - # here's where we have our authorization hooks - def dispatch(methodname, *args) - - if defined? @request and @request - unless self.available?(methodname) - raise XMLRPC::FaultException.new( - ERR_UNAUTHORIZED, - "Functionality %s not available" % - methodname.sub(/\..+/, '') - ) - end - unless self.authorize(@request, methodname) - raise XMLRPC::FaultException.new( - ERR_UNAUTHORIZED, - "Host %s not authorized to call %s" % - [@request.host, methodname] - ) - end - else - raise Puppet::DevError, "Did not get request in dispatch" - end - - #Puppet.warning "dispatch on %s called with %s" % - # [methodname, args.inspect] - for name, obj in @handler - if obj.kind_of? Proc - unless methodname == name - #Puppet.debug "obj is proc but %s != %s" % - # [methodname, name] - next - end - else - unless methodname =~ /^#{name}(.+)$/ - #Puppet.debug "methodname did not match" - next - end - unless obj.respond_to? $1 - #Puppet.debug "methodname does not respond to %s" % $1 - next - end - obj = obj.method($1) - end - - if check_arity(obj, args.size) - if @service_hook.nil? - return obj.call(*args) - else - return @service_hook.call(obj, *args) - end - else - Puppet.debug "arity is incorrect" - end - end - - if @default_handler.nil? - raise XMLRPC::FaultException.new( - ERR_METHOD_MISSING, - "Method #{methodname} missing or wrong number of parameters!" - ) - else - @default_handler.call(methodname, *args) - end - end - end -end -end |
