Browse Source

Allow API to restrict access by IP address

nfactor-troky
Kano 13 years ago
parent
commit
f19e67fe3a
  1. 24
      README
  2. 125
      api.c
  3. 13
      cgminer.c
  4. 1
      miner.h

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 '='

125
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_allow)
applog(LOG_WARNING, "API running in IP access mode");
else {
if (opt_api_network) if (opt_api_network)
applog(LOG_WARNING, "API running in UNRESTRICTED access mode"); applog(LOG_WARNING, "API running in UNRESTRICTED access mode");
else else
applog(LOG_WARNING, "API running in restricted access mode"); 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,12 +1419,22 @@ void api(void)
goto die; goto die;
} }
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) if (opt_api_network)
addrok = true; addrok = true;
else { else {
connectaddr = inet_ntoa(cli.sin_addr); connectaddr = inet_ntoa(cli.sin_addr);
addrok = (strcmp(connectaddr, localaddr) == 0); addrok = (strcmp(connectaddr, localaddr) == 0);
} }
}
if (opt_debug) { if (opt_debug) {
connectaddr = inet_ntoa(cli.sin_addr); connectaddr = inet_ntoa(cli.sin_addr);

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

@ -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…
Cancel
Save