depends: |
|
---|---|
configuration: | All authentication is done through Salt's external auth system. Be sure that it is enabled and the user you are authenticating as has permissions for all the functions you will be running. Example production configuration block; add to the Salt master config file: rest_cherrypy:
port: 8000
ssl_crt: /etc/pki/tls/certs/localhost.crt
ssl_key: /etc/pki/tls/certs/localhost.key
The REST interface strongly recommends a secure HTTPS connection since Salt authentication credentials will be sent over the wire. If you don't already have a certificate and don't wish to buy one, you can generate a self-signed certificate using the create_self_signed_cert() function in Salt (note the dependencies for this module): % salt-call tls.create_self_signed_cert
All available configuration options are detailed below. These settings configure the CherryPy HTTP server and do not apply when using an external server such as Apache or Nginx.
|
Authentication is performed by passing a session token with each request. The token may be sent either via a custom header named X-Auth-Token or sent inside a cookie. (The result is the same but browsers and some HTTP clients handle cookies automatically and transparently so it is a convenience.)
Token are generated via the Login URL.
See also
You can bypass the session handling via the Run URL.
You access a running Salt master via this module by sending HTTP requests to the URLs detailed below.
Content negotiation
This REST interface is flexible in what data formats it will accept as well as what formats it will return (e.g., JSON, YAML, x-www-form-urlencoded).
This REST interface expects data sent in POST and PUT requests to be in the format of a list of lowstate dictionaries. This allows you to specify multiple commands in a single request.
A dictionary containing various keys that instruct Salt which command to run, where that command lives, any parameters for that command, any authentication credentials, what returner to use, etc.
Salt uses the lowstate data format internally in many places to pass command data between functions. Salt also uses lowstate for the LocalClient() Python API interface.
For example (in JSON format):
[{
'client': 'local',
'tgt': '*',
'fun': 'test.fib',
'arg': ['10'],
}]
x-www-form-urlencoded
This REST interface accepts data in the x-www-form-urlencoded format. This is the format used by HTML forms, the default format used by curl, the default format used by many JavaScript AJAX libraries (such as jQuery), etc. This format will be converted to the lowstate format as best as possible with the caveats below. It is always preferable to format data in the lowstate format directly in a more capable format such as JSON or YAML.
Only a single command may be sent in this format per HTTP request.
Multiple arg params will be sent as a single list of params.
Note, some popular frameworks and languages (notably jQuery, PHP, and Ruby on Rails) will automatically append empty brackets onto repeated parameters. E.g., arg=one, arg=two will be sent as arg[]=one, arg[]=two. Again, it is preferable to send lowstate via JSON or YAML directly by specifying the Content-Type header in the request.
The main entry point is the root URL (/) and all functionality is available at that URL. The other URLs are largely convenience URLs that wrap that main entry point with shorthand or specialized functionality.
The rest_cherrypy netapi module is a standard Python WSGI app. It can be deployed one of two ways.
The default configuration is to run this module using salt-api to start the Python-based CherryPy server. This server is lightweight, multi-threaded, encrypted with SSL, and should be considered production-ready.
This module may be deplayed on any WSGI-compliant server such as Apache with mod_wsgi or Nginx with FastCGI, to name just two (there are many).
Note, external WSGI servers handle URLs, paths, and SSL certs directly. The rest_cherrypy configuration options are ignored and the salt-api daemon does not need to be running at all. Remember Salt authentication credentials are sent in the clear unless SSL is being enforced!
An example Apache virtual host configuration:
<VirtualHost *:80>
ServerName example.com
ServerAlias *.example.com
ServerAdmin webmaster@example.com
LogLevel warn
ErrorLog /var/www/example.com/logs/error.log
CustomLog /var/www/example.com/logs/access.log combined
DocumentRoot /var/www/example.com/htdocs
WSGIScriptAlias / /path/to/saltapi/netapi/rest_cherrypy/wsgi.py
</VirtualHost>
The primary entry point to the REST API. All functionality is available through this URL. The other available URLs provide convenience wrappers around this URL.
An explanation of the API with links of where to go next.
Example request:
% curl -i localhost:8000
GET / HTTP/1.1
Host: localhost:8000
Accept: application/json
Example response:
HTTP/1.1 200 OK
Content-Type: application/json
Status 200: | success |
---|---|
Status 401: | authentication required |
Status 406: | requested Content-Type not available |
All interactions with this REST API must be authenticated. Authentication is performed through Salt's eauth system. You must set the eauth backend and allowed users by editing the external_auth section in your master config.
Authentication credentials are passed to the REST API via a session id in one of two ways:
If the request is initiated from a browser it must pass a session id via a cookie and that session must be valid and active.
If the request is initiated programmatically, the request must contain a X-Auth-Token header with valid and active session id.
Present the login interface
An explanation of how to log in.
Example request:
% curl -i localhost:8000/login
GET /login HTTP/1.1
Host: localhost:8000
Accept: text/html
Example response:
HTTP/1.1 200 OK
Content-Type: text/html
Status 401: | authentication required |
---|---|
Status 406: | requested Content-Type not available |
Authenticate against Salt's eauth system
Changed in version 0.8.0: No longer returns a 302 redirect on success.
Changed in version 0.8.1: Returns 401 on authentication failure
Example request:
% curl -si localhost:8000/login \
-H "Accept: application/json" \
-d username='saltuser' \
-d password='saltpass' \
-d eauth='pam'
POST / HTTP/1.1
Host: localhost:8000
Content-Length: 42
Content-Type: application/x-www-form-urlencoded
Accept: application/json
username=saltuser&password=saltpass&eauth=pam
Example response:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 206
X-Auth-Token: 6d1b722e
Set-Cookie: session_id=6d1b722e; expires=Sat, 17 Nov 2012 03:23:52 GMT; Path=/
{"return": {
"token": "6d1b722e",
"start": 1363805943.776223,
"expire": 1363849143.776224,
"user": "saltuser",
"eauth": "pam",
"perms": [
"grains.*",
"status.*",
"sys.*",
"test.*"
]
}}
Form eauth: | the eauth backend configured in your master config |
---|---|
Form username: | username |
Form password: | password |
Status 200: | success |
Status 401: | could not authenticate using provided credentials |
Status 406: | requested Content-Type not available |
Destroy the currently active session and expire the session cookie
New in version 0.8.0.
A convenience URL for getting lists of minions or getting minion details
Get grains, modules, functions, and inline function documentation for all minions or a single minion
Example request:
% curl -i localhost:8000/minions/ms-3
GET /minions/ms-3 HTTP/1.1
Host: localhost:8000
Accept: application/x-yaml
Example response:
HTTP/1.1 200 OK
Content-Length: 129005
Content-Type: application/x-yaml
return:
- ms-3:
grains.items:
...
Parameters: | mid -- (optional) a minion id |
---|---|
Status 200: | success |
Status 401: | authentication required |
Status 406: | requested Content-Type not available |
Start an execution command and immediately return the job id
You must pass low-data in the request body either from an HTML form or as JSON or YAML. The client option is pre-set to local_async.
Example request:
% curl -sSi localhost:8000/minions \
-H "Accept: application/x-yaml" \
-d tgt='*' \
-d fun='status.diskusage'
POST /minions HTTP/1.1
Host: localhost:8000
Accept: application/x-yaml
Content-Length: 26
Content-Type: application/x-www-form-urlencoded
tgt=*&fun=status.diskusage
Example response:
HTTP/1.1 202 Accepted
Content-Length: 86
Content-Type: application/x-yaml
return:
- jid: '20130603122505459265'
minions: [ms-4, ms-3, ms-2, ms-1, ms-0]
_links:
jobs:
- href: /jobs/20130603122505459265
Form lowstate: | lowstate data for the LocalClient; the client parameter will be set to local_async Lowstate may be supplied in any supported format by specifying the Content-Type header in the request. Supported formats are listed in the Alternates response header. |
---|---|
Status 202: | success |
Status 401: | authentication required |
Status 406: | requested Content-Type not available |
A convenience URL for getting lists of previously run jobs or getting the return from a single job
Get grains, modules, functions, and inline function documentation for all minions or a single minion
Example request:
% curl -i localhost:8000/jobs
GET /jobs HTTP/1.1
Host: localhost:8000
Accept: application/x-yaml
Example response:
HTTP/1.1 200 OK
Content-Length: 165
Content-Type: application/x-yaml
return:
- '20121130104633606931':
Arguments:
- '3'
Function: test.fib
Start Time: 2012, Nov 30 10:46:33.606931
Target: jerry
Target-type: glob
Example request:
% curl -i localhost:8000/jobs/20121130104633606931
GET /jobs/20121130104633606931 HTTP/1.1
Host: localhost:8000
Accept: application/x-yaml
Example response:
HTTP/1.1 200 OK
Content-Length: 73
Content-Type: application/x-yaml
info:
- Arguments:
- '3'
Function: test.fib
Minions:
- jerry
Start Time: 2012, Nov 30 10:46:33.606931
Target: '*'
Target-type: glob
User: saltdev
jid: '20121130104633606931'
return:
- jerry:
- - 0
- 1
- 1
- 2
- 6.9141387939453125e-06
Parameters: | mid -- (optional) a minion id |
---|---|
Status 200: | success |
Status 401: | authentication required |
Status 406: | requested Content-Type not available |
Run commands bypassing the normal session handling
New in version 0.8.0.
This entry point is primarily for "one-off" commands. Each request must pass full Salt authentication credentials. Otherwise this URL is identical to the root (/) execution URL.
Example request:
% curl -sS localhost:8000/run \
-H 'Accept: application/x-yaml' \
-d client='local' \
-d tgt='*' \
-d fun='test.ping' \
-d username='saltdev' \
-d password='saltdev' \
-d eauth='pam'
POST /run HTTP/1.1
Host: localhost:8000
Accept: application/x-yaml
Content-Length: 75
Content-Type: application/x-www-form-urlencoded
client=local&tgt=*&fun=test.ping&username=saltdev&password=saltdev&eauth=pam
Example response:
HTTP/1.1 200 OK
Content-Length: 73
Content-Type: application/x-yaml
return:
- ms-0: true
ms-1: true
ms-2: true
ms-3: true
ms-4: true
Form lowstate: | A list of lowstate data appropriate for the client specified client interface. Full external authentication credentials must be included. |
---|---|
Status 200: | success |
Status 401: | authentication failed |
Status 406: | requested Content-Type not available |
The event bus on the Salt master exposes a large variety of things, notably when executions are started on the master and also when minions ultimately return their results. This URL provides a real-time window into a running Salt infrastructure.
Return an HTTP stream of the Salt master event bus; this stream is formatted per the Server Sent Events (SSE) spec
New in version 0.8.3.
Browser clients currently lack Cross-origin resource sharing (CORS) support for the EventSource() API. Cross-domain requests from a browser may instead pass the X-Auth-Token value as an URL parameter:
% curl -NsS localhost:8000/events/6d1b722e
Example request:
% curl -NsS localhost:8000/events
GET /events HTTP/1.1
Host: localhost:8000
Example response:
HTTP/1.1 200 OK
Connection: keep-alive
Cache-Control: no-cache
Content-Type: text/event-stream;charset=utf-8
retry: 400
data: {'tag': '', 'data': {'minions': ['ms-4', 'ms-3', 'ms-2', 'ms-1', 'ms-0']}}
data: {'tag': '20130802115730568475', 'data': {'jid': '20130802115730568475', 'return': True, 'retcode': 0, 'success': True, 'cmd': '_return', 'fun': 'test.ping', 'id': 'ms-1'}}
The event stream can be easily consumed via JavaScript:
# Note, you must be authenticated!
var source = new EventSource('/events');
source.onopen = function() { console.debug('opening') };
source.onerror = function(e) { console.debug('error!', e) };
source.onmessage = function(e) { console.debug(e.data) };
It is also possible to consume the stream via the shell.
Records are separated by blank lines; the data: and tag: prefixes will need to be removed manually before attempting to unserialize the JSON.
curl's -N flag turns off input buffering which is required to process the stream incrementally.
Here is a basic example of printing each event as it comes in:
% curl -NsS localhost:8000/events |\
while IFS= read -r line ; do
echo $line
done
Here is an example of using awk to filter events based on tag:
% curl -NsS localhost:8000/events |\
awk '
BEGIN { RS=""; FS="\n" }
$1 ~ /^tag: salt\/job\/[0-9]+\/new$/ { print $0 }
'
tag: salt/job/20140112010149808995/new
data: {"tag": "salt/job/20140112010149808995/new", "data": {"tgt_type": "glob", "jid": "20140112010149808995", "tgt": "jerry", "_stamp": "2014-01-12_01:01:49.809617", "user": "shouse", "arg": [], "fun": "test.ping", "minions": ["jerry"]}}
tag: 20140112010149808995
data: {"tag": "20140112010149808995", "data": {"fun_args": [], "jid": "20140112010149808995", "return": true, "retcode": 0, "success": true, "cmd": "_return", "_stamp": "2014-01-12_01:01:49.819316", "fun": "test.ping", "id": "jerry"}}
Status 200: | success |
---|---|
Status 401: | could not authenticate using provided credentials |
A generic web hook entry point that fires an event on Salt's event bus
External services can POST data to this URL to trigger an event in Salt. For example, Amazon SNS, Jenkins-CI or Travis-CI, or GitHub web hooks.
Note
Be mindful of security
Salt's Reactor can run any code. A Reactor SLS that responds to a hook event is responsible for validating that the event came from a trusted source and contains valid data.
This is a generic interface and securing it is up to you!
This URL requires authentication however not all external services can be configured to authenticate. For this reason authentication can be selectively disabled for this URL. Follow best practices -- always use SSL, pass a secret key, configure the firewall to only allow traffic from a known source, etc.
The event data is taken from the request body. The Content-Type header is respected for the payload.
The event tag is prefixed with salt/netapi/hook and the URL path is appended to the end. For example, a POST request sent to /hook/mycompany/myapp/mydata will produce a Salt event with the tag salt/netapi/hook/mycompany/myapp/mydata. See the Salt Reactor documentation for how to react to events with various tags.
The following is an example .travis.yml file to send notifications to Salt of successful test runs:
language: python
script: python -m unittest tests
after_success:
- 'curl -sS http://saltapi-url.example.com:8000/hook/travis/build/success -d branch="${TRAVIS_BRANCH}" -d commit="${TRAVIS_COMMIT}"'
Fire an event in Salt with a custom event tag and data
New in version 0.8.4.
Example request:
% curl -sS localhost:8000/hook -d foo='Foo!' -d bar='Bar!'
POST /hook HTTP/1.1
Host: localhost:8000
Content-Length: 16
Content-Type: application/x-www-form-urlencoded
foo=Foo&bar=Bar!
Example response:
HTTP/1.1 200 OK
Content-Length: 14
Content-Type: application/json
{"success": true}
As a practical example, an internal continuous-integration build server could send an HTTP POST request to the URL http://localhost:8000/hook/mycompany/build/success which contains the result of a build and the SHA of the version that was built as JSON. That would then produce the following event in Salt that could be used to kick off a deployment via Salt's Reactor:
Event fired at Fri Feb 14 17:40:11 2014
*************************
Tag: salt/netapi/hook/mycompany/build/success
Data:
{'_stamp': '2014-02-14_17:40:11.440996',
'headers': {
'X-My-Secret-Key': 'F0fAgoQjIT@W',
'Content-Length': '37',
'Content-Type': 'application/json',
'Host': 'localhost:8000',
'Remote-Addr': '127.0.0.1'},
'post': {'revision': 'aa22a3c4b2e7', 'result': True}}
Salt's Reactor could listen for the event:
reactor:
- 'salt/netapi/hook/mycompany/build/*':
- /srv/reactor/react_ci_builds.sls
And finally deploy the new build:
{% set secret_key = data.get('headers', {}).get('X-My-Secret-Key') %}
{% set build = data.get('post', {}) %}
{% if secret_key == 'F0fAgoQjIT@W' and build.result == True %}
deploy_my_app:
cmd.state.sls:
- tgt: 'application*'
- arg:
- myapp.deploy
- kwarg:
pillar:
revision: {{ revision }}
{% endif %}
Status 200: | success |
---|---|
Status 406: | requested Content-Type not available |
Status 413: | request body is too large |