mirror of
https://github.com/GOSTSec/sgminer
synced 2025-01-10 14:58:01 +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]
|
||||
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-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)
|
||||
@ -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
|
||||
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
|
||||
Also, if you add the "--api-network" option, it will accept API requests
|
||||
from any network attached computer.
|
||||
If you add the "--api-network" option, it will accept API requests from any
|
||||
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
|
||||
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.
|
||||
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
|
||||
{"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
|
||||
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 '='
|
||||
with the values after the '='
|
||||
|
||||
|
141
api.c
141
api.c
@ -342,6 +342,14 @@ struct CODES {
|
||||
static int bye = 0;
|
||||
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
|
||||
// thus for JSON, message() inserts JSON_START at the front
|
||||
// and send_result() adds JSON_END at the end
|
||||
@ -1204,6 +1212,11 @@ static void tidyup()
|
||||
sock = INVSOCK;
|
||||
}
|
||||
|
||||
if (ipaccess != NULL) {
|
||||
free(ipaccess);
|
||||
ipaccess = NULL;
|
||||
}
|
||||
|
||||
if (msg_buffer != NULL) {
|
||||
free(msg_buffer);
|
||||
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)
|
||||
{
|
||||
char buf[BUFSIZ];
|
||||
@ -1247,6 +1343,15 @@ void api(void)
|
||||
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);
|
||||
if (sock == INVSOCK) {
|
||||
applog(LOG_ERR, "API1 initialisation failed (%s)%s", SOCKERRMSG, UNAVAILABLE);
|
||||
@ -1257,9 +1362,9 @@ void api(void)
|
||||
|
||||
serv.sin_family = AF_INET;
|
||||
|
||||
if (!opt_api_network) {
|
||||
if (!opt_api_allow && !opt_api_network) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
@ -1295,10 +1400,14 @@ void api(void)
|
||||
return;
|
||||
}
|
||||
|
||||
if (opt_api_network)
|
||||
applog(LOG_WARNING, "API running in UNRESTRICTED access mode");
|
||||
else
|
||||
applog(LOG_WARNING, "API running in restricted access mode");
|
||||
if (opt_api_allow)
|
||||
applog(LOG_WARNING, "API running in IP access mode");
|
||||
else {
|
||||
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);
|
||||
msg_buffer = malloc(MYBUFSIZ+1);
|
||||
@ -1310,11 +1419,21 @@ void api(void)
|
||||
goto die;
|
||||
}
|
||||
|
||||
if (opt_api_network)
|
||||
addrok = true;
|
||||
else {
|
||||
connectaddr = inet_ntoa(cli.sin_addr);
|
||||
addrok = (strcmp(connectaddr, localaddr) == 0);
|
||||
addrok = false;
|
||||
if (opt_api_allow) {
|
||||
for (i = 0; i < ips; i++) {
|
||||
if ((cli.sin_addr.s_addr & ipaccess[i].mask) == ipaccess[i].ip) {
|
||||
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) {
|
||||
|
13
cgminer.c
13
cgminer.c
@ -127,6 +127,7 @@ static bool opt_fail_only;
|
||||
bool opt_autofan;
|
||||
bool opt_autoengine;
|
||||
bool opt_noadl;
|
||||
char *opt_api_allow = NULL;
|
||||
char *opt_api_description = PACKAGE_STRING;
|
||||
int opt_api_port = 4028;
|
||||
bool opt_api_listen = false;
|
||||
@ -535,6 +536,13 @@ static char *set_schedtime(const char *arg, struct schedtime *st)
|
||||
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)
|
||||
{
|
||||
opt_set_charp(arg, &opt_api_description);
|
||||
@ -574,6 +582,9 @@ static struct opt_table opt_config_table[] = {
|
||||
#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",
|
||||
set_api_description, NULL, NULL,
|
||||
"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++)
|
||||
if (gpus[i].enabled)
|
||||
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)
|
||||
fprintf(fcfg, ",\n\"api-description\" : \"%s\"", opt_api_description);
|
||||
fputs("\n}", fcfg);
|
||||
|
Loading…
Reference in New Issue
Block a user