A reload reads the file into a new configuration, allocated from the
defaults, and prepares its services without starting them. Only if the
whole file is read without an error does it come into force: the old
configuration's services stop accepting, the new ones start. On an
error everything prepared is dropped and the old configuration, which
never stopped, stays in force.
Each service holds its configuration until it is freed, after its last
client; a configuration no longer in force is released when the last
holder lets go (confhold/confput). freeconf() now only releases what a
configuration holds, touching nothing global.
What runs without a service of its own reads the configuration in
force, runconf, rather than conf, which a reload points at the one
being read: the resolver, which holds it while it waits (a client
resolves with its own service's), the log, the thread pool, the admin
pages, which walk the services under config_mutex, and the signal
handlers.
The users table, havelog and the resolver choice are per configuration
now, the resolver applied when the configuration comes into force, so
a refused file leaves the users as they were. The log file, the
configuration file name, the string table, daemon mode and the filter
list belong to the process and are handed from one configuration to
the next. Settings left out of a new file go back to their defaults,
where before they kept the old values.
The gap between the old and the new listening sockets remains: a
connection queued on an old service's socket when it closes is reset.
Handing the socket over is the next phase.
A test case reloads a broken file (nothing changes, alice still gets
in, bob from the refused file does not) and a good one (bob only, the
service on its new port, the old one closed, a download through the
old service finishing whole). 40 reloads, a third refused, under load
show no memory errors under AddressSanitizer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A service keeps the configuration it was started with (srvparam.config)
and its threads and its clients' read it there: limits and counters
(alwaysauth, the limiter), RADIUS, dnspr's name servers, and the checks
for a reload in the accept loop and in sockmap(). The configuration is
still reloaded in place, so it is the current one for now; this is what
lets a reload build a new one while the old one still serves.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
conf is now a pointer to the current configuration rather than the
structure itself: at first confboot, which exists before main() runs, as
plugins and the standalone services use it too; later configurations
will start from confdefaults. Both are made from confinit.h, the one list
of initial values. The timeouts are an array of the configuration
instead of a pointer to a global one. Nothing behaves differently yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What a numbered counter counted now belongs to the process: a counter
rule points to the value of its number, created the first time the
number is met (from the counter file, if one is open) and shared by the
rules of that number in every configuration loaded. A reload no longer
resets it when there is no counter file, and once configurations can
outlive one another, clients of the old and the new one count into the
same value. An unnumbered counter, and those LdapPlugin makes, keep a
value of their own as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
They are the state of the process, not configuration: conf.time (now
curtime), timetoexit and needreload are globals of their own. Plugins
reach needreload through a pointer added at the end of struct
pluginlink; LdapPlugin uses it. The extern for a paused global, never
defined, goes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
strongauth() compared short CL passwords over pwl_table.recsize-1 bytes,
reading past the client password buffer (strdup of pwlen+1) and rejecting
valid passwords whenever trailing heap bytes were non-zero. Zero-pad the
provided password to the record width and compare in constant time, like
the long-password BLAKE2 path.
Fixes#1254
'auth cacheacl ...' is identical to 'auth cache ...' except ACL is not checked for cached authentication. dstaddr, dstport, dsthost and dstoper (operation) are intended to be used with cacheacl. For example
authcache user,ip,password,dstaddr 600
auth cacheacl iponly strong
allows user to access destination ip without ACL/password revalidation if he has cached attempt to the same ip from the same ip with the same username and password.
srvaddr, srvport are useful to only match with cached attempts to the same `internal` address / service port.
- authcache switched to use hashtables, size parameter added
- overflow fixed on hashinit
- hashtable prefers new values on insert if table is full
- hashtable is able to compact/grow
example:
plugin SSLPlugin.ld.so ssl_plugin
allow user1
parent 1000 http 1.1.1.1 1111
allow user2
parent 1000 https 2.2.2.2 2222
ssl_client_mode 3
ssl_client
proxy
With ssl_client_mode 3 TLS is only handshaked for https parent type and is not handshaked for http parent.
ssl_client_mode
0 (default) - handshake immediately after connect() (with first parent or with destination if there is no parent)
1 - handshake with destination server (handshake after connection via parents is established)
2 - handshake after data channel is established (e.g. after CONNECT)
Added:
-H option - expect HAProxy proxy v1 header, e.g. `proxy -H`
parent ha type - send HAProxy proxy v1 header (must be last in redirection), e.g.
allow *
parent 1000 ha
parent 1000 proxy 1.2.3.4 3128
socks