Commit 691529b3 authored by Alain Takoudjou's avatar Alain Takoudjou

fixup for generate certificate. Node will not get certificate every time

parent 5d1130cf
......@@ -370,7 +370,8 @@ class SlapTool(BaseTool):
if certificate_request is None:
certificate_pem = software_instance.getCertificate()
else:
certificate_pem = software_instance.getCertificate()
certificate_pem = software_instance.requestCertificate(
certificate_request)
certificate_dict = dict(
key='',
certificate=certificate_pem
......@@ -768,15 +769,16 @@ class SlapTool(BaseTool):
)
result = {
'certificate': self.REQUEST.get('computer_certificate').decode("UTF-8"),
'key': '',
'url': self.REQUEST.get('computer_certificate_url').decode("UTF-8")
}
return xml_marshaller.xml_marshaller.dumps(result)
security.declareProtected(Permissions.AccessContentsInformation,
'generateComputerCertificate')
def generateComputerCertificate(self, computer_id):
def generateComputerCertificate(self, computer_id, certificate_request):
"""Fetches new computer certificate"""
return self._generateComputerCertificate(computer_id)
return self._generateComputerCertificate(computer_id, certificate_request)
@convertToREST
def _revokeComputerCertificate(self, computer_id):
......
......@@ -32,7 +32,7 @@ import os
import subprocess
import sqlite3
import re
from OpenSSL import crypto
from OpenSSL import crypto, SSL
from datetime import datetime, timedelta
......@@ -48,7 +48,7 @@ def parse_certificate_from_html(html):
return certificate
def generateCertificateRequest(self, key_string, cn,
def generateCertificateRequest(key_string, cn,
country='', state='', locality='', email='', organization='',
organization_unit='', csr_file=None, digest="sha256"):
"""
......@@ -97,20 +97,36 @@ def generatePkey(size=2048):
key.generate_key(crypto.TYPE_RSA, size)
return crypto.dump_privatekey(crypto.FILETYPE_PEM, key)
def generatePrivatekey(self, output_file, size=2048):
def generatePrivatekey(key_file, size=2048, uid=None, gid=None):
"""
Generate private key into `output_file` and return the pkey string
Generate private key into `key_file` and return the pkey string
"""
try:
key_fd = os.open(output_file,
key_fd = os.open(key_file,
os.O_CREAT|os.O_WRONLY|os.O_EXCL|os.O_TRUNC,
0600)
except OSError, e:
if e.errno != errno.EEXIST:
raise
# return existing certificate content
return open(key_file).read()
else:
pkey = generatePkey(size)
os.write(key_fd, pkey)
os.close(key_fd)
if uid and gid:
os.chown(key_file, uid, gid)
return pkey
def validateCertAndKey(cert_file, key_file):
with open(cert_file) as ct:
x509 = crypto.load_certificate(crypto.FILETYPE_PEM, ct.read())
with open(key_file) as kf:
key = crypto.load_privatekey(crypto.FILETYPE_PEM, kf.read())
ctx = SSL.Context(SSL.TLSv1_METHOD)
ctx.use_privatekey(key)
ctx.use_certificate(x509)
ctx.check_privatekey()
......@@ -51,7 +51,9 @@ from slapos.grid.exception import (BuildoutFailedError, WrongPermissionError,
PathDoesNotExistError, DiskSpaceError)
from slapos.grid.networkcache import download_network_cached, upload_network_cached
from slapos.human import bytes2human
from slapos.certificate import generateCertificateRequest, generatePrivatekey
from slapos.certificate import (generateCertificateRequest,
generatePrivatekey,
validateCertAndKey)
WATCHDOG_MARK = '-on-watch'
......@@ -407,7 +409,26 @@ class Partition(object):
required=bytes2human(required)))
def _updateCertificate(self):
key_string = generatePrivatekey(self.key_file)
"""
Get or update instance certificate.
The Master can't decide to update the certificate, only the node can request
to renew it or generate a new one.
The node generate the private key and send
"""
try:
cert_fd = os.open(self.cert_file,
os.O_CREAT|os.O_WRONLY|os.O_EXCL|os.O_TRUNC,
0600)
except OSError, e:
if e.errno != errno.EEXIST:
raise
# the certificate exists, no need to download it
return
uid, gid = self.getUserGroupId()
key_string = generatePrivatekey(self.key_file, uid, gid)
csr_string = generateCertificateRequest(key_string, cn=str(uuid.uuid4()))
try:
partition_certificate = self.computer_partition.getCertificate(
......@@ -416,23 +437,22 @@ class Partition(object):
raise NotFoundError('Partition %s is not known by SlapOS Master.' %
self.partition_id)
uid, gid = self.getUserGroupId()
os.write(cert_fd, partition_certificate)
os.close(cert_fd)
os.chown(self.cert_file, uid, gid)
self.logger.info('Certificate file saved at %r' % self.cert_file)
# Check that certificate and key are OK
try:
validateCertAndKey(self.key_file, self.cert_file)
except crypto.Error:
# Invalid Certificate file
if os.path.exists(self.cert_file):
os.unlink(self.cert_file)
raise
# except SSL.Error
# Raise when certificate and key didn't match
for name, path in [('certificate', self.cert_file)]:
new_content = partition_certificate[name]
old_content = None
if os.path.exists(path):
old_content = open(path).read()
if old_content != new_content:
if old_content is None:
self.logger.info('Missing %s file. Creating %r' % (name, path))
else:
self.logger.info('Changed %s content. Updating %r' % (name, path))
with os.fdopen(os.open(path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC, 0o400), 'wb') as fout:
fout.write(new_content)
os.chown(path, uid, gid)
def getUserGroupId(self):
"""Returns tuple of (uid, gid) of partition"""
......
......@@ -272,11 +272,14 @@ class IComputerPartition(IBuildoutController, IRequester):
log -- a text explaining why the method was called
"""
def getCertificate():
def getCertificate(certificate_request=None):
"""
Returns a dictionnary containing the authentification certificates
associated to the computer partition.
The dictionnary keys are:
certificate_request -- is a string containing the CSR in PEM format
The returned dictionnary keys are:
key -- value is a SSL key
certificate -- value is a SSL certificate
......@@ -406,13 +409,17 @@ class IComputer(Interface):
text -- message log of the status
"""
def generateCertificate():
def generateCertificate(certificate_request):
"""
Returns a dictionnary containing the new certificate files for
the computer.
certificate_request -- a string with CSR in PEM format
The dictionnary keys are:
key -- key file
certificate -- certificate file
url -- url that can be used to download the certificate
Raise ValueError is another certificate is already valid.
"""
......
......@@ -374,7 +374,8 @@ class Computer(SlapDocument):
def generateCertificate(self, certificate_request):
xml = self._connection_helper.POST('generateComputerCertificate', data={
'computer_id': self._computer_id, certificate_request=certificate_request})
'computer_id': self._computer_id,
'certificate_request': certificate_request})
return xml_marshaller.loads(xml)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment