OSQuery ClientAuthServer
dataBridges exposes powerful Client Function messaging service for devices and applications to implement high performance non-blocking request / response messages. Detailed API documentation
TODO
Project Source
Download source
Download the osQuery.clientAuthServer by clicking on this link
Download source
Download the osQuery.clientAuthServer by clicking on this link
Dependencies
Dependencies
For this application, you will require to use jsonwebtoken, databridges-sio-server-lib (dataBridges Server Library) with NodeJS.
- Install External dependencies
-
npm install jsonwebtoken --save
-
Install dataBridges library
npm install databridges-sio-server-lib --save
Download the package.json by clicking on this link
Dependencies
For this application, you will require to use jwt,asyncio and databridges-sio-server-lib (dataBridges Server Library).
- Install External dependencies
pip3 install jwt
-
pip3 install asyncio
-
Install dataBridges library
pip3 install databridges_sio_server_lib
Dependencies
For this application, you will require to use JWT and Databridges.Sio.Server.Lib (dataBridges Server Library).
- Install dataBridges library and JWT from "Manage NuGet Packages..." from the Solution -> Application Name context menu.
Set up a dataBridges account and app
Before we jump right into setting up an application with dataBridges, you’ll need to create a dataBridges account and app, if you don’t already have one:
-
Sign up for a dataBridges account.
-
Create a new app by selecting Apps and clicking Create New button.
-
You can retrieve your app credentials from the App Keys tab.
Preparing to run osQuery ClientAuthServer
Download the osQuery.clientAuthServer code from above given link and move it to application folder.
If you are preferring to use the environmental variable (preferred due to security reasons), you need to create 3 environment variables (dBridgeServerAuthURL, dBridgeServerAppKey, dBridgeServerAppSecret)
and store the required data. If you are not using environment variable, In the osQuery.clientAuthServer file you need to do few modification to store the keys and important data.
Property | Description |
---|---|
dBridgeServerAuthURL | (string) Server authentication url from dataBridges dashboard. |
dBridgeServerAppKey | (string) Server application Key from dataBridges dashboard. |
dBridgeServerAppSecret | (string) Server application Secret from dataBridges dashboard. |
We will be using in-memory authDB. For production, this will be a database or a keyValue store. dataBridges allows you to create Application. Each application can have multiple client-keys. Each client-key has and app.key and app.secret. You will need an app.secret to create a trust.token. For example if the app-key = ABC and secret = 123 and if the client wants to subscribe to a deviceGroup = customerADevices, we will need to create a token using secret = 123 and give the token back to client. Client will use this token to subscribe to the deviceGroup = customerADevices.
dataBridges allows you to create multiple client keys against an app. In this example for each device group, you can create a separate clientKey. Create the authDB json and fill up all the clientKeys and their secret and associate the same with devicegroupName.
private Dictionary<string, Dictionary<string, string>> authDB = new Dictionary<string, Dictionary<string, string>>();
public clientAuthServer() {
this.algorithm = new HMACSHA256Algorithm();
this.serializer = new JsonNetSerializer();
this.urlEncoder = new JwtBase64UrlEncoder();
this.encoder = new JwtEncoder(this.algorithm, this.serializer, this.urlEncoder);
this.Add_authDB("Orion", "__appKey__", "__appSecret__");
this.Add_authDB("Cassiopeia", "__appKey__", "__appSecret__");
}
Property | Description |
---|---|
__appKey__ | (string) Client application Key from dataBridges dashboard. |
__appSecret__ | (string) Client application Secret from dataBridges dashboard. |
Once the above changes is completed, save your code and your code is ready to be executed.
Understanding important sections of osQuery ClientAuthServer source code.
Initialize RPC Server and defining functions for trusted_token creation.
To serve RPC trusted token creation function, We need to initialize a RPC server osQueryClientAuth
. Once initialized define and create RPC functions using rpcServer.functions
method. Here in our scenrio we will be creating getToken()
function and will be exposing it as RPC function to serve trusted_token creation process.
getToken()
will be checking for deviceGroupName
in the authDB along with appKey
, if not found it will report Unauthorized
. If found it will retrive clientSecret
from the authDB
and will sign the payload
with clientSecret
. Upon successful completion of whole process it will return the accesskey back to the Caller.
// Expose and define RPC server for handling access_token requests.
let rpcServer = dbridge.rpc.init('osQueryClientAuth');
rpcServer.functions = function () {
function getToken(payload, response) {
const inJson = JSON.parse(payload.inparam);
const channelName = inJson.ch;
const sessionId = inJson.sid;
const action = inJson.act;
const appKey = inJson.appkey;
const clHostName = inJson.hostname;
const clIP = inJson.ip;
const opSystem = inJson.clos;
if (channelName.substring(4) in authDB){
if (authDB[channelName.substring(4)].dbAppKey == appKey){
const clientSecret = authDB[channelName.substring(4)].dbAppSecret;
let payLoad = {
sid: sessionId,
cname: channelName,
pub: true,
conn_info: {
sysid: clHostName + '-' + clIP,
sysinfo: {
os: opSystem
}
}
};
const accesskey = jwt.sign(payLoad, clientSecret);
response.end(JSON.stringify({
statuscode: 0,
error_message: "",
accesskey: accesskey
}));
} else {
response.end(JSON.stringify({
statuscode: 1,
error_message: "Unauthrorized",
accesskey: ''
}));
}
} else {
response.end(JSON.stringify({
statuscode: 1,
error_message: "Unauthrorized",
accesskey: ''
}));
}
}
rpcServer.regfn("getToken", getToken);
}
self.rpcServer = self.dbridge.rpc.init("osQueryClientAuth")
self.rpcServer.functions = self.rpc_expose_functions
# Expose and define RPC server for handling access_token requests.
async def rpc_expose_functions(self):
async def getToken(inparameter, response):
inJson = json.loads(inparameter["inparam"])
channelName = inJson["ch"]
sessionId = inJson["sid"]
action = inJson["act"]
appKey = inJson["appkey"]
clHostName = inJson["hostname"]
clIP = inJson["ip"]
opSystem = inJson["clos"]
try:
if (channelName[4:] in authDB):
clientSecret = authDB[channelName[4:]]["dbAppSecret"]
payLoad = { "sid": sessionId,"cname": channelName,"pub": True,
"conn_info": {"sysid": clHostName + '-' + clIP, "sysinfo": {"os": opSystem}}}
encoded_jwt = jwt.encode(payLoad, clientSecret, algorithm="HS256")
await response.end(json.dumps({ "statuscode": 0, "error_message": "","accesskey": encoded_jwt}))
else:
await response.end(json.dumps({ "statuscode": 1, "error_message": "Unauthrorized","accesskey": ""}))
except Exception as e:
print(e)
await response.exception(str(e), e)
try:
# Bind the exposed function, so that library can register it for receiving calls.
self.rpcServer.regfn("getToken", getToken)
except dBError as dberror:
print("Exception: ", dberror.source, dberror.code, dberror.message)
// Expose and define RPC server for handling access_token requests.
this.osQueryClientAuthRPC = dbridge.rpc.init("osQueryClientAuth");
Action<object> ifunctions = this.rpcfunction;
this.osQueryClientAuthRPC.functions = ifunctions;
public void rpcfunction(object eventInfo) {
try {
Action<object, object> igetToken = this.getToken;
(eventInfo as dBridges.remoteprocedure.Crpcserver).regfn("getToken", igetToken);
} catch (Exception e) {
Console.WriteLine(e);
}
}
public async void getToken(object inparameter, object response) {
jwt_response jwtResponse = new jwt_response();
dBridges.Utils.dBParams parameter = inparameter as dBridges.Utils.dBParams;
dBridges.responseHandler.CResponseHandler responsehandler = response as dBridges.responseHandler.CResponseHandler;
dynamic inJson = JsonConvert.DeserializeObject<dynamic>(parameter.inparama);
String channelName = inJson.ch;
String sessionId = inJson.sid;
String action = inJson.act;
String appKey = inJson.appkey;
String clHostName = inJson.hostname;
String clIP = inJson.ip;
String opSystem = inJson.clos;
if (this.authDB.ContainsKey(channelName.Substring(4))){
if (this.authDB[channelName.Substring(4)]["dbAppKey"] == appKey) {
string clientSecret = this.authDB[channelName.Substring(4)]["dbAppSecret"];
Dictionary<string, object> payload = new Dictionary<string, object>();
payload.Add("sid" , sessionId);
payload.Add("cname" , channelName);
payload.Add("pub" , true);
Dictionary<string, object> conn_info = new Dictionary<string, object>();
conn_info.Add("sysid" , clHostName + '-' + clIP);
Dictionary<string, string> sysinfo = new Dictionary<string, string>();
sysinfo.Add("os", opSystem);
conn_info.Add("sysinfo", sysinfo);
payload.Add("conn_info", conn_info);
var token = encoder.Encode(payload, clientSecret);
jwtResponse.statuscode = 0;
jwtResponse.accesskey = token;
string indata = JsonConvert.SerializeObject(jwtResponse);
await responsehandler.end(indata);
}else{
jwtResponse.statuscode = 1;
jwtResponse.accesskey = "";
jwtResponse.error_message = "Unauthrorized";
string indata = JsonConvert.SerializeObject(jwtResponse);
await responsehandler.end(indata);
}
}else{
jwtResponse.statuscode = 1;
jwtResponse.accesskey = "";
jwtResponse.error_message = "Unauthrorized";
string indata = JsonConvert.SerializeObject(jwtResponse);
await responsehandler.end(indata);
}
}
dataBridges Network connection and reconnection.
To make sure the dataBridges network connection is always connected, we will be tweaking few porperties and code. Application will try to do reconnection using dataBridges auto reconnection process for 500 times as defined in the property. Also after disconnection from the network (when reconnection fails) we will be calling the connection process again. This will do a infinite try till it get successfull connection.
dbridge.autoReconnect = true;
dbridge.maxReconnectionRetries = 500;
const connectTodataBridgesNetwork = () => {
// Connect to dataBridges. If any runtime error it will be caught in catch().
dbridge.connect().catch((err) => {
console.log('dBridge Connection exception..', err);
});
}
//Bind to disconnected event, to get intimation about dataBridges network disconnection. Do note that we have configured dataBridges client to retry 500 times post disconnection. This event will be encountered only after 500 retries. When that happens, we will reconnect back to dataBridges again.
dbridge.connectionstate.bind("disconnected", () => {
console.log('dataBridges network', 'disconnected');
// Attempt reconnect on disconnection.
osQueryClientAuthRPC = null;
deviceGroup = null;
connectTodataBridgesNetwork();
});
connectTodataBridgesNetwork();
self.dbridge.autoReconnect = True
self.dbridge.maxReconnectionRetries = 500
async def connectTodataBridgesNetwork(self):
# Connect to dataBridges. If any runtime error it will be caught in Exception.
try:
await asyncio.sleep(0.0001)
await self.dbridge.connect()
except Exception as e:
print(e)
# Bind to disconnected event, to get intimation about dataBridges network disconnection. Do note that we have configured dataBridges client to retry 500 times post disconnection. This event will be encountered only after 500 retries. When that happens, we will reconnect back to dataBridges again.
async def disconnected(self):
print("dataBridges network", "disconnected")
try:
# Attempt reconnect on disconnection.
await self.connectTodataBridgesNetwork()
except Exception as e:
print(e)
await self.connectTodataBridgesNetwork()
this.dbridge.autoReconnect = true;
this.dbridge.maxReconnectionRetries = 500;
public async Task connectTodataBridgesNetwork() {
try {
// Connect to dataBridges. If any runtime error it will be caught in catch().
await this.dbridge.connect();
} catch (Exception err) {
Console.WriteLine("dBridge Connection exception..", err);
}
}
//Bind to disconnected event, to get intimation about dataBridges network disconnection. Do note that we have configured dataBridges client to retry 500 times post disconnection. This event will be encountered only after 500 retries. When that happens, we will reconnect back to dataBridges again.
Action<object> disconnected_callback;
this.dbridge.connectionstate.bind("disconnected", disconnected_callback = async (object eventinfo) => {
Console.WriteLine("dataBridges network", "disconnected");
// Attempt reconnect on disconnection.
this.osQueryClientAuthRPC = null;
await this.connectTodataBridgesNetwork();
});
await this.connectTodataBridgesNetwork();
dataBridges on Connection process.
Once the dataBridge network is successfully connected, We will be registering the RPC server osQueryClientAuth
for serving trusted_token creation request.
//Bind to connected event.
dbridge.connectionstate.bind("connected", async () => {
console.log('dataBridges network', 'connected');
// Register RPC server for serving access_token request.
try {
rpcServer.register()
} catch (err) {
console.log('dBridge RPC Register exception..', 'osQueryClientAuth', err.source, err.code, err.message)
}
});
//Bind to connected event.
Action<object> connected_callback;
this.dbridge.connectionstate.bind("connected", connected_callback = async (object eventinfo) => {
Console.WriteLine("dataBridges network", "connected");
try {
await this.osQueryClientAuthRPC.register();
} catch(dBridges.exceptions.dBError err){
Console.WriteLine("dBridge RPC Register exception..", "osQueryClientAuth", err.source, err.code, err.message);
}
});