Skip to content

Commit 247d0bc

Browse files
committed
Adds validation when Crowdsec LAPI URL is set
This simply parses the URL string to ensure the prefix contains a scheme and an authority, following the RFC 3986 standard. If the scheme and authority cannot be parsed due to the passed string not following the RFC standard, Apache will fail to start and print an error to logs. The path, query, and fragment are subsequently ignored and a warning gets printed if anything besides the string "/" is found after the authority.
1 parent 61729dd commit 247d0bc

File tree

2 files changed

+93
-8
lines changed

2 files changed

+93
-8
lines changed

config/crowdsec-apache2-bouncer.conf

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
## Basic configuration
2-
CrowdsecURL http://127.0.0.1:8080 # Make sure there is no trailing forward slash
2+
3+
# Must be of the form http://<authority>
4+
# Anything after the authority (path, query, or fragment) in URL will be
5+
# ignored and a warning will be printed to logs
6+
CrowdsecURL http://127.0.0.1:8080
37
CrowdsecAPIKey $API_KEY
48

59
# Behavior if we can't reach (or timeout) LAPI

mod_crowdsec.c

+88-7
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,16 @@
101101

102102
module AP_MODULE_DECLARE_DATA crowdsec_module;
103103

104+
struct url {
105+
const char *scheme;
106+
const char *authority;
107+
const char *path;
108+
};
109+
104110
typedef struct
105111
{
106112
/* the url of the crowdsec service */
107-
const char *url;
113+
struct url *url;
108114
/* the API key of the crowdsec service */
109115
const char *key;
110116
/* shared obect cache mutex */
@@ -368,11 +374,11 @@ static int crowdsec_proxy(request_rec * r, const char **response)
368374
* filter that reads and parses the response from the API.
369375
*/
370376

371-
const char *target = apr_pstrcat(r->pool, sconf->url,
372-
"/v1/decisions?ip=",
373-
ap_escape_urlencoded(r->pool,
374-
r->useragent_ip),
375-
NULL);
377+
char *api_path = "/v1/decisions?ip=";
378+
379+
const char *target = apr_pstrcat(
380+
r->pool, sconf->url->scheme, "://", sconf->url->authority, api_path,
381+
ap_escape_urlencoded(r->pool, r->useragent_ip), NULL);
376382

377383
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
378384
"crowdsec: looking up IP '%s' at url: %s",
@@ -865,14 +871,89 @@ static const char *set_crowdsec_location(cmd_parms *cmd, void *dconf,
865871
return NULL;
866872
}
867873

874+
// Note: url *must* be null-terminated
875+
struct url *find_base_lapi_url(apr_pool_t *pool, const char *url,
876+
const char **err)
877+
{
878+
char *scheme = NULL;
879+
char *authority = NULL;
880+
apr_size_t authority_start_idx = 0;
881+
char *path = NULL;
882+
883+
for (apr_size_t i = 0; url[i] != '\0'; i++) {
884+
if (!scheme) {
885+
if (url[i] == ':') {
886+
scheme = apr_pstrndup(pool, url, i);
887+
if (apr_cstr_casecmpn(url + i + 1, "//", 2)) {
888+
*err =
889+
"invalid lapi base url: \"//\" after scheme not found";
890+
return NULL;
891+
}
892+
authority_start_idx = i + 3;
893+
i += 2;
894+
}
895+
continue;
896+
}
897+
if (!authority) {
898+
if (url[i] == '/') {
899+
authority = apr_pstrndup(pool, url + authority_start_idx,
900+
i - authority_start_idx);
901+
i--;
902+
}
903+
continue;
904+
}
905+
if (!path) {
906+
path = apr_pstrdup(pool, url + i);
907+
break;
908+
}
909+
}
910+
911+
if (!scheme) {
912+
*err = "invalid lapi base url: scheme is missing";
913+
return NULL;
914+
}
915+
916+
if (!authority) {
917+
if (url[authority_start_idx] == '\0') {
918+
*err = "invalid lapi base url: authority is missing";
919+
return NULL;
920+
}
921+
authority = apr_pstrdup(pool, url + authority_start_idx);
922+
}
923+
924+
struct url *res = apr_palloc(pool, sizeof(struct url));
925+
res->scheme = scheme;
926+
res->authority = authority;
927+
res->path = path;
928+
929+
return res;
930+
}
931+
868932
static const char *set_crowdsec_url(cmd_parms * cmd, void *dconf,
869933
const char *url)
870934
{
871935
crowdsec_server_rec *sconf = (crowdsec_server_rec *)
872936
ap_get_module_config(cmd->server->module_config,
873937
&crowdsec_module);
874938

875-
sconf->url = url;
939+
const char *err = NULL;
940+
struct url *u = find_base_lapi_url(cmd->temp_pool, url, &err);
941+
if (err) {
942+
return err;
943+
}
944+
945+
ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, sconf,
946+
"scheme: \"%s\", authority: \"%s\", path: \"%s\"", u->scheme,
947+
u->authority, u->path);
948+
if (u->path) {
949+
if (apr_strnatcmp(u->path, "/")) {
950+
ap_log_error(APLOG_MARK, APLOG_WARNING, APR_SUCCESS, sconf,
951+
"lapi url: path (\"%s\") was found and will be ignored",
952+
u->path);
953+
}
954+
}
955+
956+
sconf->url = u;
876957
sconf->url_set = 1;
877958

878959
return NULL;

0 commit comments

Comments
 (0)