Skip to content

Commit 9db5f44

Browse files
committed
Steve Shipway Secret Server v2.1
0 parents  commit 9db5f44

15 files changed

+876
-0
lines changed

Makefile

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
MANIFESTS=manifests/cert.pp manifests/init.pp manifests/password.pp
2+
PLUGINS=lib/puppet/parser/functions/ss_fetch_key.rb lib/puppet/parser/functions/password_age.rb lib/puppet/parser/functions/ss_fetch_cert.rb lib/puppet/parser/functions/ss_setpass.rb lib/puppet/parser/functions/ss_check.rb lib/puppet/parser/functions/generate_password.rb lib/facter/passwords.rb
3+
4+
all: $(MANIFESTS) $(PLUGINS) secretserver.rb
5+
@puppet-module build
6+
@echo Done

Modulefile

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
name 'sshipway-ss'
2+
version '2.1.0'
3+
description "Module to integrate Puppet with SecretServer from Thycotic.com.
4+
This manages passwords, regularly rotating them and ensuring they are
5+
stored in the SecretServer database as Unix Password objects.
6+
It also synchronises certificate and key files on the client with Certificate
7+
objects in the SecretServer database."
8+
author 'Steve Shipway'
9+
summary 'Integration with SecretServer from Thycotic to manage passwords and certificates'
10+
project_page 'http://www.steveshipway.org/forum'
11+
license 'GPLv2 or later'

README

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
Steve Shipway
2+
University of Auckland
3+
4+
Version 2.0: Dec 2011
5+
Tested with RHEL(5.3,6.2), Ubuntu(Lucid), SecretServer(7.8)
6+
7+
Requires Savon Ruby Gem to be installed on Puppet Master: 'gem install savon'
8+
This probably means you need RHEL6 since RHEL6 Ruby did not support gems.
9+
10+
Also requires the secretserver.rb module file to be installed in
11+
/usr/lib/ruby/site_ruby/1.8/secretserver.rb
12+
13+
# PARAMETERS
14+
15+
Set these in the init.pp file
16+
17+
$ss_hostname = 'secretserver.auckland.ac.nz'
18+
$ss_username = 'puppet'
19+
$ss_password = 'mypassword'
20+
$ss_folder = 'Drop-box'
21+
22+
# PASSWORD CLASS
23+
Change password if older than 30 days, updating secret server (thycotic.com)
24+
database to reflect changes.
25+
26+
Allows you to have regularly rotating passwords, stored centrally and audited,
27+
but with noone actually knowing what they are.
28+
29+
This will also change and update if password is not yet defined on SecretSvr
30+
It will NOT verify that SS record contains the correct password though as
31+
this is not necessarily possible with various backends
32+
33+
Only users with UID<500 are checked; to change this, edit the facter module
34+
to set facts for ALL users. (see comments at start of lib/facter/password.rb )
35+
Note that ubuntu/debian people may wish to make this threshold <1000 instead.
36+
37+
Facter should set facts: pwage_(.*) for all accounts <500
38+
39+
To use:
40+
ss::password { 'root': }
41+
ss::password { 'oracle': maxage=>60, folder=>'Oracle Passwords' }
42+
43+
Attributes:
44+
maxage: number of days old a password must be before it gets auto changed
45+
default is 30
46+
folder: which SecretServer folder to place the secret into, if not the
47+
default
48+
username: (namevar) username to set password for
49+
minchange: minimum number of days before password can be changed by user
50+
default is 0 (may not be supported by your unix)
51+
52+
SecretServer:
53+
The new password secure is of type 'Unix Account (SSH)'
54+
The secret name is $username@$fqdn
55+
56+
Assumptions:
57+
1. The specified user exists as a Local user with no 2FA rules
58+
2. The specified folder exists, is writeable, and defaults to appropriate
59+
sharing rules
60+
3. All passwords for servers are shared with the puppet user
61+
4. All newly created passwords will be with 'Unix Account (SSH)' template
62+
5. Passwords can be changed via /usr/sbin/chpasswd (install this if it is
63+
not present). This works for ubuntu, debian, redhat, centos, fedora,
64+
and solaris (if chpasswd is installed from sunfreeware)
65+
6. Password ages are in /etc/shadow in standard format (OK for redhat,
66+
centos, fedora, ubuntu, debian, solaris)
67+
7. Secretserver v7.x API available
68+
69+
Bugs:
70+
1. No way to detect noop mode from functions, so secretserver will be
71+
updated even though the password is not changed on the client.
72+
73+
74+
# SSL CERTIFICATE CLASS
75+
This will synchronise certificate/key files on the client with the certificate
76+
and key data held in SecretServer.
77+
78+
It will optionally restart Apache after making changes.
79+
80+
Allows you to have certificates stored centrally, and multiple servers using
81+
the same certificate automatically updated together by puppet.
82+
83+
Should also work with Windows if you have service=>false and specify a
84+
windows file location with key=> and crt=>, though this is not tested.
85+
86+
To use:
87+
ss::cert { 'www.auckland.ac.nz': }
88+
ss::cert { $fqdn: service=>false; }
89+
90+
Attributes:
91+
key, crt: Specify alternate locations for the files. Default is to put them
92+
into /etc/httpd/conf/$name.crt and /etc/httpd/conf/$name.key
93+
service: set to false if you dont want it to restart httpd if cert changes
94+
ss: set to false if you want it to pull from a file instead of secretserver
95+
96+
SecretServer:
97+
The certificates MUST be stored in an object with a Certificate template.
98+
The secret name MUST correspond exactly to the namevar.
99+
100+
Assumptions:
101+
1. The puppet master must have read access to the certificate secret
102+
2. The files are stored in the correct format. No conversion or validation
103+
is performed.
104+
3. SecretServer API 7.6 or later available
105+
106+
Bugs:
107+
1. If you have service=>true (the default) then the definition of the httpd
108+
service may conflict with something you subsequently define elsewhere.
109+

lib/facter/passwords.rb

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
username = nil
2+
pwage = nil
3+
test = {}
4+
uids = {}
5+
# For just the system users
6+
maxuid = 500
7+
# For ALL users
8+
maxuid = 100000
9+
10+
File.open("/etc/passwd").each do |line|
11+
uids[$1] = $2.to_i if line =~ /^([^:\s]+):[^:]+:(\d+):/
12+
end
13+
14+
File.open("/etc/shadow").each do |line|
15+
username = $1 and pwage = $2 if line =~ /^([^:\s]+):[^:]+:(\d+):/ && uids[$1] && uids[$1] < maxuid
16+
if username != nil && pwage != nil
17+
test['pwage_'+username] =
18+
((Time.now-Time.at(pwage.to_i*24*3600))/(24*3600)).floor
19+
username = nil
20+
pwage = nil
21+
end
22+
end
23+
24+
test.each { |name,age|
25+
Facter.add(name) do
26+
setcode do
27+
age
28+
end
29+
end
30+
}
31+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module Puppet::Parser::Functions
2+
3+
# Generate a sufficiently random string to use as a password
4+
5+
newfunction(:generate_password,:type=>:rvalue) do |args|
6+
pwlen = args[0]
7+
if pwlen then
8+
pwlen = 10 if(pwlen < 8)
9+
else
10+
pwlen = 10
11+
end
12+
pass = rand.to_s + $$.to_s + Time.now.to_s
13+
pass.crypt(Time.now.sec.to_s*2)[-pwlen,pwlen]
14+
end
15+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module Puppet::Parser::Functions
2+
3+
# Return the age of the password for the specified user, using the custom facts
4+
5+
newfunction(:password_age,:type=>:rvalue) do |args|
6+
v = lookupvar("pwage_"+args[0])
7+
v = -1 if(v == nil)
8+
v.to_i
9+
end
10+
end
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
require "secretserver"
2+
3+
module Puppet::Parser::Functions
4+
5+
# This should check the password in SecretServer
6+
7+
newfunction(:ss_check,:type=>:rvalue) do |args|
8+
itemusername = args[0]
9+
itemhostname = args[1]
10+
ssuser = args[2]
11+
sspassword = args[3]
12+
sshostname = args[4]
13+
14+
Savon.configure do |config|
15+
config.log = false # disable logging
16+
config.log_level = :error # changing the log level
17+
config.raise_errors = false
18+
end
19+
HTTPI.log = false
20+
21+
# Establish session
22+
begin
23+
ss = SecretServer.new(sshostname, "secretserver",
24+
ssuser, sspassword, '', 'Local' )
25+
rescue
26+
return 'unknown'
27+
end
28+
29+
30+
# Seek the item
31+
begin
32+
dunnit = 0
33+
s = ss.search( itemusername+'@'+itemhostname )
34+
if s.size == 0
35+
return 'false'
36+
else
37+
s.each {|r|
38+
x = ss.get_secret(r)
39+
if ( x.secret[:name] == ( itemusername+'@'+itemhostname) )
40+
dunnit = 1
41+
end
42+
}
43+
if dunnit
44+
return 'true'
45+
end
46+
return 'false'
47+
end
48+
rescue
49+
return "Unknown: ERROR: #{$!}"
50+
end
51+
return 'unknown'
52+
end # function
53+
end # module
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
require "secretserver"
2+
3+
module Puppet::Parser::Functions
4+
5+
newfunction(:ss_fetch_cert,:type=>:rvalue) do |args|
6+
itemhostname = args[0]
7+
ssuser = args[1]
8+
sspassword = args[2]
9+
sshostname = args[3]
10+
11+
Savon.configure do |config|
12+
config.log = false # disable logging
13+
config.log_level = :error # changing the log level
14+
config.raise_errors = false
15+
end
16+
HTTPI.log = false
17+
18+
# Establish session
19+
begin
20+
ss = SecretServer.new(sshostname, "secretserver",
21+
ssuser, sspassword, '', 'Local' )
22+
rescue
23+
return ''
24+
end
25+
26+
# Seek the item
27+
s = ss.search( itemhostname )
28+
if s.size == 0
29+
return ''
30+
else
31+
certificate = ''
32+
s.each {|r|
33+
if ( r.secret_name == itemhostname ) and ( r.secret_type_name == 'Certificate' )
34+
x = ss.get_secret(r)
35+
itemid = 0
36+
x.secret[:items][:secret_item].each { |si|
37+
itemid = si[:id] if si[:field_name] == 'Certificate'
38+
}
39+
certificate = ss.download(r,itemid)
40+
certificate = 'Located but not retrieved' if ! certificate
41+
end
42+
}
43+
return certificate
44+
end
45+
return ''
46+
end
47+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
require "secretserver"
2+
3+
module Puppet::Parser::Functions
4+
5+
newfunction(:ss_fetch_key,:type=>:rvalue) do |args|
6+
itemhostname = args[0]
7+
ssuser = args[1]
8+
sspassword = args[2]
9+
sshostname = args[3]
10+
11+
Savon.configure do |config|
12+
config.log = false # disable logging
13+
config.log_level = :error # changing the log level
14+
config.raise_errors = false
15+
end
16+
HTTPI.log = false
17+
18+
# Establish session
19+
begin
20+
ss = SecretServer.new(sshostname, "secretserver",
21+
ssuser, sspassword, '', 'Local' )
22+
rescue
23+
return ''
24+
end
25+
26+
# Seek the item
27+
s = ss.search( itemhostname )
28+
if s.size == 0
29+
return ''
30+
else
31+
privatekey = ''
32+
s.each {|r|
33+
if ( r.secret_name == itemhostname ) and ( r.secret_type_name == 'Certificate' )
34+
x = ss.get_secret(r)
35+
itemid = 0
36+
x.secret[:items][:secret_item].each { |si|
37+
itemid = si[:id] if si[:field_name] == 'Private Key'
38+
}
39+
privatekey = ss.download(r,itemid)
40+
privatekey = 'Located but not retrieved' if ! privatekey
41+
end
42+
}
43+
return privatekey
44+
end
45+
return ''
46+
end
47+
end

0 commit comments

Comments
 (0)