Compare commits

..

No commits in common. "b7cd1ce78e0f36cb832fa040b422d9435080c5e2" and "b66f1d4e0da9cb38e01820c09e67c2506d0f1a8a" have entirely different histories.

14 changed files with 54 additions and 274 deletions

47
Jenkinsfile vendored
View File

@ -1,47 +0,0 @@
pipeline {
agent any
options
{
skipDefaultCheckout(true)
}
stages
{
stage('Checkout')
{
steps
{
cleanWs()
checkout scm
}
}
stage('Run')
{
steps
{
sh 'ansible-playbook -vvvv --skip-tags cleanup,deploy -i ./playbooks/inventory.yaml ./playbooks/build.yaml'
}
}
stage('Deploy')
{
when
{
expression
{
currentBuild.result == null || currentBuild.result == 'SUCCESS'
}
}
steps
{
sh 'ansible-playbook --tags deploy -i ./playbooks/inventory.yaml ./playbooks/build.yaml'
}
}
}
post
{
always
{
sh 'ansible-playbook --tags cleanup -i ./playbooks/inventory.yaml ./playbooks/build.yaml'
}
}
}

6
debian/changelog vendored
View File

@ -1,5 +1,5 @@
j7s-mosquitto-plugin (0.0.7-1) stable; urgency=medium
j7s-mosquitto-plugin (0.0.1-1) unstable; urgency=medium
* New release.
* Initial release.
-- James Pace <jpace121@gmail.com> Tues, 09 August 2022 01:53:50 +0000
-- James Pace <jpace121@gmail.com> Sat, 26 Mar 2022 23:49:28 +0000

2
debian/control vendored
View File

@ -8,7 +8,7 @@ Section: misc
Priority: optional
Package: j7s-mosquitto-plugin
Architecture: amd64
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: Authentication plugin for mosquitto for the j7s project.
Authentication plugin for mosquitto for the j7s project.

View File

@ -1,3 +1 @@
default:
- /home/jimmy/Develop/mosquitto-plugin/examples/key.pem
- /home/jimmy/Develop/mosquitto-plugin/examples/service-key.pem
default: /home/jimmy/Develop/mosquitto-plugin/examples/key.pem

View File

@ -1,14 +1,17 @@
per_listener_settings false
allow_anonymous false
per_listener_settings true
log_type all
listener 8082
protocol websockets
allow_anonymous false
auth_plugin /home/jimmy/Develop/mosquitto-plugin/build/libj7s-plugin.so
auth_opt_key_file /home/jimmy/Develop/mosquitto-plugin/examples/keys.yaml
auth_opt_acl_file /home/jimmy/Develop/mosquitto-plugin/examples/acl.yaml
listener 8082
protocol websockets
listener 8081
protocol mqtt
allow_anonymous false
auth_plugin /home/jimmy/Develop/mosquitto-plugin/build/libj7s-plugin.so
auth_opt_key_file /home/jimmy/Develop/mosquitto-plugin/examples/keys.yaml
auth_opt_acl_file /home/jimmy/Develop/mosquitto-plugin/examples/acl.yaml

View File

@ -31,10 +31,9 @@ token = r.json()['access_token']
print(token)
client = paho.mqtt.client.Client(protocol=paho.mqtt.client.MQTTv5,
transport="websockets")
transport="tcp")
client.username_pw_set("jimmy", password=token)
client.tls_set()
client.connect("mqtt.jpace121.net", port=443)
client.connect("localhost", port=8081)
print("Waiting on connection.")
time.sleep(20)

View File

@ -4,26 +4,33 @@ import time
import paho.mqtt.client
import time
# RESULT=`curl --cacert ./ca.pem --cert jimmy-client.pem --key jimmy-client-key.pem --data "grant_type=password&client_id=test&username=jimmy&password=1234" https://nginx-test.internal.jpace121.net:8443/realms/master/protocol/openid-connect/token`
s = requests.Session()
s.verify = "/home/jimmy/Develop/mosquitto-plugin/test/ca.pem"
s.cert = ("/home/jimmy/Develop/keycloak/jimmy-client.pem", "/home/jimmy/Develop/keycloak/jimmy-client-key.pem")
# Get urls.
well_known = s.get("https://auth.jpace121.net/realms/jpace121-services/.well-known/openid-configuration", ).json()
print("Before get.")
well_known = s.get("https://nginx-test.internal.jpace121.net:8443/realms/jpace121-main/.well-known/openid-configuration", ).json()
print("After get.")
token_url = well_known['token_endpoint']
data = {"grant_type" : "password", "client_id" : "mqtt", "username" : "j7s-1"}
# Override for now, url doesn't include port...
token_url = "https://nginx-test.internal.jpace121.net:8443/realms/jpace121-main/protocol/openid-connect/token"
# RESULT=`curl --cacert ./ca.pem --cert jimmy-client.pem --key jimmy-client-key.pem --data "grant_type=password&client_id=mqtt&username=jimmy&password=1234" https://nginx-test.internal.jpace121.net:8443/realms/jpace121-main/protocol/openid-connect/token`
data = {"grant_type" : "password", "client_id" : "mqtt", "username" : "jimmy", "password": "1234"}
# Request token.
r = s.post(token_url, data=data)
print(r.json())
token = r.json()['access_token']
client = paho.mqtt.client.Client(protocol=paho.mqtt.client.MQTTv5,
transport="tcp")
client.username_pw_set("j7s-1", password=token)
client.tls_set(certfile="/home/jimmy/Develop/keycloak/jimmy-client.pem",
keyfile="/home/jimmy/Develop/keycloak/jimmy-client-key.pem")
client.connect("mqtt.jpace121.net", port=8883)
client.username_pw_set("jimmy", password=token)
client.connect("localhost", port=8081)
print("Waiting on connection.")
time.sleep(5)
time.sleep(20)

View File

@ -1,3 +0,0 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9DBwMe+eHkYLfq076sFq75gepyaL4LQtX8qVlGjJCtLxV308L2d8XGC35VBxrIbx8Bs5sKT4e5s9suzO8FhGMQ==
-----END PUBLIC KEY-----

View File

@ -27,5 +27,3 @@ std::string gen_token(
const std::string &priv_key,
const std::chrono::time_point<std::chrono::system_clock> &issue_time,
const std::chrono::time_point<std::chrono::system_clock> &expr_time);
std::string toString(const char* c_str);

View File

@ -1,124 +0,0 @@
---
- name: Setup container for building.
hosts: localhost
tasks:
- name: Create a container
containers.podman.podman_container:
name: j7s-mosquitto-debian-builder
image: docker.io/library/debian:bullseye
volume: "{{ playbook_dir }}/..:/work/src:Z"
command: 'sleep infinity'
state: started
- name: Add the container to inventory.
ansible.builtin.add_host:
name: j7s-mosquitto-debian-builder
ansible_connection: containers.podman.podman
ansible_user: root
ansible_python_interpreter: /usr/bin/python3
changed_when: false
- name: Set the container up for normal ansible stuff.
delegate_to: j7s-mosquitto-debian-builder
raw: bash -c "apt update && apt install -y python3"
- name: Setup build environment.
hosts: j7s-mosquitto-debian-builder
become: true
tasks:
- name: Update cache
ansible.builtin.apt:
update_cache: true
- name: Install build dependencies.
ansible.builtin.apt:
package:
- build-essential
- cmake
- dh-cmake
- mosquitto-dev
- libmosquitto-dev
- libssl-dev
- libyaml-cpp-dev
- fakeroot
- devscripts
- debhelper
state: latest
- name: Build package.
hosts: j7s-mosquitto-debian-builder
tasks:
- name: Call debuild.
ansible.builtin.shell:
chdir: "/work/src"
cmd: ls && debuild -us -uc -b
- name: Copy deb file back to the main directory.
ansible.builtin.shell:
cmd: "cp -r /work/*.deb /work/src/."
- name: Copy changes file back to the main directory.
ansible.builtin.shell:
cmd: "cp -r /work/*.changes /work/src/."
- name: Copy buildinfo file back to the main directory.
ansible.builtin.shell:
cmd: "cp -r /work/*.buildinfo /work/src/."
- name: Upload to packaging server.
hosts: packaging
tags: deploy
tasks:
- name: Copy package to packaging server.
ansible.builtin.copy:
src: "{{ item }}"
dest: ~/public/apt/mini-dinstall/incoming/
with_fileglob: ../j7s-*.deb
register: copied_files
- name: Fail if we didn't copy exactly two files. (debug and normal)
ansible.builtin.fail:
msg: "Didn't find exactly two deb file."
when: copied_files.results | length != 2
- name: Ditto the changes file.
ansible.builtin.copy:
src: "{{ item }}"
dest: ~/public/apt/mini-dinstall/incoming/
with_fileglob: ../j7s-*.changes
register: copied_changes
- name: Fail if we didn't copy exactly one file.
ansible.builtin.fail:
msg: "Didn't find exactly one changes file."
when: copied_changes.results | length != 1
- name: Ditto the buildinfo file.
ansible.builtin.copy:
src: "{{ item }}"
dest: ~/public/apt/mini-dinstall/incoming/
with_fileglob: ../j7s-*.buildinfo
register: copied_buildinfo
- name: Fail if we didn't copy exactly one file.
ansible.builtin.fail:
msg: "Didn't find exactly one buildinfo file."
when: copied_buildinfo.results | length != 1
- name: Run mini-dinstall.
ansible.builtin.shell:
cmd: "mini-dinstall --batch"
- name: wait dinstall to do its thing
ansible.builtin.pause:
seconds: 3
- name: Build and push image.
hosts: localhost
tags: deploy
tasks:
- name: Build and push image.
containers.podman.podman_image:
name: j7s-mosquitto
tag: latest
force: true
path: ..
push: yes
push_args:
dest: registry.jpace121.net
- name: Cleanup
hosts: localhost
tags: cleanup
tasks:
- name: Stop the container.
containers.podman.podman_container:
name: j7s-mosquitto-debian-builder
state: absent

View File

@ -1,5 +0,0 @@
all:
hosts:
packaging:
ansible_host: packages.jpace121.net
ansible_user: packaging

View File

@ -18,11 +18,10 @@
#include <j7s-plugin/AuthList.hpp>
#include <j7s-plugin/Authorizer.hpp>
#include <tuple>
#include <vector>
// Util.
std::tuple<bool, bool> checkACL(const std::string &user, const YAML::Node &aclFile);
std::vector<std::string> getKey(const std::string &user, const YAML::Node &keyFile);
std::optional<std::string> getKey(const std::string &user, const YAML::Node &keyFile);
// Class implementation.
Authorizer::Authorizer(const std::string &keyFilePath, const std::string &aclFilePath) :
@ -38,20 +37,15 @@ bool Authorizer::add(const std::string &token, const std::string &username)
return false;
}
const auto keys = getKey(username, _keyFile);
const auto key = getKey(username, _keyFile);
// Do any of the keys validate the token?
const bool validated = [token, username, keys]() {
for (const auto key : keys)
if (not key)
{
if (validate(token, username, key))
{
return true;
}
}
std::cerr << "Could not read key for user." << std::endl;
return false;
}();
}
const bool validated = validate(token, username, key.value());
if (not validated)
{
std::cerr << "Not validated." << std::endl;
@ -129,45 +123,18 @@ std::tuple<bool, bool> checkACL(const std::string &user, const YAML::Node &aclFi
return std::make_tuple(can_read, can_write);
}
std::vector<std::string> getKey(const std::string &user, const YAML::Node &keyFile)
{
// Find this user's entry or the default one.
YAML::Node userKey;
if(keyFile[user])
{
userKey = keyFile[user];
}
else
std::optional<std::string> getKey(const std::string &user, const YAML::Node &keyFile)
{
// TODO: Make sure default exists.
userKey = keyFile["default"];
}
// Get the paths from the yaml file as std::filesystem::paths.
std::vector<std::filesystem::path> paths;
if(not userKey.IsSequence())
std::filesystem::path pathToKey;
if (keyFile[user])
{
paths.emplace_back(userKey.as<std::string>());
pathToKey = std::filesystem::path(keyFile[user].as<std::string>());
}
else
{
for(const auto key : userKey)
{
paths.emplace_back(key.as<std::string>());
}
pathToKey = std::filesystem::path(keyFile["default"].as<std::string>());
}
// Now convert to an array of optional keys.
std::vector<std::string> keys;
for(const auto path : paths)
{
const auto key = read_key(std::filesystem::absolute(path).string());
if(key)
{
keys.emplace_back(key.value());
}
}
return keys;
return read_key(std::filesystem::absolute(pathToKey).string());
}

View File

@ -53,10 +53,10 @@ int mosquitto_plugin_init(
std::filesystem::path aclFilePath;
for (int index = 0; index < option_count; index++)
{
const auto key = toString(options[index].key);
const auto key = std::string(options[index].key);
if (key == "key_file")
{
std::string key_file_string = toString(options[index].value);
std::string key_file_string = std::string(options[index].value);
if (key_file_string.empty())
{
mosquitto_log_printf(MOSQ_LOG_ERR, "key_file not set.");
@ -66,7 +66,7 @@ int mosquitto_plugin_init(
}
else if (key == "acl_file")
{
std::string acl_file_string = toString(options[index].value);
std::string acl_file_string = std::string(options[index].value);
if (acl_file_string.empty())
{
mosquitto_log_printf(MOSQ_LOG_ERR, "acl_file not set.");
@ -124,11 +124,11 @@ int j7s_auth_basic_auth_callback(int event, void *event_data, void *userdata)
if (!auth_data->password)
{
authorizer->add_unknown(toString(auth_data->username));
authorizer->add_unknown(std::string(auth_data->username));
return MOSQ_ERR_PLUGIN_DEFER;
}
bool is_authed =
authorizer->add(toString(auth_data->password), toString(auth_data->username));
authorizer->add(std::string(auth_data->password), std::string(auth_data->username));
if (is_authed)
{
@ -150,26 +150,24 @@ int j7s_acl_check_callback(int event, void *event_data, void *userdata)
struct mosquitto_evt_acl_check *acl_data =
static_cast<struct mosquitto_evt_acl_check *>(event_data);
const std::string username = toString(mosquitto_client_username(acl_data->client));
const std::string username = std::string(mosquitto_client_username(acl_data->client));
if (authorizer->is_unknown(username))
{
mosquitto_log_printf(MOSQ_LOG_ERR, "ACL callback without username");
return MOSQ_ERR_PLUGIN_DEFER;
}
switch (acl_data->access)
{
case MOSQ_ACL_SUBSCRIBE:
return (authorizer->can_read(username) ? MOSQ_ERR_SUCCESS : MOSQ_ERR_ACL_DENIED);
return MOSQ_ERR_PLUGIN_DEFER;
case MOSQ_ACL_UNSUBSCRIBE:
return MOSQ_ERR_SUCCESS;
return MOSQ_ERR_PLUGIN_DEFER;
case MOSQ_ACL_WRITE:
return (authorizer->can_write(username) ? MOSQ_ERR_SUCCESS : MOSQ_ERR_ACL_DENIED);
case MOSQ_ACL_READ:
return (authorizer->can_read(username) ? MOSQ_ERR_SUCCESS : MOSQ_ERR_ACL_DENIED);
default:
mosquitto_log_printf(MOSQ_LOG_ERR, "Unhandled ACL check for user: %s", username.c_str());
return MOSQ_ERR_ACL_DENIED;
}
}
@ -179,7 +177,7 @@ int j7s_disconnect_callback(int event, void *event_data, void *userdata)
struct mosquitto_evt_disconnect *disconnect_data =
static_cast<struct mosquitto_evt_disconnect *>(event_data);
const std::string username = toString(mosquitto_client_username(disconnect_data->client));
const std::string username = std::string(mosquitto_client_username(disconnect_data->client));
authorizer->logout(username);
return MOSQ_ERR_SUCCESS;

View File

@ -56,6 +56,7 @@ bool validate(const std::string &token, const std::string &username, const std::
}
catch (std::system_error &exception)
{
std::cerr << "Token Verification Failed: " << exception.what() << std::endl;
return false;
}
auto claims = decoded_token.get_payload_claims();
@ -119,15 +120,3 @@ std::string gen_token(
return token;
}
std::string toString(const char* c_str)
{
if(c_str)
{
return std::string(c_str);
}
else
{
return std::string();
}
}