Add program to generate long lived tokens. Use python to get a token directly.

This commit is contained in:
James Pace 2022-03-22 23:13:43 +00:00
parent bfb110d061
commit c76ff553cc
7 changed files with 184 additions and 9 deletions

View File

@ -12,6 +12,7 @@ else()
endif()
find_package(jwt-cpp)
find_package(argparse)
find_package(OpenSSL)
set(CMAKE_CXX_STANDARD 20)
@ -23,3 +24,11 @@ target_include_directories(j7s-plugin PUBLIC
${jwt-cpp_INCLUDE_DIR}
)
target_link_libraries(j7s-plugin OpenSSL::Crypto)
add_executable(gen-token src/gen-token.cpp)
target_include_directories(gen-token PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
${jwt-cpp_INCLUDE_DIR}
)
target_link_libraries(gen-token OpenSSL::Crypto argparse::argparse)

View File

@ -4,6 +4,10 @@ Authentication using JWTs for the mosquitto mqtt broker.
## Dependencies
```
sudo apt install mosquitto-dev g++ cmake libmosquitto-dev mosquitto-clients
sudo apt install openssl libssl-dev
sudo apt install mosquitto-dev g++ cmake libmosquitto-dev mosquitto-clients openssl libssl-dev
```
## Generating offline keys
ssh-keygen -t rsa -b 4096 -m PEM -f priv.key
rm priv.key.pub
openssl rsa -in priv.key -pubout -outform PEM -out pub.key

View File

@ -16,6 +16,19 @@ list (APPEND EXTRA_CMAKE_ARGS
-Djwt-cpp_DIR=${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp -Djwt-cpp_INCLUDE_DIR=${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp/include
)
list(APPEND DEPENDENCIES ep_argparse)
ExternalProject_Add(ep_argparse
PREFIX ep_argparse
GIT_REPOSITORY "https://github.com/p-ranav/argparse.git"
GIT_TAG "v2.2"
GIT_SHALLOW "True"
CMAKE_ARGS -DARGPARSE_CMAKE_FILES_INSTALL_DIR=${CMAKE_CURRENT_BINARY_DIR}/argparse -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/argparse
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/argparse
)
list (APPEND EXTRA_CMAKE_ARGS
-Dargparse_DIR=${CMAKE_CURRENT_BINARY_DIR}/argparse
)
ExternalProject_Add (ep_j7s-mosquitto-plugin
PREFIX ep_j7s-mosquitto-plugin
DEPENDS ${DEPENDENCIES}

108
src/gen-token.cpp Normal file
View File

@ -0,0 +1,108 @@
// Copyright 2022 James Pace
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <argparse/argparse.hpp>
#include <jwt-cpp/jwt.h>
#include <iostream>
#include <chrono>
#include <optional>
#include <string>
#include <fstream>
#include <sstream>
#include <filesystem>
std::optional<std::string> read_key(const std::string& key_file);
int main(int argc, char *argv[])
{
argparse::ArgumentParser program("gen-token", "0.0.0");
program.add_argument("--pub-key")
.required()
.help("Pub key of signer.");
program.add_argument("--priv-key")
.required()
.help("Private key of signer.");
program.add_argument("--issuer")
.required()
.help("Issuer to assign to signed key.");
program.add_argument("--username")
.required()
.help("Username assigned to key.");
program.add_argument("--valid-days")
.required()
.help("Days from now until the token will be valid.");
program.add_argument("--can-read")
.help("holder can read")
.default_value(false)
.implicit_value(true);
program.add_argument("--can-write")
.help("holder can write")
.default_value(false)
.implicit_value(true);
try
{
program.parse_args(argc, argv);
}
catch (const std::runtime_error & err)
{
std::cerr << err.what() << std::endl;
std::cerr << program;
return -1;
}
const auto priv_key_file = program.get<std::string>("--priv-key");
const auto priv_key = read_key(std::filesystem::absolute(priv_key_file));
const auto pub_key_file = program.get<std::string>("--pub-key");
const auto pub_key = read_key(std::filesystem::absolute(pub_key_file));
if(not pub_key or not priv_key)
{
std::cerr << "Could not open key!" << std::endl;
return -2;
}
const std::string can_read = program.get<bool>("--can-read") ? "true" : "false";
const std::string can_write = program.get<bool>("--can-write") ? "true" : "false";
const auto expr_time = std::chrono::system_clock::now() +
std::chrono::days(std::stoi(program.get<std::string>("--valid-days")));
const auto token = jwt::create()
.set_type("JWT")
.set_issuer(program.get<std::string>("--issuer"))
.set_payload_claim("upn", jwt::claim(program.get<std::string>("--username")))
.set_payload_claim("mqtt-write", jwt::claim(can_read))
.set_payload_claim("mqtt-read", jwt::claim(can_write))
.set_expires_at(expr_time)
.sign(jwt::algorithm::rs256(pub_key.value(), priv_key.value(), "", ""));
std::cout << token;
return 0;
}
std::optional<std::string> read_key(const std::string& key_file)
{
// Read key from file.
std::ifstream key_stream(key_file, std::ios::binary);
if(not key_stream)
{
return std::nullopt;
}
std::stringstream ss;
ss << key_stream.rdbuf();
return ss.str();
}

View File

@ -1,12 +1,17 @@
per_listener_settings true
log_type all
listener 8082
protocol websockets
listener 8081
protocol mqtt
log_type all
allow_anonymous true
allow_anonymous false
auth_plugin /home/jimmy/Develop/mosquitto-plugin/build/libj7s-plugin.so
auth_opt_issuer https://auth.jpace121.net/realms/jpace121-main
auth_opt_public_key /home/jimmy/Develop/mosquitto-plugin/test/key.pem
listener 8081
protocol mqtt
allow_anonymous false
auth_plugin /home/jimmy/Develop/mosquitto-plugin/build/libj7s-plugin.so
auth_opt_issuer https://auth.jpace121.net/realms/jpace121-main
auth_opt_public_key /home/jimmy/Develop/mosquitto-plugin/test/key.pem

View File

@ -0,0 +1,36 @@
import requests
import json
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.
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']
# 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)
token = r.json()['access_token']
client = paho.mqtt.client.Client(protocol=paho.mqtt.client.MQTTv5,
transport="tcp")
client.username_pw_set("jimmy", password=token)
client.connect("localhost", port=8081)
print("Waiting on connection.")
time.sleep(20)