Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generate_artifacts verb #107

Merged
merged 3 commits into from
Apr 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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