Tableau Services Manager (TSM) API —The undocumented “Passwordless” Authentication
--
Tableau is a complex platform with tons of APIs for analysts, developers, and platform administrators. If you are a server admin, most probably you’ve already used REST API for basic stuff like managing users and contents. However, platform level activities such as starting and stoping the Server, creating backups, getting detailed status information about each service, changing topology, or retrieving license information rely on a different API: the Tableau Services Manager (TSM) API.
Usually, TSM API is used mostly from the tsm
command-line utility, which is part of the Server installation. But you can use the TSM API to perform all the functionalities in tsm
remotely, invoking simple HTTP endpoints. Some of the things you can do by this API are:
- Start and stop Tableau Server
- View the status of services and nodes
- Back up and restore Tableau Server
- Make configuration and topology changes
- Change port assignments for services
- Create log file archives
In this post, I will go thru on a basic use case: getting status information from a Tableau Server cluster, its nodes, and its services. We will check how to authenticate and get information from TSM as well as see TSM’s hidden gem: the passwordless login support.
But before trying to reimplement tsm status
from our own code, let’s see an alternative option from pre-TSM times for getting server status.
The serverinfo.xml endpoint
Historically, the only supported way to get status information from Tableau Server was this so-called serverinfo.xml
endpoint. It supports both authentication and white-list based access: allowing to retrieve the Server status information from third party tools without implementing any complex API or authentication logic, with a simple HTTP GET call.
To allow external apps (or yourself) to access systeminfo.xml
in an unauthenticated session, add your IP to the whitelist by:
tsm configuration set -k wgserver.systeminfo.allow_referrer_ips -v <ip address>
Then open /admin/serverinfo.xml
:
http://my_tableau_server/admin/systeminfo.xml
It should return something like this:
<systeminfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<machines>
<machine name="localhost">
<repository worker="localhost:8060" status="Active" preferred="false"/>
<applicationserver worker="localhost:8314" status="Active"/>
<vizqlserver worker="localhost:8969" status="Active"/>
<dataserver worker="localhost:8326" status="Active"/>
<backgrounder worker="localhost:8585" status="Active"/>
<flowprocessor worker="localhost:8598" status="Active"/>
<gateway worker="localhost:80" status="Active"/>
<hyper worker="localhost:8244" status="Active"/>
<searchandbrowse worker="localhost:8514" status="Active"/>
<cacheserver worker="localhost:8824" status="Active"/>
<filestore worker="localhost:8535" status="Active" pendingTransfers="0" failedTransfers="0" syncTimestamp="2020-12-22T09:45:21.936Z"/>
<clustercontroller worker="localhost:8179" status="Active"/>
<coordination worker="localhost:8500" status="Active"/>
<metrics worker="localhost:8201" status="Active"/>
</machine>
</machines>
<service status="Active"/>
</systeminfo>
This looks useful, however, it misses a few key points:
- We don’t know what is desired state: it’s great that the server is stopped, but did we wanted to stop it? Or, maybe it’s running but we already started a stop command. Serverinfo has no information about our intents (the service desired state).
- The
serverinfo.xml
only available when the vizportal is running. For real monitoring, we want to know the status all the time, not just when the Server is running.
The TSM API
This is when the TSM API could be helpful, as it gets the status directly from tabadmincontroller, which is supposed to be always running and available even when the Server is in stopped state.
The public part of the API is documented here. For getting status info, we need to call two endpoints:
login
to authenticate- and
status
to get server, nodes, and services status
Authenticating with TSM credentials
Under the hood, TSM uses OS credential validation (like PAM on Linux). All OS users can access the API who are members of the tsmadmin
group.
Unlike any other regular API, TSM API uses cookies for subsequent authenticated calls after login. While it sounds like an anti-pattern, I guess the reason for this cookie based security is that the API was primarily created for Tableau’s own web app, where cookie-based implementation is required anyway. Long story short, the /login
endpoint sets a cookie called AUTH_COOKIE
, and you should pass that cookie to all requests until you log out.
Login from the command line:
$ curl -k https://localhost:8850/api/0.5/login -X POST -H "Content-Type: application/json" --data '{
"authentication": {
"name": "tsmuser",
"password": "tsmpassword"
}
}' -v* About to connect() to localhost port 8850 (#0)
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8850 (#0)
[...]
> POST /api/0.5/login HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:8850
> Accept: */*
> Content-Type: application/json
> Content-Length: 104
>
< HTTP/1.1 204 No Content
< Date: Wed, 23 Dec 2020 08:16:55 GMT
< X-Application-Context: application
< Cache-Control: no-store
< Set-Cookie: AUTH_COOKIE=eyJjdHkiOiJKV1QiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiZGlyIn0..xpoNg5Hg3n9AvLFN40ND7Q.13LYw0uQT9bDF2V7R-fMu3rXWpNg7vQk7QQeLbsUUqM1pEkoLNouizD-kco2gkms0b_mn2rOIGek7htCxnf13bF5Y3ZLogNwMpQIHBIrt9Y4mapIZvLdIB_kmwaTpqLWvAjz5rpVo7l4uGUd42cvDXkOJbFElyuHwRgfEr_hXhj7lWRyB2iDJm1HBbqBovGaIsUCEvu8m6PE7UmglmFyQFW7ys3NuoKIq46Dcv4HnBD0S7bpkNZOEeMWx-0b9t.xazmQ1ReLQk-y0g7RoELL;Path=/;Expires=Wed, 23-Dec-2020 10:16:56 GMT;Max-Age=7200;Secure;HttpOnly
< Expires: Thu, 01 Jan 1970 00:00:00 GMT
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< X-Frame-Options: DENY
< Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none';
That ugly cookie is our ticket for the next calls.
Get system status info
Getting the status as simple as a GET /status
, just like:
$ curl -k https://localhost:8850/api/0.5/status -H 'Cookie: AUTH_COOKIE=eyJjdH[...]RoELL'
If all good (and why wouldn’t be), then we should see something like:
{
"clusterStatus": {
"nodes": [{
"services": [{
"serviceName": "filestore",
"instances": [{
"code": "ACTIVE",
"processStatus": "Active",
"instanceId": "0",
"timestampUtc": 1497060680268,
"currentDeploymentState": "Enabled",
"binaryVersion": "<build>"
}],
"rollupStatus": "Running",
"rollupRequestedDeploymentState": "Enabled"
}, {
"..."
}],
"nodeId": "node1",
"rollupStatus": "Running",
"rollupRequestedDeploymentState": "Enabled"
}],
"href": "/api/0.5/status",
"rollupStatus": "Running",
"rollupRequestedDeploymentState": "Enabled"
}
}
This is exactly what we wanted, detailed status for each service (we will see more services here than in serverinfo.xml
), desired state, and last, but not least, the result is in a fancy JSON format, so we don’t have to parse XML (which sucks anyway).
Using TSM Passwordless authentication
So what if I do not want to use my precious OS user credentials in monitoring tools? How can I interact with TSM API without username and passwords — just like with systeminfo
? Fear not, Tableau supports passwordless login beginning from 2019.2. The documentation mentions two requirements:
- The account you are running commands with is a member of the TSM-authorized group, by default, the
tsmadmin
group. The Tableau unprivileged user (by default, thetableau
user) and root account may also run TSM commands. - You are running commands locally on the Tableau Server that is running the Tableau Server Administration Controller service. By default, the Tableau Server Administration Controller service is installed and configured on the initial node in a distributed deployment.
But how does this passwordless auth works? It is not documented (yet), but the process is fairly easy:
- First, the application connects to a local named pipe (on Windows) or a UNIX domain socket (on Linux)
- TSM validates the connecting process user id and group membership
- If the user is part of the
tsmadmin
group it returns a cookie for the session. That cookie can be used as anAUTH_COOKIE
for other API calls.
Quite simple, huh? It is, indeed.
The communication between our process and the TSM controller is based on Apache Thrift protocol. Thus, you can generate the passwordless authenticate code in any programming language of your choice using the thrift
command.
enum PasswordLessLoginReturnCode {
PLL_SUCCESS = 0,
PLL_NOT_AUTHORIZED = 1,
PLL_ERROR = 2
}
struct PasswordLessLoginResult {
1: PasswordLessLoginReturnCode returnCode,
2: string username,
3: string cookieName,
4: string cookieValue,
5: i32 cookieMaxAge
}
service PasswordLessLogin {
PasswordLessLoginResult login()
}
After compiling the thrift file, you should be able to call login()
function which will return with the coookieName
and cookieValue
.
An example implementation in Rust will look like this:
This will print the API results as:
Ok(PasswordLessLoginResult {
return_code: Some(PllSuccess),
username: Some("tsmsvc"),
cookie_name: Some("AUTH_COOKIE"),
cookie_value: Some("fSd[...]mC8DQ"),
cookie_max_age: Some(7200)
})
In the tabadmincontroller_node1–0.log
log file, we can also see that TSM recognize our user — even without sending it over the communication channel.
2020-12-23 07:45:48.453 +0100 Thread-17 : INFO com.tableausoftware.tabadmin.webapp.impl.linux.LinuxPasswordLessLoginManager - Password-less login request from user 'tsmuser' with uid '1000'
2020-12-23 07:45:48.462 +0100 Thread-17 : INFO com.tableausoftware.tabadmin.webapp.impl.linux.LinuxPasswordLessLoginManager - User with uid '1000' and username 'tsmuser' is logged in via password-less auth
This is it. We can now get the server status from our code without storing or passing credentials over the line.
TSM functionalities for non-admins
In some circumstances, it would be great to limit who can use what functionalities in TSM. At some of our clients the Level2 support team is limited to certain actions like starting and stoping the Server, creating backups but they cannot change the configuration or reset the admin password for instance. Out of the box the authorization scheme in TSM is all or nothing: if you are logged in, you are superuser.
The way how we help our customers to fine tune TSM user levels and assign the right permissions to the right folks is to use setuid
bit binaries along with passwordless login. Let’s say you have two users: tsmuser
and support
. tsmuser
is part of the tsmadmin
group but support
isn’t. In that case, your binary that interacts with TSM API should be owned by tsmuser
and have a setuid
file system flag:
$ chmod 4755 your_tsm_app
$ ls -l your_tsm_app
-rwsr-xr-x. 2 tsmadmin tsmadin 8367064 Dec 26 08:12 your_tsm_binary
And the app should also change the effective uid (granted by the OS due to the setuid flag) to current uid:
#include <unistd.h>setuid(geteuid());
When our binary is executed by thesupport
user, the application will switch its current user to tsmuser
. Later, when the passwordless code is invoked, Tableau Service Manager will grant sessiontsmuser
‘s AUTH_COOKIE
.
Passwordless over Network
If you are brave enough, you can even map or proxy the local domain sockets to a network port or a remote host using socat(1)
. It will allow passwordless login from remote endpoints as well.
Putting everything together: tableau-monitoring-execd
I made a quick plugin for telegraf for monitoring Tableau Server using systeminfo.xml
and TSM API. It uses all the above-mentioned technics to get system status information as easily and reliably as possible. The project page here:
This small app is an execution daemon for telegraf: a continously running process that collects information from external services (like Tableau Server thru TSM API) and return the results in InfluxDB’s line protocol. We use this as a critical part of our monitoring efforts, completed with proactive monitoring and alarming.
In the next posts in this serie, I will show how can you use this nice little tool to monitor and alert your Tableau Server like a boss!