summaryrefslogtreecommitdiffstats
path: root/lib/puppet/sslcertificates.rb
blob: fb5c1b7498060261cc0006fd923c4f80995b4ca1 (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
# The library for manipulating SSL certs.

require 'puppet'

begin
    require 'openssl'
rescue LoadError
    raise Puppet::Error, "You must have the Ruby openssl library installed"
end

module Puppet::SSLCertificates
    #def self.mkcert(type, name, dnsnames, ttl, issuercert, issuername, serial, publickey)
    def self.mkcert(hash)
        [:type, :name, :ttl, :issuer, :serial, :publickey].each { |param|
            unless hash.include?(param)
                raise ArgumentError, "mkcert called without %s" % param
            end
        }

        cert = OpenSSL::X509::Certificate.new
        # Make the certificate valid as of yesterday, because
        # so many people's clocks are out of sync.
        from = Time.now - (60*60*24)

        cert.subject = hash[:name]
        if hash[:issuer]
            cert.issuer = hash[:issuer].subject
        else
            # we're a self-signed cert
            cert.issuer = hash[:name]
        end
        cert.not_before = from
        cert.not_after = from + hash[:ttl]
        cert.version = 2 # X509v3

        cert.public_key = hash[:publickey]
        cert.serial = hash[:serial]

        basic_constraint = nil
        key_usage = nil
        ext_key_usage = nil
        subject_alt_name = []

        ef = OpenSSL::X509::ExtensionFactory.new

        ef.subject_certificate = cert

        if hash[:issuer]
            ef.issuer_certificate = hash[:issuer]
        else
            ef.issuer_certificate = cert
        end

        ex = []
        case hash[:type]
        when :ca
            basic_constraint = "CA:TRUE"
            key_usage = %w{cRLSign keyCertSign}
        when :terminalsubca
            basic_constraint = "CA:TRUE,pathlen:0"
            key_usage = %w{cRLSign keyCertSign}
        when :server
            basic_constraint = "CA:FALSE"
            dnsnames = Puppet[:certdnsnames]
            name = hash[:name].to_s.sub(%r{/CN=},'')
            if dnsnames != ""
                dnsnames.split(':').each { |d| subject_alt_name << 'DNS:' + d }
                subject_alt_name << 'DNS:' + name # Add the fqdn as an alias
            elsif name == Facter.value(:fqdn) # we're a CA server, and thus probably the server
                subject_alt_name << 'DNS:' + "puppet" # Add 'puppet' as an alias
                subject_alt_name << 'DNS:' + name # Add the fqdn as an alias
                subject_alt_name << 'DNS:' + name.sub(/^[^.]+./, "puppet.") # add puppet.domain as an alias
            end
            key_usage = %w{digitalSignature keyEncipherment}
            ext_key_usage = %w{serverAuth clientAuth emailProtection}
        when :ocsp
            basic_constraint = "CA:FALSE"
            key_usage = %w{nonRepudiation digitalSignature}
            ext_key_usage = %w{serverAuth OCSPSigning}
        when :client
            basic_constraint = "CA:FALSE"
            key_usage = %w{nonRepudiation digitalSignature keyEncipherment}
            ext_key_usage = %w{clientAuth emailProtection}
            ex << ef.create_extension("nsCertType", "client,email")
        else
            raise Puppet::Error, "unknown cert type '%s'" % hash[:type]
        end

        ex << ef.create_extension("nsComment",
                                  "Puppet Ruby/OpenSSL Generated Certificate")
        ex << ef.create_extension("basicConstraints", basic_constraint, true)
        ex << ef.create_extension("subjectKeyIdentifier", "hash")

        ex << ef.create_extension("keyUsage", key_usage.join(",")) if key_usage
        ex << ef.create_extension("extendedKeyUsage", ext_key_usage.join(",")) if ext_key_usage
        ex << ef.create_extension("subjectAltName", subject_alt_name.join(",")) if ! subject_alt_name.empty?

        #if @ca_config[:cdp_location] then
        #  ex << ef.create_extension("crlDistributionPoints",
        #                            @ca_config[:cdp_location])
        #end

        #if @ca_config[:ocsp_location] then
        #  ex << ef.create_extension("authorityInfoAccess",
        #                            "OCSP;" << @ca_config[:ocsp_location])
        #end
        cert.extensions = ex

        # for some reason this _must_ be the last extension added
        ex << ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always") if hash[:type] == :ca

        return cert
    end

    def self.mkhash(dir, cert, certfile)
        # Make sure the hash is zero-padded to 8 chars
        hash = "%08x" % cert.issuer.hash
        hashpath = nil
        10.times { |i|
            path = File.join(dir, "%s.%s" % [hash, i])
            if FileTest.exists?(path)
                if FileTest.symlink?(path)
                    dest = File.readlink(path)
                    if dest == certfile
                        # the correct link already exists
                        hashpath = path
                        break
                    else
                        next
                    end
                else
                    next
                end
            end

            File.symlink(certfile, path)

            hashpath = path
            break
        }


        return hashpath
    end
    require 'puppet/sslcertificates/certificate'
    require 'puppet/sslcertificates/inventory'
    require 'puppet/sslcertificates/ca'
end