CPU usage - stunnel waking up every 100 ms
I noticed that stunnel is constantly consuming 30% of one of my CPU cores. Looking at it with strace, I see that it is frequently (every 100 milliseconds?) waking up from a poll() system call, calling getpid() a couple of times and going back to sleep. [pid 3498] poll([{fd=6, events=POLLIN|POLLRDHUP}], 1, 100) = 0 (Timeout) [pid 3498] getpid() = 3494 [pid 3498] getpid() = 3494 [pid 3498] poll([{fd=6, events=POLLIN|POLLRDHUP}], 1, 100) = 0 (Timeout) [pid 3498] getpid() = 3494 [pid 3498] getpid() = 3494 [pid 3498] poll([{fd=6, events=POLLIN|POLLRDHUP}], 1, 100) = 0 (Timeout) [pid 3498] getpid() = 3494 [pid 3498] getpid() = 3494 [pid 3498] poll([{fd=6, events=POLLIN|POLLRDHUP}], 1, 100) = 0 (Timeout) [pid 3498] getpid() = 3494 [pid 3498] getpid() = 3494 AFAICT, this is a pipe that stunnel is using to talk to itself (maybe to communicate between threads)? # ls -l /proc/3498/fd total 0 lrwx------. 1 stunnel stunnel 64 Aug 23 10:12 0 -> /dev/null lrwx------. 1 stunnel stunnel 64 Aug 23 10:12 1 -> /dev/null lrwx------. 1 stunnel stunnel 64 Aug 23 10:12 10 -> 'socket:[37674]' lrwx------. 1 stunnel stunnel 64 Aug 23 10:12 11 -> 'socket:[37675]' lrwx------. 1 stunnel stunnel 64 Aug 23 10:12 12 -> 'socket:[37676]' lrwx------. 1 stunnel stunnel 64 Aug 23 10:12 2 -> /dev/null lr-x------. 1 stunnel stunnel 64 Aug 23 10:12 4 -> 'pipe:[38360]' l-wx------. 1 stunnel stunnel 64 Aug 23 10:12 5 -> 'pipe:[38360]' lr-x------. 1 stunnel stunnel 64 Aug 23 10:12 6 -> 'pipe:[38361]' l-wx------. 1 stunnel stunnel 64 Aug 23 10:12 7 -> 'pipe:[38361]' lrwx------. 1 stunnel stunnel 64 Aug 23 10:12 8 -> 'socket:[37672]' lrwx------. 1 stunnel stunnel 64 Aug 23 10:12 9 -> 'socket:[37673]' Is this expected behavior? Anything that can be done about it? Environment: ============ stunnel 5.80 on x86_64-redhat-linux-gnu platform Compiled/running with OpenSSL 3.5.7 9 Jun 2026 Threading:PTHREAD Sockets:POLL,IPv6,SYSTEMD TLS:ENGINE,FIPS,OCSP,PSK,SNI,DTLS Global options: fips = no RNDbytes = 1024 RNDfile = /dev/urandom RNDoverwrite = yes Service-level options: ciphers = PROFILE=SYSTEM (with "fips = yes") ciphers = PROFILE=SYSTEM (with "fips = no") ciphersuites = TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256 (with TLSv1.3) curves = P-256:P-521:P-384 (with "fips = yes") curves = X25519MLKEM768:X25519:P-256:X448:P-521:P-384 (with "fips = no") debug = daemon.notice logId = sequential options = NO_SSLv2 options = NO_SSLv3 securityLevel = 2 sessionCacheSize = 1000 sessionCacheTimeout = 300 seconds stack = 131072 bytes TIMEOUTbusy = 300 seconds TIMEOUTclose = 60 seconds TIMEOUTconnect = 10 seconds TIMEOUTidle = 43200 seconds TIMEOUTocsp = 5 seconds verify = none stunnel.conf: ============= debug = warning [switch2-frontend] accept = 0.0.0.0:8402 CAfile = /etc/ipa/ca.crt cert = /etc/pki/tls/certs/stunnel/switch2.crt key = /tmp/keys/switch2.key connect = 127.255.255.254:8082 [switch2-backend] client = yes accept = 127.255.255.254:8082 connect = 172.31.4.2:443 verifyChain = yes checkHost = switch2.penurio.us CAfile = /etc/pki/tls/certs/stunnel/switch-ca.crt sslVersion = TLSv1 ciphers = DHE-RSA-AES256-SHA options = ALLOW_UNSAFE_LEGACY_RENEGOTIATION securityLevel = 0 OCSPrequire = no OCSPaia = no [switch4-frontend] accept = 0.0.0.0:8404 CAfile = /etc/ipa/ca.crt cert = /etc/pki/tls/certs/stunnel/switch4.crt key = /tmp/keys/switch4.key connect = 127.255.255.254:8084 [switch4-backend] client = yes accept = 127.255.255.254:8084 connect = 172.31.4.7:443 verifyChain = yes checkHost = switch4.penurio.us CAfile = /etc/pki/tls/certs/stunnel/switch-ca.crt sslVersion = TLSv1 ciphers = DHE-RSA-AES256-SHA options = ALLOW_UNSAFE_LEGACY_RENEGOTIATION securityLevel = 0 OCSPrequire = no OCSPaia = no -- ======================================================================== If your user interface is intuitive in retrospect ... it isn't intuitive ========================================================================
Hi Ian, A few years ago, the Red Hat/Fedora stunnel packaging was changed to use the system-wide crypto policy as the default cipher configuration: https://gitlab.com/redhat/centos-stream/rpms/stunnel/-/blob/c10s/stunnel-5.6... This third-party patch changes stunnel's default cipher list to PROFILE=SYSTEM. Since the system crypto policy enables finite-field DHE cipher suites, stunnel's existing code detects that DHE is available in your server-mode frontend sections and generates temporary 2048-bit DH parameters. The parameters are generated when the per-day worker starts and regenerated every 24 hours. The 100 ms poll() calls you are seeing come from a delay in stunnel's OpenSSL DH-generation progress callback. The delay is there to throttle CPU usage during DH parameter generation. Your explicit DHE-RSA-AES256-SHA settings in the backend sections are unrelated to this CPU usage, since those sections run in client mode and therefore do not generate DH parameters locally. Finite-field DHE cipher suites are generally not recommended for new TLS configurations in 2026. ECDHE, or TLS 1.3 key exchange, should normally be preferred. Your backend configuration appears to require DHE for compatibility with the legacy switches, but unless you also need DHE on the frontend connections, I would disable it there. Best regards, Mike On 8/23/26 17:25, Ian Pilcher via stunnel-users wrote:
I noticed that stunnel is constantly consuming 30% of one of my CPU cores. Looking at it with strace, I see that it is frequently (every 100 milliseconds?) waking up from a poll() system call, calling getpid() a couple of times and going back to sleep.
[pid 3498] poll([{fd=6, events=POLLIN|POLLRDHUP}], 1, 100) = 0 (Timeout) [pid 3498] getpid() = 3494 [pid 3498] getpid() = 3494 [pid 3498] poll([{fd=6, events=POLLIN|POLLRDHUP}], 1, 100) = 0 (Timeout) [pid 3498] getpid() = 3494 [pid 3498] getpid() = 3494 [pid 3498] poll([{fd=6, events=POLLIN|POLLRDHUP}], 1, 100) = 0 (Timeout) [pid 3498] getpid() = 3494 [pid 3498] getpid() = 3494 [pid 3498] poll([{fd=6, events=POLLIN|POLLRDHUP}], 1, 100) = 0 (Timeout) [pid 3498] getpid() = 3494 [pid 3498] getpid() = 3494
AFAICT, this is a pipe that stunnel is using to talk to itself (maybe to communicate between threads)?
# ls -l /proc/3498/fd total 0 lrwx------. 1 stunnel stunnel 64 Aug 23 10:12 0 -> /dev/null lrwx------. 1 stunnel stunnel 64 Aug 23 10:12 1 -> /dev/null lrwx------. 1 stunnel stunnel 64 Aug 23 10:12 10 -> 'socket:[37674]' lrwx------. 1 stunnel stunnel 64 Aug 23 10:12 11 -> 'socket:[37675]' lrwx------. 1 stunnel stunnel 64 Aug 23 10:12 12 -> 'socket:[37676]' lrwx------. 1 stunnel stunnel 64 Aug 23 10:12 2 -> /dev/null lr-x------. 1 stunnel stunnel 64 Aug 23 10:12 4 -> 'pipe:[38360]' l-wx------. 1 stunnel stunnel 64 Aug 23 10:12 5 -> 'pipe:[38360]' lr-x------. 1 stunnel stunnel 64 Aug 23 10:12 6 -> 'pipe:[38361]' l-wx------. 1 stunnel stunnel 64 Aug 23 10:12 7 -> 'pipe:[38361]' lrwx------. 1 stunnel stunnel 64 Aug 23 10:12 8 -> 'socket:[37672]' lrwx------. 1 stunnel stunnel 64 Aug 23 10:12 9 -> 'socket:[37673]'
Is this expected behavior? Anything that can be done about it?
Environment: ============
stunnel 5.80 on x86_64-redhat-linux-gnu platform Compiled/running with OpenSSL 3.5.7 9 Jun 2026 Threading:PTHREAD Sockets:POLL,IPv6,SYSTEMD TLS:ENGINE,FIPS,OCSP,PSK,SNI,DTLS
Global options: fips = no RNDbytes = 1024 RNDfile = /dev/urandom RNDoverwrite = yes
Service-level options: ciphers = PROFILE=SYSTEM (with "fips = yes") ciphers = PROFILE=SYSTEM (with "fips = no") ciphersuites = TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256 (with TLSv1.3) curves = P-256:P-521:P-384 (with "fips = yes") curves = X25519MLKEM768:X25519:P-256:X448:P-521:P-384 (with "fips = no") debug = daemon.notice logId = sequential options = NO_SSLv2 options = NO_SSLv3 securityLevel = 2 sessionCacheSize = 1000 sessionCacheTimeout = 300 seconds stack = 131072 bytes TIMEOUTbusy = 300 seconds TIMEOUTclose = 60 seconds TIMEOUTconnect = 10 seconds TIMEOUTidle = 43200 seconds TIMEOUTocsp = 5 seconds verify = none
stunnel.conf: =============
debug = warning
[switch2-frontend] accept = 0.0.0.0:8402 CAfile = /etc/ipa/ca.crt cert = /etc/pki/tls/certs/stunnel/switch2.crt key = /tmp/keys/switch2.key connect = 127.255.255.254:8082
[switch2-backend] client = yes accept = 127.255.255.254:8082 connect = 172.31.4.2:443 verifyChain = yes checkHost = switch2.penurio.us CAfile = /etc/pki/tls/certs/stunnel/switch-ca.crt sslVersion = TLSv1 ciphers = DHE-RSA-AES256-SHA options = ALLOW_UNSAFE_LEGACY_RENEGOTIATION securityLevel = 0 OCSPrequire = no OCSPaia = no
[switch4-frontend] accept = 0.0.0.0:8404 CAfile = /etc/ipa/ca.crt cert = /etc/pki/tls/certs/stunnel/switch4.crt key = /tmp/keys/switch4.key connect = 127.255.255.254:8084
[switch4-backend] client = yes accept = 127.255.255.254:8084 connect = 172.31.4.7:443 verifyChain = yes checkHost = switch4.penurio.us CAfile = /etc/pki/tls/certs/stunnel/switch-ca.crt sslVersion = TLSv1 ciphers = DHE-RSA-AES256-SHA options = ALLOW_UNSAFE_LEGACY_RENEGOTIATION securityLevel = 0 OCSPrequire = no OCSPaia = no
Hi Ian, hi Mike,
On 24. Aug 2026, at 13:49, Michał Trojnara <Michal.Trojnara@stunnel.org> wrote:
A few years ago, the Red Hat/Fedora stunnel packaging was changed to use the system-wide crypto policy as the default cipher configuration:
https://gitlab.com/redhat/centos-stream/rpms/stunnel/-/blob/c10s/stunnel-5.6...
This third-party patch changes stunnel's default cipher list to PROFILE=SYSTEM. Since the system crypto policy enables finite-field DHE cipher suites, stunnel's existing code detects that DHE is available in your server-mode frontend sections and generates temporary 2048-bit DH parameters.
Thanks for bringing that to my attention. This is indeed unfortunate, since we enable FFDHE system-wide in the assumption that implementations use RFC 7919 (i.e., the well-known FFDHE parameters from its Appendix A) rather than generating new parameters at runtime. We can’t disable FFDHE now in existing releases, because it would break compatibility. I brought this up with my team, and we agree that we’ll probably drop FFDHE from the DEFAULT policy in RHEL 11 completely.
The parameters are generated when the per-day worker starts and regenerated every 24 hours. The 100 ms poll() calls you are seeing come from a delay in stunnel's OpenSSL DH-generation progress callback. The delay is there to throttle CPU usage during DH parameter generation.
For now, if you want to keep FFDHE enabled and/or not opt-out of crypto-policies with your stunnel config, I’d recommend following this instruction from the stunnel manpage:
Alternatively, it is possible to specify static DH parameters in the certificate file, which disables generating temporary DH parameters:
openssl dhparam 2048 >> stunnel.pem
Instead of generating a new set of parameters using `openssl dhparam`, I would recommend grabbing a PEM-encoded copy of the RFC 7919 ones from https://github.com/internetstandards/dhe_groups/tree/26f703fe44e0b4b03840e3d... These also have the advantage that they’ll definitely work in FIPS mode, which is not a given for locally generated groups. I’ll look into maybe replacing the generation with the static RFC 7919 groups instead downstream, but no promises on that one. Could you share which RHEL version you’re using? It looks like 9 or 10 based on the OpenSSL version, but both ship the same, so it’s impossible to tell from your provided output. Clemens -- Clemens Lang RHEL Crypto Team Red Hat
On 8/26/26 2:24 PM, Clemens Lang via stunnel-users wrote:
For now, if you want to keep FFDHE enabled and/or not opt-out of crypto-policies with your stunnel config, I’d recommend following this instruction from the stunnel manpage:
Alternatively, it is possible to specify static DH parameters in the certificate file, which disables generating temporary DH parameters:
openssl dhparam 2048 >> stunnel.pem
Instead of generating a new set of parameters using `openssl dhparam`, I would recommend grabbing a PEM-encoded copy of the RFC 7919 ones from
https://github.com/internetstandards/dhe_groups/ tree/26f703fe44e0b4b03840e3dc7eaacf8257f7a184
Thanks Clemens! (Sorry about the slow response.)
Could you share which RHEL version you’re using? It looks like 9 or 10 based on the OpenSSL version, but both ship the same, so it’s impossible to tell from your provided output.
I'm on Fedora 44. openssl-3.5.7-2.fc44.x86_64 stunnel-5.80-1.fc44.x86_64 -- ======================================================================== If your user interface is intuitive in retrospect ... it isn't intuitive ========================================================================
Hi Ian,
On 31. Aug 2026, at 15:21, Ian Pilcher <arequipeno@gmail.com> wrote:
Could you share which RHEL version you’re using? It looks like 9 or 10 based on the OpenSSL version, but both ship the same, so it’s impossible to tell from your provided output.
I'm on Fedora 44.
openssl-3.5.7-2.fc44.x86_64 stunnel-5.80-1.fc44.x86_64
Ah, that’s a lot easier to change for me. I’ve filed https://bugzilla.redhat.com/show_bug.cgi?id=2526328 to track this in Fedora. -- Clemens Lang RHEL Crypto Team Red Hat
On 8/31/26 16:45, Clemens Lang wrote:
Ah, that’s a lot easier to change for me. I’ve filedhttps://bugzilla.redhat.com/show_bug.cgi?id=2526328 to track this in Fedora.
Using the RFC 7919 groups would certainly avoid the CPU cost of generating fresh DH parameters, but there is a security tradeoff. Finite-field DH permits substantial discrete-logarithm precomputation that depends only on the group modulus. Once that precomputation has been performed for a particular group, it can be reused against subsequent DH exchanges using the same group. This was demonstrated by the Logjam research: David Adrian et al., "Imperfect Forward Secrecy: How Diffie-Hellman Fails in Practice", CCS 2015. The concern is also explicitly recognized by IETF standards. RFC 8268 states: "Using a fixed set of Diffie-Hellman parameters makes them a high value target for pre-computation. Generating additional sets of primes to be used, or moving to larger values mitigates this issue." RFC 4419 gives essentially the same rationale for allowing SSH servers to generate new DH groups, specifically citing the risk of extensive precomputation against fixed groups. RFC 7919 itself also acknowledges the issue in Section 8.5, noting that "Using the same groups in multiple protocols increases the value for an attacker with the resources to crack any single group." Therefore, replacing stunnel's periodically generated parameters with a globally shared RFC 7919 group would not be security-neutral. With sufficiently large groups, particularly ffdhe3072 and above, the precomputation attack is believed to be impractical, so RFC 7919 makes a reasonable engineering tradeoff. Nevertheless, periodically generating unique DH parameters does provide additional protection against this class of attack. Best regards, Mike
On Tuesday, 1 September 2026 18:32:57 CEST, Michał Trojnara via stunnel-users wrote:
On 8/31/26 16:45, Clemens Lang wrote: Ah, that’s a lot easier to change for me. I’ve filed https://bugzilla.redhat.com/show_bug.cgi?id=2526328 to track this in Fedora. Using the RFC 7919 groups would certainly avoid the CPU cost of generating fresh DH parameters, but there is a security tradeoff.
Finite-field DH permits substantial discrete-logarithm precomputation that depends only on the group modulus. Once that precomputation has been performed for a particular group, it can be reused against subsequent DH exchanges using the same group. This was demonstrated by the Logjam research: David Adrian et al., "Imperfect Forward Secrecy: How Diffie-Hellman Fails in Practice", CCS 2015.
The concern is also explicitly recognized by IETF standards. RFC 8268 states: "Using a fixed set of Diffie-Hellman parameters makes them a high value target for pre-computation. Generating additional sets of primes to be used, or moving to larger values mitigates this issue." RFC 4419 gives essentially the same rationale for allowing SSH servers to generate new DH groups, specifically citing the risk of extensive precomputation against fixed groups.
RFC 7919 itself also acknowledges the issue in Section 8.5, noting that "Using the same groups in multiple protocols increases the value for an attacker with the resources to crack any single group."
Therefore, replacing stunnel's periodically generated parameters with a globally shared RFC 7919 group would not be security-neutral. With sufficiently large groups, particularly ffdhe3072 and above, the precomputation attack is believed to be impractical, so RFC 7919 makes a reasonable engineering tradeoff. Nevertheless, periodically generating unique DH parameters does provide additional protection against this class of attack.
Except that the precomputation is still more expensive than targetted attack for the same size of parameters. And the arguments you quoted are about 1024 bit DH, not 2048. If static DH parameters were such a big issue we wouldn't have them as the _only_ option in TLS 1.3. -- Kind regards, Alicja Kario Principal Quality Engineer, RHEL Crypto team Web: www.cz.redhat.com Red Hat Czech s.r.o., Purkyňova 115, 612 00, Brno, Czech Republic
Hi Alicja, On 9/2/26 11:06, Alicja Kario wrote:
Except that the precomputation is still more expensive than targetted attack for the same size of parameters.
Yes, but that is also why widespread reuse of the same group matters. For a unique or frequently replaced modulus, expensive modulus-specific precomputation has relatively little opportunity to pay for itself. For a standardized group used by a very large population of servers and implementations, the same precomputation can potentially be amortized over many targets. This is the concern described by the Logjam research. The concrete attacks demonstrated there involved substantially smaller groups, but the underlying property is not specific to 1024-bit DH.
And the arguments you quoted are about 1024 bit DH, not 2048.
The demonstrated attacks were, but the standards arguments are more general: * RFC 8268 explicitly states: "Using a fixed set of Diffie-Hellman parameters makes them a high value target for pre-computation. Generating additional sets of primes to be used, or moving to larger values mitigates this issue." * RFC 7919 similarly notes that using the same groups in multiple protocols increases their value to an attacker with sufficient resources to attack a group. Of course, this does not mean that ffdhe2048 is currently known to be practically breakable. _However, I don't think cryptographic design should be based solely on attacks that have already been demonstrated to be practical._ Security margins are intended to account for future improvements in cryptanalysis, hardware, and attacker capabilities. Periodically replacing the public DH group parameters removes the possibility of amortizing modulus-specific precomputation over a long-lived or globally shared group. That is a real hardening property, even if its practical value at 2048 bits is difficult to quantify today.
If static DH parameters were such a big issue we wouldn't have them as the _only_ option in TLS 1.3.
Agreed, for finite-field DHE. RFC 7919 groups are an entirely legitimate way to implement FFDHE, and TLS 1.3 demonstrates that this tradeoff is considered acceptable. The per-connection DH key shares are still ephemeral, while the public group parameters are fixed. I don't think that implies, however, that periodically replacing those public group parameters provides no additional protection. RFC 7919 represents a reasonable standardized security/performance tradeoff; it is not necessarily the strongest hardening an implementation can reasonably provide. More importantly, I think this discussion is drifting away from the original problem. I don't consider finite-field DHE a desirable default for stunnel in 2026. The expensive DH parameter generation became noticeable because Fedora/RHEL changed the cipher-suite configuration and enabled DHE cipher suites that upstream stunnel would otherwise have little reason to use. Given that, I would prefer not to change upstream stunnel further merely to reduce the cost introduced by that downstream configuration. If DHE is explicitly enabled, stunnel's periodically regenerated group parameters are an intentional additional hardening measure. Upstream stunnel is not required to limit its security measures to the defaults chosen by OpenSSL or by a distribution, particularly when the additional protection addresses a known cryptographic property. There is therefore no claim that RFC 7919 groups are insecure. They are standardized and considered secure. I simply don't see a compelling reason to remove an existing upstream hardening measure in order to optimize a cipher-suite configuration that I would not recommend in the first place. For Fedora/RHEL, I think the cleaner solution is to reconsider whether enabling finite-field DHE by default is still useful. Avoiding that downstream deviation would also avoid the regression without requiring another downstream or upstream change to compensate for it. Best regards, Mike
participants (4)
-
Alicja Kario -
Clemens Lang -
Ian Pilcher -
Michał Trojnara