Commit cb78f297 authored by Azat Khuziyakhmetov's avatar Azat Khuziyakhmetov
Browse files

implemented setuid binary wrapper to secure credentials and data

parent 40e06265
......@@ -25,5 +25,5 @@ htmlcov
......@@ -27,6 +27,18 @@ Sample configs must not be used in the code.
**Example**: `` -> ``
## Security
The aggregator tool requires an access to the database (currently InlfuxDB). DB credentials are stored in a plain text file and accessible by anyone who has right to read configuration files. In order to prevent users from accessing DB credentials and job data that does not belong to them, the toolkit provides a setuid executable to run aggregator. In order to use this feature follow the instructions:
1. Configure aggregator to use setuid binary by setting `SECUSER=True` in `conf/`.
2. Compile setuid binary: `cd setuid-runner; make`. It requires Python3 to be installed in the system.
3. You can move `setuid-runner/setuid-runner` and `setuid-runner/config.ini` anywhere where it is accessible by users
4. Set path to `` in `config.ini`
5. Change the ownership of all files to `safeuser` and remove the access for anyone else: `chown -R safeuser aggregator && chmod -R go-rwx aggregator`
6. Set **setuid** bit of the `setuid-runner` binary and ownership accordingly: `chmod u+s,a+rx setuid-runner && chown safeuser setuid-runner`
7. Now in order to fetch the data call `setuid-runner` as you call ``, for instance: `./setuid-runner -t text JOBID`
## Usage
The main executable of the aggregator module is ``. You can type `./ -h` for more help.
CXX = g++
CXXFLAGS = -Wall -pedantic -std=c++11
PYINC := $(shell python3-config --includes)
PYLIBS := $(shell python3-config --ldflags)
TARGETS = setuid-runner
SRC = setuid-runner.cpp
OBJ = setuid-runner.o
all: $(TARGETS)
$(OBJ): $(SRC)
setuid-runner: $(OBJ)
$(CXX) -o $@ $(OBJ) $(PYLIBS)
rm -f *.o $(TARGETS)
spath = /path/to/aggregator/
#include <iostream>
#include <fstream>
#include <algorithm>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include <limits.h>
#include <string.h>
#include <libgen.h>
#include <Python.h>
using namespace std;
// trim from start (in place)
static inline void ltrim(string &s) {
s.erase(s.begin(), find_if(s.begin(), s.end(), [](int ch) {
return !isspace(ch);
// trim from end (in place)
static inline void rtrim(string &s) {
s.erase(find_if(s.rbegin(), s.rend(), [](int ch) {
return !isspace(ch);
}).base(), s.end());
// trim from both ends (in place)
static inline string trim(string &s) {
return s;
// throw an error
static inline void error(string s, int errn){
cout << s << endl;
int main(int argc, char **argv)
char conffile[PATH_MAX], scriptfile[PATH_MAX];
strncpy(conffile, argv[0], sizeof(conffile));
strncat(conffile, "/config.ini", sizeof(conffile));
ifstream cFile (conffile);
if (cFile.is_open())
string line, sfile;
while(getline(cFile, line)){
auto delimiterPos = line.find("=");
auto name = line.substr(0, delimiterPos);
if (trim(name) == "spath") {
string val = line.substr(delimiterPos + 1);
val = trim(val);
strncpy(scriptfile, val.c_str(), sizeof(scriptfile));
if (strlen(scriptfile) == 0) {
error("Config file should contain a valid spath value", 1);
else {
error("Couldn't open config file for reading. Config location: " + string(conffile), 2);
// check the arguments for special characters
for(int i=1; i<argc; i++) {
if (string(argv[i]).find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890-_ ") != std::string::npos){
error(string("Invalid argument(not allowed characters): ") + argv[i], 3);
wchar_t *program = Py_DecodeLocale(scriptfile, NULL);
FILE* fp;
fp = _Py_fopen(scriptfile, "r");
if (fp == NULL) {
error(string("Cannot open the script ") + scriptfile, 4);
wchar_t **pargv;
pargv = new wchar_t* [argc];
pargv[0] = program;
for(int i=1; i<argc; i++) {
pargv[i] = Py_DecodeLocale(argv[i], NULL);
PySys_SetArgv(argc, pargv);
int pyres = PyRun_SimpleFile(fp, scriptfile);
if (pyres < 0) {
error(string("Error running the tool ") + scriptfile + " (error " + to_string(pyres) + ")", 5);
int fres = Py_FinalizeEx();
if (fres < 0) {
error(string("Error during finalizing (error ") + to_string(fres) + ")", 6);
return 0;
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment