mirror of
https://github.com/falk-werner/webfuse
synced 2024-10-27 20:34:10 +00:00
add in-protocol authentication mechanism
This commit is contained in:
parent
9423d75021
commit
5db3b28b5a
@ -1,12 +1,21 @@
|
|||||||
# Authentication
|
# Authentication
|
||||||
|
|
||||||
Webfuse supports token-based authentication using HTTP headers. To activate authentication, two command line option needs to be given:
|
Webfuse supports two authentications mechanisms:
|
||||||
|
|
||||||
|
- token-based authentication using HTTP headers
|
||||||
|
- in-protocol authentication
|
||||||
|
|
||||||
|
To activate authentication, two command line option needs to be given:
|
||||||
|
|
||||||
- `--wf-authenticator PATH`
|
- `--wf-authenticator PATH`
|
||||||
allows to specify an executable used for authentication
|
allows to specify an executable used for authentication
|
||||||
- `--wf-auth-header HEADER`
|
- `--wf-auth-header HEADER` _(optional)_
|
||||||
allows to specify the HTTP header used for authentication
|
allows to specify the HTTP header used for authentication
|
||||||
|
|
||||||
|
When `--wf-auth-header` is not specifiend or the header is not contained
|
||||||
|
in the HTTP request, the in-protocol solutions is used: Before any other
|
||||||
|
operation, the credentials are queried via `getcreds`request.
|
||||||
|
|
||||||
## Authenticator
|
## Authenticator
|
||||||
|
|
||||||
An authenticator is an executable or script used for token-based
|
An authenticator is an executable or script used for token-based
|
||||||
|
@ -313,6 +313,7 @@ _Note that the following numbers are in `hexadecimal` notation._
|
|||||||
| rmdir | 0x14 | 0x94 |
|
| rmdir | 0x14 | 0x94 |
|
||||||
| statfs | 0x15 | 0x95 |
|
| statfs | 0x15 | 0x95 |
|
||||||
| utimens | 0x16 | 0x96 |
|
| utimens | 0x16 | 0x96 |
|
||||||
|
| getcreds | 0x17 | 0x97 |
|
||||||
|
|
||||||
## Methods
|
## Methods
|
||||||
|
|
||||||
@ -807,6 +808,29 @@ _Note that handle might be invalid (-1), even if the file is open._
|
|||||||
| type | u8 | message type (0x96) |
|
| type | u8 | message type (0x96) |
|
||||||
| result | result | operation status |
|
| result | result | operation status |
|
||||||
|
|
||||||
|
### getcreds
|
||||||
|
|
||||||
|
Query credentials. When authentication is active and the in-protocol
|
||||||
|
authentication mechanism is used, this is the first request a
|
||||||
|
webfuse service sends to a provider.
|
||||||
|
|
||||||
|
#### Request
|
||||||
|
|
||||||
|
| Field | Data Type | Description |
|
||||||
|
| ------ | --------- | ----------- |
|
||||||
|
| id | u32 | message id |
|
||||||
|
| type | u8 | message type (0x17) |
|
||||||
|
|
||||||
|
_Note that handle might be invalid (-1), even if the file is open._
|
||||||
|
|
||||||
|
#### Response
|
||||||
|
|
||||||
|
| Field | Data Type | Description |
|
||||||
|
| ------ | --------- | ----------- |
|
||||||
|
| id | u32 | message id |
|
||||||
|
| type | u8 | message type (0x97) |
|
||||||
|
| creds | str | credentials |
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### Get file attributes
|
### Get file attributes
|
||||||
|
@ -5,6 +5,7 @@ import os
|
|||||||
import stat
|
import stat
|
||||||
import websockets
|
import websockets
|
||||||
import errno
|
import errno
|
||||||
|
import getpass
|
||||||
|
|
||||||
INVALID_FD = 0xffffffffffffffff
|
INVALID_FD = 0xffffffffffffffff
|
||||||
|
|
||||||
@ -269,10 +270,12 @@ class FilesystemProvider:
|
|||||||
0x14: FilesystemProvider.rmdir,
|
0x14: FilesystemProvider.rmdir,
|
||||||
0x15: FilesystemProvider.statfs,
|
0x15: FilesystemProvider.statfs,
|
||||||
0x16: FilesystemProvider.utimens,
|
0x16: FilesystemProvider.utimens,
|
||||||
|
0x17: FilesystemProvider.getcreds,
|
||||||
}
|
}
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
async with websockets.connect(self.url, extra_headers=[("X-Auth-Token", "user:bob;token=foo")]) as connection:
|
extra_headers = [("X-Auth-Token", "user:bob;token=foo")]
|
||||||
|
async with websockets.connect(self.url, extra_headers=extra_headers) as connection:
|
||||||
while True:
|
while True:
|
||||||
request = await connection.recv()
|
request = await connection.recv()
|
||||||
reader = MessageReader(request)
|
reader = MessageReader(request)
|
||||||
@ -565,7 +568,11 @@ class FilesystemProvider:
|
|||||||
writer.write_u64(buffer.f_files)
|
writer.write_u64(buffer.f_files)
|
||||||
writer.write_u64(buffer.f_ffree)
|
writer.write_u64(buffer.f_ffree)
|
||||||
writer.write_u64(buffer.f_namemax)
|
writer.write_u64(buffer.f_namemax)
|
||||||
|
|
||||||
|
def getcreds(self, reader, writer):
|
||||||
|
credentials = getpass.getpass(prompt="credentials: ")
|
||||||
|
writer.write_str(credentials)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
provider = FilesystemProvider('.', 'ws://localhost:8081')
|
provider = FilesystemProvider('.', 'ws://localhost:8081')
|
||||||
|
@ -418,6 +418,11 @@ public:
|
|||||||
return (result == 0) ? 0 : -errno;
|
return (result == 0) ? 0 : -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string get_credentials() override
|
||||||
|
{
|
||||||
|
return getpass("credentials: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string get_full_path(std::string const & path)
|
std::string get_full_path(std::string const & path)
|
||||||
|
@ -419,4 +419,11 @@ int filesystem::statfs(std::string const & path, struct statvfs * statistics)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get credentials is handled internally
|
||||||
|
std::string filesystem::get_credentials()
|
||||||
|
{
|
||||||
|
throw std::runtime_error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -47,6 +47,7 @@ public:
|
|||||||
|
|
||||||
int statfs(std::string const & path, struct statvfs * statistics) override;
|
int statfs(std::string const & path, struct statvfs * statistics) override;
|
||||||
|
|
||||||
|
std::string get_credentials() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ws_server &proxy;
|
ws_server &proxy;
|
||||||
|
@ -131,5 +131,11 @@ int empty_filesystem::statfs(std::string const & path, struct statvfs * statisti
|
|||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string empty_filesystem::get_credentials()
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -39,6 +39,8 @@ public:
|
|||||||
int rmdir(std::string const & path) override;
|
int rmdir(std::string const & path) override;
|
||||||
|
|
||||||
int statfs(std::string const & path, struct statvfs * statistics) override;
|
int statfs(std::string const & path, struct statvfs * statistics) override;
|
||||||
|
|
||||||
|
std::string get_credentials() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,8 @@ public:
|
|||||||
virtual int rmdir(std::string const & path) = 0;
|
virtual int rmdir(std::string const & path) = 0;
|
||||||
|
|
||||||
virtual int statfs(std::string const & path, struct statvfs * statistics) = 0;
|
virtual int statfs(std::string const & path, struct statvfs * statistics) = 0;
|
||||||
|
|
||||||
|
virtual std::string get_credentials() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -122,6 +122,9 @@ public:
|
|||||||
case request_type::statfs:
|
case request_type::statfs:
|
||||||
fs_statfs(reader, writer);
|
fs_statfs(reader, writer);
|
||||||
break;
|
break;
|
||||||
|
case request_type::getcreds:
|
||||||
|
fs_get_credentials(reader, writer);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
std::cout << "unknown request: " << ((int) req_type) << std::endl;
|
std::cout << "unknown request: " << ((int) req_type) << std::endl;
|
||||||
break;
|
break;
|
||||||
@ -371,6 +374,13 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fs_get_credentials(messagereader & reader, messagewriter & writer)
|
||||||
|
{
|
||||||
|
std::string credentials = fs_.get_credentials();
|
||||||
|
writer.write_str(credentials);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
filesystem_i & fs_;
|
filesystem_i & fs_;
|
||||||
ws_client client;
|
ws_client client;
|
||||||
};
|
};
|
||||||
|
@ -29,6 +29,7 @@ request_type get_request_type(uint8_t value)
|
|||||||
case static_cast<uint8_t>(request_type::rmdir): return request_type::rmdir;
|
case static_cast<uint8_t>(request_type::rmdir): return request_type::rmdir;
|
||||||
case static_cast<uint8_t>(request_type::statfs): return request_type::statfs;
|
case static_cast<uint8_t>(request_type::statfs): return request_type::statfs;
|
||||||
case static_cast<uint8_t>(request_type::utimens): return request_type::utimens;
|
case static_cast<uint8_t>(request_type::utimens): return request_type::utimens;
|
||||||
|
case static_cast<uint8_t>(request_type::getcreds): return request_type::getcreds;
|
||||||
default:
|
default:
|
||||||
return request_type::unknown;
|
return request_type::unknown;
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,8 @@ enum class request_type: uint8_t
|
|||||||
readdir = 0x13,
|
readdir = 0x13,
|
||||||
rmdir = 0x14,
|
rmdir = 0x14,
|
||||||
statfs = 0x15,
|
statfs = 0x15,
|
||||||
utimens = 0x16
|
utimens = 0x16,
|
||||||
|
getcreds =0x17
|
||||||
};
|
};
|
||||||
|
|
||||||
request_type get_request_type(uint8_t value);
|
request_type get_request_type(uint8_t value);
|
||||||
|
@ -29,6 +29,7 @@ response_type get_response_type(request_type value)
|
|||||||
case request_type::rmdir: return response_type::rmdir;
|
case request_type::rmdir: return response_type::rmdir;
|
||||||
case request_type::statfs: return response_type::statfs;
|
case request_type::statfs: return response_type::statfs;
|
||||||
case request_type::utimens: return response_type::utimens;
|
case request_type::utimens: return response_type::utimens;
|
||||||
|
case request_type::getcreds: return response_type::getcreds;
|
||||||
default:
|
default:
|
||||||
return response_type::unknown;
|
return response_type::unknown;
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,8 @@ enum class response_type: uint8_t
|
|||||||
readdir = 0x93,
|
readdir = 0x93,
|
||||||
rmdir = 0x94,
|
rmdir = 0x94,
|
||||||
statfs = 0x95,
|
statfs = 0x95,
|
||||||
utimens = 0x96
|
utimens = 0x96,
|
||||||
|
getcreds = 0x97
|
||||||
};
|
};
|
||||||
|
|
||||||
response_type get_response_type(request_type value);
|
response_type get_response_type(request_type value);
|
||||||
|
@ -54,7 +54,7 @@ static int ws_server_callback(struct lws *wsi, enum lws_callback_reasons reason,
|
|||||||
handler->on_receive(wsi, in, len);
|
handler->on_receive(wsi, in, len);
|
||||||
break;
|
break;
|
||||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||||
handler->on_writable();
|
result = handler->on_writable();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -51,6 +51,7 @@ namespace webfuse
|
|||||||
server_handler::server_handler(std::string const & auth_app, std::string const & auth_hdr)
|
server_handler::server_handler(std::string const & auth_app, std::string const & auth_hdr)
|
||||||
: connection(nullptr)
|
: connection(nullptr)
|
||||||
, id(0)
|
, id(0)
|
||||||
|
, shutdown_requested(false)
|
||||||
, is_authenticated(false)
|
, is_authenticated(false)
|
||||||
, authenticator(auth_app)
|
, authenticator(auth_app)
|
||||||
, auth_header(auth_hdr)
|
, auth_header(auth_hdr)
|
||||||
@ -69,9 +70,26 @@ int server_handler::on_established(lws * wsi)
|
|||||||
if (nullptr == connection)
|
if (nullptr == connection)
|
||||||
{
|
{
|
||||||
connection = wsi;
|
connection = wsi;
|
||||||
|
id = 0;
|
||||||
|
|
||||||
|
if ((!is_authenticated) && (!authenticator.empty()))
|
||||||
|
{
|
||||||
|
{
|
||||||
|
messagewriter writer(request_type::getcreds);
|
||||||
|
std::lock_guard<std::mutex> lock(mut);
|
||||||
|
|
||||||
|
uint32_t id = next_id();
|
||||||
|
writer.set_id(id);
|
||||||
|
requests.emplace(std::move(writer));
|
||||||
|
}
|
||||||
|
|
||||||
|
lws_callback_on_writable(wsi);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// already connected: refuse
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,22 +104,29 @@ void server_handler::on_receive(lws * wsi, void* in, int len)
|
|||||||
{
|
{
|
||||||
webfuse::messagereader reader(current_message);
|
webfuse::messagereader reader(current_message);
|
||||||
uint32_t id = reader.read_u32();
|
uint32_t id = reader.read_u32();
|
||||||
reader.read_u8(); // read message type: ToDo: use it
|
auto const message_type = reader.read_u8(); // read message type: ToDo: use it
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(mut);
|
if (static_cast<response_type>(message_type) == response_type::getcreds)
|
||||||
auto it = pending_responses.find(id);
|
|
||||||
if (it != pending_responses.end())
|
|
||||||
{
|
{
|
||||||
it->second.set_value(std::move(reader));
|
finish_authentication(wsi, std::move(reader));
|
||||||
pending_responses.erase(it);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// ToDo: log request not found
|
std::lock_guard<std::mutex> lock(mut);
|
||||||
std::cout << "warning: request not found: id=" << id << std::endl;
|
auto it = pending_responses.find(id);
|
||||||
for(auto const & entry: pending_responses)
|
if (it != pending_responses.end())
|
||||||
{
|
{
|
||||||
std::cout << "\t" << entry.first << std::endl;
|
it->second.set_value(std::move(reader));
|
||||||
|
pending_responses.erase(it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ToDo: log request not found
|
||||||
|
std::cout << "warning: request not found: id=" << id << std::endl;
|
||||||
|
for(auto const & entry: pending_responses)
|
||||||
|
{
|
||||||
|
std::cout << "\t" << entry.first << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,8 +138,10 @@ void server_handler::on_receive(lws * wsi, void* in, int len)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void server_handler::on_writable()
|
int server_handler::on_writable()
|
||||||
{
|
{
|
||||||
|
if (shutdown_requested) { return -1; }
|
||||||
|
|
||||||
webfuse::messagewriter writer(webfuse::request_type::unknown);
|
webfuse::messagewriter writer(webfuse::request_type::unknown);
|
||||||
bool has_msg = false;
|
bool has_msg = false;
|
||||||
bool has_more = false;
|
bool has_more = false;
|
||||||
@ -143,6 +170,8 @@ void server_handler::on_writable()
|
|||||||
{
|
{
|
||||||
lws_callback_on_writable(connection);
|
lws_callback_on_writable(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void server_handler::on_closed(lws * wsi)
|
void server_handler::on_closed(lws * wsi)
|
||||||
@ -258,5 +287,22 @@ uint32_t server_handler::next_id()
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void server_handler::finish_authentication(lws * wsi, messagereader reader)
|
||||||
|
{
|
||||||
|
auto const credentials = reader.read_str();
|
||||||
|
|
||||||
|
webfuse::authenticator auth(authenticator);
|
||||||
|
auto const result = auth.authenticate(credentials);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
is_authenticated = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
shutdown_requested = true;
|
||||||
|
lws_callback_on_writable(wsi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -25,7 +25,7 @@ public:
|
|||||||
|
|
||||||
int on_established(lws* wsi);
|
int on_established(lws* wsi);
|
||||||
void on_receive(lws * wsi, void* in, int len);
|
void on_receive(lws * wsi, void* in, int len);
|
||||||
void on_writable();
|
int on_writable();
|
||||||
void on_closed(lws * wsi);
|
void on_closed(lws * wsi);
|
||||||
|
|
||||||
std::future<messagereader> perform(messagewriter writer);
|
std::future<messagereader> perform(messagewriter writer);
|
||||||
@ -35,9 +35,11 @@ private:
|
|||||||
int authenticate_via_header(lws * wsi);
|
int authenticate_via_header(lws * wsi);
|
||||||
std::string get_auth_token(lws * wsi) const;
|
std::string get_auth_token(lws * wsi) const;
|
||||||
uint32_t next_id();
|
uint32_t next_id();
|
||||||
|
void finish_authentication(lws * wsi, messagereader reader);
|
||||||
|
|
||||||
struct lws * connection;
|
struct lws * connection;
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
|
bool shutdown_requested;
|
||||||
|
|
||||||
std::atomic<bool> is_authenticated;
|
std::atomic<bool> is_authenticated;
|
||||||
std::string authenticator;
|
std::string authenticator;
|
||||||
|
@ -40,6 +40,9 @@ public:
|
|||||||
MOCK_METHOD(int, rmdir, (std::string const & path));
|
MOCK_METHOD(int, rmdir, (std::string const & path));
|
||||||
|
|
||||||
MOCK_METHOD(int, statfs, (std::string const & path, struct statvfs * statistics));
|
MOCK_METHOD(int, statfs, (std::string const & path, struct statvfs * statistics));
|
||||||
|
|
||||||
|
MOCK_METHOD(std::string, get_credentials, ());
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user