Skip to content

Commit

Permalink
Merge pull request #18 from benthecarman/feature_http_auth
Browse files Browse the repository at this point in the history
Feature http auth
  • Loading branch information
benthecarman committed Apr 27, 2019
2 parents 14020ac + 5fe99b4 commit 3ec3951
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 6 deletions.
5 changes: 5 additions & 0 deletions docs/sample.conf
Expand Up @@ -13,6 +13,11 @@ daemon = false
# other users to connect with their lightning node
port = 8331

# The httpauth option specifies credentials that will be authenticated with every client request
# This option should be used when you want to only allow people with the credentials to connect
# Note: The format is specified as user:password
;httpauth = user:pass

# The host option specifies how to connect to your Bitcoin Core node's RPC server
host = http://127.0.0.1:8332/

Expand Down
7 changes: 7 additions & 0 deletions docs/user_guide.md
Expand Up @@ -4,6 +4,7 @@
- [Lightning Rods](#lightning-rods)
- [Setup](#setup)
- [Optional Features](#optional-features)
- [HTTP Authentication Credentials](#http-authentication-credentials)
- [Blocking IPs](#blocking-ips)
- [Blocking default RPC commands](#blocking-default-rpc-commands)
- [Allowing RPC commands](#allowing-rpc-commands)
Expand All @@ -26,6 +27,12 @@ For further configuration options a sample config file can be found [here](sampl

### Optional Features

#### HTTP Authentication Credentials

HTTP authentication credentials can be set to only allow clients with the given credentials to connect.
HTTP authentication credentials can be set by adding `httpauth=<credentials>` to your config file or
using `--httpauth=<credentials>` as a command line argument. The standard format is `user:pass`

#### Blocking IPs

An IP address can be blocked from connecting to your node by adding
Expand Down
4 changes: 2 additions & 2 deletions makefile
Expand Up @@ -4,8 +4,8 @@ LDFLAGS=-lcurl -lboost_filesystem -lboost_system -lboost_log -lboost_thread -lzm

default: lrod

lrod: src/rod.cpp src/config.cpp src/option.cpp src/rpcconnection.cpp src/server.cpp src/logger.cpp src/zmqserver.cpp src/config.h src/option.h src/rpcconnection.h src/server.h src/logger.h src/zmqserver.h
$(CXX) $(CXXFLAGS) src/rod.cpp src/option.cpp src/config.cpp src/server.cpp src/rpcconnection.cpp src/logger.cpp src/zmqserver.cpp -o lrod $(LDFLAGS)
lrod: src/rod.cpp src/config.cpp src/option.cpp src/rpcconnection.cpp src/server.cpp src/logger.cpp src/base64.cpp src/zmqserver.cpp src/config.h src/option.h src/rpcconnection.h src/server.h src/logger.h src/base64.h src/zmqserver.h
$(CXX) $(CXXFLAGS) src/rod.cpp src/option.cpp src/config.cpp src/server.cpp src/rpcconnection.cpp src/logger.cpp src/base64.cpp src/zmqserver.cpp -o lrod $(LDFLAGS)

#PREFIX is environment variable, but if it is not set, then set default value
ifeq ($(PREFIX),)
Expand Down
48 changes: 48 additions & 0 deletions src/base64.cpp
@@ -0,0 +1,48 @@
#include <string>
#include <vector>

std::string base64_encode(const std::string &in)
{
std::string out;

int val = 0, valb = -6;
for (unsigned char c : in)
{
val = (val << 8) + c;
valb += 8;
while (valb >= 0)
{
out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(val >> valb) & 0x3F]);
valb -= 6;
}
}
if (valb > -6)
out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[((val << 8) >> (valb + 8)) & 0x3F]);
while (out.size() % 4)
out.push_back('=');
return out;
}

std::string base64_decode(const std::string &in)
{
std::string out;

std::vector<int> T(256, -1);
for (int i = 0; i < 64; i++)
T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;

int val = 0, valb = -8;
for (unsigned char c : in)
{
if (T[c] == -1)
break;
val = (val << 6) + T[c];
valb += 6;
if (valb >= 0)
{
out.push_back(char((val >> valb) & 0xFF));
valb -= 8;
}
}
return out;
}
7 changes: 7 additions & 0 deletions src/base64.h
@@ -0,0 +1,7 @@
#ifndef BASE64_H
#define BASE64_H

std::string base64_encode(const std::string &);
std::string base64_decode(const std::string &);

#endif
19 changes: 19 additions & 0 deletions src/config.cpp
Expand Up @@ -6,6 +6,7 @@
#include "config.h"
#include "option.h"
#include "logger.h"
#include "base64.h"

Config config;

Expand All @@ -23,6 +24,7 @@ Config::Config()
this->zmqBlockPort = DEFAULT_ZMQ_BLOCK_PORT;
this->zmqTxPort = DEFAULT_ZMQ_TX_PORT;
this->host = DEFAULT_HOST;
this->httpAuth = DEFAULT_HTTP_AUTH;
this->zmqBlockHost = DEFAULT_ZMQ_BLOCK_HOST;
this->zmqTxHost = DEFAULT_ZMQ_TX_HOST;
this->rpcAuth = DEFAULT_RPC_AUTH;
Expand All @@ -32,6 +34,12 @@ Config::Config()
this->cmdWhiteList = DEFAULT_CMD_WHITE_LIST;
}

void Config::setHttpAuth(std::string const &ha)
{
this->httpAuth = ha;
this->httpAuthEncoded = base64_encode(ha);
}

std::string Config::toString()
{
std::string str;
Expand All @@ -41,6 +49,8 @@ std::string Config::toString()
str += "disablezmq: " + std::string(this->disablezmq ? "true" : "false") + "\n";
str += "host: " + this->host + "\n";
str += "port: " + std::to_string(this->port) + "\n";
if (this->hasHttpAuth())
str += "httpauth: " + this->httpAuth + "\n";
str += "rpcauth: " + this->rpcAuth + "\n";
str += "configdir: " + this->configdir + "\n";

Expand Down Expand Up @@ -296,6 +306,10 @@ void processConfigLine(const std::string &line, const bool isArg)
{
config.setHost(input);
}
else if (opt.getName() == OPTION_HTTPAUTH_NAME)
{
config.setHttpAuth(input);
}
else if (opt.getName() == OPTION_RPCAUTH_NAME)
{
config.setRPCAuth(input);
Expand Down Expand Up @@ -367,6 +381,11 @@ void createSampleConfigFile()
samplecfg << "# other users to connect with their lightning node" << std::endl;
samplecfg << "port = 8331" << std::endl;
samplecfg << std::endl;
samplecfg << "# The httpauth option specifies credentials that will be authenticated with every client request" << std::endl;
samplecfg << "# This option should be used when you want to only allow people with the credentials to connect" << std::endl;
samplecfg << "# Note: The format is specified as user:password" << std::endl;
samplecfg << ";httpauth = user:pass" << std::endl;
samplecfg << std::endl;
samplecfg << "# The host option specifies how to connect to your Bitcoin Core node's RPC server" << std::endl;
samplecfg << "host = http://127.0.0.1:8332/" << std::endl;
samplecfg << std::endl;
Expand Down
16 changes: 16 additions & 0 deletions src/config.h
Expand Up @@ -9,6 +9,7 @@ static const bool DEFAULT_ZMQ_DISABLED = false;
static const int DEFAULT_PORT = 8331;
static const int DEFAULT_ZMQ_BLOCK_PORT = 28330;
static const int DEFAULT_ZMQ_TX_PORT = 28331;
static const std::string DEFAULT_HTTP_AUTH = "";
static const std::string DEFAULT_HOST = "http://127.0.0.1:8332/";
static const std::string DEFAULT_ZMQ_BLOCK_HOST = "tcp://127.0.0.1:28332";
static const std::string DEFAULT_ZMQ_TX_HOST = "tcp://127.0.0.1:28333";
Expand Down Expand Up @@ -38,6 +39,8 @@ class Config
int port;
int zmqBlockPort;
int zmqTxPort;
std::string httpAuth;
std::string httpAuthEncoded;
std::string host;
std::string zmqBlockHost;
std::string zmqTxHost;
Expand Down Expand Up @@ -99,6 +102,19 @@ class Config
{
this->zmqTxPort = p;
}
bool hasHttpAuth()
{
return !this->httpAuth.empty();
}
void setHttpAuth(std::string const &);
std::string getHttpAuth()
{
return this->httpAuth;
}
std::string getHttpAuthEncoded()
{
return this->httpAuthEncoded;
}
std::string getHost()
{
return this->host;
Expand Down
1 change: 1 addition & 0 deletions src/option.cpp
Expand Up @@ -42,6 +42,7 @@ void registerOptions()
options.push_back(Option(OPTION_ZMQBLOCKPORT_NAME, "Port the Lightning Rod will be broadcasting blocks through ZeroMQ"));
options.push_back(Option(OPTION_ZMQTXPORT_NAME, "Port the Lightning Rod will be broadcasting transactions through ZeroMQ"));
options.push_back(Option(OPTION_PORT_NAME, "Port the Lightning Rod will be listening for connections"));
options.push_back(Option(OPTION_HTTPAUTH_NAME, "Authentication creditentials clients must use to make requests"));
options.push_back(Option(OPTION_HOST_NAME, "Specify how to connect to the bitcoind RPC server"));
options.push_back(Option(OPTION_RPCAUTH_NAME, "Authentication credentials for the bitcoind RPC server"));
options.push_back(Option(OPTION_CONFIGDIR_NAME, "Specify where the configuration file is located"));
Expand Down
1 change: 1 addition & 0 deletions src/option.h
Expand Up @@ -11,6 +11,7 @@ static const std::string OPTION_DEBUG_NAME = "debug";
static const std::string OPTION_DISABLEZMQ_NAME = "disablezmq";
static const std::string OPTION_ZMQBLOCKPORT_NAME = "zmqBlockPort";
static const std::string OPTION_ZMQTXPORT_NAME = "zmqTxPort";
static const std::string OPTION_HTTPAUTH_NAME = "httpauth";
static const std::string OPTION_PORT_NAME = "port";
static const std::string OPTION_HOST_NAME = "host";
static const std::string OPTION_RPCAUTH_NAME = "rpcauth";
Expand Down
23 changes: 19 additions & 4 deletions src/server.cpp
Expand Up @@ -157,8 +157,22 @@ void handleRequest(int sock, RPCConnection *rpc, std::string peerIP)

return;
}

std::string message(buffer);

// Invalid HTTP Auth Crendentials
if (config.hasHttpAuth() && message.find("Authorization: Basic " + config.getHttpAuthEncoded()) == std::string::npos)
{
logWarning("Peer (" + peerIP + ") attempted to connect with invalid credentials");
std::string sendString = "HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n";

send(sock, sendString.c_str(), sendString.length(), 0);

close(sock);

return;
}

int pos = message.find("{\"");
std::string data = "";

Expand All @@ -167,21 +181,22 @@ void handleRequest(int sock, RPCConnection *rpc, std::string peerIP)
data = message.substr(pos);
}

int auth = authenticateData(data);
int authData = authenticateData(data);

if (auth != 0)
// Disallowed commands attempted
if (authData != 0)
{
int f = data.find("\"id\":\"") + 5;
std::string id = data.substr(f, data.find("\"", f) - f);

f = data.find("\"method\":\"") + 10;
std::string cmd = data.substr(f, data.find("\"", f) - f);

if (auth == -1)
if (authData == -1)
{
logWarning("Peer (" + peerIP + ") attempted non-whitelisted command (" + cmd + ")");
}
else if (auth == 1)
else if (authData == 1)
{
logWarning("Peer (" + peerIP + ") attempted blacklisted command (" + cmd + ")");
}
Expand Down

0 comments on commit 3ec3951

Please sign in to comment.