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