Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ADDED: Subject Alternate Names support for certs #2

Merged
merged 1 commit into from
Oct 27, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .kitchen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,9 @@ suites:
run_list:
- recipe[ssl_certificate_test::default]
attributes:
- name: subject_alternate_names
data_bags_path: "test/kitchen/data_bags"
encrypted_data_bag_secret_key_path: "test/kitchen/encrypted_data_bag_secret"
run_list:
- recipe[ssl_certificate_test::subject_alternate_names]
attributes:
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,11 @@ By default the resource will create a self-signed certificate, but a custom one
<td>SSL cert file content in clear.</td>
<td><em>calculated</em></td>
</tr>
<tr>
<td>subject_alternate_names</td>
<td>Subject Alternate Names for the cert.</td>
<td><code>nil</code></td>
</tr>
</table>

Templates
Expand Down Expand Up @@ -397,6 +402,10 @@ When a namespace is set in the resource, it will try to read the following attri
<td><code>namespace["ssl_cert"]["content"]</code></td>
<td>SSL cert content used when reading from attributes.</td>
</tr>
<tr>
<td><code>namespace["ssl_cert"]["subject_alternate_names"]</code></td>
<td>An array of Subject Alternate Names for the SSL cert. Needed if your site has multiple domain names on the same cert.</td>
</tr>
</table>

## Examples
Expand Down Expand Up @@ -667,6 +676,21 @@ ssl_certificate "mysite" do
end
```

### Creating a Cert with Subject Alternate Names

```ruby
domain = 'mysite.com'
# SAN for mysite.com, foo.mysite.com, bar.mysite.com, www.mysite.com
node.default[ domain ]['ssl_cert']['subject_alternate_names'] = [ domain, 'foo.'+domain, 'bar.'+domain, 'www.' + domain ]

ssl_certificate 'mysite.com' do
namespace node[ domain ]
key_source 'self-signed'
cert_source 'self-signed'
end

```

Testing
=======

Expand Down
28 changes: 28 additions & 0 deletions libraries/resource_ssl_certificate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def initialize(name, run_context=nil)
cert_encrypted
cert_secret_file
cert_content
subject_alternate_names
}.each do |var|
self.instance_variable_set("@#{var}".to_sym, self.send("default_#{var}"))
end
Expand Down Expand Up @@ -363,6 +364,14 @@ def cert_content(arg=nil)
)
end

def subject_alternate_names(arg=nil)
set_or_return(
:subject_alternate_names,
arg,
:kind_of => Array
)
end

private

# key private methods
Expand Down Expand Up @@ -546,6 +555,12 @@ def default_cert_secret_file
end
end

def default_subject_alternate_names
lazy do
read_namespace(['ssl_cert', 'subject_alternate_names'])
end
end

def cert_subject
s = {}
s['common_name'] = common_name unless common_name.nil?
Expand Down Expand Up @@ -683,10 +698,23 @@ def generate_self_signed_cert(key, subject, time)
cert.add_extension(ef.create_extension('basicConstraints', 'CA:TRUE', true))
cert.add_extension(ef.create_extension('subjectKeyIdentifier', 'hash', false))
cert.add_extension(ef.create_extension('authorityKeyIdentifier', 'keyid:always,issuer:always', false))
if subject_alternate_names
handle_subject_alternative_names(cert, ef, subject_alternate_names)
end
cert.sign(key, OpenSSL::Digest::SHA256.new)
cert.to_pem
end

# Subject Alternative Names support taken and modified from
# https://github.com/cchandler/certificate_authority/blob/master/lib/certificate_authority/signing_request.rb
def handle_subject_alternative_names(cert, factory, alt_names)
raise "alt_names must be an Array" unless alt_names.is_a?(Array)

name_list = alt_names.map{|m| "DNS:#{m}"}.join(",")
ext = factory.create_ext("subjectAltName", name_list, false)
cert.add_extension(ext)
end

def verify_self_signed_cert(key, cert, hostname)
key = OpenSSL::PKey::RSA.new(key)
cert = OpenSSL::X509::Certificate.new(cert)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env bats

CERT_PATH="/etc/"
DEB_PATH="/etc/ssl/"
RH_PATH="/etc/pki/tls/"

setup() {
if [ -d $DEB_PATH ]; then
CERT_PATH=$DEB_PATH
elif [ -d $RH_PATH ]; then
CERT_PATH=$RH_PATH
fi
GREP_COMMAND1="openssl x509 -in ${CERT_PATH}certs/subject_alternate_names.pem -text -noout | grep"
GREP_COMMAND2="openssl x509 -in ${CERT_PATH}certs/subject_alternate_names2.pem -text -noout | grep"
}

@test "the non-SAN certificate has no Subject Alternative Name line" {
run bash -c "$GREP_COMMAND1 'X509v3 Subject Alternative Name'"
[ "$status" -eq 1 ]
}

@test "the SAN certificate has a Subject Alternative Name line" {
run bash -c "$GREP_COMMAND2 'X509v3 Subject Alternative Name'"
[ "$status" -eq 0 ]
}

@test "the SAN certificate has a DNS:foo entry" {
run bash -c "$GREP_COMMAND2 DNS:foo"
[ "$status" -eq 0 ]
}

@test "the SAN certificate has a DNS:bar entry" {
run bash -c "$GREP_COMMAND2 DNS:foo"
[ "$status" -eq 0 ]
}

@test "the SAN certificate has a DNS:subject-alternate-name entry" {
run bash -c "$GREP_COMMAND2 DNS:subject-alternate-name"
[ "$status" -eq 0 ]
}

@test "the SAN certificate has a DNS:foo.subject-alternate-name entry" {
run bash -c "$GREP_COMMAND2 DNS:foo.subject-alternate-name"
[ "$status" -eq 0 ]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bats

CERT_PATH="/etc/"
DEB_PATH="/etc/ssl/"
RH_PATH="/etc/pki/tls/"

setup() {
if [ -d $DEB_PATH ]; then
CERT_PATH=$DEB_PATH
elif [ -d $RH_PATH ]; then
CERT_PATH=$RH_PATH
fi
}

@test "the certificates exist" {
[ -f "${CERT_PATH}certs/subject_alternate_names.pem" -a -f "${CERT_PATH}certs/subject_alternate_names2.pem" ]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#
# Cookbook Name:: ssl_certificate_test
# Recipe:: subject_alternate_names
#
# Copyright 2014, Onddo Labs, Sl.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

ssl_certificate 'subject_alternate_names' do
key_source 'self-signed'
cert_source 'self-signed'
end

domain = node['fqdn']
node.default[ domain ]['ssl_cert']['subject_alternate_names'] = [ domain, 'foo', 'bar', 'foo.' + domain ]

ssl_certificate 'subject_alternate_names2' do
namespace node[ domain ]
key_source 'self-signed'
cert_source 'self-signed'
end