Reputation: 179
I have a rather long/complicated TCL/Expect script that runs an openssl ocsp
responder in both CGI and Daemon modes. The script is complete and functional but I want to implement a multi-threaded OCSP daemon using openssl ocsp -multi <#> ...
and it is showing some strange behavior.
Here is the most succinct example code I could come up with (saved as ocsp.tcl
):
#!/usr/bin/tclsh
#usage (run as root):
#./ocsp.tcl [multi] [sudo] [internal] [debug] [trace] [cmdtrace] [null] [raw] [rawinit] [forever]
package require Expect; #make sure expect is loaded
if {[lsearch $::argv trace]!=-1} { strace 5 }; #arg - walk through expect script
if {[lsearch $::argv debug]!=-1} { exp_debug 1 }; #arg - prompt through expect script
if {[lsearch $::argv internal]!=-1} { exp_internal 1 }; #arg - show expect match logic
if {[lsearch $::argv null]!=-1} { remove_nulls 0 }; #arg - keep nulls in expect
if {[lsearch $::argv rawinit]!=-1} { set stty_init raw }; #arg - spawn with raw terminal
if {[lsearch $::argv forever]!=-1} { set timeout -1 }; #arg - expect doesn't time out
if {[lsearch $::argv sudo]!=-1} { lappend cmd sudo -n }; #arg - sudo before command
if {[lsearch $::argv cmdtrace]!=-1} { lappend cmd strace }; #arg - trace openssl execution
lappend cmd openssl ocsp -index index.txt -CA ca.crt; #create command line
lappend cmd -rsigner ocsp.crt -rkey ocsp.key -nmin 300; #create command line
lappend cmd -text -ignore_err -port 2650 -timeout 5; #create command line
if {[lsearch $::argv multi]!=-1} { lappend cmd -multi 5 }; #arg - launch 5 child responders
puts "Command: $::argv0 $::argv"; #debug - launch arguments
puts -nonewline "TCL: ${::tcl_patchLevel}\t"; #debug - TCL 8.6.10
puts -nonewline "Expect: [exp_version]\t"; #debug - Expect 5.45.4
puts "OpenSSL: [exec -- openssl version]"; #debug - OpenSSL 1.1.1g 21 Apr 2020
puts "OS: [exec -- lsb_release -ds] [exec -- uname -rm]"; #debug - Ubuntu 20.04.1 LTS 5.4.0-42-lowlatency x86_64
puts -nonewline "remove_nulls: [remove_nulls]\t"; #debug - remove nulls
puts -nonewline "match_max: [match_max]\t"; #debug - match_max
puts "parity: [parity]\tdebug: [exp_debug]"; #debug - parity, exp_debug
lappend ps ps -o pid=,ppid=,tt=,cmd= -C openssl; #ps output syntax
#-----------------------------------------------------------
spawn {*}$cmd; #run and show openssl command
#-----------------------------------------------------------
if {[lsearch $::argv raw]!=-1} { stty raw }; #arg - raw output to expect
puts -nonewline "spawn_id: ${spawn_id}\t"; #debug - spawn_id
puts -nonewline "user_spawn_id: ${::user_spawn_id}\t"; #debug - user_spawn_id
puts -nonewline "error_spawn_id: ${::error_spawn_id}\t"; #debug - error_spawn_id
puts "tty_spawn_id: ${::tty_spawn_id}"; #debug - tty_spawn_id
catch {puts "-----Spawn-----\n[exec -- {*}$ps]"}; #debug - openssl processes
expect {
-re "Enter pass phrase for .*:" {
catch {puts "\n-----Pre-Send-----\n[exec -- {*}$ps]"}; #debug - openssl processes
exp_send "<password>\n"; #input the key password
catch {puts "-----Post-Send-----\n[exec -- {*}$ps]"}; #debug - openssl processes
exp_continue; #restart this expect loop
}
timeout { puts "Expect: Timeout"; exp_continue }; #debug - timeout
full_buffer { puts "Expect: Full Buffer"; exp_continue }; #debug - full buffer
#null { puts "Expect: Null"; exp_continue }; #debug - null (??SEGFAULTS??)
eof { puts "Expect: We Should Never Get Here" }; #!!PROGRAM SHOULD NEVER REACH EOF!!
}
lassign [wait] pid spawnid os err; #get spawn exit details
puts -nonewline "pid: ${pid}\tspawn_id: ${spawnid}\t"; #show exit status
puts "os_error: ${os}\terror_num: ${err}"; #show exit status
This script can be run with any options in any order like so: ./ocsp.tcl [multi] [sudo] [internal] [debug] [trace] [cmdtrace] [null] [raw] [rawinit] [forever]
If everything goes correctly the script shouldn't terminate. Instead it should just end up repeating:
Expect: Timeout
Expect: Timeout
Expect: Timeout
Obviously some of the options will warp this output (debug, internal, trace, cmdtrace), but with the same end result.
To summarize:
openssl ocsp -index index.txt -CA ca.crt -rsigner ocsp.crt -rkey ocsp.key -nmin 300 -text -ignore_err -port 2650 -timeout 5
multi
command works: openssl ocsp -index index.txt -CA ca.crt -rsigner ocsp.crt -rkey ocsp.key -nmin 300 -text -ignore_err -port 2650 -timeout 5 -multi 5
&
) works./ocsp.tcl [sudo] [internal] [debug] [trace] [cmdtrace] [null] [raw] [rawinit] [forever]
multi
with or without the other options fails before hitting Expect: Timeout
: ./ocsp.tcl multi [internal] [debug] [trace] [cmdtrace] [null] [raw] [rawinit] [forever]
multi
with sudo
with or without the other options works: ./ocsp.tcl multi sudo [internal] [debug] [trace] [cmdtrace] [null] [raw] [rawinit] [forever]
When the script fails, tail -f /var/log/syslog
outputs ocsp[<pid>]: fatal: error detaching from parent process group: Operation not permitted
. A look at apps/lib/http_server.c in the OpenSSL source shows that this is message is caused by a failed setpgid
. setpgid describes Operation not permitted as:
EPERM - An attempt was made to move a process into a process group in a different session, or to change the process group ID of one of the children of the calling process and the child was in a different session, or to change the process group ID of a session leader (setpgid(), setpgrp()).
Where I am at:
set stty_init [raw/cooked]; stty [-]raw
)tclsh
or expect
issue, But it may be an Expect issue, specifically with spawn
.openssl ocsp -multi <#> ..,
oddity/oversight/bug.spawn
creates/uses the pty/tty/sessionspawn -nottyinit -nottycopy
in any combination does nothing noticeable.Right now I could just admit defeat and spawn sudo ...
but it is hackish.
I'm currently trying to supplement/replace spawn with:
set pty_pid [spawn -noecho -pty]; #just create a pty? or do we make one for std[in|out|err]?
stty raw -echo < $spawn_out(slave,name); #don't echo stdin on the new pty?
#stty raw > $spawn_out(slave,name); #raw stdout on the new pty? THIS KILLS CTRL-C
set spawn_pid [exec -- {*}$cmd > $spawn_out(slave,name) < $spawn_out(slave,name) &] #redirect stin/out to the pty?
close -slave; #no idea
...but as you can tell by the snippet I'm not sure what specifically to do with these pty(s).
Any help getting -multi
working would be greatly appreciated.
Edits:
Upvotes: 1
Views: 325
Reputation: 1
I got around this problem when running in a container, using the initialization system to correctly handle process forks, I used tini, but most likely another one will do.
Perhaps the behavior of openssl-ocsp
has changed from the version in which the author of the question experimented earlier, but without this trick I got the same error.
A fragmentary example of a Containerfile/Dockerfile with the versions used:
FROM docker.io/library/alpine
…
apk add --no-cache \
tini==0.19.0-r3 \
openssl==3.3.2-r1 \
…
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["openssl", "ocsp", "-multi", …]
Upvotes: 0