summaryrefslogtreecommitdiffstats
path: root/lib/puppet/network/http_server/webrick.rb
blob: e622b712791c35dfc90d59de7b47b4c0478ca3aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
require 'puppet'
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'
require 'puppet/network/handler'

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::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
                unless File.exist?(Puppet[:cacrl])
                    # No CRL, no store needed
                    return nil
                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 if Puppet.settings[:certificate_revocation]
                raise Puppet::Error, "Could not find CA certificate" unless self.ca_cert

                store.add_file(Puppet[:localcacert])
                store.add_crl(crl)
                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.run_mode.master?
                    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
                args << WEBrick::Log::DEBUG if Puppet[:debug]

                log = WEBrick::Log.new(*args)


                log
            end

            # Create our server, yo.
            def initialize(hash = {})
                Puppet.info "Starting server for Puppet version #{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)

                begin
                    super(hash)
                rescue => detail
                    puts detail.backtrace if Puppet[:trace]
                    raise Puppet::Error, "Could not start WEBrick: #{detail}"
                end

                # make sure children don't inherit the sockets
                listeners.each { |sock|
                    sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
                }

                Puppet.info "Listening on port #{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)
                raise Puppet::Error, "Could get certificate" unless client.request_cert
            end

            # Create all of our handler instances.
            def setup_handlers(handlers)
                raise ServerError, "Handlers must have arguments" unless handlers.is_a?(Hash)

                handlers.collect { |handler, args|
                    hclass = nil
                    unless hclass = Puppet::Network::Handler.handler(handler)
                        raise ServerError, "Invalid handler #{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