Skip to content

Commit

Permalink
Merge pull request #107 from mikaelarguedas/autogenerate_artifacts
Browse files Browse the repository at this point in the history
generate_artifacts verb
  • Loading branch information
mjcarroll committed Apr 4, 2019
2 parents 97bc194 + 5136c7d commit a56d851
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 69 deletions.
3 changes: 2 additions & 1 deletion sros2/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ def package_files(directory):
'create_permission = sros2.verb.create_permission'
':CreatePermissionVerb',
'distribute_key = sros2.verb.distribute_key:DistributeKeyVerb',
'list_keys = sros2.verb.list_keys:ListKeysVerb',
'generate_artifacts = sros2.verb.generate_artifacts:GenerateArtifactsVerb',
'generate_policy = sros2.verb.generate_policy:GeneratePolicyVerb',
'list_keys = sros2.verb.list_keys:ListKeysVerb',
],
},
package_data={
Expand Down
156 changes: 93 additions & 63 deletions sros2/sros2/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2016-2017 Open Source Robotics Foundation, Inc.
# Copyright 2016-2019 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -18,6 +18,7 @@
import platform
import shutil
import subprocess
import sys

from lxml import etree

Expand All @@ -30,6 +31,7 @@
)

HIDDEN_NODE_PREFIX = '_'
DOMAIN_ID_ENV = 'ROS_DOMAIN_ID'

NodeName = namedtuple('NodeName', ('node', 'ns', 'fqn'))
TopicInfo = namedtuple('Topic', ('fqn', 'type'))
Expand Down Expand Up @@ -221,82 +223,77 @@ def create_signed_governance_file(signed_gov_path, gov_path, ca_cert_path, ca_ke
(openssl_executable, gov_path, signed_gov_path, ca_cert_path, ca_key_path))


def create_keystore(args):
root = args.ROOT
print(args)

if not os.path.exists(root):
print('creating directory: %s' % root)
os.makedirs(root, exist_ok=True)
def create_keystore(keystore_path):
if not os.path.exists(keystore_path):
print('creating directory: %s' % keystore_path)
os.makedirs(keystore_path, exist_ok=True)
else:
print('directory already exists: %s' % root)
print('directory already exists: %s' % keystore_path)

ca_conf_path = os.path.join(root, 'ca_conf.cnf')
ca_conf_path = os.path.join(keystore_path, 'ca_conf.cnf')
if not os.path.isfile(ca_conf_path):
print('creating CA file: %s' % ca_conf_path)
create_ca_conf_file(ca_conf_path)
else:
print('found CA conf file, not writing a new one!')

ecdsa_param_path = os.path.join(root, 'ecdsaparam')
ecdsa_param_path = os.path.join(keystore_path, 'ecdsaparam')
if not os.path.isfile(ecdsa_param_path):
print('creating ECDSA param file: %s' % ecdsa_param_path)
create_ecdsa_param_file(ecdsa_param_path)
else:
print('found ECDSA param file, not writing a new one!')

ca_key_path = os.path.join(root, 'ca.key.pem')
ca_cert_path = os.path.join(root, 'ca.cert.pem')
ca_key_path = os.path.join(keystore_path, 'ca.key.pem')
ca_cert_path = os.path.join(keystore_path, 'ca.cert.pem')
if not (os.path.isfile(ca_key_path) and os.path.isfile(ca_cert_path)):
print('creating new CA key/cert pair')
create_ca_key_cert(ecdsa_param_path, ca_conf_path, ca_key_path, ca_cert_path)
else:
print('found CA key and cert, not creating new ones!')

# create governance file
gov_path = os.path.join(root, 'governance.xml')
gov_path = os.path.join(keystore_path, 'governance.xml')
if not os.path.isfile(gov_path):
print('creating governance file: %s' % gov_path)
domain_id = os.getenv('ROS_DOMAIN_ID', '0')
domain_id = os.getenv(DOMAIN_ID_ENV, '0')
create_governance_file(gov_path, domain_id)
else:
print('found governance file, not creating a new one!')

# sign governance file
signed_gov_path = os.path.join(root, 'governance.p7s')
signed_gov_path = os.path.join(keystore_path, 'governance.p7s')
if not os.path.isfile(signed_gov_path):
print('creating signed governance file: %s' % signed_gov_path)
create_signed_governance_file(signed_gov_path, gov_path, ca_cert_path, ca_key_path)
else:
print('found signed governance file, not creating a new one!')

# create index file
index_path = os.path.join(root, 'index.txt')
index_path = os.path.join(keystore_path, 'index.txt')
if not os.path.isfile(index_path):
with open(index_path, 'a'):
pass

# create serial file
serial_path = os.path.join(root, 'serial')
serial_path = os.path.join(keystore_path, 'serial')
if not os.path.isfile(serial_path):
with open(serial_path, 'w') as f:
f.write('1000')

print('all done! enjoy your keystore in %s' % root)
print('all done! enjoy your keystore in %s' % keystore_path)
print('cheers!')
return True


def is_valid_keystore(path):
ca_conf_found = os.path.isfile(os.path.join(path, 'ca_conf.cnf'))
ecdsa_param_found = os.path.isfile(os.path.join(path, 'ecdsaparam'))
index_found = os.path.isfile(os.path.join(path, 'index.txt'))
ca_key_found = os.path.isfile(os.path.join(path, 'ca.key.pem'))
ca_cert_found = os.path.isfile(os.path.join(path, 'ca.cert.pem'))
signed_gov_found = os.path.isfile(os.path.join(path, 'governance.p7s'))
return ecdsa_param_found and ca_key_found and \
ca_cert_found and signed_gov_found and \
index_found and ca_conf_found
res = os.path.isfile(os.path.join(path, 'ca_conf.cnf'))
res &= os.path.isfile(os.path.join(path, 'ecdsaparam'))
res &= os.path.isfile(os.path.join(path, 'index.txt'))
res &= os.path.isfile(os.path.join(path, 'ca.key.pem'))
res &= os.path.isfile(os.path.join(path, 'ca.cert.pem'))
res &= os.path.isfile(os.path.join(path, 'governance.p7s'))
return res


def is_key_name_valid(name):
Expand Down Expand Up @@ -363,7 +360,10 @@ def create_permission_file(path, domain_id, policy_element):

def get_policy(name, policy_file_path):
policy_tree = load_policy(policy_file_path)
return get_policy_from_tree(name, policy_tree)


def get_policy_from_tree(name, policy_tree):
ns, node = name.rsplit('/', 1)
ns = '/' if not ns else ns
profile_element = policy_tree.find(
Expand Down Expand Up @@ -391,54 +391,50 @@ def create_signed_permissions_file(
(openssl_executable, permissions_path, signed_permissions_path, ca_cert_path, ca_key_path))


def create_permission(args):
print(args)
root = args.ROOT
name = args.NAME
policy_file_path = args.POLICY_FILE_PATH
domain_id = os.getenv('ROS_DOMAIN_ID', '0')
def create_permission(keystore_path, identity, policy_file_path):
policy_element = get_policy(identity, policy_file_path)
create_permissions_from_policy_element(keystore_path, identity, policy_element)
return True

relative_path = os.path.normpath(name.lstrip('/'))
key_dir = os.path.join(root, relative_path)

def create_permissions_from_policy_element(keystore_path, identity, policy_element):
domain_id = os.getenv(DOMAIN_ID_ENV, '0')
relative_path = os.path.normpath(identity.lstrip('/'))
key_dir = os.path.join(keystore_path, relative_path)
print('key_dir %s' % key_dir)
policy_element = get_policy(name, policy_file_path)
permissions_path = os.path.join(key_dir, 'permissions.xml')
create_permission_file(permissions_path, domain_id, policy_element)

signed_permissions_path = os.path.join(key_dir, 'permissions.p7s')
keystore_ca_cert_path = os.path.join(root, 'ca.cert.pem')
keystore_ca_key_path = os.path.join(root, 'ca.key.pem')
keystore_ca_cert_path = os.path.join(keystore_path, 'ca.cert.pem')
keystore_ca_key_path = os.path.join(keystore_path, 'ca.key.pem')
create_signed_permissions_file(
permissions_path, signed_permissions_path,
keystore_ca_cert_path, keystore_ca_key_path)
return True


def create_key(args):
print(args)
root = args.ROOT
name = args.NAME
if not is_valid_keystore(root):
print('root path is not a valid keystore: %s' % root)
def create_key(keystore_path, identity):
if not is_valid_keystore(keystore_path):
print("'%s' is not a valid keystore " % keystore_path)
return False
if not is_key_name_valid(name):
print('bad character in requested key name: %s' % name)
if not is_key_name_valid(identity):
print("bad character in requested identity: '%s'" % identity)
return False
print('creating key for node name: %s' % name)
print("creating key for identity: '%s'" % identity)

relative_path = os.path.normpath(name.lstrip('/'))
key_dir = os.path.join(root, relative_path)
relative_path = os.path.normpath(identity.lstrip('/'))
key_dir = os.path.join(keystore_path, relative_path)
os.makedirs(key_dir, exist_ok=True)

# copy the CA cert in there
keystore_ca_cert_path = os.path.join(root, 'ca.cert.pem')
keystore_ca_cert_path = os.path.join(keystore_path, 'ca.cert.pem')
dest_identity_ca_cert_path = os.path.join(key_dir, 'identity_ca.cert.pem')
dest_permissions_ca_cert_path = os.path.join(key_dir, 'permissions_ca.cert.pem')
shutil.copyfile(keystore_ca_cert_path, dest_identity_ca_cert_path)
shutil.copyfile(keystore_ca_cert_path, dest_permissions_ca_cert_path)

# copy the governance file in there
keystore_governance_path = os.path.join(root, 'governance.p7s')
keystore_governance_path = os.path.join(keystore_path, 'governance.p7s')
dest_governance_path = os.path.join(key_dir, 'governance.p7s')
shutil.copyfile(keystore_governance_path, dest_governance_path)

Expand All @@ -451,7 +447,7 @@ def create_key(args):

cnf_path = os.path.join(key_dir, 'request.cnf')
if not os.path.isfile(cnf_path):
create_request_file(cnf_path, name)
create_request_file(cnf_path, identity)
else:
print('config file exists, not creating a new one: %s' % cnf_path)

Expand All @@ -460,7 +456,7 @@ def create_key(args):
if not os.path.isfile(key_path) or not os.path.isfile(req_path):
print('creating key and cert request')
create_key_and_cert_req(
root,
keystore_path,
relative_path,
cnf_path,
ecdsa_param_path,
Expand All @@ -471,7 +467,7 @@ def create_key(args):
cert_path = os.path.join(key_dir, 'cert.pem')
if not os.path.isfile(cert_path):
print('creating cert')
create_cert(root, relative_path)
create_cert(keystore_path, relative_path)
else:
print('found cert; not creating a new one!')

Expand All @@ -480,30 +476,64 @@ def create_key(args):
policy_file_path = get_policy_default('policy.xml')
policy_element = get_policy('/default', policy_file_path)
profile_element = policy_element.find('profiles/profile')
ns, node = name.rsplit('/', 1)
ns, node = identity.rsplit('/', 1)
ns = '/' if not ns else ns
profile_element.attrib['ns'] = ns
profile_element.attrib['node'] = node

permissions_path = os.path.join(key_dir, 'permissions.xml')
domain_id = os.getenv('ROS_DOMAIN_ID', '0')
domain_id = os.getenv(DOMAIN_ID_ENV, '0')
create_permission_file(permissions_path, domain_id, policy_element)

signed_permissions_path = os.path.join(key_dir, 'permissions.p7s')
keystore_ca_key_path = os.path.join(root, 'ca.key.pem')
keystore_ca_key_path = os.path.join(keystore_path, 'ca.key.pem')
create_signed_permissions_file(
permissions_path, signed_permissions_path,
keystore_ca_cert_path, keystore_ca_key_path)

return True


def list_keys(args):
for name in os.listdir(args.ROOT):
if os.path.isdir(os.path.join(args.ROOT, name)):
def list_keys(keystore_path):
for name in os.listdir(keystore_path):
if os.path.isdir(os.path.join(keystore_path, name)):
print(name)
return True


def distribute_key(args):
def distribute_key(source_keystore_path, taget_keystore_path):
raise NotImplementedError()


def get_keystore_path_from_env():
root_keystore_env_var = 'ROS_SECURITY_ROOT_DIRECTORY'
root_keystore_path = os.getenv(root_keystore_env_var)
if root_keystore_path is None:
print('%s is empty' % root_keystore_env_var, file=sys.stderr)
return root_keystore_path


def generate_artifacts(keystore_path=None, identity_names=[], policy_files=[]):
if keystore_path is None:
keystore_path = get_keystore_path_from_env()
if keystore_path is None:
return False
if not is_valid_keystore(keystore_path):
print('%s is not a valid keystore, creating new keystore' % keystore_path)
create_keystore(keystore_path)

# create keys for all provided identities
for identity in identity_names:
if not create_key(keystore_path, identity):
return False
for policy_file in policy_files:
policy_tree = load_policy(policy_file)
profiles_element = policy_tree.find('profiles')
for profile in profiles_element:
identity_name = profile.get('ns').rstrip('/') + '/' + profile.get('node')
if not create_key(keystore_path, identity_name):
return False
policy_element = get_policy_from_tree(identity_name, policy_tree)
create_permissions_from_policy_element(
keystore_path, identity_name, policy_element)
return True
2 changes: 1 addition & 1 deletion sros2/sros2/verb/create_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ def add_arguments(self, parser, cli_name):
parser.add_argument('NAME', help='key name, aka ROS node name')

def main(self, *, args):
success = create_key(args)
success = create_key(args.ROOT, args.NAME)
return 0 if success else 1
2 changes: 1 addition & 1 deletion sros2/sros2/verb/create_keystore.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ def add_arguments(self, parser, cli_name):
arg.completer = DirectoriesCompleter()

def main(self, *, args):
success = create_keystore(args)
success = create_keystore(args.ROOT)
return 0 if success else 1
2 changes: 1 addition & 1 deletion sros2/sros2/verb/create_permission.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def add_arguments(self, parser, cli_name):

def main(self, *, args):
try:
success = create_permission(args)
success = create_permission(args.ROOT, args.NAME, args.POLICY_FILE_PATH)
except FileNotFoundError as e:
raise RuntimeError(str(e))
return 0 if success else 1
2 changes: 1 addition & 1 deletion sros2/sros2/verb/distribute_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ def add_arguments(self, parser, cli_name):
arg.completer = DirectoriesCompleter()

def main(self, *, args):
success = distribute_key(args)
success = distribute_key(args.ROOT, args.TARGET)
return 0 if success else 1
Loading

0 comments on commit a56d851

Please sign in to comment.