webfuse-server: add header-based authentication

pull/105/head
Falk Werner 1 year ago
parent f12f461154
commit 8c290b8c02

@ -22,6 +22,7 @@ add_library(webfuse_static STATIC
src/webfuse/response_type.cpp
src/webfuse/util/commandline_args.cpp
src/webfuse/util/commandline_reader.cpp
src/webfuse/util/authenticator.cpp
src/webfuse/filesystem.cpp
src/webfuse/filesystem/status.cpp
src/webfuse/filesystem/accessmode.cpp

@ -0,0 +1,13 @@
#!/usr/bin/bash
AUTH_TOKEN="$1"
if [[ "$AUTH_TOKEN" == "user:bob;token=foo" ]]
then
echo "$(date): webfuse: auth granted: $AUTH_TOKEN" >> /tmp/webfuse_auth.log
else
echo "$(date): webfuse: auth denied: $AUTH_TOKEN" >> /tmp/webfuse_auth.log
exit 1
fi

@ -272,7 +272,7 @@ class FilesystemProvider:
}
async def run(self):
async with websockets.connect(self.url) as connection:
async with websockets.connect(self.url, extra_headers=[("X-Auth-Token", "user:bob;token=foo")]) as connection:
while True:
request = await connection.recv()
reader = MessageReader(request)

@ -0,0 +1,55 @@
#include "webfuse/util/authenticator.hpp"
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
namespace webfuse
{
authenticator::authenticator(std::string const & app)
: app_(app)
{
}
bool authenticator::authenticate(std::string const & token)
{
bool result = false;
pid_t const pid = fork();
if (pid == 0)
{
// child
// prepare file descriptors
closefrom(0);
open("/dev/null", O_RDONLY);
open("/dev/null", O_WRONLY);
dup2(STDOUT_FILENO, STDERR_FILENO);
execl(app_.c_str(), app_.c_str(), token.c_str(), nullptr);
exit(EXIT_FAILURE);
}
else if (pid > 0)
{
// parent
int exit_status = EXIT_FAILURE;
int status = 0;
int const rc = waitpid(pid, &status, 0);
if (rc == pid)
{
exit_status = WEXITSTATUS(status);
}
result = (exit_status == EXIT_SUCCESS);
}
return result;
}
}

@ -0,0 +1,23 @@
#ifndef WEBFUSE_AUTHENTICATOR_HPP
#define WEBFUSE_AUTHENTICATOR_HPP
#include <string>
namespace webfuse
{
class authenticator
{
public:
explicit authenticator(std::string const & app);
~authenticator() = default;
bool authenticate(std::string const & token);
private:
std::string app_;
};
}
#endif

@ -30,10 +30,12 @@ int app::run(int argc, char * argv[]) // NOLINT(readability-convert-member-funct
fuse::print_usage();
std::cout << R"(
WEBFUSE options:
--wf-port PORT port number of websocket server (default: 8081)
--wf-vhost VHOST name of the virtual host (default: localhost)
--wf-cert PATH path of the server's public certificate (optional)
--wf-key PATH path of the server's private key (optional)
--wf-port PORT port number of websocket server (default: 8081)
--wf-vhost VHOST name of the virtual host (default: localhost)
--wf-cert PATH path of the server's public certificate (optional)
--wf-key PATH path of the server's private key (optional)
--wf-authenticator PATH path of authenticatior app (optional)
--wf-auth-header NAME name of the authentication header (optional)
)";
}
break;

@ -1,6 +1,8 @@
#include "webfuse/ws/config.hpp"
#include "webfuse/util/commandline_reader.hpp"
#include <cctype>
#include <algorithm>
#include <iostream>
namespace
@ -87,6 +89,18 @@ ws_config::ws_config(int argc, char * argv[])
use_tls = true;
}
}
else if (arg == "--wf-authenticator")
{
get_arg(*this, reader, authenticator, "missing AUTHENTICATOR");
}
else if (arg == "--wf-auth-header")
{
if (get_arg(*this, reader, auth_header, "missing AUTH_HEADER"))
{
std::transform(auth_header.begin(), auth_header.end(), auth_header.begin(),
[](auto c) {return std::tolower(c); });
}
}
else
{
args.push(arg.c_str());

@ -30,6 +30,9 @@ public:
bool use_tls;
std::string cert_path;
std::string key_path;
std::string authenticator;
std::string auth_header;
};
}

@ -1,4 +1,5 @@
#include "webfuse/ws/server.hpp"
#include "webfuse/util/authenticator.hpp"
#include <libwebsockets.h>
@ -32,6 +33,9 @@ struct user_data
uint32_t id = 0;
std::queue<webfuse::messagewriter> requests;
std::unordered_map<uint32_t, std::promise<webfuse::messagereader>> pending_responses;
std::string authenticator;
std::string auth_header;
};
@ -72,6 +76,75 @@ void do_receive(void * in, int len, lws* wsi, user_data * data)
}
}
std::string get_auth_token_of_known_header(lws * wsi, lws_token_indexes header)
{
std::string token;
int const length = lws_hdr_total_length(wsi, header);
if (length > 0)
{
std::vector<char> data(length + 1);
int const actual_length = lws_hdr_copy(wsi, data.data(), length + 1, header);
if (actual_length > 0)
{
token = data.data();
}
}
return token;
}
std::string get_auth_token_from_custom_header(lws * wsi, std::string const & auth_header)
{
std::string token;
int const length = lws_hdr_custom_length(wsi, auth_header.c_str(), auth_header.size());
if (length > 0)
{
std::vector<char> data(length + 1);
int const actual_length = lws_hdr_custom_copy(wsi, data.data(), length + 1,
auth_header.c_str(), auth_header.size());
if (actual_length > 0)
{
token = data.data();
}
}
return token;
}
std::string get_auth_token(lws * wsi, std::string const & auth_header)
{
if (auth_header == "authorization")
{
return get_auth_token_of_known_header(wsi, WSI_TOKEN_HTTP_AUTHORIZATION);
}
if (auth_header == "x-auth-token")
{
return get_auth_token_of_known_header(wsi, WSI_TOKEN_X_AUTH_TOKEN);
}
return get_auth_token_from_custom_header(wsi, auth_header);
}
int do_authenticate(lws * wsi, std::string const & authenticator_app, std::string const & auth_header)
{
int result = 0;
if ((!authenticator_app.empty()) && (!auth_header.empty()))
{
std::string token = get_auth_token(wsi, auth_header);
if (!token.empty())
{
webfuse::authenticator authenticator(authenticator_app);
result = authenticator.authenticate(token) ? 0 : -1;
}
else
{
result = -1;
}
}
return result;
}
}
@ -90,6 +163,11 @@ static int ws_server_callback(struct lws *wsi, enum lws_callback_reasons reason,
int result = 0;
switch(reason)
{
case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
{
result = do_authenticate(wsi, data->authenticator, data->auth_header);
}
break;
case LWS_CALLBACK_ESTABLISHED:
if (nullptr == data->connection)
{
@ -164,6 +242,9 @@ public:
detail(ws_config const & config)
: shutdown_requested(false)
{
data.authenticator = config.authenticator;
data.auth_header = config.auth_header;
lws_set_log_level(0, nullptr);
memset(reinterpret_cast<void*>(protocols), 0, sizeof(protocols));

@ -80,7 +80,7 @@ int process::wait()
{
int status = 0;
int rc = waitpid(pid, &status, 0);
if (rc == 0)
if (rc == pid)
{
exit_code = WEXITSTATUS(status);
pid = 0;

Loading…
Cancel
Save