Re: [users@httpd] graceful-stop closes established connections without response
i have confirmed that the patch has been applied, and the behavior still persists, as confirmed by comparing the counts of [SYN,ACK] and accept() ~$ tcpdump -n -r /tmp/tcpdump.pcap | grep -Fc '[S.]'; grep -Fh 'accept4' /tmp/strace-apache2.out.* | grep -Fc .240.209 reading from file /tmp/tcpdump.pcap, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 Warning: interface names might be incorrect 3485 3483 as well as ~$ for i in '> GET /index.html' '< HTTP/1.1 200 OK'; do echo -e "'$i':\t$( cat /tmp/curl-[0-9]*.out | grep -Fc "$i" )"; done '> GET /index.html': 3485 '< HTTP/1.1 200 OK': 3483 ~$ grep -F reset /tmp/curl-[0-9]*.out /tmp/curl-13.out:* Recv failure: Connection reset by peer /tmp/curl-13.out:curl: (56) Recv failure: Connection reset by peer /tmp/curl-2.out:* Recv failure: Connection reset by peer /tmp/curl-2.out:curl: (56) Recv failure: Connection reset by peer which all seem to agree that there were two failed connections and, in case this shines some light on things: ~$ grep -F '19:10:50' /var/log/apache2/error.log | grep -F XXX [Tue Jan 30 19:10:50.357074 2024] [mpm_event:notice] [pid 151381:tid 140267709454016] XXX: may exit (0, 1) [Tue Jan 30 19:10:50.357498 2024] [mpm_event:notice] [pid 151382:tid 140267709454016] XXX: may exit (0, 2) [Tue Jan 30 19:10:50.357610 2024] [mpm_event:notice] [pid 151381:tid 140267709454016] XXX: draining [Tue Jan 30 19:10:50.358011 2024] [mpm_event:notice] [pid 151381:tid 140267709454016] XXX: exiting [Tue Jan 30 19:10:50.358091 2024] [mpm_event:notice] [pid 151382:tid 140267709454016] XXX: draining [Tue Jan 30 19:10:50.358201 2024] [mpm_event:notice] [pid 151381:tid 140267709454016] XXX: closing [Tue Jan 30 19:10:50.358492 2024] [mpm_event:notice] [pid 151382:tid 140267709454016] XXX: exiting [Tue Jan 30 19:10:50.358906 2024] [mpm_event:notice] [pid 151382:tid 140267709454016] XXX: closing [Tue Jan 30 19:10:50.366948 2024] [mpm_event:notice] [pid 151381:tid 140267709454016] XXX: exited [Tue Jan 30 19:10:50.367124 2024] [mpm_event:notice] [pid 151382:tid 140267709454016] XXX: exited On 1/30/24 06:09 AM, Yann Ylavic wrote: On Tue, Jan 30, 2024 at 11:54 AM Yann Ylavic wrote: On Tue, Jan 30, 2024 at 4:37 AM Sherrard Burton wrote: i was going to add some debugging lines, but when i took a quick look at the patch, i wasn't clear on which sections of the code i should be guaranteed to hit. can you be so kind as to send an updated patch with some gratuitous logging in the appropriate sections so that there will be positive affirmation that the patch has (or hasn't) been applied and is falling into the expected sections? Sure, here is a v2 (which also includes a fix w.r.t. v1). Argh, please use this v3 instead, I missed that EINTR could interfere and should be ignored while draining. Regards; Yann. - To unsubscribe, e-mail: users-unsubscr...@httpd.apache.org For additional commands, e-mail: users-h...@httpd.apache.org - To unsubscribe, e-mail: users-unsubscr...@httpd.apache.org For additional commands, e-mail: users-h...@httpd.apache.org
Re: [users@httpd] If statement against AUTHENTICATE_memberOf variable created by authnz_ldap
On 1/26/24 18:13, Eric Covener wrote: So the first question is: Is it normal that I have to use mod_rewrite to check for group membership ? I tried hundred of syntaxes with SetEnvIf or SetEnvIfExpr but I never managed to get it working. I'm not sure why but I guess it's somehow related to "race condition" (lazy evaluation) while evaluating environment variable, does it makes sense ? SetEnvIf[expr] is evaluated very early, long before authn/authz occurs and those environment variables can be filled out. Second question is: I cannot use "$" to make a proper regex matcher. If the group is not the last one, I can match it with ;.*$, if it is the last one, I should be able to match [...]DC=internal$, however that does not work. There's is one unknown character and I have no idea what it is. Matching with DC=internal.?$ works, so that's one SINGLE char... Any idea ? Hello, Thanks a lot for your help. I ended up going for a completely different way because I found it was too messy... So here is the LUA script I use instead: require 'apache2' local admin_profiles = { 'CN=Group1,OU=Groups,DC=domain,DC=com', 'CG=Group2,DC=domain,DC=com', } local manager_profiles = { 'CN=Group1,OU=Groups,DC=domain,DC=com', 'CN=Group3,OU=Groups,DC=domain,DC=com', } local operator_profiles = { } local viewer_profiles = { 'CN=Group4,OU=Groups,DC=domain,DC=com', 'CN=Group5,OU=Groups,DC=domain,DC=com', } local function has_value_case_insensitive(tab, val) for index, value in ipairs(tab) do if value:lower() == val:lower() then return true end end return false end function authcheck_hook(r) -- Extract username and group list from Active Directory authentication local sam_account_name = r.subprocess_env['AUTHENTICATE_SAMACCOUNTNAME'] local member_of = r.subprocess_env['AUTHENTICATE_MEMBEROF'] -- Check if Active Directory variables are not set, if so, return 403 and log error if (sam_account_name == nil or sam_account_name == '') then r:err("ActiveDirectory group to PCC profile LUA script called but AUTHENTICATE_SAMACCOUNTNAME was not set") return 403 end if (member_of == nil or member_of == '') then r:err("ActiveDirectory group to PCC profile LUA script called but AUTHENTICATE_MEMBEROF was not set") return 403 end -- Split group member ship into an array and match corresponding profile local member_of_split_regex = '([^;%s*]+)' local member_of_profiles = {} for member_of_entry in member_of:gmatch(member_of_split_regex) do local member_of_entry_profiles = {} if has_value_case_insensitive(admin_profiles, member_of_entry) then if not has_value_case_insensitive(member_of_entry_profiles, "admin") then table.insert(member_of_entry_profiles, "admin") end if not has_value_case_insensitive(member_of_profiles, "admin") then table.insert(member_of_profiles, "admin") end end if has_value_case_insensitive(manager_profiles, member_of_entry) then if not has_value_case_insensitive(member_of_entry_profiles, "manager") then table.insert(member_of_entry_profiles, "manager") end if not has_value_case_insensitive(member_of_profiles, "manager") then table.insert(member_of_profiles, "manager") end end if has_value_case_insensitive(operator_profiles, member_of_entry) then if not has_value_case_insensitive(member_of_entry_profiles, "operator") then table.insert(member_of_entry_profiles, "operator") end if not has_value_case_insensitive(member_of_profiles, "operator") then table.insert(member_of_profiles, "operator") end end if has_value_case_insensitive(viewer_profiles, member_of_entry) then if not has_value_case_insensitive(member_of_entry_profiles, "viewer") then table.insert(member_of_entry_profiles, "viewer") end if not has_value_case_insensitive(member_of_profiles, "viewer") then table.insert(member_of_profiles, "viewer") end end if next(member_of_entry_profiles) == nil then r:debug(("Username %s ActiveDirectory group %s gives no PCC privileges"):format(sam_account_name, member_of_entry)) else r:debug(("Username %s ActiveDirectory group %s gives PCC privileges: %s"):format(sam_account_name, member_of_entry, table.concat(member_of_entry_profiles, ', '))) end end -- Compare all matched group resulting profile and get the one with highest privileges if has_value_case_insensitive(member_of_profiles, "admin") then r:info(("Username %s authorized with PCC admin privileges"):format(sam_account_name, member_of_entry)) r.headers_in['X-PCC-Group'] = "admin" elseif has_value_case_insensitive(member_of_profiles, "manager") then r:info(("Username %s authorized with PCC manager privileges"):format(sam_account_name, member_of_entry)) r.headers_in['X-PCC-Group'] = "manager" elseif
Re: [users@httpd] graceful-stop closes established connections without response
On Tue, Jan 30, 2024 at 11:54 AM Yann Ylavic wrote: > > On Tue, Jan 30, 2024 at 4:37 AM Sherrard Burton wrote: > > > > i was going to add some debugging lines, but when i took a quick look at > > the patch, i wasn't clear on which sections of the code i should be > > guaranteed to hit. can you be so kind as to send an updated patch with > > some gratuitous logging in the appropriate sections so that there will > > be positive affirmation that the patch has (or hasn't) been applied and > > is falling into the expected sections? > > Sure, here is a v2 (which also includes a fix w.r.t. v1). Argh, please use this v3 instead, I missed that EINTR could interfere and should be ignored while draining. > > Regards; > Yann. Index: server/mpm/event/event.c === --- server/mpm/event/event.c (revision 1915442) +++ server/mpm/event/event.c (working copy) @@ -174,7 +174,7 @@ static int had_healthy_child = 0; static volatile int dying = 0; static volatile int workers_may_exit = 0; static volatile int start_thread_may_exit = 0; -static volatile int listener_may_exit = 0; +static volatile apr_uint32_t listener_may_exit = 0; static int listener_is_wakeable = 0;/* Pollset supports APR_POLLSET_WAKEABLE */ static int num_listensocks = 0; static apr_int32_t conns_this_child;/* MaxConnectionsPerChild, only access @@ -481,8 +481,7 @@ static void disable_listensocks(void) static void enable_listensocks(void) { int i; -if (listener_may_exit -|| apr_atomic_cas32(_disabled, 0, 1) != 1) { +if (dying || apr_atomic_cas32(_disabled, 0, 1) != 1) { return; } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00457) @@ -575,8 +574,7 @@ static void wakeup_listener(void) ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, "wake up listener%s", listener_may_exit ? " again" : ""); -listener_may_exit = 1; -disable_listensocks(); +apr_atomic_cas32(_may_exit, 1, 0); /* Unblock the listener if it's poll()ing */ if (event_pollset && listener_is_wakeable) { @@ -1184,12 +1182,9 @@ read_request: cs->pub.state = CONN_STATE_READ_REQUEST_LINE; goto read_request; } -else if (!listener_may_exit) { +else { cs->pub.state = CONN_STATE_CHECK_REQUEST_LINE_READABLE; } -else { -cs->pub.state = CONN_STATE_LINGER; -} } if (cs->pub.state == CONN_STATE_CHECK_REQUEST_LINE_READABLE) { @@ -1256,18 +1251,21 @@ static void check_infinite_requests(void) } } -static int close_listeners(int *closed) +static int close_listeners(void) { ap_log_error(APLOG_MARK, APLOG_TRACE6, 0, ap_server_conf, "clos%s listeners (connection_count=%u)", - *closed ? "ed" : "ing", apr_atomic_read32(_count)); -if (!*closed) { + dying ? "ed" : "ing", apr_atomic_read32(_count)); +if (!dying) { int i; +dying = 1; /* once */ + +ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, + "XXX: closing"); + ap_close_listeners_ex(my_bucket->listeners); -*closed = 1; /* once */ -dying = 1; ap_scoreboard_image->parent[ap_child_slot].quiescing = 1; for (i = 0; i < threads_per_child; ++i) { ap_update_child_status_from_indexes(ap_child_slot, i, @@ -1654,8 +1652,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_ proc_info *ti = dummy; int process_slot = ti->pslot; struct process_score *ps = ap_get_scoreboard_process(process_slot); -int closed = 0; -int have_idle_worker = 0; +int have_idle_worker = 0, exiting = 0; apr_time_t last_log; last_log = apr_time_now(); @@ -1678,8 +1675,8 @@ static void * APR_THREAD_FUNC listener_thread(apr_ if (conns_this_child <= 0) check_infinite_requests(); -if (listener_may_exit) { -int first_close = close_listeners(); +if (exiting) { +int first_close = close_listeners(); if (terminate_mode == ST_UNGRACEFUL || apr_atomic_read32(_count) == 0) @@ -1710,7 +1707,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_ apr_atomic_read32(keepalive_q->total), apr_atomic_read32(_count), apr_atomic_read32(_count)); -if (dying) { +if (exiting) { ap_log_error(APLOG_MARK, APLOG_TRACE6, 0, ap_server_conf, "%u/%u workers shutdown", apr_atomic_read32(_shutdown), @@ -1792,6 +1789,13 @@ static void * APR_THREAD_FUNC listener_thread(apr_ } num = 0; } +if (!exiting && apr_atomic_read32(_may_exit)) { +
Re: [users@httpd] graceful-stop closes established connections without response
On Tue, Jan 30, 2024 at 4:37 AM Sherrard Burton wrote: > > i was going to add some debugging lines, but when i took a quick look at > the patch, i wasn't clear on which sections of the code i should be > guaranteed to hit. can you be so kind as to send an updated patch with > some gratuitous logging in the appropriate sections so that there will > be positive affirmation that the patch has (or hasn't) been applied and > is falling into the expected sections? Sure, here is a v2 (which also includes a fix w.r.t. v1). Regards; Yann. Index: server/mpm/event/event.c === --- server/mpm/event/event.c (revision 1915442) +++ server/mpm/event/event.c (working copy) @@ -174,7 +174,7 @@ static int had_healthy_child = 0; static volatile int dying = 0; static volatile int workers_may_exit = 0; static volatile int start_thread_may_exit = 0; -static volatile int listener_may_exit = 0; +static volatile apr_uint32_t listener_may_exit = 0; static int listener_is_wakeable = 0;/* Pollset supports APR_POLLSET_WAKEABLE */ static int num_listensocks = 0; static apr_int32_t conns_this_child;/* MaxConnectionsPerChild, only access @@ -481,8 +481,7 @@ static void disable_listensocks(void) static void enable_listensocks(void) { int i; -if (listener_may_exit -|| apr_atomic_cas32(_disabled, 0, 1) != 1) { +if (dying || apr_atomic_cas32(_disabled, 0, 1) != 1) { return; } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00457) @@ -575,8 +574,7 @@ static void wakeup_listener(void) ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, "wake up listener%s", listener_may_exit ? " again" : ""); -listener_may_exit = 1; -disable_listensocks(); +apr_atomic_cas32(_may_exit, 1, 0); /* Unblock the listener if it's poll()ing */ if (event_pollset && listener_is_wakeable) { @@ -1184,12 +1182,9 @@ read_request: cs->pub.state = CONN_STATE_READ_REQUEST_LINE; goto read_request; } -else if (!listener_may_exit) { +else { cs->pub.state = CONN_STATE_CHECK_REQUEST_LINE_READABLE; } -else { -cs->pub.state = CONN_STATE_LINGER; -} } if (cs->pub.state == CONN_STATE_CHECK_REQUEST_LINE_READABLE) { @@ -1256,18 +1251,21 @@ static void check_infinite_requests(void) } } -static int close_listeners(int *closed) +static int close_listeners(void) { ap_log_error(APLOG_MARK, APLOG_TRACE6, 0, ap_server_conf, "clos%s listeners (connection_count=%u)", - *closed ? "ed" : "ing", apr_atomic_read32(_count)); -if (!*closed) { + dying ? "ed" : "ing", apr_atomic_read32(_count)); +if (!dying) { int i; +dying = 1; /* once */ + +ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, + "XXX: closing"); + ap_close_listeners_ex(my_bucket->listeners); -*closed = 1; /* once */ -dying = 1; ap_scoreboard_image->parent[ap_child_slot].quiescing = 1; for (i = 0; i < threads_per_child; ++i) { ap_update_child_status_from_indexes(ap_child_slot, i, @@ -1654,8 +1652,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_ proc_info *ti = dummy; int process_slot = ti->pslot; struct process_score *ps = ap_get_scoreboard_process(process_slot); -int closed = 0; -int have_idle_worker = 0; +int have_idle_worker = 0, exiting = 0; apr_time_t last_log; last_log = apr_time_now(); @@ -1678,8 +1675,8 @@ static void * APR_THREAD_FUNC listener_thread(apr_ if (conns_this_child <= 0) check_infinite_requests(); -if (listener_may_exit) { -int first_close = close_listeners(); +if (exiting) { +int first_close = close_listeners(); if (terminate_mode == ST_UNGRACEFUL || apr_atomic_read32(_count) == 0) @@ -1710,7 +1707,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_ apr_atomic_read32(keepalive_q->total), apr_atomic_read32(_count), apr_atomic_read32(_count)); -if (dying) { +if (exiting) { ap_log_error(APLOG_MARK, APLOG_TRACE6, 0, ap_server_conf, "%u/%u workers shutdown", apr_atomic_read32(_shutdown), @@ -1792,6 +1789,10 @@ static void * APR_THREAD_FUNC listener_thread(apr_ } num = 0; } +if (!exiting && apr_atomic_read32(_may_exit)) { +ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, + "XXX: may exit (%d, %d)", rc, num); +} if (APLOGtrace7(ap_server_conf)) { now =