def install_agent(self, agent_wheel, vip_identity=None, publickey=None, secretkey=None): if self.secure_agent_user: _log.info("Installing secure Volttron agent...") while True: agent_uuid = str(uuid.uuid4()) if agent_uuid in self.agents: continue agent_path = os.path.join(self.install_dir, agent_uuid) try: os.mkdir(agent_path) break except OSError as exc: if exc.errno != errno.EEXIST: raise try: if auth is not None and self.env.verify_agents: unpacker = auth.VolttronPackageWheelFile(agent_wheel, certsobj=certs.Certs()) unpacker.unpack(dest=agent_path) else: unpack(agent_wheel, dest=agent_path) # Is it ok to remove the wheel file after unpacking? os.remove(agent_wheel) final_identity = self._setup_agent_vip_id( agent_uuid, vip_identity=vip_identity) keystore = self.get_agent_keystore(agent_uuid, publickey, secretkey) self._authorize_agent_keys(agent_uuid, final_identity, keystore.public) if self.message_bus == 'rmq': rmq_user = get_fq_identity(final_identity, self.instance_name) certs.Certs().create_signed_cert_files(rmq_user, overwrite=False) if self.secure_agent_user: # When installing, we always create a new user, as anything # that already exists is untrustworthy created_user = self.add_agent_user(self.agent_name(agent_uuid), agent_path) self.set_agent_user_permissions(created_user, agent_uuid, agent_path) except Exception: shutil.rmtree(agent_path) raise return agent_uuid
def _sign_agent_package(agent_package, **kwargs): """Sign an agent package""" if not os.path.exists(agent_package): raise AgentPackageError('Invalid package {}'.format(agent_package)) cert_type = _cert_type_from_kwargs(**kwargs) files = _files_from_kwargs(**kwargs) certs_dir = kwargs.get('certs_dir', None) certsobj = None if certs_dir is not None: certsobj = certs.Certs(certs_dir) if cert_type == 'admin': if files: raise AgentPackageError("admin's aren't allowed to add files.") verified = auth.sign_as_admin(agent_package, 'admin', certsobj = certsobj) elif cert_type == 'creator': verified = auth.sign_as_creator(agent_package, 'creator', files, certsobj = certsobj) elif cert_type == 'initiator': verified = auth.sign_as_initiator(agent_package, 'initiator', files, certsobj = certsobj) elif cert_type == 'platform': verified = auth.sign_as_platform(agent_package, 'platform', files) else: raise AgentPackageError('Unknown packaging options') if verified: print('{} signed as {}'.format(agent_package, cert_type)) else: print('Verification of signing failed!')
def __init__(self): self.instance_name = get_platform_instance_name() # This is written durn the bootstrap phase of the rabbitmq installation # however for docker we don't write this at all so we need to # use the default location for this rmq_home_file = os.path.expanduser("~/.volttron_rmq_home") if os.path.isfile(rmq_home_file): with open(os.path.expanduser("~/.volttron_rmq_home")) as f: self.rabbitmq_server = f.read().strip() else: self.rabbitmq_server = os.path.expanduser( "~/rabbitmq_server/rabbitmq_server-3.7.7/") assert os.path.isdir(self.rabbitmq_server ), "Missing rabbitmq server directory{}".format( self.rabbitmq_server) self.crts = certs.Certs() self.volttron_home = get_home() self.volttron_rmq_config = os.path.join(self.volttron_home, 'rabbitmq_config.yml') self.default_pass = "******" self.config_opts = None try: self.load_rmq_config() except (IOError, yaml.YAMLError) as exc: self.config_opts = {} self._set_default_config()
def _create_certs(cfg_path, platform_cfg): print("Creating CA Certificate...") crts = certs.Certs() data = { "C": "US", "ST": "WA", "L": "Richmond", "O": "PNNL", "OU": "Volttron", "CN": f"{platform_cfg.get('instance-name')}-root-ca", } crts.create_root_ca(overwrite=False, **data) copy(crts.cert_file(crts.root_ca_name), crts.cert_file(crts.trusted_ca_name)) print("Creating new web server certificate.") print( "Creating and signing new certificate using the newly created CA certificate." ) name = f"{platform_cfg.get('instance-name')}-{PLATFORM_WEB}" crts.create_signed_cert_files( name=name + "-server", cert_type="server", ca_name=crts.root_ca_name, fqdn=get_hostname(), ) master_web_cert = os.path.join(VOLTTRON_HOME, "certificates/certs/", name + "-server.crt") master_web_key = os.path.join(VOLTTRON_HOME, "certificates/private/", name + "-server.pem") print("Writing ssl cert and key paths to config.") with open(os.path.join(cfg_path), "a") as fout: fout.write(f"web-ssl-cert = {master_web_cert}\n") fout.write(f"web-ssl-key = {master_web_key}\n")
def _create_web_certs(): global config_opts """ Utility to create web server certificates Designed to be used in conjecture with get_cert_and_key As such, it assumes that the program has already checked for existing certs, and prompted the user to enter in certificates that they have generated separately. """ crts = certs.Certs() try: crts.ca_cert() except certs.CertError: print("WARNING! CA certificate does not exist.") prompt_str = "Create new root CA?" prompt = prompt_response(prompt_str, valid_answers=y_or_n, default='Y') if prompt in y: cert_data = {} print( "\nPlease enter the following details for web server certificate:" ) prompt = '\tCountry:' cert_data['country'] = prompt_response(prompt, default='US') prompt = '\tState:' cert_data['state'] = prompt_response(prompt, mandatory=True) prompt = '\tLocation:' cert_data['location'] = prompt_response(prompt, mandatory=True) prompt = '\tOrganization:' cert_data['organization'] = prompt_response(prompt, mandatory=True) prompt = '\tOrganization Unit:' cert_data['organization-unit'] = prompt_response(prompt, mandatory=True) cert_data['common-name'] = get_platform_instance_name( ) + '-root-ca' data = { 'C': cert_data.get('country'), 'ST': cert_data.get('state'), 'L': cert_data.get('location'), 'O': cert_data.get('organization'), 'OU': cert_data.get('organization-unit'), 'CN': cert_data.get('common-name') } crts.create_root_ca(overwrite=False, **data) copy(crts.cert_file(crts.root_ca_name), crts.cert_file(crts.trusted_ca_name)) else: return 1 print("Creating new web server certificate.") crts.create_signed_cert_files(name=PLATFORM_WEB + "-server", cert_type='server', ca_name=crts.root_ca_name, fqdn=get_hostname()) return 0
def _create_ca(override=True, data=None): """Creates a root ca cert using the Certs class""" crts = certs.Certs() if crts.ca_exists(): if override: msg = '''Creating a new root ca will overwrite the current ca and invalidate any signed certs. Are you sure you want to do this? type 'yes' to continue: ''' continue_yes = input(msg) if continue_yes.upper() != 'YES': return if not data: data = _create_cert_ui(crts.default_root_ca_cn) crts.create_root_ca(**data)
def _create_cert(name=None, **kwargs): """Create a cert using options specified on the command line""" crts = certs.Certs() if not crts.ca_exists(): sys.stderr.write('Root CA must be created before certificates\n') sys.exit(0) cert_type = _cert_type_from_kwargs(**kwargs) if name == None: name = cert_type cert_data = _create_cert_ui(cert_type) else: cert_data = _create_cert_ui('{} ({})'.format(cert_type, name)) crts.create_ca_signed_cert(name, **cert_data)
def install_agent(self, agent_wheel, vip_identity=None, publickey=None, secretkey=None): while True: agent_uuid = str(uuid.uuid4()) if agent_uuid in self.agents: continue agent_path = os.path.join(self.install_dir, agent_uuid) try: os.mkdir(agent_path) break except OSError as exc: if exc.errno != errno.EEXIST: raise try: if auth is not None and self.env.verify_agents: unpacker = auth.VolttronPackageWheelFile( agent_wheel, certsobj=certs.Certs()) unpacker.unpack(dest=agent_path) else: unpack(agent_wheel, dest=agent_path) final_identity = self._setup_agent_vip_id( agent_uuid, vip_identity=vip_identity) if publickey is not None and secretkey is not None: keystore = self.get_agent_keystore(agent_uuid) keystore.public = publickey keystore.secret = secretkey self._authorize_agent_keys(agent_uuid, final_identity) except Exception: shutil.rmtree(agent_path) raise return agent_uuid
def start_agent(self, agent_uuid): name = self.agent_name(agent_uuid) agent_dir = os.path.join(self.install_dir, agent_uuid) agent_path_with_name = os.path.join(agent_dir, name) execenv = self.agents.get(agent_uuid) if execenv and execenv.process.poll() is None: _log.warning('request to start already running agent %s', agent_path_with_name) raise ValueError('agent is already running') pkg = UnpackedPackage(agent_path_with_name) if auth is not None and self.env.verify_agents: auth.UnpackedPackageVerifier(pkg.distinfo).verify() metadata = pkg.metadata try: exports = metadata['extensions']['python.exports'] except KeyError: try: exports = metadata['exports'] except KeyError: raise ValueError('no entry points exported') try: module = exports['volttron.agent']['launch'] except KeyError: try: module = exports['setuptools.installation']['eggsecutable'] except KeyError: _log.error('no agent launch class specified in package %s', agent_path_with_name) raise ValueError('no agent launch class specified in package') config = os.path.join(pkg.distinfo, 'config') tag = self.agent_tag(agent_uuid) environ = os.environ.copy() environ['PYTHONPATH'] = ':'.join([agent_path_with_name] + sys.path) environ['PATH'] = (os.path.abspath(os.path.dirname(sys.executable)) + ':' + environ['PATH']) if os.path.exists(config): environ['AGENT_CONFIG'] = config else: environ.pop('AGENT_CONFIG', None) if tag: environ['AGENT_TAG'] = tag else: environ.pop('AGENT_TAG', None) environ['AGENT_SUB_ADDR'] = self.subscribe_address environ['AGENT_PUB_ADDR'] = self.publish_address environ['AGENT_UUID'] = agent_uuid environ['_LAUNCHED_BY_PLATFORM'] = '1' # For backwards compatibility create the identity file if it does not # exist. identity_file = os.path.join(self.install_dir, agent_uuid, "IDENTITY") if not os.path.exists(identity_file): _log.debug( 'IDENTITY FILE MISSING: CREATING IDENTITY FILE WITH VALUE: {}'. format(agent_uuid)) with open(identity_file, 'w') as fp: fp.write(agent_uuid) with open(identity_file, 'r') as fp: agent_vip_identity = fp.read() environ['AGENT_VIP_IDENTITY'] = agent_vip_identity module, _, func = module.partition(':') # if func: # code = '__import__({0!r}, fromlist=[{1!r}]).{1}()'.format(module, # func) # argv = [sys.executable, '-c', code] # else: argv = [sys.executable, '-m', module] resmon = getattr(self.env, 'resmon', None) agent_user = None data_dir = self._get_agent_data_dir(agent_path_with_name) if self.secure_agent_user: _log.info("Starting agent securely...") user_id_path = os.path.join(agent_dir, "USER_ID") try: with open(user_id_path, "r") as user_id_file: volttron_agent_id = user_id_file.readline() pwd.getpwnam(volttron_agent_id) agent_user = volttron_agent_id _log.info("Found secure volttron agent user {}".format( agent_user)) except (IOError, KeyError) as err: _log.info("No existing volttron agent user was found at {} due " "to {}".format(user_id_path, err)) # May be switched from normal to secure mode with existing agents. To handle this case # create users and also set permissions again for existing files agent_user = self.add_agent_user(name, agent_dir) self.set_agent_user_permissions(agent_user, agent_uuid, agent_dir) # additionally give permissions to contents of agent-data dir. # This is needed only for agents installed before switching to # secure mode. Agents installed in secure mode will own files # in agent-data dir # Moved this to the top so that "agent-data" directory gets # created in the beginning #data_dir = self._get_agent_data_dir(agent_path_with_name) for (root, directories, files) in os.walk(data_dir, topdown=True): for directory in directories: self.set_acl_for_path("rwx", agent_user, os.path.join(root, directory)) for f in files: self.set_acl_for_path("rwx", agent_user, os.path.join(root, f)) if self.message_bus == 'rmq': rmq_user = get_fq_identity(agent_vip_identity, self.instance_name) _log.info("Create RMQ user {} for agent {}".format(rmq_user, agent_vip_identity)) self.rmq_mgmt.create_user_with_permissions(rmq_user, self.rmq_mgmt.get_default_permissions(rmq_user), ssl_auth=True) key_file = certs.Certs().private_key_file(rmq_user) if not os.path.exists(key_file): # This could happen when user switches from zmq to rmq after installing agent _log.info(f"agent certs don't exists. creating certs for agent") certs.Certs().create_signed_cert_files(rmq_user, overwrite=False) if self.secure_agent_user: # give read access to user to its own private key file. self.set_acl_for_path("r", agent_user, key_file) if resmon is None: if agent_user: execenv = SecureExecutionEnvironment(agent_user=agent_user) else: execenv = ExecutionEnvironment() else: execreqs = self._read_execreqs(pkg.distinfo) execenv = self._reserve_resources(resmon, execreqs, agent_user=agent_user) execenv.name = name or agent_path_with_name _log.info('starting agent %s', agent_path_with_name) # data_dir = self._get_agent_data_dir(agent_path_with_name) _log.info("starting agent using {} ".format(type(execenv))) execenv.execute(argv, cwd=agent_path_with_name, env=environ, close_fds=True, stdin=open(os.devnull), stdout=PIPE, stderr=PIPE) self.agents[agent_uuid] = execenv proc = execenv.process _log.info('agent %s has PID %s', agent_path_with_name, proc.pid) gevent.spawn(log_stream, 'agents.stderr', name, proc.pid, argv[0], log_entries('agents.log', name, proc.pid, logging.ERROR, proc.stderr)) gevent.spawn(log_stream, 'agents.stdout', name, proc.pid, argv[0], ((logging.INFO, line) for line in (l.splitlines() for l in proc.stdout))) return self.agent_status(agent_uuid)