Halite is long gone

This commit is contained in:
Pedro Algarvio 2020-12-21 04:08:54 +00:00
parent 20b274710f
commit 6bc0f8e3a5
3 changed files with 0 additions and 621 deletions

View file

@ -1,237 +0,0 @@
.. _tutorial-halite:
=================================
Installing and Configuring Halite
=================================
.. warning:: Halite is deprecated
The Halite project is retired. The code will remain available on GitHub.
In this tutorial, we'll walk through installing and setting up Halite. The
current version of Halite is considered pre-alpha and is supported only in Salt
``v2014.1.0`` or greater. Additional information is available on GitHub:
https://github.com/saltstack/halite
Before beginning this tutorial, ensure that the salt-master is installed. To
install the salt-master, please review the installation documentation:
http://docs.saltstack.com/topics/installation/index.html
.. note::
Halite only works with Salt versions greater than 2014.1.0.
Installing Halite Via Package
=============================
On CentOS, RHEL, or Fedora:
.. code-block:: bash
$ yum install python-halite
.. note::
By default python-halite only installs CherryPy. If you would like to use
a different webserver please review the instructions below to install
pip and your server of choice. The package does not modify the master
configuration with ``/etc/salt/master``.
Installing Halite Using pip
===========================
To begin the installation of Halite from PyPI, you'll need to install pip. The
Salt package, as well as the bootstrap, do not install pip by default.
On CentOS, RHEL, or Fedora:
.. code-block:: bash
$ yum install python-pip
On Debian:
.. code-block:: bash
$ apt-get install python-pip
Once you have pip installed, use it to install halite:
.. code-block:: bash
$ pip install -U halite
Depending on the webserver you want to run halite through, you'll need to
install that piece as well. On RHEL based distros, use one of the following:
.. code-block:: bash
$ pip install cherrypy
.. code-block:: bash
$ pip install paste
.. code-block:: bash
$ yum install python-devel
$ yum install gcc
$ pip install gevent
$ pip install pyopenssl
On Debian based distributions:
.. code-block:: bash
$ pip install CherryPy
.. code-block:: bash
$ pip install paste
.. code-block:: bash
$ apt-get install gcc
$ apt-get install python-dev
$ apt-get install libevent-dev
$ pip install gevent
$ pip install pyopenssl
Configuring Halite Permissions
==============================
Configuring Halite access permissions is easy. By default, you only need to
ensure that the @runner group is configured. In the ``/etc/salt/master`` file,
uncomment and modify the following lines:
.. code-block:: yaml
external_auth:
pam:
testuser:
- .*
- '@runner'
.. note::
You cannot use the root user for pam login; it will fail to authenticate.
Halite uses the runner manage.present to get the status of minions, so runner
permissions are required. For example:
.. code-block:: yaml
external_auth:
pam:
mytestuser:
- .*
- '@runner'
- '@wheel'
Currently Halite allows, but does not require, any wheel modules.
Configuring Halite Settings
===========================
Once you've configured the permissions for Halite, you'll need to set up the
Halite settings in the /etc/salt/master file. Halite supports CherryPy, Paste, and Gevent out of the box.
To configure cherrypy, add the following to the bottom of your /etc/salt/master file:
.. code-block:: yaml
halite:
level: 'debug'
server: 'cherrypy'
host: '0.0.0.0'
port: '8080'
cors: False
tls: True
certpath: '/etc/pki/tls/certs/localhost.crt'
keypath: '/etc/pki/tls/certs/localhost.key'
pempath: '/etc/pki/tls/certs/localhost.pem'
If you wish to use paste:
.. code-block:: yaml
halite:
level: 'debug'
server: 'paste'
host: '0.0.0.0'
port: '8080'
cors: False
tls: True
certpath: '/etc/pki/tls/certs/localhost.crt'
keypath: '/etc/pki/tls/certs/localhost.key'
pempath: '/etc/pki/tls/certs/localhost.pem'
To use gevent:
.. code-block:: yaml
halite:
level: 'debug'
server: 'gevent'
host: '0.0.0.0'
port: '8080'
cors: False
tls: True
certpath: '/etc/pki/tls/certs/localhost.crt'
keypath: '/etc/pki/tls/certs/localhost.key'
pempath: '/etc/pki/tls/certs/localhost.pem'
The "cherrypy" and "gevent" servers require the certpath and keypath files
to run tls/ssl. The .crt file holds the public cert and the .key file holds
the private key. Whereas the "paste" server requires a single .pem file that
contains both the cert and key. This can be created simply by concatenating
the .crt and .key files.
If you want to use a self-signed cert, you can create one using the Salt.tls
module:
.. note::
The following command needs to be run on your salt master.
.. code-block:: bash
salt-call tls.create_self_signed_cert tls
Note that certs generated by the above command can be found under the ``/etc/pki/tls/certs/`` directory.
When using self-signed certs, browsers will need approval before accepting the
cert. If the web application page has been cached with a non-HTTPS version of
the app, then the browser cache will have to be cleared before it will
recognize and prompt to accept the self-signed certificate.
Starting Halite
===============
Once you've configured the halite section of your /etc/salt/master, you can
restart the salt-master service, and your halite instance will be available.
Depending on your configuration, the instance will be available either at
https://localhost:8080/app, https://domain:8080/app, or
https://123.456.789.012:8080/app .
.. note::
halite requires an HTML 5 compliant browser.
All logs relating to halite are logged to the default /var/log/salt/master file.

View file

@ -1,333 +0,0 @@
"""
This module provides the point of entry for client applications to interface to
salt. The purpose is to have a simplified consistent interface for various
client applications.
.. warning:: This API is not yet public or stable!
This API exists in its current form as an entry point for Halite only. This
interface is likely to change without warning. Long-term plans are to make
this public as a unified interface to Salt's *Client() APIs. Until that
time please use Salt's *Client() interfaces individually:
http://docs.saltstack.com/ref/clients/index.html
"""
import os
import salt.auth
import salt.client
import salt.config
import salt.runner
import salt.syspaths as syspaths
import salt.utils.args
import salt.utils.event
import salt.wheel
from salt.exceptions import EauthAuthenticationError
def tokenify(cmd, token=None):
"""
If token is not None Then assign token to 'token' key of cmd dict
and return cmd
Otherwise return cmd
"""
if token is not None:
cmd["token"] = token
return cmd
class APIClient:
"""
Provide a uniform method of accessing the various client interfaces in Salt
in the form of low-data data structures. For example:
"""
def __init__(self, opts=None, listen=True):
if not opts:
opts = salt.config.client_config(
os.environ.get(
"SALT_MASTER_CONFIG", os.path.join(syspaths.CONFIG_DIR, "master")
)
)
self.opts = opts
self.localClient = salt.client.get_local_client(self.opts["conf_file"])
self.runnerClient = salt.runner.RunnerClient(self.opts)
self.wheelClient = salt.wheel.Wheel(self.opts)
self.resolver = salt.auth.Resolver(self.opts)
self.event = salt.utils.event.get_event(
"master",
self.opts["sock_dir"],
self.opts["transport"],
opts=self.opts,
listen=listen,
)
def run(self, cmd):
"""
Execute the salt command given by cmd dict.
cmd is a dictionary of the following form:
{
'mode': 'modestring',
'fun' : 'modulefunctionstring',
'kwarg': functionkeywordargdictionary,
'tgt' : 'targetpatternstring',
'tgt_type' : 'targetpatterntype',
'ret' : 'returner namestring',
'timeout': 'functiontimeout',
'arg' : 'functionpositionalarg sequence',
'token': 'salttokenstring',
'username': 'usernamestring',
'password': 'passwordstring',
'eauth': 'eauthtypestring',
}
Implied by the fun is which client is used to run the command, that is, either
the master local minion client, the master runner client, or the master wheel client.
The cmd dict items are as follows:
mode: either 'sync' or 'asynchronous'. Defaults to 'asynchronous' if missing
fun: required. If the function is to be run on the master using either
a wheel or runner client then the fun: includes either
'wheel.' or 'runner.' as a prefix and has three parts separated by '.'.
Otherwise the fun: specifies a module to be run on a minion via the local
minion client.
Example:
fun of 'wheel.config.values' run with master wheel client
fun of 'runner.manage.status' run with master runner client
fun of 'test.ping' run with local minion client
fun of 'wheel.foobar' run with with local minion client not wheel
kwarg: A dictionary of keyword function parameters to be passed to the eventual
salt function specified by fun:
tgt: Pattern string specifying the targeted minions when the implied client is local
tgt_type: Optional target pattern type string when client is local minion.
Defaults to 'glob' if missing
ret: Optional name string of returner when local minion client.
arg: Optional positional argument string when local minion client
token: the salt token. Either token: is required or the set of username:,
password: , and eauth:
username: the salt username. Required if token is missing.
password: the user's password. Required if token is missing.
eauth: the authentication type such as 'pam' or 'ldap'. Required if token is missing
"""
cmd = dict(cmd) # make copy
client = "minion" # default to local minion client
mode = cmd.get("mode", "async")
# check for wheel or runner prefix to fun name to use wheel or runner client
funparts = cmd.get("fun", "").split(".")
if len(funparts) > 2 and funparts[0] in ["wheel", "runner"]: # master
client = funparts[0]
cmd["fun"] = ".".join(funparts[1:]) # strip prefix
if not (
"token" in cmd
or ("eauth" in cmd and "password" in cmd and "username" in cmd)
):
raise EauthAuthenticationError("No authentication credentials given")
executor = getattr(self, "{}_{}".format(client, mode))
result = executor(**cmd)
return result
def minion_async(self, **kwargs):
"""
Wrap LocalClient for running :ref:`execution modules <all-salt.modules>`
and immediately return the job ID. The results of the job can then be
retrieved at a later time.
.. seealso:: :ref:`python-api`
"""
return self.localClient.run_job(**kwargs)
def minion_sync(self, **kwargs):
"""
Wrap LocalClient for running :ref:`execution modules <all-salt.modules>`
.. seealso:: :ref:`python-api`
"""
return self.localClient.cmd(**kwargs)
def runner_async(self, **kwargs):
"""
Wrap RunnerClient for executing :ref:`runner modules <all-salt.runners>`
Expects that one of the kwargs is key 'fun' whose value is the namestring
of the function to call
"""
return self.runnerClient.master_call(**kwargs)
runner_sync = runner_async # always runner asynchronous, so works in either mode
def wheel_sync(self, **kwargs):
"""
Wrap Wheel to enable executing :ref:`wheel modules <all-salt.wheel>`
Expects that one of the kwargs is key 'fun' whose value is the namestring
of the function to call
"""
return self.wheelClient.master_call(**kwargs)
wheel_async = wheel_sync # always wheel_sync, so it works either mode
def signature(self, cmd):
"""
Convenience function that returns dict of function signature(s) specified by cmd.
cmd is dict of the form:
{
'module' : 'modulestring',
'tgt' : 'targetpatternstring',
'tgt_type' : 'targetpatterntype',
'token': 'salttokenstring',
'username': 'usernamestring',
'password': 'passwordstring',
'eauth': 'eauthtypestring',
}
The cmd dict items are as follows:
module: required. This is either a module or module function name for
the specified client.
tgt: Optional pattern string specifying the targeted minions when client
is 'minion'
tgt_type: Optional target pattern type string when client is 'minion'.
Example: 'glob' defaults to 'glob' if missing
token: the salt token. Either token: is required or the set of username:,
password: , and eauth:
username: the salt username. Required if token is missing.
password: the user's password. Required if token is missing.
eauth: the authentication type such as 'pam' or 'ldap'. Required if token is missing
Adds client per the command.
"""
cmd["client"] = "minion"
if len(cmd["module"].split(".")) > 2 and cmd["module"].split(".")[0] in [
"runner",
"wheel",
]:
cmd["client"] = "master"
return self._signature(cmd)
def _signature(self, cmd):
"""
Expects everything that signature does and also a client type string.
client can either be master or minion.
"""
result = {}
client = cmd.get("client", "minion")
if client == "minion":
cmd["fun"] = "sys.argspec"
cmd["kwarg"] = dict(module=cmd["module"])
result = self.run(cmd)
elif client == "master":
parts = cmd["module"].split(".")
client = parts[0]
module = ".".join(parts[1:]) # strip prefix
if client == "wheel":
functions = self.wheelClient.functions
elif client == "runner":
functions = self.runnerClient.functions
result = {"master": salt.utils.args.argspec_report(functions, module)}
return result
def create_token(self, creds):
"""
Create token with creds.
Token authorizes salt access if successful authentication
with the credentials in creds.
creds format is as follows:
{
'username': 'namestring',
'password': 'passwordstring',
'eauth': 'eauthtypestring',
}
examples of valid eauth type strings: 'pam' or 'ldap'
Returns dictionary of token information with the following format:
{
'token': 'tokenstring',
'start': starttimeinfractionalseconds,
'expire': expiretimeinfractionalseconds,
'name': 'usernamestring',
'user': 'usernamestring',
'username': 'usernamestring',
'eauth': 'eauthtypestring',
'perms: permslistofstrings,
}
The perms list provides those parts of salt for which the user is authorised
to execute.
example perms list:
[
"grains.*",
"status.*",
"sys.*",
"test.*"
]
"""
try:
tokenage = self.resolver.mk_token(creds)
except Exception as ex: # pylint: disable=broad-except
raise EauthAuthenticationError(
"Authentication failed with {}.".format(repr(ex))
)
if "token" not in tokenage:
raise EauthAuthenticationError(
"Authentication failed with provided credentials."
)
# Grab eauth config for the current backend for the current user
tokenage_eauth = self.opts["external_auth"][tokenage["eauth"]]
if tokenage["name"] in tokenage_eauth:
tokenage["perms"] = tokenage_eauth[tokenage["name"]]
else:
tokenage["perms"] = tokenage_eauth["*"]
tokenage["user"] = tokenage["name"]
tokenage["username"] = tokenage["name"]
return tokenage
def verify_token(self, token):
"""
If token is valid Then returns user name associated with token
Else False.
"""
try:
result = self.resolver.get_token(token)
except Exception as ex: # pylint: disable=broad-except
raise EauthAuthenticationError(
"Token validation failed with {}.".format(repr(ex))
)
return result
def get_event(self, wait=0.25, tag="", full=False):
"""
Get a single salt event.
If no events are available, then block for up to ``wait`` seconds.
Return the event if it matches the tag (or ``tag`` is empty)
Otherwise return None
If wait is 0 then block forever or until next event becomes available.
"""
return self.event.get_event(wait=wait, tag=tag, full=full, auto_reconnect=True)
def fire_event(self, data, tag):
"""
fires event with data and tag
This only works if api is running with same user permissions as master
Need to convert this to a master call with appropriate authentication
"""
return self.event.fire_event(data, salt.utils.event.tagify(tag, "wui"))

View file

@ -78,14 +78,6 @@ except ImportError:
# resource is not available on windows
HAS_RESOURCE = False
try:
import halite # pylint: disable=import-error
HAS_HALITE = True
except ImportError:
HAS_HALITE = False
log = logging.getLogger(__name__)
@ -760,10 +752,6 @@ class Master(SMaster):
except Exception: # pylint: disable=broad-except
log.error("Error creating ext_processes process: %s", proc)
if HAS_HALITE and "halite" in self.opts:
log.info("Creating master halite process")
self.process_manager.add_process(Halite, args=(self.opts["halite"],))
# TODO: remove, or at least push into the transport stuff (pre-fork probably makes sense there)
if self.opts["con_cache"]:
log.info("Creating master concache process")
@ -832,45 +820,6 @@ class Master(SMaster):
sys.exit(0)
class Halite(salt.utils.process.SignalHandlingProcess):
"""
Manage the Halite server
"""
def __init__(self, hopts, **kwargs):
"""
Create a halite instance
:param dict hopts: The halite options
"""
super().__init__(**kwargs)
self.hopts = hopts
# __setstate__ and __getstate__ are only used on Windows.
# We do this so that __init__ will be invoked on Windows in the child
# process so that a register_after_fork() equivalent will work on Windows.
def __setstate__(self, state):
self.__init__(
state["hopts"],
log_queue=state["log_queue"],
log_queue_level=state["log_queue_level"],
)
def __getstate__(self):
return {
"hopts": self.hopts,
"log_queue": self.log_queue,
"log_queue_level": self.log_queue_level,
}
def run(self):
"""
Fire up halite!
"""
salt.utils.process.appendproctitle(self.__class__.__name__)
halite.start(self.hopts)
class ReqServer(salt.utils.process.SignalHandlingProcess):
"""
Starts up the master request server, minions send results to this