Skip to content

Commit

Permalink
fix: handle incorrect cluster RESTconfig without panic (argoproj#20150)
Browse files Browse the repository at this point in the history
Signed-off-by: cef <moncef.abboud95@gmail.com>
Signed-off-by: Adrian Aneci <aneci@adobe.com>
  • Loading branch information
CefBoud authored and adriananeci committed Dec 4, 2024
1 parent 6d07bf1 commit ad551ac
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 22 deletions.
4 changes: 3 additions & 1 deletion cmd/argocd/commands/admin/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,9 @@ argocd admin cluster kubeconfig https://cluster-api-url:6443 /path/to/output/kub

cluster, err := db.NewDB(namespace, settings.NewSettingsManager(ctx, kubeclientset, namespace), kubeclientset).GetCluster(ctx, serverUrl)
errors.CheckError(err)
err = kube.WriteKubeConfig(cluster.RawRestConfig(), namespace, output)
rawConfig, err := cluster.RawRestConfig()
errors.CheckError(err)
err = kube.WriteKubeConfig(rawConfig, namespace, output)
errors.CheckError(err)
},
}
Expand Down
6 changes: 5 additions & 1 deletion controller/appcontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -1163,7 +1163,11 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic
logCtx.Infof("Resource entries removed from undefined cluster")
return nil
}
config := metrics.AddMetricsTransportWrapper(ctrl.metricsServer, app, cluster.RESTConfig())
clusterRESTConfig, err := cluster.RESTConfig()
if err != nil {
return err
}
config := metrics.AddMetricsTransportWrapper(ctrl.metricsServer, app, clusterRESTConfig)

if app.CascadedDeletion() {
logCtx.Infof("Deleting resources")
Expand Down
12 changes: 10 additions & 2 deletions controller/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,10 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
return nil, fmt.Errorf("error getting value for %v: %w", settings.RespectRBAC, err)
}

clusterCacheConfig := cluster.RESTConfig()
clusterCacheConfig, err := cluster.RESTConfig()
if err != nil {
return nil, fmt.Errorf("error getting cluster RESTConfig: %w", err)
}
// Controller dynamically fetches all resource types available on the cluster
// using a discovery API that may contain deprecated APIs.
// This causes log flooding when managing a large number of clusters.
Expand Down Expand Up @@ -826,7 +829,12 @@ func (c *liveStateCache) handleModEvent(oldCluster *appv1.Cluster, newCluster *a

var updateSettings []clustercache.UpdateSettingsFunc
if !reflect.DeepEqual(oldCluster.Config, newCluster.Config) {
updateSettings = append(updateSettings, clustercache.SetConfig(newCluster.RESTConfig()))
newClusterRESTConfig, err := newCluster.RESTConfig()
if err == nil {
updateSettings = append(updateSettings, clustercache.SetConfig(newClusterRESTConfig))
} else {
log.Errorf("error getting cluster REST config: %v", err)
}
}
if !reflect.DeepEqual(oldCluster.Namespaces, newCluster.Namespaces) {
updateSettings = append(updateSettings, clustercache.SetNamespaces(newCluster.Namespaces))
Expand Down
23 changes: 20 additions & 3 deletions controller/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,12 @@ func (m *appStateManager) getResourceOperations(server string) (kube.ResourceOpe
if err != nil {
return nil, nil, fmt.Errorf("error getting cluster: %w", err)
}
ops, cleanup, err := m.kubectl.ManageResources(cluster.RawRestConfig(), clusterCache.GetOpenAPISchema())

rawConfig, err := cluster.RawRestConfig()
if err != nil {
return nil, nil, fmt.Errorf("error getting cluster REST config: %w", err)
}
ops, cleanup, err := m.kubectl.ManageResources(rawConfig, clusterCache.GetOpenAPISchema())
if err != nil {
return nil, nil, fmt.Errorf("error creating kubectl ResourceOperations: %w", err)
}
Expand Down Expand Up @@ -217,8 +222,20 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
return
}

rawConfig := clst.RawRestConfig()
restConfig := metrics.AddMetricsTransportWrapper(m.metricsServer, app, clst.RESTConfig())
rawConfig, err := clst.RawRestConfig()
if err != nil {
state.Phase = common.OperationError
state.Message = err.Error()
return
}

clusterRESTConfig, err := clst.RESTConfig()
if err != nil {
state.Phase = common.OperationError
state.Message = err.Error()
return
}
restConfig := metrics.AddMetricsTransportWrapper(m.metricsServer, app, clusterRESTConfig)

resourceOverrides, err := m.settingsMgr.GetResourceOverrides()
if err != nil {
Expand Down
19 changes: 11 additions & 8 deletions pkg/apis/application/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -3100,7 +3100,7 @@ func SetK8SConfigDefaults(config *rest.Config) error {
}

// RawRestConfig returns a go-client REST config from cluster that might be serialized into the file using kube.WriteKubeConfig method.
func (c *Cluster) RawRestConfig() *rest.Config {
func (c *Cluster) RawRestConfig() (*rest.Config, error) {
var config *rest.Config
var err error
if c.Server == KubernetesInternalAPIServerAddr && env.ParseBoolFromEnv(EnvVarFakeInClusterConfig, false) {
Expand Down Expand Up @@ -3184,22 +3184,25 @@ func (c *Cluster) RawRestConfig() *rest.Config {
}
}
if err != nil {
panic(fmt.Sprintf("Unable to create K8s REST config: %v", err))
return nil, fmt.Errorf("Unable to create K8s REST config: %w", err)
}
config.Timeout = K8sServerSideTimeout
config.QPS = K8sClientConfigQPS
config.Burst = K8sClientConfigBurst
return config
return config, nil
}

// RESTConfig returns a go-client REST config from cluster with tuned throttling and HTTP client settings.
func (c *Cluster) RESTConfig() *rest.Config {
config := c.RawRestConfig()
err := SetK8SConfigDefaults(config)
func (c *Cluster) RESTConfig() (*rest.Config, error) {
config, err := c.RawRestConfig()
if err != nil {
panic(fmt.Sprintf("Unable to apply K8s REST config defaults: %v", err))
return nil, fmt.Errorf("Unable to get K8s RAW REST config: %w", err)
}
return config
err = SetK8SConfigDefaults(config)
if err != nil {
return nil, fmt.Errorf("Unable to apply K8s REST config defaults: %w", err)
}
return config, nil
}

// UnmarshalToUnstructured unmarshals a resource representation in JSON to unstructured data
Expand Down
6 changes: 5 additions & 1 deletion server/application/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -1289,7 +1289,11 @@ func (s *Server) getApplicationClusterConfig(ctx context.Context, a *appv1.Appli
if err != nil {
return nil, fmt.Errorf("error getting cluster: %w", err)
}
config := clst.RESTConfig()
config, err := clst.RESTConfig()
if err != nil {
return nil, fmt.Errorf("error getting cluster REST config: %w", err)
}

return config, err
}

Expand Down
6 changes: 5 additions & 1 deletion server/application/terminal.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ func (s *terminalHandler) getApplicationClusterRawConfig(ctx context.Context, a
if err != nil {
return nil, err
}
return clst.RawRestConfig(), nil
rawConfig, err := clst.RawRestConfig()
if err != nil {
return nil, err
}
return rawConfig, nil
}

type GetSettingsFunc func() (*settings.ArgoCDSettings, error)
Expand Down
24 changes: 20 additions & 4 deletions server/cluster/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,12 @@ func (s *Server) Create(ctx context.Context, q *cluster.ClusterCreateRequest) (*
return nil, err
}
c := q.Cluster
serverVersion, err := s.kubectl.GetServerVersion(c.RESTConfig())
clusterRESTConfig, err := c.RESTConfig()
if err != nil {
return nil, err
}

serverVersion, err := s.kubectl.GetServerVersion(clusterRESTConfig)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -313,9 +318,13 @@ func (s *Server) Update(ctx context.Context, q *cluster.ClusterUpdateRequest) (*
}
q.Cluster = c
}
clusterRESTConfig, err := q.Cluster.RESTConfig()
if err != nil {
return nil, err
}

// Test the token we just created before persisting it
serverVersion, err := s.kubectl.GetServerVersion(q.Cluster.RESTConfig())
serverVersion, err := s.kubectl.GetServerVersion(clusterRESTConfig)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -406,7 +415,10 @@ func (s *Server) RotateAuth(ctx context.Context, q *cluster.ClusterQuery) (*clus
for _, server := range servers {
logCtx := log.WithField("cluster", server)
logCtx.Info("Rotating auth")
restCfg := clust.RESTConfig()
restCfg, err := clust.RESTConfig()
if err != nil {
return nil, err
}
if restCfg.BearerToken == "" {
return nil, status.Errorf(codes.InvalidArgument, "Cluster '%s' does not use bearer token authentication", server)
}
Expand All @@ -428,8 +440,12 @@ func (s *Server) RotateAuth(ctx context.Context, q *cluster.ClusterQuery) (*clus
clust.Config.CertData = nil
clust.Config.BearerToken = string(newSecret.Data["token"])

clusterRESTConfig, err := clust.RESTConfig()
if err != nil {
return nil, err
}
// Test the token we just created before persisting it
serverVersion, err := s.kubectl.GetServerVersion(clust.RESTConfig())
serverVersion, err := s.kubectl.GetServerVersion(clusterRESTConfig)
if err != nil {
return nil, err
}
Expand Down
104 changes: 104 additions & 0 deletions server/cluster/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,74 @@ import (
"github.com/argoproj/argo-cd/v2/util/settings"
)

const (
rootCACert = `-----BEGIN CERTIFICATE-----
MIIC4DCCAcqgAwIBAgIBATALBgkqhkiG9w0BAQswIzEhMB8GA1UEAwwYMTAuMTMu
MTI5LjEwNkAxNDIxMzU5MDU4MB4XDTE1MDExNTIxNTczN1oXDTE2MDExNTIxNTcz
OFowIzEhMB8GA1UEAwwYMTAuMTMuMTI5LjEwNkAxNDIxMzU5MDU4MIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAunDRXGwsiYWGFDlWH6kjGun+PshDGeZX
xtx9lUnL8pIRWH3wX6f13PO9sktaOWW0T0mlo6k2bMlSLlSZgG9H6og0W6gLS3vq
s4VavZ6DbXIwemZG2vbRwsvR+t4G6Nbwelm6F8RFnA1Fwt428pavmNQ/wgYzo+T1
1eS+HiN4ACnSoDSx3QRWcgBkB1g6VReofVjx63i0J+w8Q/41L9GUuLqquFxu6ZnH
60vTB55lHgFiDLjA1FkEz2dGvGh/wtnFlRvjaPC54JH2K1mPYAUXTreoeJtLJKX0
ycoiyB24+zGCniUmgIsmQWRPaOPircexCp1BOeze82BT1LCZNTVaxQIDAQABoyMw
ITAOBgNVHQ8BAf8EBAMCAKQwDwYDVR0TAQH/BAUwAwEB/zALBgkqhkiG9w0BAQsD
ggEBADMxsUuAFlsYDpF4fRCzXXwrhbtj4oQwcHpbu+rnOPHCZupiafzZpDu+rw4x
YGPnCb594bRTQn4pAu3Ac18NbLD5pV3uioAkv8oPkgr8aUhXqiv7KdDiaWm6sbAL
EHiXVBBAFvQws10HMqMoKtO8f1XDNAUkWduakR/U6yMgvOPwS7xl0eUTqyRB6zGb
K55q2dejiFWaFqB/y78txzvz6UlOZKE44g2JAVoJVM6kGaxh33q8/FmrL4kuN3ut
W+MmJCVDvd4eEqPwbp7146ZWTqpIJ8lvA6wuChtqV8lhAPka2hD/LMqY8iXNmfXD
uml0obOEy+ON91k+SWTJ3ggmF/U=
-----END CERTIFICATE-----`

certData = `-----BEGIN CERTIFICATE-----
MIIC6jCCAdSgAwIBAgIBCzALBgkqhkiG9w0BAQswIzEhMB8GA1UEAwwYMTAuMTMu
MTI5LjEwNkAxNDIxMzU5MDU4MB4XDTE1MDExNTIyMDEzMVoXDTE2MDExNTIyMDEz
MlowGzEZMBcGA1UEAxMQb3BlbnNoaWZ0LWNsaWVudDCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAKtdhz0+uCLXw5cSYns9rU/XifFSpb/x24WDdrm72S/v
b9BPYsAStiP148buylr1SOuNi8sTAZmlVDDIpIVwMLff+o2rKYDicn9fjbrTxTOj
lI4pHJBH+JU3AJ0tbajupioh70jwFS0oYpwtneg2zcnE2Z4l6mhrj2okrc5Q1/X2
I2HChtIU4JYTisObtin10QKJX01CLfYXJLa8upWzKZ4/GOcHG+eAV3jXWoXidtjb
1Usw70amoTZ6mIVCkiu1QwCoa8+ycojGfZhvqMsAp1536ZcCul+Na+AbCv4zKS7F
kQQaImVrXdUiFansIoofGlw/JNuoKK6ssVpS5Ic3pgcCAwEAAaM1MDMwDgYDVR0P
AQH/BAQDAgCgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwCwYJ
KoZIhvcNAQELA4IBAQCKLREH7bXtXtZ+8vI6cjD7W3QikiArGqbl36bAhhWsJLp/
p/ndKz39iFNaiZ3GlwIURWOOKx3y3GA0x9m8FR+Llthf0EQ8sUjnwaknWs0Y6DQ3
jjPFZOpV3KPCFrdMJ3++E3MgwFC/Ih/N2ebFX9EcV9Vcc6oVWMdwT0fsrhu683rq
6GSR/3iVX1G/pmOiuaR0fNUaCyCfYrnI4zHBDgSfnlm3vIvN2lrsR/DQBakNL8DJ
HBgKxMGeUPoneBv+c8DMXIL0EhaFXRlBv9QW45/GiAIOuyFJ0i6hCtGZpJjq4OpQ
BRjCI+izPzFTjsxD4aORE+WOkyWFCGPWKfNejfw0
-----END CERTIFICATE-----`

keyData = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAq12HPT64ItfDlxJiez2tT9eJ8VKlv/HbhYN2ubvZL+9v0E9i
wBK2I/Xjxu7KWvVI642LyxMBmaVUMMikhXAwt9/6jaspgOJyf1+NutPFM6OUjikc
kEf4lTcAnS1tqO6mKiHvSPAVLShinC2d6DbNycTZniXqaGuPaiStzlDX9fYjYcKG
0hTglhOKw5u2KfXRAolfTUIt9hcktry6lbMpnj8Y5wcb54BXeNdaheJ22NvVSzDv
RqahNnqYhUKSK7VDAKhrz7JyiMZ9mG+oywCnXnfplwK6X41r4BsK/jMpLsWRBBoi
ZWtd1SIVqewiih8aXD8k26gorqyxWlLkhzemBwIDAQABAoIBAD2XYRs3JrGHQUpU
FkdbVKZkvrSY0vAZOqBTLuH0zUv4UATb8487anGkWBjRDLQCgxH+jucPTrztekQK
aW94clo0S3aNtV4YhbSYIHWs1a0It0UdK6ID7CmdWkAj6s0T8W8lQT7C46mWYVLm
5mFnCTHi6aB42jZrqmEpC7sivWwuU0xqj3Ml8kkxQCGmyc9JjmCB4OrFFC8NNt6M
ObvQkUI6Z3nO4phTbpxkE1/9dT0MmPIF7GhHVzJMS+EyyRYUDllZ0wvVSOM3qZT0
JMUaBerkNwm9foKJ1+dv2nMKZZbJajv7suUDCfU44mVeaEO+4kmTKSGCGjjTBGkr
7L1ySDECgYEA5ElIMhpdBzIivCuBIH8LlUeuzd93pqssO1G2Xg0jHtfM4tz7fyeI
cr90dc8gpli24dkSxzLeg3Tn3wIj/Bu64m2TpZPZEIlukYvgdgArmRIPQVxerYey
OkrfTNkxU1HXsYjLCdGcGXs5lmb+K/kuTcFxaMOs7jZi7La+jEONwf8CgYEAwCs/
rUOOA0klDsWWisbivOiNPII79c9McZCNBqncCBfMUoiGe8uWDEO4TFHN60vFuVk9
8PkwpCfvaBUX+ajvbafIfHxsnfk1M04WLGCeqQ/ym5Q4sQoQOcC1b1y9qc/xEWfg
nIUuia0ukYRpl7qQa3tNg+BNFyjypW8zukUAC/kCgYB1/Kojuxx5q5/oQVPrx73k
2bevD+B3c+DYh9MJqSCNwFtUpYIWpggPxoQan4LwdsmO0PKzocb/ilyNFj4i/vII
NToqSc/WjDFpaDIKyuu9oWfhECye45NqLWhb/6VOuu4QA/Nsj7luMhIBehnEAHW+
GkzTKM8oD1PxpEG3nPKXYQKBgQC6AuMPRt3XBl1NkCrpSBy/uObFlFaP2Enpf39S
3OZ0Gv0XQrnSaL1kP8TMcz68rMrGX8DaWYsgytstR4W+jyy7WvZwsUu+GjTJ5aMG
77uEcEBpIi9CBzivfn7hPccE8ZgqPf+n4i6q66yxBJflW5xhvafJqDtW2LcPNbW/
bvzdmQKBgExALRUXpq+5dbmkdXBHtvXdRDZ6rVmrnjy4nI5bPw+1GqQqk6uAR6B/
F6NmLCQOO4PDG/cuatNHIr2FrwTmGdEL6ObLUGWn9Oer9gJhHVqqsY5I4sEPo4XX
stR0Yiw0buV6DL/moUO0HIM9Bjh96HJp+LxiIS6UCdIhMPp5HoQa
-----END RSA PRIVATE KEY-----`
)

func newServerInMemoryCache() *servercache.Cache {
return servercache.NewCache(
appstatecache.NewCache(
Expand Down Expand Up @@ -238,6 +306,42 @@ func TestGetCluster_NameWithUrlEncodingButShouldNotBeUnescaped(t *testing.T) {
assert.Equal(t, "test%2fing", cluster.Name)
}

func TestGetCluster_CannotSetCADataAndInsecureTrue(t *testing.T) {
testNamespace := "default"
cluster := &v1alpha1.Cluster{
Name: "my-cluster-name",
Server: "https://my-cluster-server",
Namespaces: []string{testNamespace},
Config: v1alpha1.ClusterConfig{
TLSClientConfig: v1alpha1.TLSClientConfig{
Insecure: true,
CAData: []byte(rootCACert),
CertData: []byte(certData),
KeyData: []byte(keyData),
},
},
}
clientset := getClientset(nil, testNamespace)
db := db.NewDB(testNamespace, settings.NewSettingsManager(context.Background(), clientset, testNamespace), clientset)
server := NewServer(db, newNoopEnforcer(), newServerInMemoryCache(), &kubetest.MockKubectlCmd{})

t.Run("Create Fails When CAData is Set and Insecure is True", func(t *testing.T) {
_, err := server.Create(context.Background(), &clusterapi.ClusterCreateRequest{
Cluster: cluster,
})

assert.EqualError(t, err, `Unable to apply K8s REST config defaults: specifying a root certificates file with the insecure flag is not allowed`)
})

cluster.Config.TLSClientConfig.CAData = nil
t.Run("Create Succeeds When CAData is nil and Insecure is True", func(t *testing.T) {
_, err := server.Create(context.Background(), &clusterapi.ClusterCreateRequest{
Cluster: cluster,
})
require.NoError(t, err)
})
}

func TestUpdateCluster_NoFieldsPaths(t *testing.T) {
db := &dbmocks.ArgoDB{}
var updated *v1alpha1.Cluster
Expand Down
5 changes: 4 additions & 1 deletion util/argo/argo.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,10 @@ func ValidateRepo(
})
return conditions, nil
}
config := cluster.RESTConfig()
config, err := cluster.RESTConfig()
if err != nil {
return nil, fmt.Errorf("error getting cluster REST config: %w", err)
}
// nolint:staticcheck
cluster.ServerVersion, err = kubectl.GetServerVersion(config)
if err != nil {
Expand Down

0 comments on commit ad551ac

Please sign in to comment.