Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions rel/overlay/etc/default.ini
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,11 @@ bind_address = 127.0.0.1
; Set to false to avoid scrubbing and revert to the previous behavior.
;scrub_json_request = true

; Set to a string to override how couchdb determines the peer of a request
; Default is undefined, so couchdb delegates to mochiweb which uses
; X-Forwarded-For logic.
;peer_header =

;[jwt_auth]
; List of claims to validate
; can be the name of a claim like "exp" or a tuple if the claim requires
Expand Down
26 changes: 25 additions & 1 deletion src/chttpd/src/chttpd.erl
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ handle_request_int(MochiReq) ->
P
end,

Peer = MochiReq:get(peer),
Peer = couch_httpd:peer(MochiReq),

Method1 =
case MochiReq:get(method) of
Expand Down Expand Up @@ -1694,4 +1694,28 @@ handle_req_after_auth_test() ->
ok = meck:unload(chttpd_handlers),
ok = meck:unload(chttpd_auth).

custom_peer_test() ->
Headers = mochiweb_headers:make([
{"HOST", "127.0.0.1:15984"}, {"X-Couch-Client-IP", "1.2.3.4"}
]),
MochiReq = mochiweb_request:new(
socket,
[],
'PUT',
"/newdb",
version,
Headers
),
Req = #httpd{
mochi_req = MochiReq,
begin_ts = {1458, 588713, 124003},
original_method = 'GET',
peer = "4.3.2.1",
nonce = "nonce"
},
ok = meck:new(config, [passthrough]),
ok = meck:expect(config, get, fun("chttpd", "peer_header") -> "x-couch-client-ip" end),
?assertEqual("1.2.3.4", couch_httpd:peer(Req)),
ok = meck:unload(config).
Comment thread
nickva marked this conversation as resolved.

-endif.
2 changes: 1 addition & 1 deletion src/chttpd/src/chttpd_external.erl
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ json_req_obj_field(<<"body">>, #httpd{req_body = undefined, mochi_req = Req}, _D
json_req_obj_field(<<"body">>, #httpd{req_body = Body}, _Db, _DocId) ->
Body;
json_req_obj_field(<<"peer">>, #httpd{peer = undefined, mochi_req = Req}, _, _) ->
?l2b(Req:get(peer));
?l2b(couch_httpd:peer(Req));
json_req_obj_field(<<"peer">>, #httpd{peer = Peer}, _Db, _DocId) ->
?l2b(Peer);
json_req_obj_field(<<"form">>, #httpd{mochi_req = Req, method = Method} = HttpReq, Db, DocId) ->
Expand Down
17 changes: 4 additions & 13 deletions src/couch/src/couch_auth_lockout.erl
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ is_locked_out(#httpd{} = Req, UserName, UserSalt) ->

is_locked_out_int(#httpd{} = Req, Mode, UserName, UserSalt) ->
LockoutThreshold = lockout_threshold(),
case peer(Req) of
nopeer ->
case Req#httpd.peer of
Comment thread
nickva marked this conversation as resolved.
Peer when is_atom(Peer) ->
false;
Peer ->
case ets_lru:lookup_d(?LRU, {Peer, UserName, UserSalt}) of
Expand All @@ -48,8 +48,8 @@ is_locked_out_int(#httpd{} = Req, Mode, UserName, UserSalt) ->
end.

lockout(#httpd{} = Req, UserName, UserSalt) ->
case peer(Req) of
nopeer ->
case Req#httpd.peer of
Peer when is_atom(Peer) ->
ok;
Peer ->
case lockout_mode() of
Expand All @@ -74,12 +74,3 @@ lockout_mode() ->

lockout_threshold() ->
config:get_integer("chttpd_auth_lockout", "threshold", 5).

peer(#httpd{mochi_req = MochiReq}) ->
Socket = mochiweb_request:get(socket, MochiReq),
case Socket of
{remote, _Pid, _Ref} ->
nopeer;
_ ->
mochiweb_request:get(peer, MochiReq)
end.
17 changes: 17 additions & 0 deletions src/couch/src/couch_httpd.erl
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
-export([handle_request/1]).
-export([set_auth_handlers/0]).
-export([maybe_decompress/2]).
-export([peer/1]).

-define(HANDLER_NAME_IN_MODULE_POS, 6).
-define(MAX_DRAIN_BYTES, 1048576).
Expand Down Expand Up @@ -1424,7 +1425,23 @@ http_respond_(#httpd{mochi_req = MochiReq}, 413, Headers, Args, Type) ->
http_respond_(#httpd{mochi_req = MochiReq}, Code, Headers, Args, Type) ->
MochiReq:Type({Code, Headers, Args}).

peer(#httpd{} = Req) ->
peer(Req#httpd.mochi_req);
peer(MochiReq) ->
case config:get("chttpd", "peer_header") of
undefined ->
peer_from_socket(MochiReq);
PeerHeader ->
Peer = MochiReq:get_header_value(PeerHeader),
case inet:parse_address(Peer) of
{ok, Addr} ->
inet:ntoa(Addr);
_ ->
peer_from_socket(MochiReq)
end
end.

peer_from_socket(MochiReq) ->
case MochiReq:get(socket) of
{remote, Pid, _} ->
node(Pid);
Expand Down
10 changes: 4 additions & 6 deletions src/couch/src/couch_httpd_auth.erl
Original file line number Diff line number Diff line change
Expand Up @@ -813,18 +813,16 @@ integer_to_binary(Int, Len) when is_integer(Int), is_integer(Len) ->
Padded = <<Padding/binary, Unpadded/binary>>,
binary:part(Padded, byte_size(Padded), -Len).

authentication_warning(#httpd{mochi_req = Req}, User) ->
Peer = Req:get(peer),
authentication_warning(#httpd{} = Req, User) ->
couch_log:warning(
"~p: Authentication failed for user ~s from ~s",
[?MODULE, User, Peer]
[?MODULE, User, Req#httpd.peer]
).

lockout_warning(#httpd{mochi_req = Req}, User) ->
Peer = Req:get(peer),
lockout_warning(#httpd{} = Req, User) ->
couch_log:warning(
"~p: Authentication rejected for locked-out user ~s from ~s",
[?MODULE, User, Peer]
[?MODULE, User, Req#httpd.peer]
).

-ifdef(TEST).
Expand Down
13 changes: 13 additions & 0 deletions src/docs/src/config/http.rst
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,19 @@ HTTP Server Options
[chttpd]
disconnect_check_jitter_msec = 15000

.. config:option:: peer_header :: Header to use for peer

.. versionadded:: 3.5.3

If defined, CouchDB will use the value of this request header to determine peer.
Comment thread
nickva marked this conversation as resolved.
The header value must be a single IPv4 or IPv6 address. If the header is missing,
CouchDB will look at the ``X-Forwarded-For`` header if present, or the socket
peer address if not.
This affects logging as well as the auth lockout feature. ::

[chttpd]
peer_header = X-Couch-Client-IP

.. config:section:: httpd :: HTTP Server Options

.. versionchanged:: 3.2 These options were moved to [chttpd] section:
Expand Down