Commit ccd042ab authored by Marco Mariani's avatar Marco Mariani

new command: "slapos configure client"

parent f1a4fd39
......@@ -91,6 +91,7 @@ setup(name=name,
'node instance = slapos.cli.slapgrid:InstanceCommand',
# SlapOS client commands
'console = slapos.cli.console:ConsoleCommand',
'configure client = slapos.cli.configure_client:ConfigureClientCommand',
'proxy start = slapos.cli.proxy_start:ProxyStartCommand',
'proxy show = slapos.cli.proxy_show:ProxyShowCommand',
'supply = slapos.cli.supply:SupplyCommand',
......
......@@ -28,6 +28,13 @@ class ConfigCommand(Command):
(self.default_config_var, self.default_config_path))
return ap
def config_path(self, args):
if args.cfg:
cfg_path = args.cfg
else:
cfg_path = os.environ.get(self.default_config_var, self.default_config_path)
return os.path.expanduser(cfg_path)
def fetch_config(self, args):
"""
Returns a configuration object if file exists/readable/valid,
......@@ -36,14 +43,9 @@ class ConfigCommand(Command):
and will clearly show what is wrong with the file.
"""
if args.cfg:
cfg_path = args.cfg
else:
cfg_path = os.environ.get(self.default_config_var, self.default_config_path)
cfg_path = os.path.expanduser(cfg_path)
cfg_path = self.config_path(args)
self.log.debug('Loading config: %s' % cfg_path)
self.log.debug('Loading config: %s', cfg_path)
if not os.path.exists(cfg_path):
raise ConfigError('Configuration file does not exist: %s' % cfg_path)
......
# -*- coding: utf-8 -*-
import logging
import re
import os
import sys
import requests
from slapos.cli.config import ClientConfigCommand
from slapos.util import mkdir_p, parse_certificate_key_pair
class ConfigureClientCommand(ClientConfigCommand):
"""
register a node in the SlapOS cloud
"""
log = logging.getLogger('configure-client')
def get_parser(self, prog_name):
ap = super(ConfigureClientCommand, self).get_parser(prog_name)
ap.add_argument('--master-url',
default='https://slap.vifib.com',
help='URL of SlapOS Master REST API'
' (default: %(default)s)')
ap.add_argument('--master-url-web',
default='https://www.slapos.org',
help='URL of SlapOS Master webservice to register certificates'
' (default: %(default)s)')
ap.add_argument('--token',
help="SlapOS 'credential security' authentication token "
"(use '--token ask' for interactive prompt)")
return ap
def take_action(self, args):
do_configure_client(logger=self.log,
master_url_web=args.master_url_web,
token=args.token,
config_path=self.config_path(args),
master_url=args.master_url)
def get_certificate_key_pair(logger, master_url_web, token):
url = '/'.join([master_url_web, 'myspace/my_account/request-a-certificate'])
req = requests.post('/'.join([master_url_web, 'myspace/my_account/request-a-certificate/WebSection_requestNewCertificate']),
data={},
headers={'X-Access-Token': token},
verify=False)
if req.status_code == 403:
logger.critical('Access denied to the SlapOS Master. '
'Please check the authentication token or require a new one.')
sys.exit(1)
req.raise_for_status()
return parse_certificate_key_pair(req.text)
def fetch_configuration_template():
req = requests.get('http://git.erp5.org/gitweb/slapos.core.git/blob_plain/HEAD:/slapos-client.cfg.example')
req.raise_for_status()
return req.text
def do_configure_client(logger, master_url_web, token, config_path, master_url):
while not token:
token = raw_input('Credential security token: ').strip()
# Check for existence of previous configuration, certificate or key files
# where we expect to create them. If so, ask the use to manually remove them.
if os.path.exists(config_path):
logger.critical('There is file in %s. '
'Please remove it before creating a new configuration.', config_path)
sys.exit(1)
basedir = os.path.dirname(config_path)
if not os.path.isdir(basedir):
logger.debug('Creating directory %s', basedir)
mkdir_p(basedir, mode=0o700)
cert_path = os.path.join(basedir, 'client.crt')
if os.path.exists(cert_path):
logger.critical('There is a file in %s. '
'Please remove it before creating a new certificate.', cert_path)
sys.exit(1)
key_path = os.path.join(basedir, 'client.key')
if os.path.exists(key_path):
logger.critical('There is a file in %s. '
'Please remove it before creating a new key.', key_path)
sys.exit(1)
# retrieve a template for the configuration file
cfg = fetch_configuration_template()
cfg = re.sub('master_url = .*', 'master_url = %s' % master_url, cfg)
cfg = re.sub('cert_file = .*', 'cert_file = %s' % cert_path, cfg)
cfg = re.sub('key_file = .*', 'key_file = %s' % key_path, cfg)
# retrieve and parse the certicate and key
certificate, key = get_certificate_key_pair(logger, master_url_web, token)
# write everything
with os.fdopen(os.open(config_path, os.O_CREAT | os.O_WRONLY, 0o600), 'w') as fout:
logger.debug('Writing configuration to %s', config_path)
fout.write(cfg)
with os.fdopen(os.open(cert_path, os.O_CREAT | os.O_WRONLY, 0o600), 'w') as fout:
logger.debug('Writing certificate to %s', cert_path)
fout.write(certificate)
with os.fdopen(os.open(key_path, os.O_CREAT | os.O_WRONLY, 0o600), 'w') as fout:
logger.debug('Writing key to %s', key_path)
fout.write(key)
logger.info('SlapOS client configuration: DONE')
......@@ -61,7 +61,7 @@ class RegisterCommand(Command):
'should be avoided for security reasons.')
ap.add_argument('--token',
help="SlapOS Master authentication token "
help="SlapOS 'computer security' authentication token "
"(use '--token ask' for interactive prompt)")
ap.add_argument('-t', '--create-tap',
......
......@@ -42,6 +42,8 @@ from subprocess import CalledProcessError
import requests
from slapos.util import parse_certificate_key_pair
def check_credentials(url, login, password):
"""Check if login and password are correct"""
......@@ -65,7 +67,9 @@ def get_certificate_key_pair(logger, master_url_web, node_name, token=None, logi
# raise a readable exception if the computer name is already used,
# instead of an opaque 500 Internal Error.
# this will not work with the new API.
logger.error('The node name "%s" is already in use. Please change the name, or revoke the active certificate if you want to replace the node.' % node_name)
logger.error('The node name "%s" is already in use. '
'Please change the name, or revoke the active '
'certificate if you want to replace the node.', node_name)
sys.exit(1)
if req.status_code == 403:
......@@ -73,7 +77,7 @@ def get_certificate_key_pair(logger, master_url_web, node_name, token=None, logi
msg = 'Please check the authentication token or require a new one.'
else:
msg = 'Please check username and password.'
logger.error('Access denied to the SlapOS Master. %s', msg)
logger.critical('Access denied to the SlapOS Master. %s', msg)
sys.exit(1)
else:
req.raise_for_status()
......@@ -81,19 +85,6 @@ def get_certificate_key_pair(logger, master_url_web, node_name, token=None, logi
return parse_certificate_key_pair(req.text)
def parse_certificate_key_pair(html):
"""Parse html gotten from SlapOS Master to make certificate and key files"""
c_start = html.find("Certificate:")
c_end = html.find("</textarea>", c_start)
certificate = html[c_start:c_end]
k_start = html.find("-----BEGIN PRIVATE KEY-----")
k_end = html.find("</textarea>", k_start)
key = html[k_start:k_end]
return certificate, key
def get_computer_name(certificate):
"""Parse certificate to get computer name and return it"""
k = certificate.find("COMP-")
......@@ -119,7 +110,8 @@ def save_former_config(conf):
saved += '.1'
else:
break
conf.logger.info("Former slapos configuration detected in %s moving to %s" % (former, saved))
conf.logger.info('Former slapos configuration detected '
'in %s moving to %s', former, saved)
shutil.move(former, saved)
......@@ -151,13 +143,13 @@ def slapconfig(conf):
directory = os.path.dirname(directory)
if not os.path.exists(slap_conf_dir):
conf.logger.info("Creating directory: %s" % slap_conf_dir)
conf.logger.info('Creating directory: %s', slap_conf_dir)
if not dry_run:
os.mkdir(slap_conf_dir, 0o711)
user_certificate_repository_path = os.path.join(slap_conf_dir, 'ssl')
if not os.path.exists(user_certificate_repository_path):
conf.logger.info("Creating directory: %s" % user_certificate_repository_path)
conf.logger.info('Creating directory: %s', user_certificate_repository_path)
if not dry_run:
os.mkdir(user_certificate_repository_path, 0o711)
......@@ -167,7 +159,7 @@ def slapconfig(conf):
(conf.key, key_file),
(conf.certificate, cert_file)
]:
conf.logger.info("Copying to %r, and setting minimum privileges" % dst)
conf.logger.info('Copying to %r, and setting minimum privileges', dst)
if not dry_run:
with open(dst, 'w') as destination:
destination.write(''.join(src))
......@@ -176,13 +168,13 @@ def slapconfig(conf):
certificate_repository_path = os.path.join(slap_conf_dir, 'ssl', 'partition_pki')
if not os.path.exists(certificate_repository_path):
conf.logger.info("Creating directory: %s" % certificate_repository_path)
conf.logger.info('Creating directory: %s', certificate_repository_path)
if not dry_run:
os.mkdir(certificate_repository_path, 0o711)
# Put slapos configuration file
slap_conf_file = os.path.join(slap_conf_dir, 'slapos.cfg')
conf.logger.info("Creating slap configuration: %s" % slap_conf_file)
conf.logger.info('Creating slap configuration: %s', slap_conf_file)
# Get example configuration file
slapos_cfg_example = get_slapos_conf_example()
......@@ -210,7 +202,7 @@ def slapconfig(conf):
with open(slap_conf_file, 'w') as fout:
new_configp.write(fout)
conf.logger.info("SlapOS configuration: DONE")
conf.logger.info('SlapOS configuration: DONE')
class RegisterConfig(object):
......@@ -236,12 +228,12 @@ class RegisterConfig(object):
self.key = key
def displayUserConfig(self):
self.logger.debug("Computer Name: %s" % self.node_name)
self.logger.debug("Master URL: %s" % self.master_url)
self.logger.debug("Number of partition: %s" % self.partition_number)
self.logger.info("Using Interface %s" % self.interface_name)
self.logger.debug("Ipv4 sub network: %s" % self.ipv4_local_network)
self.logger.debug("Ipv6 Interface: %s" % self.ipv6_interface)
self.logger.debug('Computer Name: %s', self.node_name)
self.logger.debug('Master URL: %s', self.master_url)
self.logger.debug('Number of partition: %s', self.partition_number)
self.logger.info('Using Interface %s', self.interface_name)
self.logger.debug('Ipv4 sub network: %s', self.ipv4_local_network)
self.logger.debug('Ipv6 Interface: %s', self.ipv6_interface)
def gen_auth(conf):
......@@ -261,7 +253,7 @@ def do_register(conf):
if conf.token == 'ask':
while True:
conf.token = raw_input('SlapOS Token: ').strip()
conf.token = raw_input('Computer security token: ').strip()
if conf.token:
break
......
......@@ -27,3 +27,19 @@ def chownDirectory(path, uid, gid):
for item in items:
if not os.path.islink(os.path.join(root, item)):
os.chown(os.path.join(root, item), uid, gid)
def parse_certificate_key_pair(html):
"""
Extract (certificate, key) pair 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]
k_start = html.find("-----BEGIN PRIVATE KEY-----")
k_end = html.find("</textarea>", k_start)
key = html[k_start:k_end]
return certificate, key
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