Commit ccd042ab authored by Marco Mariani's avatar Marco Mariani

new command: "slapos configure client"

parent f1a4fd39
...@@ -91,6 +91,7 @@ setup(name=name, ...@@ -91,6 +91,7 @@ setup(name=name,
'node instance = slapos.cli.slapgrid:InstanceCommand', 'node instance = slapos.cli.slapgrid:InstanceCommand',
# SlapOS client commands # SlapOS client commands
'console = slapos.cli.console:ConsoleCommand', 'console = slapos.cli.console:ConsoleCommand',
'configure client = slapos.cli.configure_client:ConfigureClientCommand',
'proxy start = slapos.cli.proxy_start:ProxyStartCommand', 'proxy start = slapos.cli.proxy_start:ProxyStartCommand',
'proxy show = slapos.cli.proxy_show:ProxyShowCommand', 'proxy show = slapos.cli.proxy_show:ProxyShowCommand',
'supply = slapos.cli.supply:SupplyCommand', 'supply = slapos.cli.supply:SupplyCommand',
......
...@@ -28,6 +28,13 @@ class ConfigCommand(Command): ...@@ -28,6 +28,13 @@ class ConfigCommand(Command):
(self.default_config_var, self.default_config_path)) (self.default_config_var, self.default_config_path))
return ap 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): def fetch_config(self, args):
""" """
Returns a configuration object if file exists/readable/valid, Returns a configuration object if file exists/readable/valid,
...@@ -36,14 +43,9 @@ class ConfigCommand(Command): ...@@ -36,14 +43,9 @@ class ConfigCommand(Command):
and will clearly show what is wrong with the file. and will clearly show what is wrong with the file.
""" """
if args.cfg: cfg_path = self.config_path(args)
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)
self.log.debug('Loading config: %s' % cfg_path) self.log.debug('Loading config: %s', cfg_path)
if not os.path.exists(cfg_path): if not os.path.exists(cfg_path):
raise ConfigError('Configuration file does not exist: %s' % 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): ...@@ -61,7 +61,7 @@ class RegisterCommand(Command):
'should be avoided for security reasons.') 'should be avoided for security reasons.')
ap.add_argument('--token', ap.add_argument('--token',
help="SlapOS Master authentication token " help="SlapOS 'computer security' authentication token "
"(use '--token ask' for interactive prompt)") "(use '--token ask' for interactive prompt)")
ap.add_argument('-t', '--create-tap', ap.add_argument('-t', '--create-tap',
......
...@@ -42,6 +42,8 @@ from subprocess import CalledProcessError ...@@ -42,6 +42,8 @@ from subprocess import CalledProcessError
import requests import requests
from slapos.util import parse_certificate_key_pair
def check_credentials(url, login, password): def check_credentials(url, login, password):
"""Check if login and password are correct""" """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 ...@@ -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, # raise a readable exception if the computer name is already used,
# instead of an opaque 500 Internal Error. # instead of an opaque 500 Internal Error.
# this will not work with the new API. # 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) sys.exit(1)
if req.status_code == 403: if req.status_code == 403:
...@@ -73,7 +77,7 @@ def get_certificate_key_pair(logger, master_url_web, node_name, token=None, logi ...@@ -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.' msg = 'Please check the authentication token or require a new one.'
else: else:
msg = 'Please check username and password.' 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) sys.exit(1)
else: else:
req.raise_for_status() req.raise_for_status()
...@@ -81,19 +85,6 @@ def get_certificate_key_pair(logger, master_url_web, node_name, token=None, logi ...@@ -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) 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): def get_computer_name(certificate):
"""Parse certificate to get computer name and return it""" """Parse certificate to get computer name and return it"""
k = certificate.find("COMP-") k = certificate.find("COMP-")
...@@ -119,7 +110,8 @@ def save_former_config(conf): ...@@ -119,7 +110,8 @@ def save_former_config(conf):
saved += '.1' saved += '.1'
else: else:
break 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) shutil.move(former, saved)
...@@ -151,13 +143,13 @@ def slapconfig(conf): ...@@ -151,13 +143,13 @@ def slapconfig(conf):
directory = os.path.dirname(directory) directory = os.path.dirname(directory)
if not os.path.exists(slap_conf_dir): 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: if not dry_run:
os.mkdir(slap_conf_dir, 0o711) os.mkdir(slap_conf_dir, 0o711)
user_certificate_repository_path = os.path.join(slap_conf_dir, 'ssl') user_certificate_repository_path = os.path.join(slap_conf_dir, 'ssl')
if not os.path.exists(user_certificate_repository_path): 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: if not dry_run:
os.mkdir(user_certificate_repository_path, 0o711) os.mkdir(user_certificate_repository_path, 0o711)
...@@ -167,7 +159,7 @@ def slapconfig(conf): ...@@ -167,7 +159,7 @@ def slapconfig(conf):
(conf.key, key_file), (conf.key, key_file),
(conf.certificate, cert_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: if not dry_run:
with open(dst, 'w') as destination: with open(dst, 'w') as destination:
destination.write(''.join(src)) destination.write(''.join(src))
...@@ -176,13 +168,13 @@ def slapconfig(conf): ...@@ -176,13 +168,13 @@ def slapconfig(conf):
certificate_repository_path = os.path.join(slap_conf_dir, 'ssl', 'partition_pki') certificate_repository_path = os.path.join(slap_conf_dir, 'ssl', 'partition_pki')
if not os.path.exists(certificate_repository_path): 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: if not dry_run:
os.mkdir(certificate_repository_path, 0o711) os.mkdir(certificate_repository_path, 0o711)
# Put slapos configuration file # Put slapos configuration file
slap_conf_file = os.path.join(slap_conf_dir, 'slapos.cfg') 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 # Get example configuration file
slapos_cfg_example = get_slapos_conf_example() slapos_cfg_example = get_slapos_conf_example()
...@@ -210,7 +202,7 @@ def slapconfig(conf): ...@@ -210,7 +202,7 @@ def slapconfig(conf):
with open(slap_conf_file, 'w') as fout: with open(slap_conf_file, 'w') as fout:
new_configp.write(fout) new_configp.write(fout)
conf.logger.info("SlapOS configuration: DONE") conf.logger.info('SlapOS configuration: DONE')
class RegisterConfig(object): class RegisterConfig(object):
...@@ -236,12 +228,12 @@ class RegisterConfig(object): ...@@ -236,12 +228,12 @@ class RegisterConfig(object):
self.key = key self.key = key
def displayUserConfig(self): def displayUserConfig(self):
self.logger.debug("Computer Name: %s" % self.node_name) self.logger.debug('Computer Name: %s', self.node_name)
self.logger.debug("Master URL: %s" % self.master_url) self.logger.debug('Master URL: %s', self.master_url)
self.logger.debug("Number of partition: %s" % self.partition_number) self.logger.debug('Number of partition: %s', self.partition_number)
self.logger.info("Using Interface %s" % self.interface_name) self.logger.info('Using Interface %s', self.interface_name)
self.logger.debug("Ipv4 sub network: %s" % self.ipv4_local_network) self.logger.debug('Ipv4 sub network: %s', self.ipv4_local_network)
self.logger.debug("Ipv6 Interface: %s" % self.ipv6_interface) self.logger.debug('Ipv6 Interface: %s', self.ipv6_interface)
def gen_auth(conf): def gen_auth(conf):
...@@ -261,7 +253,7 @@ def do_register(conf): ...@@ -261,7 +253,7 @@ def do_register(conf):
if conf.token == 'ask': if conf.token == 'ask':
while True: while True:
conf.token = raw_input('SlapOS Token: ').strip() conf.token = raw_input('Computer security token: ').strip()
if conf.token: if conf.token:
break break
......
...@@ -27,3 +27,19 @@ def chownDirectory(path, uid, gid): ...@@ -27,3 +27,19 @@ def chownDirectory(path, uid, gid):
for item in items: for item in items:
if not os.path.islink(os.path.join(root, item)): if not os.path.islink(os.path.join(root, item)):
os.chown(os.path.join(root, item), uid, gid) 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