Commit 5d1130cf authored by Alain Takoudjou's avatar Alain Takoudjou

update certificate client request

parent 2fc808b8
......@@ -48,6 +48,7 @@ setup(name=name,
'netifaces', # to fetch information about network devices
'setuptools', # namespaces
'supervisor', # slapgrid uses supervisor to manage processes
'pyOpenSSL', # manage ssl certificates
'psutil>=2.0.0',
'xml_marshaller>=0.9.3', # to unmarshall/marshall python objects to/from
# XML
......@@ -56,7 +57,6 @@ setup(name=name,
'cliff',
'requests>=2.4.3',
'six',
'caucase', # used to manage ca client request
'uritemplate', # used by hateoas navigator
] + additional_install_requires,
extras_require={
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010-2014 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import errno
import os
import subprocess
import sqlite3
import re
from OpenSSL import crypto
from datetime import datetime, timedelta
def parse_certificate_from_html(html):
"""
Extract certificate from an HTML page received by SlapOS Master.
"""
regex = r"(-{5}BEGIN\sCERTIFICATE-{5}.*-{5}END\sCERTIFICATE-{5})"
result = re.search(regex, html, re.DOTALL)
if result:
return result.groups()[0]
return certificate
def generateCertificateRequest(self, key_string, cn,
country='', state='', locality='', email='', organization='',
organization_unit='', csr_file=None, digest="sha256"):
"""
Generate certificate Signature request.
Parameter `cn` is mandatory
"""
key = crypto.load_privatekey(crypto.FILETYPE_PEM, key_string)
req = crypto.X509Req()
subject = req.get_subject()
subject.CN = cn
if country:
subject.C = country
if state:
subject.ST = state
if locality:
subject.L = locality
if organization:
subject.O = organization
if organization_unit:
subject.OU = organization_unit
if email:
subject.emailAddress = email
req.set_pubkey(key)
req.add_extensions([
crypto.X509Extension("basicConstraints", False, "CA:FALSE"),
crypto.X509Extension("keyUsage", False,
"nonRepudiation, digitalSignature, keyEncipherment")
])
req.sign(key, digest)
csr = crypto.dump_certificate_request(crypto.FILETYPE_PEM, req)
if csr_file is not None:
with open(csr_file, 'w') as req_file:
req_file.write(csr)
os.chmod(csr_file, 0640)
return csr
def generatePkey(size=2048):
key = crypto.PKey()
key.generate_key(crypto.TYPE_RSA, size)
return crypto.dump_privatekey(crypto.FILETYPE_PEM, key)
def generatePrivatekey(self, output_file, size=2048):
"""
Generate private key into `output_file` and return the pkey string
"""
try:
key_fd = os.open(output_file,
os.O_CREAT|os.O_WRONLY|os.O_EXCL|os.O_TRUNC,
0600)
except OSError, e:
if e.errno != errno.EEXIST:
raise
else:
pkey = generatePkey(size)
os.write(key_fd, pkey)
os.close(key_fd)
return pkey
......@@ -36,7 +36,10 @@ import requests
from caucase.cli_flask import CertificateAuthorityRequest
from slapos.cli.config import ClientConfigCommand
from slapos.util import mkdir_p, parse_certificate_key_pair
from slapos.util import mkdir_p
from slapos.certificate import (parse_certificate_from_html,
generateCertificateRequest,
generatePkey)
class ConfigureClientCommand(ClientConfigCommand):
......@@ -138,9 +141,9 @@ def do_configure_client(logger, master_url_web, token, config_path, master_url):
ca_url='')
logger.debug('Generating key to %s', key_path)
ca_client.generatePrivatekey(key_path, size=2048)
csr_string = ca_client.generateCertificateRequest(
key_path,
key_string =generatePrivatekey(key_path, size=2048)
csr_string = generateCertificateRequest(
key_string,
cn=str(uuid.uuid4()))
# retrieve a template for the configuration file
......
......@@ -37,9 +37,10 @@ import pkg_resources
import requests
import uuid
from caucase.cli_flask import CertificateAuthorityRequest
from slapos.cli.command import Command, must_be_root
from slapos.util import parse_certificate_key_pair
from slapos.certificate import (parse_certificate_from_html,
generateCertificateRequest,
generatePkey)
class RegisterCommand(Command):
......@@ -183,8 +184,10 @@ def sign_certificate(logger, master_url_web, node_name, csr_string,
else:
req.raise_for_status()
return (get_computer_name(req.text), parse_certificate_from_html(req.text))
certificate = parse_certificate_from_html(req.text)
if certificate is None:
raise Exception("Computer Certificate was not returned by the Master")
return (get_computer_name(req.text), certificate)
def get_computer_name(text_string):
......@@ -247,12 +250,16 @@ def slapconfig(conf):
if not dry_run:
os.mkdir(user_certificate_repository_path, 0o711)
conf.logger.info('Copying to %r, and setting minimum privileges', conf.certificate_path)
key_file = os.path.join(user_certificate_repository_path, 'key')
cert_file = os.path.join(user_certificate_repository_path, 'certificate')
for src, dst in [(conf.key, key_file), (conf.certificate, cert_file)]:
conf.logger.info('Copying to %r, and setting minimum privileges', dst)
if not dry_run:
with open(conf.certificate_path, 'w') as destination:
with open(dst, 'w') as destination:
destination.write(''.join(src))
os.chmod(conf.certificate_path, 0o600)
os.chown(conf.certificate_path, 0, 0)
os.chmod(dst, 0o600)
os.chown(dst, 0, 0)
certificate_repository_path = os.path.join(slap_conf_dir, 'ssl', 'partition_pki')
if not os.path.exists(certificate_repository_path):
......@@ -299,6 +306,7 @@ class RegisterConfig(object):
self.logger = logger
self.computer_id = None
self.certificate = None
self.dry_run = False
def setConfig(self, options):
"""
......@@ -308,11 +316,12 @@ class RegisterConfig(object):
for option, value in options.__dict__.items():
setattr(self, option, value)
def COMPConfig(self, slapos_configuration):
def COMPConfig(self, slapos_configuration, key):
ssl_path = os.path.join(slapos_configuration, 'ssl')
self.slapos_configuration = slapos_configuration
self.certificate_path = os.path.join(ssl_path, 'certificate')
self.key_path = os.path.join(ssl_path, 'key')
self.key = key
def displayUserConfig(self):
self.logger.debug('Computer Name: %s', self.node_name)
......@@ -339,20 +348,10 @@ def do_register(conf):
"""Register new computer on SlapOS Master and generate slapos.cfg"""
# Getting configuration parameters
conf.COMPConfig(slapos_configuration='/etc/opt/slapos/')
# create certificate authority client
ca_client = CertificateAuthorityRequest(
conf.key_path,
conf.certificate_path,
ca_cert_path,
ca_url='')
conf.COMPConfig(slapos_configuration='/etc/opt/slapos/',
key=generatePkey(size=2048))
conf.logger.info('Generating private key to %s', conf.key_path)
ca_client.generatePrivatekey(conf.key_path, size=2048)
csr_string = ca_client.generateCertificateRequest(
conf.key_path,
cn=str(uuid.uuid4()))
csr_string = generateCertificateRequest(conf.key, cn=str(uuid.uuid4()))
if conf.login or conf.login_auth:
for login, password in gen_auth(conf):
......@@ -388,6 +387,6 @@ def do_register(conf):
# Prepare Slapos Configuration
slapconfig(conf)
conf.logger.info('Node has successfully been configured as %s.', COMP)
conf.logger.info('Node has successfully been configured as %s.', computer_id)
conf.logger.info('Now please invoke slapos node boot on your site.')
return 0
......@@ -38,6 +38,7 @@ import tarfile
import tempfile
import time
import xmlrpclib
import uuid
from supervisor import xmlrpc
......@@ -50,6 +51,7 @@ 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
WATCHDOG_MARK = '-on-watch'
......@@ -405,15 +407,18 @@ class Partition(object):
required=bytes2human(required)))
def _updateCertificate(self):
key_string = generatePrivatekey(self.key_file)
csr_string = generateCertificateRequest(key_string, cn=str(uuid.uuid4()))
try:
partition_certificate = self.computer_partition.getCertificate()
partition_certificate = self.computer_partition.getCertificate(
certificate_request=csr_string)
except NotFoundError:
raise NotFoundError('Partition %s is not known by SlapOS Master.' %
self.partition_id)
uid, gid = self.getUserGroupId()
for name, path in [('certificate', self.cert_file), ('key', self.key_file)]:
for name, path in [('certificate', self.cert_file)]:
new_content = partition_certificate[name]
old_content = None
if os.path.exists(path):
......
......@@ -372,9 +372,9 @@ class Computer(SlapDocument):
self._connection_helper.POST('revokeComputerCertificate', data={
'computer_id': self._computer_id})
def generateCertificate(self):
def generateCertificate(self, certificate_request):
xml = self._connection_helper.POST('generateComputerCertificate', data={
'computer_id': self._computer_id})
'computer_id': self._computer_id, certificate_request=certificate_request})
return xml_marshaller.loads(xml)
......@@ -641,11 +641,12 @@ class ComputerPartition(SlapRequester):
# XXX: this implementation has not been reviewed
self.usage = usage_log
def getCertificate(self):
def getCertificate(self, certificate_request=None):
xml = self._connection_helper.GET('getComputerPartitionCertificate',
params={
'computer_id': self._computer_id,
'computer_partition_id': self._partition_id,
'certificate_request': certificate_request,
}
)
return xml_marshaller.loads(xml)
......
......@@ -31,6 +31,7 @@ import errno
import os
import subprocess
import sqlite3
import re
def mkdir_p(path, mode=0o700):
......@@ -60,18 +61,6 @@ def chownDirectory(path, uid, gid):
])
def parse_certificate_from_html(html):
"""
Extract certificate from an HTML page received by SlapOS Master.
"""
c_start = html.find("Certificate:")
c_end = html.find("</textarea>", c_start)
certificate = html[c_start:c_end]
return certificate
def string_to_boolean(string):
"""
Return True if the value of the "string" parameter can be parsed as True.
......
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