Skip to content

Commit dc7b11b

Browse files
authored
server: fix TLS credentials not passed to tiflow in old architecture mode (#3720)
close #3718
1 parent b9a04c4 commit dc7b11b

File tree

2 files changed

+135
-4
lines changed

2 files changed

+135
-4
lines changed

cmd/cdc/server/server.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,12 @@ func isNewArchEnabled(o *options) bool {
297297
}
298298

299299
func runTiFlowServer(o *options, cmd *cobra.Command) error {
300+
// Populate security credentials from CLI flags before marshaling to JSON.
301+
// In old architecture mode, complete() is not called, so we need to transfer
302+
// TLS credentials (--ca, --cert, --key) to serverConfig.Security here.
303+
// Without this, the Security struct is empty and tiflow uses HTTP instead of HTTPS.
304+
o.serverConfig.Security = o.getCredential()
305+
300306
cfgData, err := json.Marshal(o.serverConfig)
301307
if err != nil {
302308
return errors.Trace(err)
@@ -312,10 +318,6 @@ func runTiFlowServer(o *options, cmd *cobra.Command) error {
312318
oldOptions.ServerConfig = &oldCfg
313319
oldOptions.ServerPdAddr = strings.Join(o.pdEndpoints, ",")
314320
oldOptions.ServerConfigFilePath = o.serverConfigFilePath
315-
oldOptions.CaPath = o.caPath
316-
oldOptions.CertPath = o.certPath
317-
oldOptions.KeyPath = o.keyPath
318-
oldOptions.AllowedCertCN = o.allowedCertCN
319321

320322
return tiflowServer.Run(&oldOptions, cmd)
321323
}

cmd/cdc/server/server_test.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Copyright 2024 PingCAP, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package server
15+
16+
import (
17+
"encoding/json"
18+
"testing"
19+
20+
tiflowConfig "github.com/pingcap/tiflow/pkg/config"
21+
"github.com/stretchr/testify/require"
22+
)
23+
24+
func TestRunTiFlowServerPopulatesSecurityConfig(t *testing.T) {
25+
// This test verifies that TLS credentials from CLI flags are properly
26+
// transferred to serverConfig.Security before being passed to tiflow.
27+
// See https://github.com/pingcap/ticdc/issues/3718
28+
29+
o := newOptions()
30+
o.caPath = "/path/to/ca.crt"
31+
o.certPath = "/path/to/server.crt"
32+
o.keyPath = "/path/to/server.key"
33+
o.allowedCertCN = "cn1,cn2"
34+
35+
// Verify that before calling getCredential, serverConfig.Security is empty
36+
require.Empty(t, o.serverConfig.Security.CAPath)
37+
require.Empty(t, o.serverConfig.Security.CertPath)
38+
require.Empty(t, o.serverConfig.Security.KeyPath)
39+
40+
// Simulate what runTiFlowServer does: populate Security from CLI flags
41+
o.serverConfig.Security = o.getCredential()
42+
43+
// Verify Security is now populated
44+
require.Equal(t, "/path/to/ca.crt", o.serverConfig.Security.CAPath)
45+
require.Equal(t, "/path/to/server.crt", o.serverConfig.Security.CertPath)
46+
require.Equal(t, "/path/to/server.key", o.serverConfig.Security.KeyPath)
47+
require.Equal(t, []string{"cn1", "cn2"}, o.serverConfig.Security.CertAllowedCN)
48+
49+
// Verify that JSON marshaling preserves the Security config
50+
cfgData, err := json.Marshal(o.serverConfig)
51+
require.NoError(t, err)
52+
53+
var oldCfg tiflowConfig.ServerConfig
54+
err = json.Unmarshal(cfgData, &oldCfg)
55+
require.NoError(t, err)
56+
57+
// This is the critical assertion: tiflow's ServerConfig.Security
58+
// should have the TLS credentials after unmarshaling
59+
require.Equal(t, "/path/to/ca.crt", oldCfg.Security.CAPath)
60+
require.Equal(t, "/path/to/server.crt", oldCfg.Security.CertPath)
61+
require.Equal(t, "/path/to/server.key", oldCfg.Security.KeyPath)
62+
require.Equal(t, []string{"cn1", "cn2"}, oldCfg.Security.CertAllowedCN)
63+
}
64+
65+
func TestGetCredential(t *testing.T) {
66+
testCases := []struct {
67+
name string
68+
caPath string
69+
certPath string
70+
keyPath string
71+
allowedCertCN string
72+
expectedCAPath string
73+
expectedCert string
74+
expectedKey string
75+
expectedCertCN []string
76+
}{
77+
{
78+
name: "all fields populated",
79+
caPath: "/ca/path",
80+
certPath: "/cert/path",
81+
keyPath: "/key/path",
82+
allowedCertCN: "test-cn",
83+
expectedCAPath: "/ca/path",
84+
expectedCert: "/cert/path",
85+
expectedKey: "/key/path",
86+
expectedCertCN: []string{"test-cn"},
87+
},
88+
{
89+
name: "multiple CNs",
90+
allowedCertCN: "cn1,cn2,cn3",
91+
expectedCertCN: []string{"cn1", "cn2", "cn3"},
92+
},
93+
{
94+
name: "empty CN",
95+
allowedCertCN: "",
96+
expectedCertCN: nil,
97+
},
98+
}
99+
100+
for _, tc := range testCases {
101+
t.Run(tc.name, func(t *testing.T) {
102+
o := newOptions()
103+
o.caPath = tc.caPath
104+
o.certPath = tc.certPath
105+
o.keyPath = tc.keyPath
106+
o.allowedCertCN = tc.allowedCertCN
107+
108+
cred := o.getCredential()
109+
110+
require.Equal(t, tc.expectedCAPath, cred.CAPath)
111+
require.Equal(t, tc.expectedCert, cred.CertPath)
112+
require.Equal(t, tc.expectedKey, cred.KeyPath)
113+
require.Equal(t, tc.expectedCertCN, cred.CertAllowedCN)
114+
})
115+
}
116+
}
117+
118+
func TestNewOptionsDefaultSecurity(t *testing.T) {
119+
o := newOptions()
120+
121+
// serverConfig should be initialized with default values
122+
require.NotNil(t, o.serverConfig)
123+
require.NotNil(t, o.serverConfig.Security)
124+
125+
// Security should have empty paths by default
126+
require.Empty(t, o.serverConfig.Security.CAPath)
127+
require.Empty(t, o.serverConfig.Security.CertPath)
128+
require.Empty(t, o.serverConfig.Security.KeyPath)
129+
}

0 commit comments

Comments
 (0)