Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for agent-tls-mode Rancher setting #863

Merged
merged 1 commit into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,8 @@ spec:
type: string
secret-namespace:
type: string
strictTLSMode:
type: boolean
token:
type: string
url:
Expand Down
2 changes: 2 additions & 0 deletions api/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ type Registration struct {
}

type SystemAgent struct {
// +optional
StrictTLSMode bool `json:"strictTLSMode,omitempty" yaml:"strictTLSMode,omitempty"`
// +optional
URL string `json:"url,omitempty" yaml:"url,omitempty"`
// +optional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ spec:
type: string
secret-namespace:
type: string
strictTLSMode:
type: boolean
token:
type: string
url:
Expand Down
7 changes: 7 additions & 0 deletions pkg/install/_testdata/after-hook-config-install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ stages:
connectionInfoFile: /var/lib/elemental/agent/elemental_connection.json
encoding: ""
ownerstring: ""
- path: /etc/rancher/elemental/agent/envs
permissions: 384
owner: 0
group: 0
content: CATTLE_AGENT_STRICT_VERIFY="true"
encoding: ""
ownerstring: ""
encoding: ""
ownerstring: ""
name: Elemental System Agent Config
Expand Down
7 changes: 7 additions & 0 deletions pkg/install/_testdata/after-hook-config-reset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ stages:
connectionInfoFile: /var/lib/elemental/agent/elemental_connection.json
encoding: ""
ownerstring: ""
- path: /etc/rancher/elemental/agent/envs
permissions: 384
owner: 0
group: 0
content: CATTLE_AGENT_STRICT_VERIFY="true"
encoding: ""
ownerstring: ""
encoding: ""
ownerstring: ""
name: Elemental System Agent Config
Expand Down
17 changes: 17 additions & 0 deletions pkg/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,10 @@ func (i *installer) getAgentConfigBytes() ([]byte, error) {
return agentConfigBytes, nil
}

func (i *installer) getAgentConfigEnvs(config elementalv1.Elemental) []byte {
return []byte(fmt.Sprintf("CATTLE_AGENT_STRICT_VERIFY=\"%t\"", config.SystemAgent.StrictTLSMode))
}

// Write system agent config files to local filesystem
func (i *installer) WriteLocalSystemAgentConfig(config elementalv1.Elemental) error {
connectionInfoBytes, err := i.getConnectionInfoBytes(config)
Expand All @@ -495,6 +499,13 @@ func (i *installer) WriteLocalSystemAgentConfig(config elementalv1.Elemental) er
}
log.Infof("connection info file '%s' written.", connectionInfoFile)

elementalAgentEnvsFile := filepath.Join(agentConfDir, "envs")
err = os.WriteFile(elementalAgentEnvsFile, i.getAgentConfigEnvs(config), 0600)
if err != nil {
return fmt.Errorf("writing agent envs file: %w", err)
}
log.Infof("agent envs file '%s' written.", elementalAgentEnvsFile)

agentConfigBytes, err := i.getAgentConfigBytes()
if err != nil {
return fmt.Errorf("getting agent config: %w", err)
Expand Down Expand Up @@ -523,6 +534,7 @@ func (i *installer) elementalSystemAgentYip(config elementalv1.Elemental) ([]byt
if err != nil {
return nil, fmt.Errorf("getting agent config: %w", err)
}
agentEnvsBytes := i.getAgentConfigEnvs(config)

var stages []schema.Stage

Expand All @@ -538,6 +550,11 @@ func (i *installer) elementalSystemAgentYip(config elementalv1.Elemental) ([]byt
Content: string(agentConfigBytes),
Permissions: 0600,
},
{
Path: filepath.Join(agentConfDir, "envs"),
Content: string(agentEnvsBytes),
Permissions: 0600,
},
},
})

Expand Down
1 change: 1 addition & 0 deletions pkg/install/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ var (
Reboot: true,
},
SystemAgent: elementalv1.SystemAgent{
StrictTLSMode: true,
URL: "https://127.0.0.1.sslip.io/test/control/plane/endpoint",
Token: "a test token",
SecretName: "a test secret name",
Expand Down
18 changes: 18 additions & 0 deletions pkg/server/api_registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ func (i *InventoryServer) writeMachineInventoryCloudConfig(conn *websocket.Conn,
return err
}
config.Elemental.SystemAgent = elementalv1.SystemAgent{
StrictTLSMode: i.isAgentTLSModeStrict(),
URL: fmt.Sprintf("%s/k8s/clusters/local", serverURL),
Token: string(secret.Data["token"]),
SecretName: inventory.Name,
Expand Down Expand Up @@ -221,6 +222,23 @@ func (i *InventoryServer) getRancherCACert() string {
return cacert
}

// Support for agent-tls-mode
func (i *InventoryServer) isAgentTLSModeStrict() bool {
agentTLSMode, err := i.getValue("agent-tls-mode")
if err != nil {
log.Errorf("Error getting agent-tls-mode: %s", err.Error())
}
switch agentTLSMode {
case "strict":
return true
case "system-store":
return false
default:
// Historically the default has been strict TLS verification
return true
}
}

func (i *InventoryServer) serveLoop(conn *websocket.Conn, inventory *elementalv1.MachineInventory, registration *elementalv1.MachineRegistration) error { //nolint: gocyclo
protoVersion := register.MsgUndefined
tmpl := templater.NewTemplater()
Expand Down
46 changes: 46 additions & 0 deletions pkg/server/api_registration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client/fake"

elementalv1 "github.com/rancher/elemental-operator/api/v1beta1"
Expand Down Expand Up @@ -687,6 +688,51 @@ func TestRegistrationDynamicLabels(t *testing.T) {
})
}

func TestAgentTLSMode(t *testing.T) {
type test struct {
name string
agentTLSModeValue *string
wantStrictTLSMode bool
}

tests := []test{
{
name: "missing agent-tls-mode",
agentTLSModeValue: nil,
wantStrictTLSMode: true,
},
{
name: "strict agent-tls-mode",
agentTLSModeValue: ptr.To("strict"),
wantStrictTLSMode: true,
},
{
name: "system-store agent-tls-mode",
agentTLSModeValue: ptr.To("system-store"),
wantStrictTLSMode: false,
},
}

for _, tt := range tests {
server := NewInventoryServer(&FakeAuthServer{})

t.Run(tt.name, func(t *testing.T) {
if tt.agentTLSModeValue != nil {
server.Client.Create(context.Background(), &managementv3.Setting{
ObjectMeta: metav1.ObjectMeta{
Name: "agent-tls-mode",
},

Value: *tt.agentTLSModeValue,
})
}

assert.Equal(t, server.isAgentTLSModeStrict(), tt.wantStrictTLSMode)
})
}

}

func NewInventoryServer(auth authenticator) *InventoryServer {
scheme := runtime.NewScheme()
elementalv1.AddToScheme(scheme)
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ nginxURL: https://raw.githubusercontent.com/kubernetes/ingress-nginx/${NGINX_VER
certManagerVersion: v1.14.2
certManagerChartURL: https://charts.jetstack.io/charts/cert-manager-${CERT_MANAGER_VERSION}.tgz

rancherVersion: 2.8.2
rancherVersion: 2.9.2
rancherChartURL: https://releases.rancher.com/server-charts/latest/rancher-${RANCHER_VERSION}.tgz

systemUpgradeControllerVersion: v0.13.4
Expand Down
46 changes: 38 additions & 8 deletions tests/e2e/e2e_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ var _ = BeforeSuite(func() {
Expect(err).ShouldNot(HaveOccurred())
})

By("installing rancher"+e2eCfg.RancherVersion, func() {
By("installing rancher: "+e2eCfg.RancherVersion, func() {
if isAlreadyInstalled(cattleSystemNamespace) {
By("already installed")
return
Expand All @@ -262,24 +262,35 @@ var _ = BeforeSuite(func() {
"--set", "extraEnv[1].name=CATTLE_BOOTSTRAP_PASSWORD",
"--set", "extraEnv[1].value="+password,
"--set", "privateCA=true",
"--set", "agentTLSMode=system-store",
"--namespace", cattleSystemNamespace,
)).To(Succeed())

Eventually(func() bool {
return isDeploymentReady(cattleSystemNamespace, rancherName)
}, 5*time.Minute, 2*time.Second).Should(BeTrue())

Eventually(func() bool {
return isDeploymentReady(cattleFleetNamespace, fleetAgent)
}, 5*time.Minute, 2*time.Second).Should(BeTrue())
rancherVer, err := checkver.NewVersion(e2eCfg.RancherVersion)
Expect(err).ToNot(HaveOccurred())

// capi-controller exists only since Rancher Manager v2.7.8
refVer, err := checkver.NewVersion("2.7.8")
// fleet is deployed as statefulSet since 2.9
fleetStatefulSetVer, err := checkver.NewVersion("2.9.0")
Expect(err).ToNot(HaveOccurred())
rancherVer, err := checkver.NewVersion(e2eCfg.RancherVersion)
if rancherVer.GreaterThanOrEqual(fleetStatefulSetVer) {
Eventually(func() bool {
return isStatefulSetReady(cattleFleetNamespace, fleetAgent)
}, 5*time.Minute, 2*time.Second).Should(BeTrue())
} else {
Eventually(func() bool {
return isDeploymentReady(cattleFleetNamespace, fleetAgent)
}, 5*time.Minute, 2*time.Second).Should(BeTrue())
}

// capi-controller exists only since Rancher Manager v2.7.8
nonCapiVer, err := checkver.NewVersion("2.7.8")
Expect(err).ToNot(HaveOccurred())

if rancherVer.GreaterThanOrEqual(refVer) {
if rancherVer.GreaterThanOrEqual(nonCapiVer) {
Eventually(func() bool {
return isDeploymentReady(cattleCapiNamespace, capiController)
}, 5*time.Minute, 2*time.Second).Should(BeTrue())
Expand Down Expand Up @@ -423,6 +434,25 @@ func isDeploymentReady(namespace, name string) bool {
return false
}

func isStatefulSetReady(namespace, name string) bool {
statefulSet := &appsv1.StatefulSet{}
if err := cl.Get(ctx,
runtimeclient.ObjectKey{
Namespace: namespace,
Name: name,
},
statefulSet,
); err != nil {
return false
}

if statefulSet.Status.AvailableReplicas == *statefulSet.Spec.Replicas {
return true
}

return false
}

func doesSecretExist(namespace, name string) bool {
secret := &corev1.Secret{}
if err := cl.Get(ctx,
Expand Down