Skip to content

Commit 5e12797

Browse files
Merge pull request #1955 from tesshuflower/tls_profile_compliance_rsync-tls
OpenShift Tls profile compliance rsync-tls
2 parents 9b3704c + 310ab13 commit 5e12797

6 files changed

Lines changed: 123 additions & 5 deletions

File tree

internal/controller/mover/rsynctls/mover.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import (
4242
volsyncv1alpha1 "github.com/backube/volsync/api/v1alpha1"
4343
vserrors "github.com/backube/volsync/internal/controller/errors"
4444
"github.com/backube/volsync/internal/controller/mover"
45+
"github.com/backube/volsync/internal/controller/platform"
4546
"github.com/backube/volsync/internal/controller/utils"
4647
"github.com/backube/volsync/internal/controller/volumehandler"
4748
)
@@ -500,6 +501,25 @@ func (m *Mover) ensureJob(ctx context.Context, dataPVC *corev1.PersistentVolumeC
500501
})
501502
}
502503

504+
tlsProfileSpec, err := platform.GetTLSProfileIfOpenShift(ctx, m.client, logger)
505+
if err != nil {
506+
return err
507+
}
508+
if tlsProfileSpec != nil {
509+
// TLS ProfileSpec is set, this is OpenShift
510+
minTLSVersion, err := platform.ParseTLSVersionForStunnelPSK(tlsProfileSpec.MinTLSVersion)
511+
if err != nil {
512+
logger.Error(err, "Unable to parse minTLSVersion from TLSProfileSpec",
513+
"tlsProfileSpec.MinTLSVersion", tlsProfileSpec.MinTLSVersion)
514+
return err
515+
}
516+
// Set env var to set sslVersionMin in sTunnel
517+
podSpec.Containers[0].Env = append(podSpec.Containers[0].Env, corev1.EnvVar{
518+
Name: "SSL_VERSION_MIN",
519+
Value: minTLSVersion,
520+
})
521+
}
522+
503523
// Run mover in debug mode if required
504524
podSpec.Containers[0].Env = utils.AppendDebugMoverEnvVar(m.owner, podSpec.Containers[0].Env)
505525

internal/controller/platform/properties_test.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@ import (
3232
"k8s.io/apimachinery/pkg/types"
3333
"k8s.io/apimachinery/pkg/util/yaml"
3434
"k8s.io/client-go/kubernetes/scheme"
35+
"k8s.io/utils/ptr"
3536
ctrl "sigs.k8s.io/controller-runtime"
3637
"sigs.k8s.io/controller-runtime/pkg/client"
37-
"sigs.k8s.io/controller-runtime/pkg/log/zap"
38+
"sigs.k8s.io/controller-runtime/pkg/config"
3839
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
3940

4041
ocpconfigv1 "github.com/openshift/api/config/v1"
@@ -44,8 +45,6 @@ import (
4445
"github.com/backube/volsync/internal/controller/utils"
4546
)
4647

47-
var logger = zap.New(zap.UseDevMode(true), zap.WriteTo(GinkgoWriter))
48-
4948
var _ = Describe("A cluster w/o StorageContextConstraints", func() {
5049
BeforeEach(func() {
5150
// Make sure we're not caching properties
@@ -361,11 +360,21 @@ var _ = Describe("A cluster w/ StorageContextConstraints", func() {
361360
cancelFuncCalled := false
362361

363362
var testManagerCancel context.CancelFunc
363+
var testManagerDone chan struct{}
364364

365365
BeforeEach(func() {
366+
cancelFuncCalled = false
367+
366368
testManager, err := ctrl.NewManager(cfg, ctrl.Options{
367369
Scheme: scheme.Scheme,
368370
Metrics: metricsserver.Options{BindAddress: "0"},
371+
// Set global controller options - allow dup controller names as these don't get unregistered
372+
// when tearing down the manager - so our TLSSecurityProfileWatcher will fail the dup name
373+
// validation (we don't have the option of setting the name ourself) when this runs multiple
374+
// times for separate tests
375+
Controller: config.Controller{
376+
SkipNameValidation: ptr.To(true),
377+
},
369378
})
370379
Expect(err).ToNot(HaveOccurred())
371380

@@ -384,15 +393,20 @@ var _ = Describe("A cluster w/ StorageContextConstraints", func() {
384393

385394
var testManagerCtx context.Context
386395
testManagerCtx, testManagerCancel = context.WithCancel(ctx)
387-
// Start the manager
396+
testManagerDone = make(chan struct{})
397+
// Start the manager; wait for Start to return in AfterEach so the next test
398+
// does not register another controller (with same name) while this mgr is running
388399
go func() {
389400
defer GinkgoRecover()
390-
err = testManager.Start(testManagerCtx)
401+
defer close(testManagerDone)
402+
err := testManager.Start(testManagerCtx)
391403
Expect(err).NotTo(HaveOccurred())
404+
logger.Info("test manager stopped")
392405
}()
393406
})
394407
AfterEach(func() {
395408
testManagerCancel()
409+
Eventually(testManagerDone, maxWait, interval).Should(BeClosed())
396410
})
397411

398412
It("Should not call cancel function if no TLS profile change", func() {

internal/controller/platform/suite_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ var testEnv *envtest.Environment
4747
var cancel context.CancelFunc
4848
var ctx context.Context
4949

50+
var logger = zap.New(zap.UseDevMode(true), zap.WriteTo(GinkgoWriter))
51+
5052
func TestAPIs(t *testing.T) {
5153
RegisterFailHandler(Fail)
5254

internal/controller/platform/tlsprofile.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package platform
1919

2020
import (
2121
"context"
22+
"fmt"
2223

2324
"crypto/tls"
2425

@@ -92,3 +93,24 @@ func InitTLSSecurityProfileWatcherWithManager(mgr manager.Manager,
9293

9394
return tlsProfileWatcher.SetupWithManager(mgr)
9495
}
96+
97+
// Parse string version of ocpconfigv1.TLSProtocolVersion in format that others (such as stunnel) can interpret
98+
// For our stunnel implementation (used by rsync-tls) we will use tlsv1.3 as the minimum
99+
// unless something higher is picked (in future). No need to support older TLS versions
100+
// as we're not allowing generic clients, only our rsync-tls client from replicationsource
101+
func ParseTLSVersionForStunnelPSK(version ocpconfigv1.TLSProtocolVersion) (string, error) {
102+
switch version {
103+
case ocpconfigv1.VersionTLS10:
104+
fallthrough
105+
case ocpconfigv1.VersionTLS11:
106+
fallthrough
107+
case ocpconfigv1.VersionTLS12:
108+
fallthrough
109+
case ocpconfigv1.VersionTLS13:
110+
return "TLSv1.3", nil
111+
// Unfortunately this will need an update when new TLS versions become
112+
// available so we can convert to the format expected by openssl/stunnel
113+
default:
114+
return "", fmt.Errorf("unknown TLS version: %s", version)
115+
}
116+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
Copyright 2026 The VolSync authors.
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU Affero General Public License as published
6+
by the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU Affero General Public License for more details.
13+
14+
You should have received a copy of the GNU Affero General Public License
15+
along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
18+
package platform
19+
20+
import (
21+
. "github.com/onsi/ginkgo/v2"
22+
. "github.com/onsi/gomega"
23+
24+
ocpconfigv1 "github.com/openshift/api/config/v1"
25+
)
26+
27+
var _ = Describe("Test tlsprofile helper funcs", func() {
28+
Describe("ParseTLSVersionForStunnelPSK - should use TLS 1.3", func() {
29+
It("Should convert the golang TLS version string into one sTunnel/openssl can use", func() {
30+
tlsVersion, err := ParseTLSVersionForStunnelPSK(ocpconfigv1.VersionTLS10)
31+
Expect(err).NotTo(HaveOccurred())
32+
Expect(tlsVersion).To(Equal("TLSv1.3"))
33+
34+
tlsVersion, err = ParseTLSVersionForStunnelPSK(ocpconfigv1.VersionTLS11)
35+
Expect(err).NotTo(HaveOccurred())
36+
Expect(tlsVersion).To(Equal("TLSv1.3"))
37+
38+
tlsVersion, err = ParseTLSVersionForStunnelPSK(ocpconfigv1.VersionTLS12)
39+
Expect(err).NotTo(HaveOccurred())
40+
Expect(tlsVersion).To(Equal("TLSv1.3"))
41+
42+
tlsVersion, err = ParseTLSVersionForStunnelPSK(ocpconfigv1.VersionTLS13)
43+
Expect(err).NotTo(HaveOccurred())
44+
Expect(tlsVersion).To(Equal("TLSv1.3"))
45+
46+
// Unknown version
47+
var fakeVersion ocpconfigv1.TLSProtocolVersion = "VersionNotExisting"
48+
tlsVersion, err = ParseTLSVersionForStunnelPSK(fakeVersion)
49+
Expect(err).To(HaveOccurred())
50+
Expect(tlsVersion).To(Equal(""))
51+
})
52+
})
53+
})

mover-rsync-tls/server.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ if [[ ! -d $TARGET ]] && ! test -b $BLOCK_TARGET; then
7272
exit 1
7373
fi
7474

75+
if [[ -n ${SSL_VERSION_MIN} ]]; then
76+
# Append sslVersionMin to stunnel conf
77+
SSLVERSIONMIN="sslVersionMin = ${SSL_VERSION_MIN}"
78+
fi
79+
7580
if [[ -d $TARGET ]]; then
7681
##############################
7782
## Filesystem volume, use rsync
@@ -131,6 +136,7 @@ syslog = no
131136
[rsync]
132137
ciphers = PSK
133138
PSKsecrets = $PSK_FILE
139+
$SSLVERSIONMIN
134140
; Port to listen for incoming connections from remote
135141
accept = $STUNNEL_LISTEN_PORT
136142
; We are the server
@@ -176,6 +182,7 @@ syslog = no
176182
[diskrsync]
177183
ciphers = PSK
178184
PSKsecrets = $PSK_FILE
185+
$SSLVERSIONMIN
179186
; Port to listen for incoming connections from remote
180187
accept = $STUNNEL_LISTEN_PORT
181188
; We are the server

0 commit comments

Comments
 (0)