Skip to content

Commit d340c42

Browse files
committed
fix: improve rsync tunnel reliability and mover pod security
Fix race-free premature-exit detection in rsync daemon using channel-based select instead of signal probing. Increase max connections from 4 to 8. Add munge symlinks and SELinux label/numeric ID sync for rsync passes. Add SCC rules for mover pods. Assisted-by: Claude <noreply@anthropic.com> Signed-off-by: Rakshith R <rar@redhat.com>
1 parent ab80c70 commit d340c42

3 files changed

Lines changed: 38 additions & 13 deletions

File tree

cmd/manager/openshift/mover_scc.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ runAsUser:
2626
type: RunAsAny # allow mover to run as root
2727
seLinuxContext:
2828
type: MustRunAs
29+
seLinuxOptions:
30+
type: spc_t
2931
seccompProfiles:
3032
- runtime/default
3133
supplementalGroups:

internal/worker/tunnel/rsync.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type RsyncDaemon struct {
3737
port string
3838
cmd *exec.Cmd
3939
logger logr.Logger
40+
exitCh chan error
4041
}
4142

4243
// NewRsyncDaemon creates a new RsyncDaemon manager.
@@ -79,12 +80,24 @@ func (r *RsyncDaemon) Start() error {
7980
)
8081
}
8182

83+
// Background goroutine reaps the daemon process,
84+
// preventing zombies if it exits before Stop().
85+
r.exitCh = make(chan error, 1)
86+
go func() {
87+
r.exitCh <- r.cmd.Wait()
88+
}()
89+
8290
// Wait for rsync daemon to initialize
8391
time.Sleep(2 * time.Second)
8492

85-
// Check if it's still running
86-
if err := r.cmd.Process.Signal(syscall.Signal(0)); err != nil {
87-
return fmt.Errorf("rsync daemon exited prematurely: %w", err)
93+
// Check if it exited during startup
94+
select {
95+
case err := <-r.exitCh:
96+
if err != nil {
97+
return fmt.Errorf("rsync daemon exited prematurely: %w", err)
98+
}
99+
return fmt.Errorf("rsync daemon exited prematurely")
100+
default:
88101
}
89102

90103
r.logger.Info(
@@ -112,15 +125,22 @@ func (r *RsyncDaemon) Stop() {
112125
)
113126
}
114127

115-
_ = r.cmd.Wait()
128+
if r.exitCh != nil {
129+
// Non-blocking: exitCh may already be drained if
130+
// daemon exited prematurely during startup check.
131+
select {
132+
case <-r.exitCh:
133+
default:
134+
}
135+
}
116136
}
117137

118138
func (r *RsyncDaemon) generateConfig() error {
119139
conf := `# Rsync daemon configuration
120140
uid = root
121141
gid = root
122142
use chroot = no
123-
max connections = 4
143+
max connections = 8
124144
pid file = /tmp/rsyncd.pid
125145
log file = /tmp/rsyncd.log
126146
lock file = /tmp/rsyncd.lock
@@ -130,6 +150,7 @@ lock file = /tmp/rsyncd.lock
130150
comment = Data volume
131151
read only = false
132152
list = yes
153+
munge symlinks = no
133154
# No authentication - stunnel already provides PSK authentication
134155
`
135156
return os.WriteFile(rsyncdConf, []byte(conf), 0600)

internal/worker/tunnel/stunnel_test.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@ import (
2828
"github.com/stretchr/testify/require"
2929
)
3030

31+
const testDestAddr = "10.0.0.1"
32+
3133
func TestWriteSourceConfig_UsesUDS(t *testing.T) {
3234
// Clean up any existing config from previous tests
3335
_ = os.RemoveAll(stunnelDir)
3436

3537
cfg := StunnelConfig{
36-
WorkerType: "source",
37-
DestinationAddress: "10.0.0.1",
38+
WorkerType: workerTypeSource,
39+
DestinationAddress: testDestAddr,
3840
DestinationPort: "8000",
3941
EnableRsyncTunnel: true,
4042
RsyncPort: "8873",
@@ -115,8 +117,8 @@ func TestWriteDestinationConfig_StaysTCP(t *testing.T) {
115117

116118
func TestSourceGRPCAddress_ReturnsUnixScheme(t *testing.T) {
117119
cfg := StunnelConfig{
118-
WorkerType: "source",
119-
DestinationAddress: "10.0.0.1",
120+
WorkerType: workerTypeSource,
121+
DestinationAddress: testDestAddr,
120122
DestinationPort: "8000",
121123
}
122124

@@ -135,8 +137,8 @@ func TestCheckSocket_ExistingSocket(t *testing.T) {
135137
socketPath := filepath.Join(tempDir, "test.sock")
136138

137139
cfg := StunnelConfig{
138-
WorkerType: "source",
139-
DestinationAddress: "10.0.0.1",
140+
WorkerType: workerTypeSource,
141+
DestinationAddress: testDestAddr,
140142
DestinationPort: "8000",
141143
}
142144

@@ -169,8 +171,8 @@ func TestCheckSocket_NonexistentSocket(t *testing.T) {
169171
socketPath := filepath.Join(tempDir, "nonexistent.sock")
170172

171173
cfg := StunnelConfig{
172-
WorkerType: "source",
173-
DestinationAddress: "10.0.0.1",
174+
WorkerType: workerTypeSource,
175+
DestinationAddress: testDestAddr,
174176
DestinationPort: "8000",
175177
}
176178

0 commit comments

Comments
 (0)