forked from seveas/python-hpilo
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhpilo_ca
More file actions
executable file
·269 lines (239 loc) · 10.2 KB
/
hpilo_ca
File metadata and controls
executable file
·269 lines (239 loc) · 10.2 KB
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
#!/usr/bin/python
#
# (c) 2011-2013 Dennis Kaarsemaker <dennis@kaarsemaker.net>
# see COPYING for license details
try:
import ConfigParser
except ImportError:
import configparser as ConfigParser
import hpilo
import optparse
import os
import subprocess
import sys
import time
import re
def main():
usage = """%prog [options] init
%prog [options] sign hostname [hostname ...]"""
p = optparse.OptionParser(usage=usage)
p.add_option("-l", "--login", dest="login", default=None,
help="Username to access the iLO")
p.add_option("-p", "--password", dest="password", default=None,
help="Password to access the iLO")
p.add_option("-c", "--config", dest="config", default="~/.ilo.conf",
help="File containing authentication and config details", metavar="FILE")
p.add_option("-t", "--timeout", dest="timeout", type="int", default=60,
help="Timeout for iLO connections")
p.add_option("-P", "--protocol", dest="protocol", choices=("http","raw"), default=None,
help="Use the specified protocol instead of autodetecting")
p.add_option("-d", "--debug", dest="debug", action="count", default=0,
help="Output debug information, repeat to see all XML data")
p.add_option("-o", "--port", dest="port", type="int", default=443,
help="SSL port to connect to")
p.add_option("-s", "--openssl", dest="openssl_bin", default="/usr/bin/openssl",
help="Path to the openssl binary")
p.add_option("-u", "--upgrade", dest="upgrade", action="store_true", default=False,
help="Upgrade iLO firmware that is too old automatically")
opts, args = p.parse_args()
if not args or args[0] not in ('init','sign'):
p.print_help()
p.exit(1)
if not os.path.exists(os.path.expanduser(opts.config)):
p.error("COnfiguration file does not exist")
config = ConfigParser.ConfigParser()
config.read(os.path.expanduser(opts.config))
if not config.has_option('ca', 'path'):
p.error("No CA path specified in the config")
ca_path = os.path.expanduser(config.get('ca', 'path'))
os.environ['CA_PATH'] = ca_path
openssl_cnf = os.path.join(ca_path, 'openssl.cnf')
openssl = OpenSSL(opts.openssl_bin, openssl_cnf)
if args[0] == 'init':
if len(args) > 2:
p.error("Too many arguments")
if not os.path.exists(ca_path):
os.makedirs(ca_path)
if not os.path.exists(openssl_cnf):
open(openssl_cnf, 'w').write(default_openssl_cnf)
for dir in ('ca', 'certs', 'crl', 'archive'):
if not os.path.exists(os.path.join(ca_path, dir)):
os.mkdir(os.path.join(ca_path, dir))
if not os.path.exists(os.path.join(ca_path, 'archive', 'index')):
open(os.path.join(ca_path, 'archive', 'index'),'w').close()
if not os.path.exists(os.path.join(ca_path, 'archive', 'serial')):
fd = open(os.path.join(ca_path, 'archive', 'serial'),'w')
fd.write("00\n")
fd.close()
if not os.path.exists(os.path.join(ca_path, 'ca', 'hpilo_ca.key')):
openssl('genrsa', '-out', os.path.join(ca_path, 'ca', 'hpilo_ca.key'), '2048')
if not os.path.exists(os.path.join(ca_path, 'ca', 'hpilo_ca.crt')):
openssl('req', '-new', '-x509', '-days', '7300', # 20 years should be enough for everyone :-)
'-key', os.path.join(ca_path, 'ca', 'hpilo_ca.key'),
'-out', os.path.join(ca_path, 'ca', 'hpilo_ca.crt'))
sys.exit(0)
# Do we have login information
login = None
password = None
if config.has_option('ilo', 'login'):
login = config.get('ilo', 'login')
if config.has_option('ilo', 'password'):
password = config.get('ilo', 'password')
if opts.login:
login = opts.login
if opts.password:
password = opts.password
if not login or not password:
p.error("No login details provided")
for hostname in args[1:]:
csr_path = os.path.join(ca_path, 'certs', hostname + '.csr')
crt_path = os.path.join(ca_path, 'certs', hostname + '.crt')
if os.path.exists(crt_path):
print("Certificate already signed, skipping")
continue
ilo = hpilo.Ilo(hostname, login, password, opts.timeout, opts.port)
ilo.debug = opts.debug
if opts.protocol == 'http':
ilo.protocol = hpilo.ILO_HTTP
elif opts.protocol == 'raw':
ilo.protocol = hpilo.ILO_RAW
print("(1/5) Checking certificate config of %s" % hostname)
fw_version = ilo.get_fw_version()
if fw_version['management_processor'] == 'iLO2':
version = fw_version['firmware_version']
i_version = [int(x) for x in version.split('.')]
if i_version < [2,6]:
print("iLO2 firmware version %s is too old" % version)
if not opts.upgrade:
print("Please upgrade firmware to 2.06 or newer")
continue
if not config.has_option('firmware', 'ilo2'):
print("Cannot upgrade firmware, no firmware specified in the config")
continue
print("Upgrading iLO firmware")
ilo.update_rib_firmware(config.get('firmware', 'ilo2'), print_progress)
print("Waiting a minute to let firmware upgrade settle...")
time.sleep(60)
cn = ilo.get_cert_subject_info()['csr_subject_common_name']
if '.' not in cn:
ilo.cert_fqdn(use_fqdn=True)
cn = ilo.get_cert_subject_info()['csr_subject_common_name']
if cn != hostname:
print("Hostname (%s) and CN (%s) do not match, fixing" % (hostname, cn))
ilo.mod_network_settings(dns_name=hostname[:hostname.find('.')])
print("Waiting a minute to let the iLO reset itself")
time.sleep(60)
elif re.match(r'^iLO[3-9]$', fw_version['management_processor']):
cn = ilo.get_network_settings()['dns_name']
if cn != hostname[:hostname.find('.')]:
print("Hostname (%s) and CN (%s) do not match, fixing" % (hostname, cn))
ilo.mod_network_settings(dns_name=hostname[:hostname.find('.')])
print("Waiting a minute to let the iLO reset itself")
time.sleep(60)
else:
print("%s: hpilo_ca cannot manage certificates for %s, only iLO2 and newer are supported" % (hostname, fw_version['management_processor']))
continue
print("(2/5) Retrieving certificate signing request")
try:
csr = ilo.certificate_signing_request()
except hpilo.IloError as e:
if str(e).endswith("run script after 10 minutes or more to receive the CSR."):
print("iLO is still preparing certificate signing request, waiting 2 minutes")
time.sleep(120)
csr = ilo.certificate_signing_request()
else:
raise e
if not csr:
print("Reveived an empty CSR")
continue
fd = open(csr_path, 'w')
fd.write(csr)
fd.close()
print("(3/5) Signing certificate")
openssl('ca', '-batch', '-in', csr_path, '-out', crt_path)
print("(4/5) Uploading certificate")
fd = open(crt_path)
cert = fd.read()
fd.close()
cert = cert[cert.find('-----BEGIN'):]
ilo.import_certificate(cert)
print("(5/5) Resetting iLO")
ilo.reset_rib()
class OpenSSL(object):
def __init__(self, openssl_bin, openssl_cnf):
self.openssl_bin = openssl_bin
self.openssl_cnf = openssl_cnf
def __call__(self, *args):
args = list(args)
if args[0] in ('req', 'ca'):
args = args[:1] + ['-config', self.openssl_cnf] + args[1:]
return subprocess.call([self.openssl_bin] + args)
default_openssl_cnf = """default_ca = hpilo_ca
[hpilo_ca]
dir = $ENV::CA_PATH
certs = $dir/certs
crl_dir = $dir/crl
private_key = $dir/ca/hpilo_ca.key
certificate = $dir/ca/hpilo_ca.crt
database = $dir/archive/index
new_certs_dir = $dir/archive
serial = $dir/archive/serial
crlnumber = $dir/crl/crlnumber
crl = $dir/crl/crl.pem
RANDFILE = $dir/.rand
x509_extensions = usr_cert
name_opt = ca_default
cert_opt = ca_default
default_days = 1825 # 5 years
default_crl_days = 30
default_md = sha1
preserve = no
policy = req_policy
[req_policy]
countryName = supplied
stateOrProvinceName = supplied
organizationName = supplied
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[req]
dir = $ENV::CA_PATH
default_bits = 2048
default_md = sha1
default_keyfile = $dir/ca/hpilo_ca.key
distinguished_name = req_distinguished_name
x509_extensions = ca_cert # The extentions to add to the self signed cert
string_mask = MASK:0x2002
[req_distinguished_name]
countryName = Country Name (2 letter code)
countryName_default = NL
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Flevoland
localityName = Locality Name (eg, city)
localityName_default = Lelystad
0.organizationName = Organization Name (eg, company)
0.organizationName_default = Kaarsemaker.net
commonName = Common Name (eg, your name or your server's hostname)
commonName_max = 64
commonName_default = hpilo_ca
[usr_cert]
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
nsComment = "Certificate generated by iLO CA"
[ca_cert]
basicConstraints = CA:true
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
nsComment = "Certificate generated by iLO CA"
"""
def print_progress(text):
if text.startswith('\r'):
sys.stdout.write(text)
sys.stdout.flush()
else:
print(text)
if __name__ == '__main__':
main()