mirror of
https://github.com/SynologyOpenSource/synology-csi.git
synced 2026-08-19 11:36:33 +00:00
The DSM WebAPI client disabled TLS certificate verification for all
HTTPS connections (InsecureSkipVerify: true), then sent the configured
DSM account and password to the endpoint. An attacker able to intercept,
redirect, or impersonate the DSM HTTPS endpoint could therefore obtain
the DSM credentials stored in the CSI client config / Kubernetes secret
(CWE-295 Improper Certificate Validation, OWASP A3:2017 Sensitive Data Exposure)
TLS certificate verification is now enabled by default. The client trusts
the system CA pool, so certificates signed by a public CA work without
extra configuration. Three optional client-info fields are added:
- tlsCACert: PEM CA cert to trust (for DSM self-signed certs);
merged with the system CA pool.
- tlsServerName: override the name checked during verification,
e.g. when connecting by IP.
- insecureSkipVerify: explicit opt-out that restores the old behavior;
logs a warning on every connection.
The new fields are propagated through all DSM construction sites
(service, synocli, and the HA GetAnotherController path).
BREAKING CHANGE: deployments using `https: true` against a DSM with a
self-signed certificate (the DSM default) will fail to connect after
upgrade until they set `tlsCACert`, `tlsServerName`, or (discouraged)
`insecureSkipVerify: true`.
Adds TLS tests covering default-reject, valid-CA accept, wrong-CA reject,
insecureSkipVerify opt-in, and the tlsServerName DNS-SAN scenarios.
153 lines
3.8 KiB
Go
153 lines
3.8 KiB
Go
/*
|
|
* Copyright 2021 Synology Inc.
|
|
*/
|
|
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"github.com/spf13/cobra"
|
|
"github.com/SynologyOpenSource/synology-csi/pkg/dsm/common"
|
|
"github.com/SynologyOpenSource/synology-csi/pkg/dsm/webapi"
|
|
)
|
|
|
|
var https = false
|
|
var port = -1
|
|
var tlsCACertFile = ""
|
|
var tlsServerName = ""
|
|
var insecureSkipVerify = false
|
|
|
|
var cmdDsm = &cobra.Command{
|
|
Use: "dsm",
|
|
Short: "dsm",
|
|
Long: `dsm`,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
cmd.Help()
|
|
},
|
|
}
|
|
|
|
var cmdDsmLogin = &cobra.Command{
|
|
Use: "login <ip> <username> <password>",
|
|
Short: "login dsm",
|
|
Args: cobra.MinimumNArgs(3),
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
var defaultPort = 5000
|
|
if https {
|
|
defaultPort = 5001
|
|
}
|
|
if port != -1 {
|
|
defaultPort = port
|
|
}
|
|
|
|
var tlsCACert string
|
|
if tlsCACertFile != "" {
|
|
pem, err := os.ReadFile(tlsCACertFile)
|
|
if err != nil {
|
|
fmt.Printf("Failed to read TLS CA cert file: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
tlsCACert = string(pem)
|
|
}
|
|
|
|
dsmApi := &webapi.DSM{
|
|
Ip: args[0],
|
|
Username: args[1],
|
|
Password: args[2],
|
|
Port: defaultPort,
|
|
Https: https,
|
|
TLSCACert: tlsCACert,
|
|
TLSServerName: tlsServerName,
|
|
InsecureSkipVerify: insecureSkipVerify,
|
|
}
|
|
|
|
err := dsmApi.Login()
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
os.Exit(1)
|
|
}
|
|
},
|
|
}
|
|
|
|
var cmdDsmList = &cobra.Command{
|
|
Use: "list",
|
|
Short: "list DSM infos in the config file",
|
|
Args: cobra.MinimumNArgs(0),
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
fmt.Printf("List DSM infos in: %s\n", ConfigFile)
|
|
dsms, err := ListDsms(-1)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
fmt.Printf("%-2s\t%-32s\t%-6s\t%-32s\t%s\n", "id", "Host", "Port", "Username", "Https")
|
|
for i, client := range dsms {
|
|
fmt.Printf("%-2d\t%-32s\t", i, client.Ip)
|
|
fmt.Printf("%-6d\t", client.Port)
|
|
fmt.Printf("%-32s\t", client.Username)
|
|
fmt.Printf("%v\t", client.Https)
|
|
fmt.Printf("\n")
|
|
}
|
|
},
|
|
}
|
|
|
|
// Always get the first client from ClientInfo for synocli testing
|
|
func LoginDsmForTest(id int) (*webapi.DSM, error) {
|
|
dsms, err := ListDsms(id)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to list dsms: %v", err)
|
|
}
|
|
|
|
if err := dsms[0].Login(); err != nil {
|
|
return nil, fmt.Errorf("Failed to login to DSM: [%s]. err: %v", dsms[0].Ip, err)
|
|
}
|
|
|
|
fmt.Printf("Login DSM: %s\n", dsms[0].Ip)
|
|
return dsms[0], nil
|
|
}
|
|
|
|
func ListDsms(id int) ([]*webapi.DSM, error) {
|
|
var dsms []*webapi.DSM
|
|
|
|
info, err := common.LoadConfig(ConfigFile)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to read config[%s]: %v", ConfigFile, err)
|
|
}
|
|
if id < -1 || id >= len(info.Clients) {
|
|
return nil, fmt.Errorf("Invalid dsmId: %d", id)
|
|
}
|
|
|
|
for i, _ := range info.Clients {
|
|
if id != -1 && id != i {
|
|
continue
|
|
}
|
|
|
|
dsm := &webapi.DSM{
|
|
Ip: info.Clients[i].Host,
|
|
Port: info.Clients[i].Port,
|
|
Username: info.Clients[i].Username,
|
|
Password: info.Clients[i].Password,
|
|
Https: info.Clients[i].Https,
|
|
TLSCACert: info.Clients[i].TLSCACert,
|
|
TLSServerName: info.Clients[i].TLSServerName,
|
|
InsecureSkipVerify: info.Clients[i].InsecureSkipVerify,
|
|
}
|
|
dsms = append(dsms, dsm)
|
|
}
|
|
|
|
if len(dsms) == 0 {
|
|
return nil, fmt.Errorf("No client in config")
|
|
}
|
|
return dsms, nil
|
|
}
|
|
|
|
func init() {
|
|
cmdDsm.AddCommand(cmdDsmLogin)
|
|
cmdDsm.AddCommand(cmdDsmList)
|
|
|
|
cmdDsmLogin.PersistentFlags().BoolVar(&https, "https", false, "Use HTTPS to login DSM")
|
|
cmdDsmLogin.PersistentFlags().IntVarP(&port, "port", "p", -1, "Use assigned port to login DSM")
|
|
cmdDsmLogin.PersistentFlags().StringVar(&tlsCACertFile, "tlsCACertFile", "", "Path to TLS CA certificate PEM file")
|
|
cmdDsmLogin.PersistentFlags().StringVar(&tlsServerName, "tlsServerName", "", "Override TLS server name for certificate validation")
|
|
cmdDsmLogin.PersistentFlags().BoolVar(&insecureSkipVerify, "insecureSkipVerify", false, "Skip TLS certificate verification (insecure)")
|
|
} |