示例#1
0
    def __init__(self, certificate_dir=None):
        """Creates a Certs instance"""

        self.default_certs_dir = os.path.join(get_home(), 'certificates')
        self.root_ca_name = get_platform_instance_name() + '-root-ca'
        self.trusted_ca_name = get_platform_instance_name() + '-trusted-cas'
        self.default_root_ca_cn = '{} {}'.format(gethostname(),
                                                 self.root_ca_name)

        if not certificate_dir:
            certificate_dir = self.default_certs_dir
            # If user provided explicit directory then it should exist
            if not os.path.exists(certificate_dir):
                if certificate_dir != self.default_certs_dir:
                    raise ValueError('Invalid cert_dir {}'.format(self.cert_dir))

        self.cert_dir = os.path.join(os.path.expanduser(certificate_dir),
                                     'certs')
        self.private_dir = os.path.join(os.path.expanduser(certificate_dir),
                                        'private')
        self.ca_db_dir = os.path.join(os.path.expanduser(certificate_dir),
                                      'ca_db')
        self.csr_pending_dir = os.path.join(os.path.expanduser(certificate_dir),
                                            'pending_csr')
        self.remote_cert_dir = os.path.join(os.path.expanduser(certificate_dir),
                                            'remote_certs')
        self.certs_pending_dir = os.path.join(os.path.expanduser(certificate_dir),
                                              'pending_certs')
        self.rejected_dir = os.path.join(os.path.expanduser(certificate_dir),
                                         'rejected')

        required_paths = (self.cert_dir, self.private_dir, self.ca_db_dir,
                          self.csr_pending_dir, self.remote_cert_dir, self.certs_pending_dir)

        try:
            dir_created = False
            for p in required_paths:
                if not os.path.exists(p):
                    # explicitly provide rx to others since agent users should
                    # have read access to these dirs
                    os.makedirs(p)
                    os.chmod(p, 0o755)
                    dir_created = True
                else:
                    # if one exists all of them should exist. break
                    break
            if dir_created:
                os.chmod(os.path.expanduser(certificate_dir), 0o755)
        except Exception:
            raise RuntimeError("No permission to create certificates directory")
示例#2
0
    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()
示例#3
0
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
示例#4
0
    def __init__(self, certificate_dir=None):
        """Creates a Certs instance"""

        self.default_certs_dir = os.path.join(get_home(), 'certificates')
        self.root_ca_name = get_platform_instance_name() + '-root-ca'
        self.trusted_ca_name = get_platform_instance_name() + '-trusted-cas'
        self.default_root_ca_cn = '{} {}'.format(gethostname(),
                                                 self.root_ca_name)

        if not certificate_dir:
            certificate_dir = self.default_certs_dir
            # If user provided explicit directory then it should exist
            if not os.path.exists(certificate_dir):
                if certificate_dir != self.default_certs_dir:
                    raise ValueError('Invalid cert_dir {}'.format(self.cert_dir))

        self.cert_dir = os.path.join(os.path.expanduser(certificate_dir),
                                     'certs')
        self.private_dir = os.path.join(os.path.expanduser(certificate_dir),
                                        'private')
        self.ca_db_dir = os.path.join(os.path.expanduser(certificate_dir),
                                      'ca_db')
        self.csr_pending_dir = os.path.join(os.path.expanduser(certificate_dir),
                                            'pending_csr')
        self.remote_cert_dir = os.path.join(os.path.expanduser(certificate_dir),
                                            'remote_certs')
        self.certs_pending_dir = os.path.join(os.path.expanduser(certificate_dir),
                                              'pending_certs')
        self.rejected_dir = os.path.join(os.path.expanduser(certificate_dir),
                                         'rejected')

        required_paths = (self.cert_dir, self.private_dir, self.ca_db_dir,
                          self.csr_pending_dir, self.remote_cert_dir, self.certs_pending_dir)
        for p in required_paths:
            if not os.path.exists(p):
                os.makedirs(p, 0o755)
示例#5
0
    def _build_connection_parameters(self):
        param = None

        if self.identity is None:
            raise ValueError("Agent's VIP identity is not set")
        else:
            try:
                if self.instance_name == get_platform_instance_name():
                    param = self.rmq_mgmt.build_agent_connection(self.identity,
                                                                 self.instance_name)
                else:
                    param = self.rmq_mgmt.build_remote_connection_param(self.rmq_user,
                                                                        self.rmq_address,
                                                                        True)
            except AttributeError:
                _log.error("RabbitMQ broker may not be running. Restart the broker first")
                param = None

        return param
示例#6
0
    def _update_external_subscriptions(self, frames):
        """
        Store external subscriptions
        :param frames: frames containing external subscriptions
        :return:
        """
        results = []

        if len(frames) <= 7:
            return False
        else:
            _log.debug(f"external subscription frames {frames}")
            msg = frames[7]
            if not isinstance(msg, dict):
                raise ValueError(f"Invalid frame passed for frame {frames[7]}")

            try:
                this_platform_instance_name = get_platform_instance_name()
                for instance_name in msg:
                    if instance_name == this_platform_instance_name:
                        _log.error(
                            "Invalid configuraiton of external instances!\n"
                            f"The name {instance_name} is specified as local and "
                            "external instance name.  Please fix this issue in the "
                            "external_platform_discovery.json file in the "
                            "the VOLTTRON_HOME of the external instance.")
                        continue
                    prefixes = msg[instance_name]
                    # Store external subscription list for later use (during publish)
                    self._ext_subscriptions[instance_name] = prefixes
                    self._logger.debug(
                        "PUBSUBSERVICE New external list from {0}: List: {1}".
                        format(instance_name, self._ext_subscriptions))
                    if self._rabbitmq_agent:
                        for prefix in prefixes:
                            self._rabbitmq_agent.vip.pubsub.subscribe(
                                'pubsub', prefix, self.publish_callback)
            except KeyError as exc:
                self._logger.error(
                    "Unknown external instance name: {}".format(instance_name))
                return False
            return True
示例#7
0
    def _csr_request_new(self, env, data):
        _log.debug("New csr request")
        if not isinstance(data, dict):
            try:
                request_data = jsonapi.loads(data)
            except:
                _log.error(
                    "Invalid data for csr request.  Must be json serializable")
                return Response()
        else:
            request_data = data.copy()

        csr = request_data.get('csr').encode("utf-8")
        identity = self._certs.get_csr_common_name(csr)

        # The identity must start with the current instances name or it is a failure.
        if not identity.startswith(get_platform_instance_name() + "."):
            json_response = dict(
                status="ERROR",
                message="CSR must start with instance name: {}".format(
                    get_platform_instance_name()))
            Response(jsonapi.dumps(json_response),
                     content_type='application/json',
                     headers={'Content-type': 'application/json'})

        csr_file = self._certs.save_pending_csr_request(
            env.get('REMOTE_ADDR'), identity, csr)

        if self._auto_allow_csr:
            _log.debug(
                "Creating cert and permissions for user: {}".format(identity))
            status = self._certs.get_csr_status(identity)
            json_response = dict(status=status)
            if status == 'APPROVED':
                try:
                    json_response['cert'] = self._certs.get_cert_from_csr(
                        identity)
                except Exception as e:
                    _log.error(f"Exception getting cert from csr {e}")

            else:
                try:
                    cert = self._certs.approve_csr(identity)
                    #permissions = self._core().rmq_mgmt.get_default_permissions(identity)
                    _log.debug(f"CREATING NEW RMQ USER: {identity}")
                    permissions = dict(configure=".*", read=".*", write=".*")
                    self._core().rmq_mgmt.create_user_with_permissions(
                        identity, permissions, True)
                    json_response = dict(status="SUCCESSFUL", cert=cert)
                except BaseException as e:
                    _log.error(
                        f"Exception in approving csr/creating user in auto_allow_csr mode: {e}"
                    )
        else:

            status = self._certs.get_csr_status(identity)
            cert = self._certs.get_cert_from_csr(identity)

            json_response = dict(status=status)
            if status == "APPROVED":
                json_response['cert'] = self._certs.get_cert_from_csr(identity)
            elif status == "PENDING":
                json_response[
                    'message'] = "The request is pending admininstrator approval."
            elif status == "DENIED":
                json_response[
                    'message'] = "The request has been denied by the administrator."
            elif status == "UNKNOWN":
                json_response[
                    'message'] = "An unknown common name was specified to the server {}".format(
                        identity)
            else:
                json_response[
                    'message'] = "An unkonwn error has occured during the respons phase"

        response = None
        try:
            if json_response.get('cert', None):
                json_response['cert'] = json_response['cert'].decode('utf-8')
            response = Response(jsonapi.dumps(json_response),
                                content_type='application/json',
                                headers={'Content-type': 'application/json'})
        except BaseException as e:
            _log.error(f"Exception creating Response {e}")
        return response
示例#8
0
文件: auth.py 项目: VOLTTRON/volttron
    def connect_remote_platform(
            self,
            address,
            serverkey=None,
            agent_class=None
    ):
        """
        Agent attempts to connect to a remote platform to exchange data.

        address must start with http, https, tcp, ampq, or ampqs or a
        ValueError will be
        raised

        If this function is successful it will return an instance of the
        `agent_class`
        parameter if not then this function will return None.

        If the address parameter begins with http or https
        TODO: use the known host functionality here
        the agent will attempt to use Discovery to find the values
        associated with it.

        Discovery should return either an rmq-address or a vip-address or
        both.  In
        that situation the connection will be made using zmq.  In the event
        that
        fails then rmq will be tried.  If both fail then None is returned
        from this
        function.

        """
        from volttron.platform.vip.agent.utils import build_agent
        from volttron.platform.vip.agent import Agent

        if agent_class is None:
            agent_class = Agent

        parsed_address = urlparse(address)
        _log.debug("Begining auth.connect_remote_platform: {}".format(address))

        value = None
        if parsed_address.scheme == "tcp":
            # ZMQ connection
            hosts = KnownHostsStore()
            temp_serverkey = hosts.serverkey(address)
            if not temp_serverkey:
                _log.info(
                    "Destination serverkey not found in known hosts file, "
                    "using config"
                )
                destination_serverkey = serverkey
            elif not serverkey:
                destination_serverkey = temp_serverkey
            else:
                if temp_serverkey != serverkey:
                    raise ValueError(
                        "server_key passed and known hosts serverkey do not "
                        ""
                        "match!"
                    )
                destination_serverkey = serverkey

            publickey, secretkey = (
                self._core().publickey,
                self._core().secretkey,
            )
            _log.debug(
                "Connecting using: %s", get_fq_identity(self._core().identity)
            )

            value = build_agent(
                agent_class=agent_class,
                identity=get_fq_identity(self._core().identity),
                serverkey=destination_serverkey,
                publickey=publickey,
                secretkey=secretkey,
                message_bus="zmq",
                address=address,
            )
        elif parsed_address.scheme in ("https", "http"):
            from volttron.platform.web import DiscoveryInfo
            from volttron.platform.web import DiscoveryError

            try:
                # TODO: Use known host instead of looking up for discovery
                #  info if possible.

                # We need to discover which type of bus is at the other end.
                info = DiscoveryInfo.request_discovery_info(address)
                remote_identity = "{}.{}.{}".format(
                    info.instance_name,
                    get_platform_instance_name(),
                    self._core().identity,
                )
                # if the current message bus is zmq then we need
                # to connect a zmq on the remote, whether that be the
                # rmq router or proxy.  Also note that we are using the
                # fully qualified
                # version of the identity because there will be conflicts if
                # volttron central has more than one platform.agent connecting
                if get_messagebus() == "zmq":
                    if not info.vip_address or not info.serverkey:
                        err = (
                            "Discovery from {} did not return serverkey "
                            "and/or vip_address".format(address)
                        )
                        raise ValueError(err)

                    _log.debug(
                        "Connecting using: %s",
                        get_fq_identity(self._core().identity),
                    )

                    # use fully qualified identity
                    value = build_agent(
                        identity=get_fq_identity(self._core().identity),
                        address=info.vip_address,
                        serverkey=info.serverkey,
                        secretkey=self._core().secretkey,
                        publickey=self._core().publickey,
                        agent_class=agent_class,
                    )

                else:  # we are on rmq messagebus

                    # This is if both remote and local are rmq message buses.
                    if info.messagebus_type == "rmq":
                        _log.debug("Both remote and local are rmq messagebus.")
                        fqid_local = get_fq_identity(self._core().identity)

                        # Check if we already have the cert, if so use it
                        # instead of requesting cert again
                        remote_certs_dir = self.get_remote_certs_dir()
                        remote_cert_name = "{}.{}".format(
                            info.instance_name, fqid_local
                        )
                        certfile = os.path.join(
                            remote_certs_dir, remote_cert_name + ".crt"
                        )
                        if os.path.exists(certfile):
                            response = certfile
                        else:
                            response = self.request_cert(
                                address, fqid_local, info
                            )

                        if response is None:
                            _log.error("there was no response from the server")
                            value = None
                        elif isinstance(response, tuple):
                            if response[0] == "PENDING":
                                _log.info(
                                    "Waiting for administrator to accept a "
                                    "CSR request."
                                )
                            value = None
                        # elif isinstance(response, dict):
                        #     response
                        elif os.path.exists(response):
                            # info = DiscoveryInfo.request_discovery_info(
                            # address)
                            # From the remote platforms perspective the
                            # remote user name is
                            #   remoteinstance.localinstance.identity,
                            #   this is what we must
                            #   pass to the build_remote_connection_params
                            #   for a successful

                            remote_rmq_user = get_fq_identity(
                                fqid_local, info.instance_name
                            )
                            _log.debug(
                                "REMOTE RMQ USER IS: %s", remote_rmq_user
                            )
                            remote_rmq_address = self._core().rmq_mgmt.build_remote_connection_param(
                                remote_rmq_user,
                                info.rmq_address,
                                ssl_auth=True,
                                cert_dir=self.get_remote_certs_dir(),
                            )

                            value = build_agent(
                                identity=fqid_local,
                                address=remote_rmq_address,
                                instance_name=info.instance_name,
                                publickey=self._core().publickey,
                                secretkey=self._core().secretkey,
                                message_bus="rmq",
                                enable_store=False,
                                agent_class=agent_class,
                            )
                        else:
                            raise ValueError(
                                "Unknown path through discovery process!"
                            )

                    else:
                        # TODO: cache the connection so we don't always have
                        #  to ping the server to connect.

                        # This branch happens when the message bus is not
                        # the same note
                        # this writes to the agent-data directory of this
                        # agent if the agent
                        # is installed.
                        if get_messagebus() == "rmq":
                            if not os.path.exists("keystore.json"):
                                with open("keystore.json", "w") as file_pointer:
                                    file_pointer.write(
                                        jsonapi.dumps(
                                            KeyStore.generate_keypair_dict()
                                        )
                                    )

                            with open("keystore.json") as file_pointer:
                                keypair = jsonapi.loads(file_pointer.read())

                        value = build_agent(
                            agent_class=agent_class,
                            identity=remote_identity,
                            serverkey=info.serverkey,
                            publickey=keypair.get("publickey"),
                            secretkey=keypair.get("secretekey"),
                            message_bus="zmq",
                            address=info.vip_address,
                        )
            except DiscoveryError:
                _log.error(
                    "Couldn't connect to %s or incorrect response returned "
                    "response was %s",
                    address,
                    value,
                )

        else:
            raise ValueError(
                "Invalid configuration found the address: {} has an invalid "
                "scheme".format(address)
            )

        return value
示例#9
0
def setup_rabbitmq_volttron(setup_type, verbose=False, prompt=False, instance_name=None,
                            rmq_conf_file=None, env=None):
    """
    Setup VOLTTRON instance to run with RabbitMQ message bus.
    :param setup_type:
            single - Setup to run as single instance
            federation - Setup to connect multiple VOLTTRON instances as
                         a federation
            shovel - Setup shovels to forward local messages to remote instances
    :param verbose
    :param prompt
    :raises RabbitMQSetupAlreadyError
    """
    if not instance_name:
        instance_name = get_platform_instance_name(prompt=True)
    # Store config this is checked at startup
    store_message_bus_config(message_bus='rmq', instance_name=instance_name)

    rmq_config = RMQConfig()
    if verbose:
        _log.setLevel(logging.DEBUG)
        _log.debug("verbose set to True")
        _log.debug(get_home())
        logging.getLogger("requests.packages.urllib3.connectionpool"
                          "").setLevel(logging.DEBUG)
    else:
        _log.setLevel(logging.INFO)
        logging.getLogger("requests.packages.urllib3.connectionpool"
                          "").setLevel(logging.WARN)

    if prompt:
        # ignore any existing rabbitmq_config.yml in vhome. Prompt user and
        # generate a new rabbitmq_config.yml
        _create_rabbitmq_config(rmq_config, setup_type)

    # Load either the newly created config or config passed
    try:
        rmq_config.load_rmq_config()

    except (yaml.parser.ParserError, yaml.scanner.ScannerError, yaml.YAMLError) as exc:
        _log.error("Error: YAML file cannot parsed properly. Check the contents of the file")
        return exc

    except IOError as exc:
        _log.error("Error opening {}. Please create a rabbitmq_config.yml "
                   "file in your volttron home. If you want to point to a "
                   "volttron home other than {} please set it as the "
                   "environment variable VOLTTRON_HOME".format(
            rmq_config.volttron_rmq_config, rmq_config.volttron_home))
        _log.error("\nFor single setup, configuration file must at least "
                   "contain host and ssl certificate details. For federation "
                   "and shovel setup, config should contain details about the "
                   "volttron instance with which communication needs "
                   "to be established. Please refer to example config file "
                   "at examples/configurations/rabbitmq/rabbitmq_config.yml")
        raise

    if not rmq_conf_file:
        rmq_conf_file = os.path.join(rmq_config.rmq_home, "etc/rabbitmq/rabbitmq.conf")

    invalid = True
    if setup_type in ["all", "single"]:
        invalid = False
        # Verify that the rmq_conf_file if exists is removed before continuing.
        message = f"A rabbitmq conf file {rmq_conf_file} already exists.\n" \
                  "In order for setup to proceed it must be removed.\n"
        if os.path.exists(rmq_conf_file):
            print(message)
            while os.path.exists(rmq_conf_file):
                value = prompt_response(f"Remove {rmq_conf_file}? ", y_or_n)
                if value in y:
                    os.remove(rmq_conf_file)

        _start_rabbitmq_without_ssl(rmq_config, rmq_conf_file, env=env)
        _log.debug("Creating rabbitmq virtual hosts and required users for "
                   "volttron")
        # Create local RabbitMQ setup - vhost, exchange etc.
        # should be called after _start_rabbitmq_without_ssl
        rmq_mgmt = RabbitMQMgmt()
        success = rmq_mgmt.init_rabbitmq_setup()
        if success and rmq_config.is_ssl:
            _setup_for_ssl_auth(rmq_config, rmq_conf_file, env=env)

        # Create utility scripts
        script_path = os.path.dirname(os.path.realpath(__file__))
        src_home = os.path.dirname(os.path.dirname(script_path))
        start_script = os.path.join(src_home, 'start-rabbitmq')
        with open(start_script, 'w+') as f:
            f.write(os.path.join(rmq_config.rmq_home, 'sbin',
                                 'rabbitmq-server') + ' -detached')
            f.write(os.linesep)
            f.write("sleep 5")  # give a few seconds for all plugins to be ready
        os.chmod(start_script, 0o755)

        stop_script = os.path.join(src_home, 'stop-rabbitmq')
        with open(stop_script, 'w+') as f:
            f.write(os.path.join(rmq_config.rmq_home, 'sbin',
                                 'rabbitmqctl') + ' stop')
        os.chmod(stop_script, 0o755)

        # symlink to rmq log
        log_name = os.path.join(src_home, 'rabbitmq.log')
        if os.path.lexists(log_name):
            os.unlink(log_name)

        os.symlink(os.path.join(rmq_config.rmq_home,
                                'var/log/rabbitmq',
                                rmq_config.node_name + "@" +
                                rmq_config.hostname.split('.')[0] + ".log"),
                   log_name)

    if setup_type in ["all", "federation"]:
        # Create a multi-platform federation setup
        invalid = False
        _create_federation_setup(rmq_config.admin_user,
                                 rmq_config.admin_pwd,
                                 rmq_config.is_ssl,
                                 rmq_config.virtual_host,
                                 rmq_config.volttron_home)
    if setup_type in ["all", "shovel"]:
        # Create shovel setup
        invalid = False
        if rmq_config.is_ssl:
            port = rmq_config.amqp_port_ssl
        else:
            port = rmq_config.amqp_port
        _create_shovel_setup(rmq_config.instance_name,
                             rmq_config.hostname,
                             port,
                             rmq_config.virtual_host,
                             rmq_config.volttron_home,
                             rmq_config.is_ssl)
    if invalid:
        _log.error("Unknown option. Exiting....")