├── .gitignore ├── LICENSE ├── README.md └── src └── c ├── config └── ngx_http_access_plus_module.c /.gitignore: -------------------------------------------------------------------------------- 1 | /.cproject 2 | /.project 3 | /.settings 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2002-2019 Igor Sysoev 2 | Copyright (C) 2011-2019 Nginx, Inc. 3 | Copyright (c) 2016-2019 Zhang, Yuexiang (xfeep) 4 | 5 | All rights reserved. 6 | 7 | The BSD 3-Clause License 8 | 9 | Redistribution and use in source and binary forms, with or without modification, 10 | are permitted provided that the following conditions are met: 11 | 12 | * Redistributions of source code must retain the above copyright notice, this 13 | list of conditions and the following disclaimer. 14 | 15 | * Redistributions in binary form must reproduce the above copyright notice, this 16 | list of conditions and the following disclaimer in the documentation and/or 17 | other materials provided with the distribution. 18 | 19 | * Neither the name of Zhang, Yuexiang (xfeep) nor the names of its 20 | contributors may be used to endorse or promote products derived from 21 | this software without specific prior written permission. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 27 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 28 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 30 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 32 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Nginx-Access-Plus 2 | ============= 3 | 4 | Nginx-Access-Plus is a [Nginx](http://nginx.org/) module allows limiting access to certain http request methods and client addresses. 5 | 6 | 7 | Installation 8 | ================= 9 | 10 | ```shell 11 | #If nginx source is checked out from hg, please replace ./configure with auto/configure 12 | $./configure \ 13 | --add-module=nginx-access-plus/src/c 14 | $ make 15 | $ make install 16 | ``` 17 | 18 | 19 | User Guide 20 | ================= 21 | 22 | Only Allow GET and HEAD Requests 23 | ----------------- 24 | 25 | ```nginx 26 | location / { 27 | allow_method all get|head; 28 | deny_method all all; 29 | } 30 | ``` 31 | 32 | All GET|HEAD Requests But Deny All POST|DELETE Requests Except for 192.168.1.* 33 | ----------------- 34 | 35 | ```nginx 36 | location / { 37 | allow_method all get|head; 38 | allow_method 192.168.1.0/24 post|delete; 39 | deny_method all all; 40 | } 41 | ``` 42 | 43 | Deny POST|PUT|DELETE Requests from 192.168.1.* 44 | ----------------- 45 | 46 | ```nginx 47 | location / { 48 | deny_method 192.168.1.0/24 post|put|delete; 49 | } 50 | ``` 51 | 52 | License 53 | ================= 54 | Copyright © 2015 Zhang, Yuexiang (xfeep) and released under the BSD 3-Clause license. 55 | 56 | -------------------------------------------------------------------------------- /src/c/config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_http_access_plus_module 2 | 3 | if test -n "$ngx_module_link"; then 4 | ngx_module_type=HTTP 5 | ngx_module_name=$ngx_addon_name 6 | ngx_module_srcs="$ngx_addon_dir/$ngx_addon_name.c" 7 | 8 | . auto/module 9 | else 10 | HTTP_MODULES="$HTTP_MODULES $ngx_addon_name" 11 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/$ngx_addon_name.c" 12 | fi 13 | -------------------------------------------------------------------------------- /src/c/ngx_http_access_plus_module.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) Igor Sysoev 3 | * Copyright (C) Nginx, Inc. 4 | * Copyright (C) Zhang,Yuexiang (xfeep) 5 | * 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | static char* ngx_http_access_plus_methods[] = {"unknown", "get", "head", 13 | "post", "put", "delete", "mkcol", "copy", "move", "options", "propfind", 14 | "proppatch", "lock", "unlock", "patch", "trace", NULL}; 15 | 16 | typedef struct { 17 | in_addr_t mask; 18 | in_addr_t addr; 19 | ngx_uint_t deny; /* unsigned deny:1; */ 20 | ngx_uint_t methods; /* http request methods*/ 21 | } ngx_http_access_plus_rule_t; 22 | 23 | #if (NGX_HAVE_INET6) 24 | 25 | typedef struct { 26 | struct in6_addr addr; 27 | struct in6_addr mask; 28 | ngx_uint_t deny; /* unsigned deny:1; */ 29 | ngx_uint_t methods; /* http request methods*/ 30 | } ngx_http_access_plus_rule6_t; 31 | 32 | #endif 33 | 34 | #if (NGX_HAVE_UNIX_DOMAIN) 35 | 36 | typedef struct { 37 | ngx_uint_t deny; /* unsigned deny:1; */ 38 | ngx_uint_t methods; /* http request methods*/ 39 | } ngx_http_access_plus_rule_un_t; 40 | 41 | #endif 42 | 43 | typedef struct { 44 | ngx_array_t *rules; /* array of ngx_http_access_plus_rule_t */ 45 | #if (NGX_HAVE_INET6) 46 | ngx_array_t *rules6; /* array of ngx_http_access_plus_rule6_t */ 47 | #endif 48 | #if (NGX_HAVE_UNIX_DOMAIN) 49 | ngx_array_t *rules_un; /* array of ngx_http_access_plus_rule_un_t */ 50 | #endif 51 | } ngx_http_access_plus_loc_conf_t; 52 | 53 | 54 | static ngx_int_t ngx_http_access_plus_handler(ngx_http_request_t *r); 55 | static ngx_int_t ngx_http_access_plus_inet(ngx_http_request_t *r, 56 | ngx_http_access_plus_loc_conf_t *alcf, in_addr_t addr); 57 | #if (NGX_HAVE_INET6) 58 | static ngx_int_t ngx_http_access_plus_inet6(ngx_http_request_t *r, 59 | ngx_http_access_plus_loc_conf_t *alcf, u_char *p); 60 | #endif 61 | #if (NGX_HAVE_UNIX_DOMAIN) 62 | static ngx_int_t ngx_http_access_plus_unix(ngx_http_request_t *r, 63 | ngx_http_access_plus_loc_conf_t *alcf); 64 | #endif 65 | static ngx_int_t ngx_http_access_plus_found(ngx_http_request_t *r, ngx_uint_t deny); 66 | static char *ngx_http_access_plus_rule(ngx_conf_t *cf, ngx_command_t *cmd, 67 | void *conf); 68 | static void *ngx_http_access_plus_create_loc_conf(ngx_conf_t *cf); 69 | static char *ngx_http_access_plus_merge_loc_conf(ngx_conf_t *cf, 70 | void *parent, void *child); 71 | static ngx_int_t ngx_http_access_plus_init(ngx_conf_t *cf); 72 | 73 | 74 | static ngx_command_t ngx_http_access_plus_commands[] = { 75 | 76 | { ngx_string("allow_method"), 77 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF 78 | |NGX_CONF_TAKE2, 79 | ngx_http_access_plus_rule, 80 | NGX_HTTP_LOC_CONF_OFFSET, 81 | 0, 82 | NULL }, 83 | 84 | { ngx_string("deny_method"), 85 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF 86 | |NGX_CONF_TAKE2, 87 | ngx_http_access_plus_rule, 88 | NGX_HTTP_LOC_CONF_OFFSET, 89 | 0, 90 | NULL }, 91 | 92 | ngx_null_command 93 | }; 94 | 95 | 96 | 97 | static ngx_http_module_t ngx_http_access_plus_module_ctx = { 98 | NULL, /* preconfiguration */ 99 | ngx_http_access_plus_init, /* postconfiguration */ 100 | 101 | NULL, /* create main configuration */ 102 | NULL, /* init main configuration */ 103 | 104 | NULL, /* create server configuration */ 105 | NULL, /* merge server configuration */ 106 | 107 | ngx_http_access_plus_create_loc_conf, /* create location configuration */ 108 | ngx_http_access_plus_merge_loc_conf /* merge location configuration */ 109 | }; 110 | 111 | 112 | ngx_module_t ngx_http_access_plus_module = { 113 | NGX_MODULE_V1, 114 | &ngx_http_access_plus_module_ctx, /* module context */ 115 | ngx_http_access_plus_commands, /* module directives */ 116 | NGX_HTTP_MODULE, /* module type */ 117 | NULL, /* init master */ 118 | NULL, /* init module */ 119 | NULL, /* init process */ 120 | NULL, /* init thread */ 121 | NULL, /* exit thread */ 122 | NULL, /* exit process */ 123 | NULL, /* exit master */ 124 | NGX_MODULE_V1_PADDING 125 | }; 126 | 127 | 128 | static ngx_int_t 129 | ngx_http_access_plus_handler(ngx_http_request_t *r) 130 | { 131 | struct sockaddr_in *sin; 132 | ngx_http_access_plus_loc_conf_t *alcf; 133 | #if (NGX_HAVE_INET6) 134 | u_char *p; 135 | in_addr_t addr; 136 | struct sockaddr_in6 *sin6; 137 | #endif 138 | 139 | alcf = ngx_http_get_module_loc_conf(r, ngx_http_access_plus_module); 140 | 141 | switch (r->connection->sockaddr->sa_family) { 142 | 143 | case AF_INET: 144 | if (alcf->rules) { 145 | sin = (struct sockaddr_in *) r->connection->sockaddr; 146 | return ngx_http_access_plus_inet(r, alcf, sin->sin_addr.s_addr); 147 | } 148 | break; 149 | 150 | #if (NGX_HAVE_INET6) 151 | 152 | case AF_INET6: 153 | sin6 = (struct sockaddr_in6 *) r->connection->sockaddr; 154 | p = sin6->sin6_addr.s6_addr; 155 | 156 | if (alcf->rules && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 157 | addr = p[12] << 24; 158 | addr += p[13] << 16; 159 | addr += p[14] << 8; 160 | addr += p[15]; 161 | return ngx_http_access_plus_inet(r, alcf, htonl(addr)); 162 | } 163 | 164 | if (alcf->rules6) { 165 | return ngx_http_access_plus_inet6(r, alcf, p); 166 | } 167 | 168 | break; 169 | 170 | #endif 171 | 172 | #if (NGX_HAVE_UNIX_DOMAIN) 173 | 174 | case AF_UNIX: 175 | if (alcf->rules_un) { 176 | return ngx_http_access_plus_unix(r, alcf); 177 | } 178 | 179 | break; 180 | 181 | #endif 182 | } 183 | 184 | return NGX_DECLINED; 185 | } 186 | 187 | 188 | static ngx_int_t 189 | ngx_http_access_plus_inet(ngx_http_request_t *r, ngx_http_access_plus_loc_conf_t *alcf, 190 | in_addr_t addr) 191 | { 192 | ngx_uint_t i; 193 | ngx_http_access_plus_rule_t *rule; 194 | 195 | rule = alcf->rules->elts; 196 | for (i = 0; i < alcf->rules->nelts; i++) { 197 | 198 | ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 199 | "access: %08XD %08XD %08XD", 200 | addr, rule[i].mask, rule[i].addr); 201 | 202 | if ((addr & rule[i].mask) == rule[i].addr && (r->method & rule[i].methods)) { 203 | return ngx_http_access_plus_found(r, rule[i].deny); 204 | } 205 | } 206 | 207 | return NGX_DECLINED; 208 | } 209 | 210 | 211 | #if (NGX_HAVE_INET6) 212 | 213 | static ngx_int_t 214 | ngx_http_access_plus_inet6(ngx_http_request_t *r, ngx_http_access_plus_loc_conf_t *alcf, 215 | u_char *p) 216 | { 217 | ngx_uint_t n; 218 | ngx_uint_t i; 219 | ngx_http_access_plus_rule6_t *rule6; 220 | 221 | rule6 = alcf->rules6->elts; 222 | for (i = 0; i < alcf->rules6->nelts; i++) { 223 | 224 | if (!(r->method & rule6[i].methods)) { 225 | continue; 226 | } 227 | #if (NGX_DEBUG) 228 | { 229 | size_t cl, ml, al; 230 | u_char ct[NGX_INET6_ADDRSTRLEN]; 231 | u_char mt[NGX_INET6_ADDRSTRLEN]; 232 | u_char at[NGX_INET6_ADDRSTRLEN]; 233 | 234 | cl = ngx_inet6_ntop(p, ct, NGX_INET6_ADDRSTRLEN); 235 | ml = ngx_inet6_ntop(rule6[i].mask.s6_addr, mt, NGX_INET6_ADDRSTRLEN); 236 | al = ngx_inet6_ntop(rule6[i].addr.s6_addr, at, NGX_INET6_ADDRSTRLEN); 237 | 238 | ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 239 | "access: %*s %*s %*s", cl, ct, ml, mt, al, at); 240 | } 241 | #endif 242 | 243 | for (n = 0; n < 16; n++) { 244 | if ((p[n] & rule6[i].mask.s6_addr[n]) != rule6[i].addr.s6_addr[n]) { 245 | goto next; 246 | } 247 | } 248 | 249 | return ngx_http_access_plus_found(r, rule6[i].deny); 250 | 251 | next: 252 | continue; 253 | } 254 | 255 | return NGX_DECLINED; 256 | } 257 | 258 | #endif 259 | 260 | 261 | #if (NGX_HAVE_UNIX_DOMAIN) 262 | 263 | static ngx_int_t 264 | ngx_http_access_plus_unix(ngx_http_request_t *r, ngx_http_access_plus_loc_conf_t *alcf) 265 | { 266 | ngx_uint_t i; 267 | ngx_http_access_plus_rule_un_t *rule_un; 268 | 269 | rule_un = alcf->rules_un->elts; 270 | for (i = 0; i < alcf->rules_un->nelts; i++) { 271 | 272 | /* TODO: check path */ 273 | if (r->method & rule_un[i].methods) { 274 | return ngx_http_access_plus_found(r, rule_un[i].deny); 275 | } 276 | } 277 | 278 | return NGX_DECLINED; 279 | } 280 | 281 | #endif 282 | 283 | 284 | static ngx_int_t 285 | ngx_http_access_plus_found(ngx_http_request_t *r, ngx_uint_t deny) 286 | { 287 | ngx_http_core_loc_conf_t *clcf; 288 | 289 | if (deny) { 290 | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); 291 | 292 | if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) { 293 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 294 | "access forbidden by rule"); 295 | } 296 | 297 | return NGX_HTTP_FORBIDDEN; 298 | } 299 | 300 | return NGX_OK; 301 | } 302 | 303 | static ngx_uint_t ngx_http_access_plus_compute_methods(ngx_str_t *str_methods) { 304 | ngx_uint_t m = NGX_HTTP_UNKNOWN; 305 | int i = 0; 306 | ngx_uint_t methods = 0; 307 | 308 | if (ngx_strstr(str_methods->data, "*") || ngx_strstr(str_methods->data, "all")) { 309 | return ~0; 310 | } 311 | 312 | do { 313 | if (ngx_strstr(str_methods->data, ngx_http_access_plus_methods[i])) { 314 | methods |= m; 315 | } 316 | m <<= 1; 317 | }while (ngx_http_access_plus_methods[++i]); 318 | 319 | return methods; 320 | } 321 | 322 | static char * 323 | ngx_http_access_plus_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 324 | { 325 | ngx_http_access_plus_loc_conf_t *alcf = conf; 326 | 327 | ngx_int_t rc; 328 | ngx_uint_t all; 329 | ngx_str_t *value; 330 | ngx_cidr_t cidr; 331 | ngx_http_access_plus_rule_t *rule; 332 | #if (NGX_HAVE_INET6) 333 | ngx_http_access_plus_rule6_t *rule6; 334 | #endif 335 | #if (NGX_HAVE_UNIX_DOMAIN) 336 | ngx_http_access_plus_rule_un_t *rule_un; 337 | #endif 338 | 339 | ngx_memzero(&cidr, sizeof(ngx_cidr_t)); 340 | 341 | value = cf->args->elts; 342 | 343 | 344 | all = (value[1].len == 3 && ngx_strcmp(value[1].data, "all") == 0); 345 | 346 | if (!all) { 347 | 348 | #if (NGX_HAVE_UNIX_DOMAIN) 349 | 350 | if (value[1].len == 5 && ngx_strcmp(value[1].data, "unix:") == 0) { 351 | cidr.family = AF_UNIX; 352 | rc = NGX_OK; 353 | 354 | } else { 355 | rc = ngx_ptocidr(&value[1], &cidr); 356 | } 357 | 358 | #else 359 | rc = ngx_ptocidr(&value[1], &cidr); 360 | #endif 361 | 362 | if (rc == NGX_ERROR) { 363 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 364 | "invalid parameter \"%V\"", &value[1]); 365 | return NGX_CONF_ERROR; 366 | } 367 | 368 | if (rc == NGX_DONE) { 369 | ngx_conf_log_error(NGX_LOG_WARN, cf, 0, 370 | "low address bits of %V are meaningless", &value[1]); 371 | } 372 | } 373 | 374 | if (cidr.family == AF_INET || all) { 375 | 376 | if (alcf->rules == NULL) { 377 | alcf->rules = ngx_array_create(cf->pool, 4, 378 | sizeof(ngx_http_access_plus_rule_t)); 379 | if (alcf->rules == NULL) { 380 | return NGX_CONF_ERROR; 381 | } 382 | } 383 | 384 | rule = ngx_array_push(alcf->rules); 385 | if (rule == NULL) { 386 | return NGX_CONF_ERROR; 387 | } 388 | 389 | rule->mask = cidr.u.in.mask; 390 | rule->addr = cidr.u.in.addr; 391 | rule->deny = (value[0].data[0] == 'd') ? 1 : 0; 392 | rule->methods = ngx_http_access_plus_compute_methods(value+2); 393 | } 394 | 395 | #if (NGX_HAVE_INET6) 396 | if (cidr.family == AF_INET6 || all) { 397 | 398 | if (alcf->rules6 == NULL) { 399 | alcf->rules6 = ngx_array_create(cf->pool, 4, 400 | sizeof(ngx_http_access_plus_rule6_t)); 401 | if (alcf->rules6 == NULL) { 402 | return NGX_CONF_ERROR; 403 | } 404 | } 405 | 406 | rule6 = ngx_array_push(alcf->rules6); 407 | if (rule6 == NULL) { 408 | return NGX_CONF_ERROR; 409 | } 410 | 411 | rule6->mask = cidr.u.in6.mask; 412 | rule6->addr = cidr.u.in6.addr; 413 | rule6->deny = (value[0].data[0] == 'd') ? 1 : 0; 414 | rule6->methods = ngx_http_access_plus_compute_methods(value+2); 415 | } 416 | #endif 417 | 418 | #if (NGX_HAVE_UNIX_DOMAIN) 419 | if (cidr.family == AF_UNIX || all) { 420 | 421 | if (alcf->rules_un == NULL) { 422 | alcf->rules_un = ngx_array_create(cf->pool, 1, 423 | sizeof(ngx_http_access_plus_rule_un_t)); 424 | if (alcf->rules_un == NULL) { 425 | return NGX_CONF_ERROR; 426 | } 427 | } 428 | 429 | rule_un = ngx_array_push(alcf->rules_un); 430 | if (rule_un == NULL) { 431 | return NGX_CONF_ERROR; 432 | } 433 | 434 | rule_un->deny = (value[0].data[0] == 'd') ? 1 : 0; 435 | rule_un->methods = ngx_http_access_plus_compute_methods(value+2); 436 | } 437 | #endif 438 | 439 | return NGX_CONF_OK; 440 | } 441 | 442 | 443 | static void * 444 | ngx_http_access_plus_create_loc_conf(ngx_conf_t *cf) 445 | { 446 | ngx_http_access_plus_loc_conf_t *conf; 447 | 448 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_access_plus_loc_conf_t)); 449 | if (conf == NULL) { 450 | return NULL; 451 | } 452 | 453 | return conf; 454 | } 455 | 456 | 457 | static char * 458 | ngx_http_access_plus_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 459 | { 460 | ngx_http_access_plus_loc_conf_t *prev = parent; 461 | ngx_http_access_plus_loc_conf_t *conf = child; 462 | 463 | if (conf->rules == NULL 464 | #if (NGX_HAVE_INET6) 465 | && conf->rules6 == NULL 466 | #endif 467 | #if (NGX_HAVE_UNIX_DOMAIN) 468 | && conf->rules_un == NULL 469 | #endif 470 | ) { 471 | conf->rules = prev->rules; 472 | #if (NGX_HAVE_INET6) 473 | conf->rules6 = prev->rules6; 474 | #endif 475 | #if (NGX_HAVE_UNIX_DOMAIN) 476 | conf->rules_un = prev->rules_un; 477 | #endif 478 | } 479 | 480 | return NGX_CONF_OK; 481 | } 482 | 483 | 484 | static ngx_int_t 485 | ngx_http_access_plus_init(ngx_conf_t *cf) 486 | { 487 | ngx_http_handler_pt *h; 488 | ngx_http_core_main_conf_t *cmcf; 489 | 490 | cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 491 | 492 | h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); 493 | if (h == NULL) { 494 | return NGX_ERROR; 495 | } 496 | 497 | *h = ngx_http_access_plus_handler; 498 | 499 | return NGX_OK; 500 | } 501 | --------------------------------------------------------------------------------