Revision bdd5e420
b/Makefile.am | ||
---|---|---|
458 | 458 |
echo "PKGLIBDIR = '$(pkglibdir)'"; \ |
459 | 459 |
echo "DRBD_BARRIERS = $(DRBD_BARRIERS)"; \ |
460 | 460 |
echo "SYSLOG_USAGE = '$(SYSLOG_USAGE)'"; \ |
461 |
echo "OPENSSL_PATH = '$(OPENSSL)'"; \ |
|
462 | 461 |
} > $@ |
463 | 462 |
|
464 | 463 |
$(REPLACE_VARS_SED): Makefile |
b/configure.ac | ||
---|---|---|
201 | 201 |
AC_MSG_WARN([pylint not found, checking code will not be possible]) |
202 | 202 |
fi |
203 | 203 |
|
204 |
# Check for openssl |
|
205 |
AC_ARG_VAR(OPENSSL, [openssl path]) |
|
206 |
AC_PATH_PROG(OPENSSL, [openssl], []) |
|
207 |
if test -z "$OPENSSL" |
|
208 |
then |
|
209 |
AC_MSG_ERROR([openssl not found]) |
|
210 |
fi |
|
211 |
|
|
212 | 204 |
# Check for socat |
213 | 205 |
AC_ARG_VAR(SOCAT, [socat path]) |
214 | 206 |
AC_PATH_PROG(SOCAT, [socat], []) |
b/lib/constants.py | ||
---|---|---|
112 | 112 |
SYSCONFDIR = _autoconf.SYSCONFDIR |
113 | 113 |
TOOLSDIR = _autoconf.TOOLSDIR |
114 | 114 |
CONF_DIR = SYSCONFDIR + "/ganeti" |
115 |
OPENSSL_PATH = _autoconf.OPENSSL_PATH |
|
116 | 115 |
|
117 | 116 |
MASTER_SOCKET = SOCKET_DIR + "/ganeti-master" |
118 | 117 |
|
... | ... | |
171 | 170 |
SOCAT_USE_ESCAPE = _autoconf.SOCAT_USE_ESCAPE |
172 | 171 |
SOCAT_ESCAPE_CODE = "0x1d" |
173 | 172 |
|
173 |
# For RSA keys more bits are better, but they also make operations more |
|
174 |
# expensive. NIST SP 800-131 recommends a minimum of 2048 bits from the year |
|
175 |
# 2010 on. |
|
176 |
RSA_KEY_BITS = 2048 |
|
177 |
|
|
178 |
# Digest used to sign certificates ("openssl x509" uses SHA1 by default) |
|
179 |
X509_CERT_SIGN_DIGEST = "SHA1" |
|
180 |
|
|
174 | 181 |
VALUE_DEFAULT = "default" |
175 | 182 |
VALUE_AUTO = "auto" |
176 | 183 |
VALUE_GENERATE = "generate" |
b/lib/utils.py | ||
---|---|---|
43 | 43 |
import logging |
44 | 44 |
import logging.handlers |
45 | 45 |
import signal |
46 |
import OpenSSL |
|
46 | 47 |
|
47 | 48 |
from cStringIO import StringIO |
48 | 49 |
|
... | ... | |
2371 | 2372 |
wait_fn(current_delay) |
2372 | 2373 |
|
2373 | 2374 |
|
2374 |
def GenerateSelfSignedSslCert(file_name, validity=(365 * 5)):
|
|
2375 |
"""Generates a self-signed SSL certificate.
|
|
2375 |
def GetClosedTempfile(*args, **kwargs):
|
|
2376 |
"""Creates a temporary file and returns its path.
|
|
2376 | 2377 |
|
2377 |
@type file_name: str |
|
2378 |
@param file_name: Path to output file |
|
2378 |
""" |
|
2379 |
(fd, path) = tempfile.mkstemp(*args, **kwargs) |
|
2380 |
_CloseFDNoErr(fd) |
|
2381 |
return path |
|
2382 |
|
|
2383 |
|
|
2384 |
def GenerateSelfSignedX509Cert(common_name, validity): |
|
2385 |
"""Generates a self-signed X509 certificate. |
|
2386 |
|
|
2387 |
@type common_name: string |
|
2388 |
@param common_name: commonName value |
|
2379 | 2389 |
@type validity: int |
2380 |
@param validity: Validity for certificate in days
|
|
2390 |
@param validity: Validity for certificate in seconds
|
|
2381 | 2391 |
|
2382 | 2392 |
""" |
2383 |
(fd, tmp_file_name) = tempfile.mkstemp(dir=os.path.dirname(file_name)) |
|
2384 |
try: |
|
2385 |
try: |
|
2386 |
# Set permissions before writing key |
|
2387 |
os.chmod(tmp_file_name, 0600) |
|
2388 |
|
|
2389 |
result = RunCmd([constants.OPENSSL_PATH, "req", |
|
2390 |
"-new", "-newkey", "rsa:1024", |
|
2391 |
"-days", str(validity), "-nodes", "-x509", |
|
2392 |
"-keyout", tmp_file_name, "-out", tmp_file_name, |
|
2393 |
"-batch"]) |
|
2394 |
if result.failed: |
|
2395 |
raise errors.OpExecError("Could not generate SSL certificate, command" |
|
2396 |
" %s had exitcode %s and error message %s" % |
|
2397 |
(result.cmd, result.exit_code, result.output)) |
|
2398 |
|
|
2399 |
# Make read-only |
|
2400 |
os.chmod(tmp_file_name, 0400) |
|
2401 |
|
|
2402 |
os.rename(tmp_file_name, file_name) |
|
2403 |
finally: |
|
2404 |
RemoveFile(tmp_file_name) |
|
2405 |
finally: |
|
2406 |
os.close(fd) |
|
2393 |
# Create private and public key |
|
2394 |
key = OpenSSL.crypto.PKey() |
|
2395 |
key.generate_key(OpenSSL.crypto.TYPE_RSA, constants.RSA_KEY_BITS) |
|
2396 |
|
|
2397 |
# Create self-signed certificate |
|
2398 |
cert = OpenSSL.crypto.X509() |
|
2399 |
if common_name: |
|
2400 |
cert.get_subject().CN = common_name |
|
2401 |
cert.set_serial_number(1) |
|
2402 |
cert.gmtime_adj_notBefore(0) |
|
2403 |
cert.gmtime_adj_notAfter(validity) |
|
2404 |
cert.set_issuer(cert.get_subject()) |
|
2405 |
cert.set_pubkey(key) |
|
2406 |
cert.sign(key, constants.X509_CERT_SIGN_DIGEST) |
|
2407 |
|
|
2408 |
key_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) |
|
2409 |
cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) |
|
2410 |
|
|
2411 |
return (key_pem, cert_pem) |
|
2412 |
|
|
2413 |
|
|
2414 |
def GenerateSelfSignedSslCert(filename, validity=(5 * 365)): |
|
2415 |
"""Legacy function to generate self-signed X509 certificate. |
|
2416 |
|
|
2417 |
""" |
|
2418 |
(key_pem, cert_pem) = GenerateSelfSignedX509Cert(None, |
|
2419 |
validity * 24 * 60 * 60) |
|
2420 |
|
|
2421 |
WriteFile(filename, mode=0400, data=key_pem + cert_pem) |
|
2407 | 2422 |
|
2408 | 2423 |
|
2409 | 2424 |
class FileLock(object): |
b/test/ganeti.utils_unittest.py | ||
---|---|---|
35 | 35 |
import select |
36 | 36 |
import string |
37 | 37 |
import fcntl |
38 |
import OpenSSL |
|
38 | 39 |
|
39 | 40 |
import ganeti |
40 | 41 |
import testutils |
... | ... | |
1158 | 1159 |
self.failUnlessEqual(UnescapeAndSplit(sep.join(a), sep=sep), b) |
1159 | 1160 |
|
1160 | 1161 |
|
1161 |
class TestGenerateSelfSignedSslCert(unittest.TestCase):
|
|
1162 |
class TestGenerateSelfSignedX509Cert(unittest.TestCase):
|
|
1162 | 1163 |
def setUp(self): |
1163 | 1164 |
self.tmpdir = tempfile.mkdtemp() |
1164 | 1165 |
|
1165 | 1166 |
def tearDown(self): |
1166 | 1167 |
shutil.rmtree(self.tmpdir) |
1167 | 1168 |
|
1168 |
def _checkPrivateRsaKey(self, key):
|
|
1169 |
def _checkRsaPrivateKey(self, key):
|
|
1169 | 1170 |
lines = key.splitlines() |
1170 |
self.assert_("-----BEGIN RSA PRIVATE KEY-----" in lines)
|
|
1171 |
self.assert_("-----END RSA PRIVATE KEY-----" in lines)
|
|
1171 |
return ("-----BEGIN RSA PRIVATE KEY-----" in lines and
|
|
1172 |
"-----END RSA PRIVATE KEY-----" in lines)
|
|
1172 | 1173 |
|
1173 |
def _checkRsaCertificate(self, cert):
|
|
1174 |
def _checkCertificate(self, cert): |
|
1174 | 1175 |
lines = cert.splitlines() |
1175 |
self.assert_("-----BEGIN CERTIFICATE-----" in lines)
|
|
1176 |
self.assert_("-----END CERTIFICATE-----" in lines)
|
|
1176 |
return ("-----BEGIN CERTIFICATE-----" in lines and
|
|
1177 |
"-----END CERTIFICATE-----" in lines)
|
|
1177 | 1178 |
|
1178 |
def testSingleFile(self): |
|
1179 |
def test(self): |
|
1180 |
for common_name in [None, ".", "Ganeti", "node1.example.com"]: |
|
1181 |
(key_pem, cert_pem) = utils.GenerateSelfSignedX509Cert(common_name, 300) |
|
1182 |
self._checkRsaPrivateKey(key_pem) |
|
1183 |
self._checkCertificate(cert_pem) |
|
1184 |
|
|
1185 |
key = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, |
|
1186 |
key_pem) |
|
1187 |
self.assert_(key.bits() >= 1024) |
|
1188 |
self.assertEqual(key.bits(), constants.RSA_KEY_BITS) |
|
1189 |
self.assertEqual(key.type(), OpenSSL.crypto.TYPE_RSA) |
|
1190 |
|
|
1191 |
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, |
|
1192 |
cert_pem) |
|
1193 |
self.failIf(x509.has_expired()) |
|
1194 |
self.assertEqual(x509.get_issuer().CN, common_name) |
|
1195 |
self.assertEqual(x509.get_subject().CN, common_name) |
|
1196 |
self.assertEqual(x509.get_pubkey().bits(), constants.RSA_KEY_BITS) |
|
1197 |
|
|
1198 |
def testLegacy(self): |
|
1179 | 1199 |
cert1_filename = os.path.join(self.tmpdir, "cert1.pem") |
1180 | 1200 |
|
1181 | 1201 |
utils.GenerateSelfSignedSslCert(cert1_filename, validity=1) |
1182 | 1202 |
|
1183 | 1203 |
cert1 = utils.ReadFile(cert1_filename) |
1184 | 1204 |
|
1185 |
self._checkPrivateRsaKey(cert1)
|
|
1186 |
self._checkRsaCertificate(cert1)
|
|
1205 |
self.assert_(self._checkRsaPrivateKey(cert1))
|
|
1206 |
self.assert_(self._checkCertificate(cert1))
|
|
1187 | 1207 |
|
1188 | 1208 |
|
1189 | 1209 |
if __name__ == '__main__': |
Also available in: Unified diff