mirror of
https://github.com/GOSTSec/sgminer
synced 2025-01-25 14:04:25 +00:00
Allow API to restrict access by IP address
This commit is contained in:
parent
93f4e991ae
commit
f19e67fe3a
24
README
24
README
@ -119,6 +119,8 @@ Usage instructions: Run "cgminer --help" to see options:
|
|||||||
|
|
||||||
Usage: . [-atDdGCgIKklmpPQqrRsTouvwOchnV]
|
Usage: . [-atDdGCgIKklmpPQqrRsTouvwOchnV]
|
||||||
Options for both config file and command line:
|
Options for both config file and command line:
|
||||||
|
--api-allow Allow API access (if enabled) only to the given list of IP[/Prefix] address[/subnets]
|
||||||
|
This overrides --api-network and you must specify 127.0.0.1 if it is required
|
||||||
--api-description Description placed in the API status header (default: cgminer version)
|
--api-description Description placed in the API status header (default: cgminer version)
|
||||||
--api-listen Listen for API requests (default: disabled)
|
--api-listen Listen for API requests (default: disabled)
|
||||||
--api-network Allow API (if enabled) to listen on/for any address (default: only 127.0.0.1)
|
--api-network Allow API (if enabled) to listen on/for any address (default: only 127.0.0.1)
|
||||||
@ -526,15 +528,24 @@ cgminer shuts down because of this.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
API
|
RPC API
|
||||||
|
|
||||||
If you start cgminer with the "--api-listen" option, it will listen on a
|
If you start cgminer with the "--api-listen" option, it will listen on a
|
||||||
simple TCP/IP socket for single string API requests from the same machine
|
simple TCP/IP socket for single string API requests from the same machine
|
||||||
running cgminer and reply with a string and then close the socket each time
|
running cgminer and reply with a string and then close the socket each time
|
||||||
Also, if you add the "--api-network" option, it will accept API requests
|
If you add the "--api-network" option, it will accept API requests from any
|
||||||
from any network attached computer.
|
network attached computer.
|
||||||
|
|
||||||
The request can be either simple text or JSON.
|
You can specify IP addresses/prefixes that are only allowed to access the API
|
||||||
|
with the "--api-access" option e.g. --api-access 192.168.0.1,10.0.0/24
|
||||||
|
will allow 192.168.0.1 or any address matching 10.0.0.*, but nothing else
|
||||||
|
IP addresses are automatically padded with extra '.0's as needed
|
||||||
|
Without a /prefix is the same as specifying /32
|
||||||
|
Using the "--api-access" option overides the "--api-network" option if they
|
||||||
|
are both specified
|
||||||
|
With "--api-access", 127.0.0.1 is not by default given access unless specified
|
||||||
|
|
||||||
|
The RPC API request can be either simple text or JSON.
|
||||||
|
|
||||||
If the request is JSON (starts with '{'), it will reply with a JSON formatted
|
If the request is JSON (starts with '{'), it will reply with a JSON formatted
|
||||||
response, otherwise it replies with text formatted as described further below.
|
response, otherwise it replies with text formatted as described further below.
|
||||||
@ -544,7 +555,7 @@ The JSON request format required is '{"command":"CMD","parameter":"PARAM"}'
|
|||||||
where "CMD" is from the "Request" column below and "PARAM" would be e.g.
|
where "CMD" is from the "Request" column below and "PARAM" would be e.g.
|
||||||
the CPU/GPU number if required.
|
the CPU/GPU number if required.
|
||||||
|
|
||||||
An example request in both formats:
|
An example request in both formats to set GPU 0 fan to 80%:
|
||||||
gpufan|0,80
|
gpufan|0,80
|
||||||
{"command":"gpufan","parameter":"0,80"}
|
{"command":"gpufan","parameter":"0,80"}
|
||||||
|
|
||||||
@ -652,6 +663,9 @@ The list of requests and replies are:
|
|||||||
When you enable, disable or restart a GPU, you will also get Thread messages in
|
When you enable, disable or restart a GPU, you will also get Thread messages in
|
||||||
the cgminer status window
|
the cgminer status window
|
||||||
|
|
||||||
|
When you switch to a different pool to the current one, you will get a
|
||||||
|
'Switching to URL' message in the cgminer status windows
|
||||||
|
|
||||||
Obviously, the JSON format is simply just the names as given before the '='
|
Obviously, the JSON format is simply just the names as given before the '='
|
||||||
with the values after the '='
|
with the values after the '='
|
||||||
|
|
||||||
|
141
api.c
141
api.c
@ -342,6 +342,14 @@ struct CODES {
|
|||||||
static int bye = 0;
|
static int bye = 0;
|
||||||
static bool ping = true;
|
static bool ping = true;
|
||||||
|
|
||||||
|
struct IP4ACCESS {
|
||||||
|
in_addr_t ip;
|
||||||
|
in_addr_t mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct IP4ACCESS *ipaccess = NULL;
|
||||||
|
static int ips = 0;
|
||||||
|
|
||||||
// All replies (except BYE) start with a message
|
// All replies (except BYE) start with a message
|
||||||
// thus for JSON, message() inserts JSON_START at the front
|
// thus for JSON, message() inserts JSON_START at the front
|
||||||
// and send_result() adds JSON_END at the end
|
// and send_result() adds JSON_END at the end
|
||||||
@ -1204,6 +1212,11 @@ static void tidyup()
|
|||||||
sock = INVSOCK;
|
sock = INVSOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ipaccess != NULL) {
|
||||||
|
free(ipaccess);
|
||||||
|
ipaccess = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg_buffer != NULL) {
|
if (msg_buffer != NULL) {
|
||||||
free(msg_buffer);
|
free(msg_buffer);
|
||||||
msg_buffer = NULL;
|
msg_buffer = NULL;
|
||||||
@ -1215,6 +1228,89 @@ static void tidyup()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interpret IP[/Prefix][,IP2[/Prefix2][,...]] --api-allow option
|
||||||
|
*
|
||||||
|
* N.B. IP4 addresses are by Definition 32bit big endian on all platforms
|
||||||
|
*/
|
||||||
|
static void setup_ipaccess()
|
||||||
|
{
|
||||||
|
char *buf, *ptr, *comma, *slash, *dot;
|
||||||
|
int ipcount, mask, octet, i;
|
||||||
|
|
||||||
|
buf = malloc(strlen(opt_api_allow) + 1);
|
||||||
|
if (unlikely(!buf))
|
||||||
|
quit(1, "Failed to malloc ipaccess buf");
|
||||||
|
|
||||||
|
strcpy(buf, opt_api_allow);
|
||||||
|
|
||||||
|
ipcount = 1;
|
||||||
|
ptr = buf;
|
||||||
|
while (*ptr)
|
||||||
|
if (*(ptr++) == ',')
|
||||||
|
ipcount++;
|
||||||
|
|
||||||
|
// possibly more than needed, but never less
|
||||||
|
ipaccess = calloc(ipcount, sizeof(struct IP4ACCESS));
|
||||||
|
if (unlikely(!ipaccess))
|
||||||
|
quit(1, "Failed to calloc ipaccess");
|
||||||
|
|
||||||
|
ips = 0;
|
||||||
|
ptr = buf;
|
||||||
|
while (ptr && *ptr) {
|
||||||
|
while (*ptr == ' ' || *ptr == '\t')
|
||||||
|
ptr++;
|
||||||
|
|
||||||
|
if (*ptr == ',') {
|
||||||
|
ptr++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
comma = strchr(ptr, ',');
|
||||||
|
if (comma)
|
||||||
|
*(comma++) = '\0';
|
||||||
|
|
||||||
|
slash = strchr(ptr, '/');
|
||||||
|
if (!slash)
|
||||||
|
ipaccess[ips].mask = 0xffffffff;
|
||||||
|
else {
|
||||||
|
*(slash++) = '\0';
|
||||||
|
mask = atoi(slash);
|
||||||
|
if (mask < 1 || mask > 32)
|
||||||
|
goto popipo; // skip invalid/zero
|
||||||
|
|
||||||
|
ipaccess[ips].mask = 0;
|
||||||
|
while (mask-- >= 0) {
|
||||||
|
octet = 1 << (mask % 8);
|
||||||
|
ipaccess[ips].mask |= (octet << (8 * (mask >> 3)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ipaccess[ips].ip = 0; // missing default to '.0'
|
||||||
|
for (i = 0; ptr && (i < 4); i++) {
|
||||||
|
dot = strchr(ptr, '.');
|
||||||
|
if (dot)
|
||||||
|
*(dot++) = '\0';
|
||||||
|
|
||||||
|
octet = atoi(ptr);
|
||||||
|
if (octet < 0 || octet > 0xff)
|
||||||
|
goto popipo; // skip invalid
|
||||||
|
|
||||||
|
ipaccess[ips].ip |= (octet << (i * 8));
|
||||||
|
|
||||||
|
ptr = dot;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipaccess[ips].ip &= ipaccess[ips].mask;
|
||||||
|
|
||||||
|
ips++;
|
||||||
|
popipo:
|
||||||
|
ptr = comma;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
void api(void)
|
void api(void)
|
||||||
{
|
{
|
||||||
char buf[BUFSIZ];
|
char buf[BUFSIZ];
|
||||||
@ -1247,6 +1343,15 @@ void api(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opt_api_allow) {
|
||||||
|
setup_ipaccess();
|
||||||
|
|
||||||
|
if (ips == 0) {
|
||||||
|
applog(LOG_WARNING, "API not running (no valid IPs specified)%s", UNAVAILABLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
if (sock == INVSOCK) {
|
if (sock == INVSOCK) {
|
||||||
applog(LOG_ERR, "API1 initialisation failed (%s)%s", SOCKERRMSG, UNAVAILABLE);
|
applog(LOG_ERR, "API1 initialisation failed (%s)%s", SOCKERRMSG, UNAVAILABLE);
|
||||||
@ -1257,9 +1362,9 @@ void api(void)
|
|||||||
|
|
||||||
serv.sin_family = AF_INET;
|
serv.sin_family = AF_INET;
|
||||||
|
|
||||||
if (!opt_api_network) {
|
if (!opt_api_allow && !opt_api_network) {
|
||||||
serv.sin_addr.s_addr = inet_addr(localaddr);
|
serv.sin_addr.s_addr = inet_addr(localaddr);
|
||||||
if (serv.sin_addr.s_addr == INVINETADDR) {
|
if (serv.sin_addr.s_addr == (in_addr_t)INVINETADDR) {
|
||||||
applog(LOG_ERR, "API2 initialisation failed (%s)%s", SOCKERRMSG, UNAVAILABLE);
|
applog(LOG_ERR, "API2 initialisation failed (%s)%s", SOCKERRMSG, UNAVAILABLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1295,10 +1400,14 @@ void api(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opt_api_network)
|
if (opt_api_allow)
|
||||||
applog(LOG_WARNING, "API running in UNRESTRICTED access mode");
|
applog(LOG_WARNING, "API running in IP access mode");
|
||||||
else
|
else {
|
||||||
applog(LOG_WARNING, "API running in restricted access mode");
|
if (opt_api_network)
|
||||||
|
applog(LOG_WARNING, "API running in UNRESTRICTED access mode");
|
||||||
|
else
|
||||||
|
applog(LOG_WARNING, "API running in local access mode");
|
||||||
|
}
|
||||||
|
|
||||||
io_buffer = malloc(MYBUFSIZ+1);
|
io_buffer = malloc(MYBUFSIZ+1);
|
||||||
msg_buffer = malloc(MYBUFSIZ+1);
|
msg_buffer = malloc(MYBUFSIZ+1);
|
||||||
@ -1310,11 +1419,21 @@ void api(void)
|
|||||||
goto die;
|
goto die;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opt_api_network)
|
addrok = false;
|
||||||
addrok = true;
|
if (opt_api_allow) {
|
||||||
else {
|
for (i = 0; i < ips; i++) {
|
||||||
connectaddr = inet_ntoa(cli.sin_addr);
|
if ((cli.sin_addr.s_addr & ipaccess[i].mask) == ipaccess[i].ip) {
|
||||||
addrok = (strcmp(connectaddr, localaddr) == 0);
|
addrok = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (opt_api_network)
|
||||||
|
addrok = true;
|
||||||
|
else {
|
||||||
|
connectaddr = inet_ntoa(cli.sin_addr);
|
||||||
|
addrok = (strcmp(connectaddr, localaddr) == 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opt_debug) {
|
if (opt_debug) {
|
||||||
|
13
cgminer.c
13
cgminer.c
@ -127,6 +127,7 @@ static bool opt_fail_only;
|
|||||||
bool opt_autofan;
|
bool opt_autofan;
|
||||||
bool opt_autoengine;
|
bool opt_autoengine;
|
||||||
bool opt_noadl;
|
bool opt_noadl;
|
||||||
|
char *opt_api_allow = NULL;
|
||||||
char *opt_api_description = PACKAGE_STRING;
|
char *opt_api_description = PACKAGE_STRING;
|
||||||
int opt_api_port = 4028;
|
int opt_api_port = 4028;
|
||||||
bool opt_api_listen = false;
|
bool opt_api_listen = false;
|
||||||
@ -535,6 +536,13 @@ static char *set_schedtime(const char *arg, struct schedtime *st)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *set_api_allow(const char *arg)
|
||||||
|
{
|
||||||
|
opt_set_charp(arg, &opt_api_allow);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static char *set_api_description(const char *arg)
|
static char *set_api_description(const char *arg)
|
||||||
{
|
{
|
||||||
opt_set_charp(arg, &opt_api_description);
|
opt_set_charp(arg, &opt_api_description);
|
||||||
@ -574,6 +582,9 @@ static struct opt_table opt_config_table[] = {
|
|||||||
#endif
|
#endif
|
||||||
),
|
),
|
||||||
#endif
|
#endif
|
||||||
|
OPT_WITH_ARG("--api-allow",
|
||||||
|
set_api_allow, NULL, NULL,
|
||||||
|
"Allow API access only to the given list of IP[/Prefix] addresses[/subnets]"),
|
||||||
OPT_WITH_ARG("--api-description",
|
OPT_WITH_ARG("--api-description",
|
||||||
set_api_description, NULL, NULL,
|
set_api_description, NULL, NULL,
|
||||||
"Description placed in the API status header, default: cgminer version"),
|
"Description placed in the API status header, default: cgminer version"),
|
||||||
@ -2295,6 +2306,8 @@ void write_config(FILE *fcfg)
|
|||||||
for (i = 0; i < nDevs; i++)
|
for (i = 0; i < nDevs; i++)
|
||||||
if (gpus[i].enabled)
|
if (gpus[i].enabled)
|
||||||
fprintf(fcfg, ",\n\"device\" : \"%d\"", i);
|
fprintf(fcfg, ",\n\"device\" : \"%d\"", i);
|
||||||
|
if (opt_api_allow != NULL)
|
||||||
|
fprintf(fcfg, ",\n\"api-allow\" : \"%s\"", opt_api_allow);
|
||||||
if (strcmp(opt_api_description, PACKAGE_STRING) != 0)
|
if (strcmp(opt_api_description, PACKAGE_STRING) != 0)
|
||||||
fprintf(fcfg, ",\n\"api-description\" : \"%s\"", opt_api_description);
|
fprintf(fcfg, ",\n\"api-description\" : \"%s\"", opt_api_description);
|
||||||
fputs("\n}", fcfg);
|
fputs("\n}", fcfg);
|
||||||
|
1
miner.h
1
miner.h
@ -446,6 +446,7 @@ extern char *cgminer_path;
|
|||||||
extern bool opt_autofan;
|
extern bool opt_autofan;
|
||||||
extern bool opt_autoengine;
|
extern bool opt_autoengine;
|
||||||
extern bool use_curses;
|
extern bool use_curses;
|
||||||
|
extern char *opt_api_allow;
|
||||||
extern char *opt_api_description;
|
extern char *opt_api_description;
|
||||||
extern int opt_api_port;
|
extern int opt_api_port;
|
||||||
extern bool opt_api_listen;
|
extern bool opt_api_listen;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user