diff options
| author | Rick Bradley <rick@rickbradley.com> | 2007-10-05 19:51:47 -0500 |
|---|---|---|
| committer | Rick Bradley <rick@rickbradley.com> | 2007-10-05 19:51:47 -0500 |
| commit | a7d220b828ec5f277a3a3bfb33f517fe864579d0 (patch) | |
| tree | a538fd3d621e5eab38a5ee874f64e5e89235bd10 /lib/puppet/network/http_server | |
| parent | 7086ce17d14274f93ef0c03fba531bdb6710e5f7 (diff) | |
| download | puppet-a7d220b828ec5f277a3a3bfb33f517fe864579d0.tar.gz puppet-a7d220b828ec5f277a3a3bfb33f517fe864579d0.tar.xz puppet-a7d220b828ec5f277a3a3bfb33f517fe864579d0.zip | |
Moving the webrick/mongrel "servers" over to HTTPServer module instead of Server. Using Server as the master class for client connections. Server (former RESTServer) will instantiate the appropriate subclass based upon Puppet configurator setting. There are now tests broken in the network section which I can't seem to figure out yet. Not a happy place to be.
Diffstat (limited to 'lib/puppet/network/http_server')
| -rw-r--r-- | lib/puppet/network/http_server/mongrel.rb | 139 | ||||
| -rw-r--r-- | lib/puppet/network/http_server/webrick.rb | 163 |
2 files changed, 302 insertions, 0 deletions
diff --git a/lib/puppet/network/http_server/mongrel.rb b/lib/puppet/network/http_server/mongrel.rb new file mode 100644 index 000000000..ba4b048b3 --- /dev/null +++ b/lib/puppet/network/http_server/mongrel.rb @@ -0,0 +1,139 @@ +#!/usr/bin/env ruby +# File: 06-11-14-mongrel_xmlrpc.rb +# Author: Manuel Holtgrewe <purestorm at ggnore.net> +# +# Copyright (c) 2006 Manuel Holtgrewe, 2007 Luke Kanies +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# This file is based heavily on a file retrieved from +# http://ttt.ggnore.net/2006/11/15/xmlrpc-with-mongrel-and-ruby-off-rails/ + +require 'rubygems' +require 'mongrel' +require 'xmlrpc/server' +require 'puppet/network/xmlrpc/server' +require 'puppet/network/http_server' +require 'puppet/network/client_request' +require 'puppet/daemon' + +require 'resolv' + +# This handler can be hooked into Mongrel to accept HTTP requests. After +# checking whether the request itself is sane, the handler forwards it +# to an internal instance of XMLRPC::BasicServer to process it. +# +# You can access the server by calling the Handler's "xmlrpc_server" +# attribute accessor method and add XMLRPC handlers there. For example: +# +# <pre> +# handler = XmlRpcHandler.new +# handler.xmlrpc_server.add_handler("my.add") { |a, b| a.to_i + b.to_i } +# </pre> +module Puppet::Network + class HTTPServer::Mongrel < ::Mongrel::HttpHandler + include Puppet::Daemon + attr_reader :xmlrpc_server + + def initialize(handlers) + # Create a new instance of BasicServer. We are supposed to subclass it + # but that does not make sense since we would not introduce any new + # behaviour and we have to subclass Mongrel::HttpHandler so our handler + # works for Mongrel. + @xmlrpc_server = Puppet::Network::XMLRPCServer.new + handlers.each do |name, args| + unless handler = Puppet::Network::Handler.handler(name) + raise ArgumentError, "Invalid handler %s" % name + end + @xmlrpc_server.add_handler(handler.interface, handler.new(args)) + end + end + + # This method produces the same results as XMLRPC::CGIServer.serve + # from Ruby's stdlib XMLRPC implementation. + def process(request, response) + # Make sure this has been a POST as required for XMLRPC. + request_method = request.params[Mongrel::Const::REQUEST_METHOD] || Mongrel::Const::GET + if request_method != "POST" then + response.start(405) { |head, out| out.write("Method Not Allowed") } + return + end + + # Make sure the user has sent text/xml data. + request_mime = request.params["CONTENT_TYPE"] || "text/plain" + if parse_content_type(request_mime).first != "text/xml" then + response.start(400) { |head, out| out.write("Bad Request") } + return + end + + # Make sure there is data in the body at all. + length = request.params[Mongrel::Const::CONTENT_LENGTH].to_i + if length <= 0 then + response.start(411) { |head, out| out.write("Length Required") } + return + end + + # Check the body to be valid. + if request.body.nil? or request.body.size != length then + response.start(400) { |head, out| out.write("Bad Request") } + return + end + + info = client_info(request) + + # All checks above passed through + response.start(200) do |head, out| + head["Content-Type"] = "text/xml; charset=utf-8" + begin + out.write(@xmlrpc_server.process(request.body, info)) + rescue => detail + puts detail.backtrace + raise + end + end + end + + private + + def client_info(request) + params = request.params + ip = params["REMOTE_ADDR"] + if dn = params[Puppet[:ssl_client_header]] and dn.include?("/CN=") + client = dn.sub("/CN=", '') + valid = (params[Puppet[:ssl_client_verify_header]] == 'SUCCESS') + else + client = Resolv.getname(ip) + valid = false + end + + info = Puppet::Network::ClientRequest.new(client, ip, valid) + + return info + end + + # Taken from XMLRPC::ParseContentType + def parse_content_type(str) + a, *b = str.split(";") + return a.strip, *b + end + end +end + diff --git a/lib/puppet/network/http_server/webrick.rb b/lib/puppet/network/http_server/webrick.rb new file mode 100644 index 000000000..2cf85b7b6 --- /dev/null +++ b/lib/puppet/network/http_server/webrick.rb @@ -0,0 +1,163 @@ +require 'puppet' +require 'puppet/daemon' +require 'webrick' +require 'webrick/https' +require 'fcntl' + +require 'puppet/sslcertificates/support' +require 'puppet/network/xmlrpc/webrick_servlet' +require 'puppet/network/http_server' +require 'puppet/network/client' + +module Puppet + class ServerError < RuntimeError; end + module Network + # The old-school, pure ruby webrick server, which is the default serving + # mechanism. + class HTTPServer::WEBrick < WEBrick::HTTPServer + include Puppet::Daemon + include Puppet::SSLCertificates::Support + + # Read the CA cert and CRL and populate an OpenSSL::X509::Store + # with them, with flags appropriate for checking client + # certificates for revocation + def x509store + if Puppet[:cacrl] == 'none' + # No CRL, no store needed + return nil + end + unless File.exist?(Puppet[:cacrl]) + raise Puppet::Error, "Could not find CRL" + end + crl = OpenSSL::X509::CRL.new(File.read(Puppet[:cacrl])) + store = OpenSSL::X509::Store.new + store.purpose = OpenSSL::X509::PURPOSE_ANY + store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK + unless self.ca_cert + raise Puppet::Error, "No CA certificate" + end + + store.add_file(Puppet[:localcacert]) + store.add_crl(crl) + return store + end + + # Set up the http log. + def httplog + args = [] + + # yuck; separate http logs + file = nil + Puppet.settings.use(:main, :ssl, Puppet[:name]) + if Puppet[:name] == "puppetmasterd" + file = Puppet[:masterhttplog] + else + file = Puppet[:httplog] + end + + # open the log manually to prevent file descriptor leak + file_io = open(file, "a+") + file_io.sync + file_io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) + + args << file_io + if Puppet[:debug] + args << WEBrick::Log::DEBUG + end + + log = WEBrick::Log.new(*args) + + + return log + end + + # Create our server, yo. + def initialize(hash = {}) + Puppet.info "Starting server for Puppet version %s" % Puppet.version + + if handlers = hash[:Handlers] + handler_instances = setup_handlers(handlers) + else + raise ServerError, "A server must have handlers" + end + + unless self.read_cert + if ca = handler_instances.find { |handler| handler.is_a?(Puppet::Network::Handler.ca) } + request_cert(ca) + else + raise Puppet::Error, "No certificate and no CA; cannot get cert" + end + end + + setup_webrick(hash) + + super(hash) + + # make sure children don't inherit the sockets + listeners.each { |sock| + sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) + } + + Puppet.info "Listening on port %s" % hash[:Port] + + # this creates a new servlet for every connection, + # but all servlets have the same list of handlers + # thus, the servlets can have their own state -- passing + # around the requests and such -- but the handlers + # have a global state + + # mount has to be called after the server is initialized + servlet = Puppet::Network::XMLRPC::WEBrickServlet.new( + handler_instances) + self.mount("/RPC2", servlet) + end + + # Create a ca client to set up our cert for us. + def request_cert(ca) + client = Puppet::Network::Client.ca.new(:CA => ca) + unless client.request_cert + raise Puppet::Error, "Could get certificate" + end + end + + # Create all of our handler instances. + def setup_handlers(handlers) + unless handlers.is_a?(Hash) + raise ServerError, "Handlers must have arguments" + end + + handlers.collect { |handler, args| + hclass = nil + unless hclass = Handler.handler(handler) + raise ServerError, "Invalid handler %s" % handler + end + hclass.new(args) + } + end + + # Handle all of the many webrick arguments. + def setup_webrick(hash) + hash[:Port] ||= Puppet[:masterport] + hash[:Logger] ||= self.httplog + hash[:AccessLog] ||= [ + [ self.httplog, WEBrick::AccessLog::COMMON_LOG_FORMAT ], + [ self.httplog, WEBrick::AccessLog::REFERER_LOG_FORMAT ] + ] + + hash[:SSLCertificateStore] = x509store + hash[:SSLCertificate] = self.cert + hash[:SSLPrivateKey] = self.key + hash[:SSLStartImmediately] = true + hash[:SSLEnable] = true + hash[:SSLCACertificateFile] = Puppet[:localcacert] + hash[:SSLVerifyClient] = OpenSSL::SSL::VERIFY_PEER + hash[:SSLCertName] = nil + + if addr = Puppet[:bindaddress] and addr != "" + hash[:BindAddress] = addr + end + end + end + end +end + |
