Compare commits
10 Commits
08bd3462b8
...
ad92a7ebba
Author | SHA1 | Date |
---|---|---|
Tyler Perkins | ad92a7ebba | |
Tyler Perkins | 7e8f1e30b7 | |
Tyler Perkins | d2ad27ea3b | |
Tyler Perkins | ab9010d945 | |
Tyler Perkins | 0a9af99427 | |
Tyler Perkins | 146d31efea | |
Tyler Perkins | 3db4a8fcaa | |
Tyler Perkins | 174e1ac2a9 | |
Tyler Perkins | 654dcbc065 | |
Tyler Perkins | 5f85e772d8 |
|
@ -19,6 +19,16 @@ types
|
|||
* Returns exact response from the `/proc` file system
|
||||
* For more about the format of these responses, refer to the proc(5) manpage
|
||||
|
||||
Security tokens
|
||||
---------------
|
||||
|
||||
The application provides the `-a` flag, which accepts a path as a required
|
||||
argument. The file specified in the path should contain a series of strings,
|
||||
each on a new line. Each line represents a valid token that the client can send
|
||||
to authorize the request. This token must be send in a `Authorization:` header.
|
||||
If the token is not given, or the wrong token is given, the server will instead
|
||||
return a 403.
|
||||
|
||||
Proc file responses
|
||||
============
|
||||
|
||||
|
@ -65,7 +75,21 @@ Sample response
|
|||
}
|
||||
```
|
||||
|
||||
Special formatted responses
|
||||
[GET] /proc/sys/kernel/hostname
|
||||
--------------------------------
|
||||
|
||||
* returns hostname
|
||||
* Same as /proc/sys/kernel/hostname file
|
||||
|
||||
Sample response
|
||||
|
||||
```
|
||||
{
|
||||
"hostname": "Samplebox"
|
||||
}
|
||||
```
|
||||
|
||||
Special formatted responses and aliases
|
||||
===========================
|
||||
|
||||
[GET] /uptime
|
||||
|
@ -94,3 +118,14 @@ Sample response
|
|||
"memavailable" : 12324924,
|
||||
}
|
||||
```
|
||||
|
||||
[GET] /hostname
|
||||
---------------
|
||||
|
||||
* Returns redirect to /proc/sys/kernel/hostname
|
||||
|
||||
[GET] /up
|
||||
---------
|
||||
|
||||
* Always returns a 200 OK
|
||||
* Can be used for testing latency
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
FROM ubuntu:latest AS build
|
||||
|
||||
RUN apt-get update && apt-get install -y curl build-essential git libboost-all-dev
|
||||
RUN curl https://github.com/CrowCpp/Crow/releases/download/v1.0%2B2/crow-v1.0+2.deb -o crow.deb -L
|
||||
RUN apt-get purge -y curl
|
||||
RUN apt install -y ./crow.deb
|
||||
|
||||
WORKDIR /work
|
||||
COPY ./ ./
|
||||
RUN make clean && make
|
||||
|
||||
|
||||
|
||||
FROM ubuntu:latest
|
||||
#WORKDIR /opt/proc-api
|
||||
COPY --from=build /work/bin/proc-api ./
|
||||
|
||||
ENV PORT="5000"
|
||||
ENV NAME="proc-api"
|
||||
ENV THREADS="2"
|
||||
ENV AUTH=""
|
||||
|
||||
CMD ./proc-api -p $PORT -n $NAME -t $THREADS $(if [ -z $AUTH ]; then echo ""; else echo "-a $AUTH"; fi)
|
4
Makefile
4
Makefile
|
@ -10,7 +10,7 @@ FLAGS = -pipe
|
|||
|
||||
CFLAGS = -Wall
|
||||
CFLAGS += -Ofast
|
||||
#CFLAGS += -flto
|
||||
CFLAGS += -flto
|
||||
#CFLAGS += -g
|
||||
#CFLAGS += -pg
|
||||
|
||||
|
@ -23,7 +23,7 @@ BIN = ./bin
|
|||
PREFIX = /usr/local
|
||||
MANPREFIX = $(PREFIX)/share/man
|
||||
|
||||
TARGET = crowtest
|
||||
TARGET = proc-api
|
||||
|
||||
MAKEFLAGS += --jobs=4
|
||||
|
||||
|
|
25
README.md
25
README.md
|
@ -1,4 +1,25 @@
|
|||
Learn crow
|
||||
Proc-API
|
||||
==========
|
||||
|
||||
Learning the crow web framework
|
||||
This is a lightweight, fast, RESTful API designed around the /proc filesystem
|
||||
in linux. It allows for stateless monitoring of remote linux systems with
|
||||
automated tools.
|
||||
|
||||
What is this good for?
|
||||
----------------------
|
||||
|
||||
* Remote monitoring
|
||||
* Collecting stats on a remote system
|
||||
* Client applications for VPS hosters
|
||||
|
||||
How do I talk to the API?
|
||||
-------------------------
|
||||
|
||||
See [DOCUMENTATION.md](DOCUMENTATION.md)
|
||||
|
||||
Features/TODO
|
||||
-------------
|
||||
|
||||
* [X] Basic routing
|
||||
* [X] Authentication
|
||||
* [ ] Support for querying a PID
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# NOTE: This is a sample docker-compose for how you could set up your own set
|
||||
# of containers
|
||||
version: '3'
|
||||
services:
|
||||
proc-api_service:
|
||||
image: proc-api
|
||||
container_name: proc-api_service
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 5000:5000
|
||||
- 6123:6123
|
||||
environment:
|
||||
- PORT=6123
|
||||
- NAME="My Cool Service"
|
||||
- THREADS=4
|
||||
#-AUTH="./auth.txt"
|
||||
#NOTE for auth to work, you will need to mount a volume with the authfile
|
||||
#in it
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Tyler Perkins
|
||||
// 29-4-22
|
||||
// Auth definition
|
||||
//
|
||||
|
||||
#include "auth.hpp"
|
||||
|
||||
bool auth::checkAuth(const crow::request& req){
|
||||
if(auth::auth_check::checker == nullptr)
|
||||
return true;
|
||||
else
|
||||
return auth::auth_check::checker->checkAuth(req);
|
||||
}
|
||||
|
||||
auth::auth_check::auth_check(std::string& path){
|
||||
std::ifstream f (path);
|
||||
|
||||
if(f.is_open()){
|
||||
std::string line;
|
||||
while(std::getline(f, line)){
|
||||
tokens.insert(line);
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
|
||||
auth::auth_check::~auth_check(){
|
||||
//nothing to clean up
|
||||
}
|
||||
|
||||
bool auth::auth_check::checkAuth(const crow::request& req) const {
|
||||
if(tokens.find(req.get_header_value(AUTH_HEADER)) != tokens.end())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Tyler Perkins
|
||||
// 29-4-22
|
||||
// Auth definition
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <crow.h>
|
||||
#include <unordered_set>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
constexpr char AUTH_HEADER[] = "Authorization";
|
||||
|
||||
namespace auth{
|
||||
class auth_check {
|
||||
public:
|
||||
auth_check(std::string&);
|
||||
~auth_check();
|
||||
|
||||
static auth_check* checker;
|
||||
|
||||
bool checkAuth(const crow::request&) const;
|
||||
|
||||
private:
|
||||
std::unordered_set<std::string> tokens;
|
||||
};
|
||||
|
||||
bool checkAuth(const crow::request&);
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
#include "memory.hpp"
|
||||
|
||||
bool memory::getProcMem(crow::json::wvalue& ret){
|
||||
std::ifstream f (procmempath);
|
||||
std::ifstream f (PROC_MEM_PATH);
|
||||
std::string line;
|
||||
if(f.is_open()){
|
||||
while(std::getline(f, line)){
|
||||
|
@ -36,7 +36,7 @@ bool memory::getProcMem(crow::json::wvalue& ret){
|
|||
}
|
||||
|
||||
bool memory::getRawProcMem(std::string& ret){
|
||||
std::ifstream f (procmempath);
|
||||
std::ifstream f (PROC_MEM_PATH);
|
||||
std::string line;
|
||||
if(f.is_open()){
|
||||
while(std::getline(f, line)){
|
||||
|
@ -53,7 +53,7 @@ bool memory::getRawProcMem(std::string& ret){
|
|||
}
|
||||
|
||||
bool memory::getEasyMem(crow::json::wvalue& ret){
|
||||
std::ifstream f (procmempath);
|
||||
std::ifstream f (PROC_MEM_PATH);
|
||||
std::string line;
|
||||
if(f.is_open()){
|
||||
for(int j = 0; j < 3; ++j){
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
constexpr char procmempath[] = "/proc/meminfo";
|
||||
constexpr char PROC_MEM_PATH[] = "/proc/meminfo";
|
||||
|
||||
namespace memory{
|
||||
bool getProcMem(crow::json::wvalue&);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include "state.hpp"
|
||||
|
||||
bool state::getUptime(crow::json::wvalue& ret){
|
||||
std::ifstream f (procuptimepath);
|
||||
std::ifstream f (PROC_UPTIME_PATH);
|
||||
std::string line;
|
||||
if(f.is_open()){
|
||||
int space = -1;
|
||||
|
@ -34,7 +34,7 @@ bool state::getUptime(crow::json::wvalue& ret){
|
|||
}
|
||||
|
||||
bool state::getRawUptime(std::string& ret){
|
||||
std::ifstream f (procuptimepath);
|
||||
std::ifstream f (PROC_UPTIME_PATH);
|
||||
if(f.is_open()){
|
||||
std::getline(f, ret);
|
||||
ret += '\n';
|
||||
|
@ -48,7 +48,7 @@ bool state::getRawUptime(std::string& ret){
|
|||
}
|
||||
|
||||
bool state::getLoadAvg(crow::json::wvalue& ret){
|
||||
std::ifstream f (procloadavgpath);
|
||||
std::ifstream f (PROC_LOADAVG_PATH);
|
||||
std::string line;
|
||||
if(f.is_open()){
|
||||
int spaces[4];
|
||||
|
@ -78,7 +78,37 @@ bool state::getLoadAvg(crow::json::wvalue& ret){
|
|||
}
|
||||
|
||||
bool state::getRawLoadAvg(std::string& ret){
|
||||
std::ifstream f (procloadavgpath);
|
||||
std::ifstream f (PROC_LOADAVG_PATH);
|
||||
if(f.is_open()){
|
||||
std::getline(f, ret);
|
||||
ret += '\n';
|
||||
f.close();
|
||||
} else {
|
||||
ret = "Failed to open proc filesystem";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool state::getHostname(crow::json::wvalue& ret){
|
||||
std::ifstream f (PROC_HOSTNAME_PATH);
|
||||
std::string line;
|
||||
if(f.is_open()){
|
||||
std::getline(f, line);
|
||||
|
||||
ret["hostname"] = line;
|
||||
f.close();
|
||||
} else {
|
||||
ret["message"] = "Failed to open proc filesystem";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool state::getRawHostname(std::string& ret){
|
||||
std::ifstream f (PROC_HOSTNAME_PATH);
|
||||
if(f.is_open()){
|
||||
std::getline(f, ret);
|
||||
ret += '\n';
|
||||
|
|
|
@ -11,12 +11,15 @@
|
|||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
constexpr char procuptimepath[] = "/proc/uptime";
|
||||
constexpr char procloadavgpath[] = "/proc/loadavg";
|
||||
constexpr char PROC_UPTIME_PATH[] = "/proc/uptime";
|
||||
constexpr char PROC_LOADAVG_PATH[] = "/proc/loadavg";
|
||||
constexpr char PROC_HOSTNAME_PATH[] = "/proc/sys/kernel/hostname";
|
||||
|
||||
namespace state{
|
||||
bool getUptime(crow::json::wvalue&);
|
||||
bool getRawUptime(std::string&);
|
||||
bool getLoadAvg(crow::json::wvalue&);
|
||||
bool getRawLoadAvg(std::string&);
|
||||
bool getHostname(crow::json::wvalue&);
|
||||
bool getRawHostname(std::string&);
|
||||
}
|
||||
|
|
10
src/main.cpp
10
src/main.cpp
|
@ -7,6 +7,9 @@
|
|||
#include <iostream>
|
||||
#include <crow.h>
|
||||
#include "opt/parseopt.hpp"
|
||||
#include "auth/auth.hpp"
|
||||
|
||||
auth::auth_check* auth::auth_check::checker;
|
||||
|
||||
#include "routes.hpp"
|
||||
|
||||
|
@ -14,13 +17,18 @@ int main(int argc, char** argv){
|
|||
|
||||
option_flags* flags = parse_options(argc, argv);
|
||||
|
||||
if(!flags->auth_path.empty()){
|
||||
auth::auth_check::checker = new auth::auth_check(flags->auth_path);
|
||||
CROW_LOG_INFO << "Added authorization using authfile " << flags->auth_path;
|
||||
}
|
||||
|
||||
crow::SimpleApp app;
|
||||
|
||||
setRoutes(app);
|
||||
|
||||
app.port(flags->port)
|
||||
.server_name(flags->name)
|
||||
.multithreaded();
|
||||
.concurrency(flags->threads);
|
||||
|
||||
delete flags;
|
||||
|
||||
|
|
|
@ -11,6 +11,9 @@ void help(char* progName){
|
|||
std::cout << "Options:\n";
|
||||
std::cout << " [-p PORT] Port to listen on (Default 5000)\n";
|
||||
std::cout << " [-n NAME] Server name (Default \"proc-api\")\n";
|
||||
std::cout << " [-a PATH] Path to file containing authorization tokens\n";
|
||||
std::cout << " If the -a flag is not passed, no authorization is used\n";
|
||||
std::cout << " [-t THREADS] Number of threads to use. By default, uses 2 threads\n";
|
||||
std::cout << " [-h] Display this help message\n\n";
|
||||
exit(1);
|
||||
}
|
||||
|
@ -21,7 +24,9 @@ option_flags* parse_options(int argc, char** argv){
|
|||
option_flags* ret = new option_flags;
|
||||
|
||||
ret->port = 5000;
|
||||
ret->threads = 2;
|
||||
ret->name = "proc-api";
|
||||
ret->auth_path = "";
|
||||
|
||||
while((c = getopt(argc, argv, optarg_string)) != -1){
|
||||
switch(c){
|
||||
|
@ -31,6 +36,14 @@ option_flags* parse_options(int argc, char** argv){
|
|||
case 'n':
|
||||
ret->name = std::string(optarg);
|
||||
break;
|
||||
case 'a':
|
||||
ret->auth_path = std::string(optarg);
|
||||
break;
|
||||
case 't':
|
||||
ret->threads = atoi(optarg);
|
||||
if(ret->threads == 0)
|
||||
ret->threads = 1;
|
||||
break;
|
||||
case '?':
|
||||
std::cerr << "Unkown option: " << (char)optopt << "\n";
|
||||
case 'h':
|
||||
|
|
|
@ -12,11 +12,13 @@
|
|||
///////////////////////////////////////
|
||||
// cli options
|
||||
|
||||
constexpr char optarg_string[] = "n:p:h";
|
||||
constexpr char optarg_string[] = "n:p:a:t:h";
|
||||
|
||||
struct option_flags {
|
||||
uint16_t port;
|
||||
uint16_t threads;
|
||||
std::string name;
|
||||
std::string auth_path;
|
||||
};
|
||||
|
||||
void help(char*);
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
void setRoutes(crow::SimpleApp& app){
|
||||
CROW_ROUTE(app, "/proc/meminfo")([](const crow::request& req){
|
||||
if(!auth::checkAuth(req))
|
||||
return crow::response(403, "Authentication required");
|
||||
|
||||
bool status;
|
||||
std::string accept = req.get_header_value("Accept");
|
||||
|
||||
|
@ -24,7 +27,9 @@ void setRoutes(crow::SimpleApp& app){
|
|||
}
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/mem")([](){
|
||||
CROW_ROUTE(app, "/mem")([](const crow::request& req){
|
||||
if(!auth::checkAuth(req))
|
||||
return crow::response(403, "Authentication required");
|
||||
bool status;
|
||||
|
||||
crow::json::wvalue json;
|
||||
|
@ -34,6 +39,8 @@ void setRoutes(crow::SimpleApp& app){
|
|||
});
|
||||
|
||||
CROW_ROUTE(app, "/proc/uptime")([](const crow::request& req){
|
||||
if(!auth::checkAuth(req))
|
||||
return crow::response(403, "Authentication required");
|
||||
bool status;
|
||||
std::string accept = req.get_header_value("Accept");
|
||||
|
||||
|
@ -50,13 +57,17 @@ void setRoutes(crow::SimpleApp& app){
|
|||
}
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/uptime")([](){
|
||||
CROW_ROUTE(app, "/uptime")([](const crow::request& req){
|
||||
if(!auth::checkAuth(req))
|
||||
return crow::response(403, "Authentication required");
|
||||
crow::response ret;
|
||||
ret.moved_perm("/proc/uptime");
|
||||
return ret;
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/proc/loadavg")([](const crow::request& req){
|
||||
if(!auth::checkAuth(req))
|
||||
return crow::response(403, "Authentication required");
|
||||
bool status;
|
||||
std::string accept = req.get_header_value("Accept");
|
||||
|
||||
|
@ -73,10 +84,58 @@ void setRoutes(crow::SimpleApp& app){
|
|||
}
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/load")([](){
|
||||
CROW_ROUTE(app, "/load")([](const crow::request& req){
|
||||
if(!auth::checkAuth(req))
|
||||
return crow::response(403, "Authentication required");
|
||||
crow::response ret;
|
||||
ret.moved_perm("/proc/loadavg");
|
||||
return ret;
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/proc/sys/kernel/hostname")([](const crow::request& req){
|
||||
if(!auth::checkAuth(req))
|
||||
return crow::response(403, "Authentication required");
|
||||
bool status;
|
||||
std::string accept = req.get_header_value("Accept");
|
||||
|
||||
std::transform(accept.begin(), accept.end(), accept.begin(), ::tolower);
|
||||
|
||||
if(accept == "text/plain"){
|
||||
accept.clear();
|
||||
status = state::getRawHostname(accept);
|
||||
return crow::response(status ? 200 : 503, accept);
|
||||
} else {
|
||||
crow::json::wvalue json;
|
||||
status = state::getHostname(json);
|
||||
return crow::response(status ? 200 : 503, json.dump());
|
||||
}
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/hostname")([](const crow::request& req){
|
||||
if(!auth::checkAuth(req))
|
||||
return crow::response(403, "Authentication required");
|
||||
crow::response ret;
|
||||
ret.moved_perm("/proc/sys/kernel/hostname");
|
||||
return ret;
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/up")([](const crow::request& req){
|
||||
if(!auth::checkAuth(req))
|
||||
return crow::response(403, "Authentication required");
|
||||
|
||||
crow::json::wvalue ret;
|
||||
ret["message"] = "Alive and well!";
|
||||
return crow::response(200, ret.dump());
|
||||
});
|
||||
|
||||
//catchall route
|
||||
CROW_CATCHALL_ROUTE(app)([](const crow::request& req){
|
||||
if(!auth::checkAuth(req))
|
||||
return crow::response(403, "Authentication required");
|
||||
crow::json::wvalue ret;
|
||||
ret["message"] = "Route not understood. Please refer to the documentation";
|
||||
|
||||
return crow::response(404, ret.dump());
|
||||
});
|
||||
|
||||
}
|
||||
|
|
|
@ -13,4 +13,6 @@
|
|||
#include "components/memory.hpp"
|
||||
#include "components/state.hpp"
|
||||
|
||||
#include "auth/auth.hpp"
|
||||
|
||||
void setRoutes(crow::SimpleApp&);
|
||||
|
|
Loading…
Reference in New Issue