Skip to content

Commit

Permalink
[E2E] Secrets Encryption test (#5234)
Browse files Browse the repository at this point in the history
* Add e2e for secrets encryption

Signed-off-by: Derek Nola <[email protected]>

* Cleanup vagrant files

Signed-off-by: Derek Nola <[email protected]>

* Add support for the classic secrets-encyrption rotate workflow

Signed-off-by: Derek Nola <[email protected]>

---------

Signed-off-by: Derek Nola <[email protected]>
  • Loading branch information
dereknola authored Feb 7, 2024
1 parent e6a0d1b commit c82cbda
Show file tree
Hide file tree
Showing 10 changed files with 600 additions and 19 deletions.
5 changes: 1 addition & 4 deletions tests/e2e/dualstack/Vagrantfile
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,7 @@ Vagrant.configure("2") do |config|
NODE_BOXES = NODE_BOXES.split(" ", -1)
end

# Must iterate on the index, vagrant does not understand iterating
# over the node roles themselves
NODE_ROLES.length.times do |i|
name = NODE_ROLES[i]
NODE_ROLES.each_with_index do |name, i|
config.vm.define name do |node|
roles = name.split("-", -1)
role_num = roles.pop.to_i
Expand Down
5 changes: 1 addition & 4 deletions tests/e2e/mixedos/Vagrantfile
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,7 @@ Vagrant.configure("2") do |config|
NODE_BOXES = NODE_BOXES.split(" ", -1)
end

# Must iterate on the index, vagrant does not understand iterating
# over the node roles themselves
NODE_ROLES.length.times do |i|
name = NODE_ROLES[i]
NODE_ROLES.each_with_index do |name, i|
config.vm.define name do |node|
role_num = name.split("-", -1).pop.to_i
provision(node.vm, name, role_num, i)
Expand Down
49 changes: 49 additions & 0 deletions tests/e2e/resource_files/secrets.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
apiVersion: v1
kind: Secret
metadata:
name: e2e-secret1
type: Opaque
stringData:
config.yaml: |
key: "hello"
val: "world"
---
apiVersion: v1
kind: Secret
metadata:
name: e2e-secret2
type: Opaque
stringData:
config.yaml: |
key: "good"
val: "day"
---
apiVersion: v1
kind: Secret
metadata:
name: e2e-secret3
type: Opaque
stringData:
config.yaml: |
key: "top-secret"
val: "information"
---
apiVersion: v1
kind: Secret
metadata:
name: e2e-secret4
type: Opaque
stringData:
config.yaml: |
key: "lock"
val: "key"
---
apiVersion: v1
kind: Secret
metadata:
name: e2e-secret5
type: Opaque
stringData:
config.yaml: |
key: "last"
val: "call"
83 changes: 83 additions & 0 deletions tests/e2e/secretsencryption/Vagrantfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
ENV['VAGRANT_NO_PARALLEL'] = 'no'
NODE_ROLES = (ENV['E2E_NODE_ROLES'] || ["server-0", "server-1", "server-2"])
NODE_BOXES = (ENV['E2E_NODE_BOXES'] || ['generic/ubuntu2204', 'generic/ubuntu2204', 'generic/ubuntu2204'])
GITHUB_BRANCH = (ENV['E2E_GITHUB_BRANCH'] || "master")
RELEASE_VERSION = (ENV['E2E_RELEASE_VERSION'] || "")
NODE_CPUS = (ENV['E2E_NODE_CPUS'] || 2).to_i
NODE_MEMORY = (ENV['E2E_NODE_MEMORY'] || 3072).to_i
REGISTRY = (ENV['E2E_REGISTRY'] || "")
# Virtualbox >= 6.1.28 require `/etc/vbox/network.conf` for expanded private networks,
# see https://www.virtualbox.org/manual/ch06.html#network_hostonly
NETWORK_PREFIX = "10.10.10"

def provision(vm, roles, role_num, node_num)
vm.box = NODE_BOXES[node_num]
vm.hostname = "#{roles[0]}-#{role_num}"
# An expanded netmask is required to allow VM<-->VM communication, virtualbox defaults to /32
node_ip = "#{NETWORK_PREFIX}.#{100+node_num}"
vm.network "private_network", ip: node_ip, netmask: "255.255.255.0"

scripts_location = Dir.exists?("./scripts") ? "./scripts" : "../scripts"
vagrant_defaults = File.exists?("./vagrantdefaults.rb") ? "./vagrantdefaults.rb" : "../vagrantdefaults.rb"
load vagrant_defaults

defaultOSConfigure(vm)
install_type = getInstallType(vm, RELEASE_VERSION, GITHUB_BRANCH)

vm.provision "shell", inline: "ping -c 2 rke2.io"

if !REGISTRY.empty?
vm.provision "Set private registry", type: "shell", path: scripts_location + "/registry.sh", args: [ "#{NETWORK_PREFIX}.1" ]
end

if roles.include?("server") && role_num == 0
vm.provision :rke2, run: 'once' do |rke2|
rke2.env = %W[INSTALL_RKE2_TYPE=server #{install_type}]
rke2.config = <<~YAML
write-kubeconfig-mode: '0644'
node-external-ip: #{NETWORK_PREFIX}.100
node-ip: #{NETWORK_PREFIX}.100
token: vagrant-rke2
YAML
end
elsif roles.include?("server") && role_num != 0
vm.provision :rke2, run: 'once' do |rke2|
rke2.env = %W[INSTALL_RKE2_TYPE=server #{install_type}]
rke2.config = <<~YAML
write-kubeconfig-mode: '0644'
node-external-ip: #{node_ip}
node-ip: #{node_ip}
server: https://#{NETWORK_PREFIX}.100:9345
token: vagrant-rke2
YAML
end
end
end

Vagrant.configure("2") do |config|
config.vagrant.plugins = ["vagrant-rke2", "vagrant-reload"]
# Default provider is libvirt, virtualbox is only provided as a backup
config.vm.provider "libvirt" do |v|
v.cpus = NODE_CPUS
v.memory = NODE_MEMORY
end
config.vm.provider "virtualbox" do |v|
v.cpus = NODE_CPUS
v.memory = NODE_MEMORY
end

if NODE_ROLES.kind_of?(String)
NODE_ROLES = NODE_ROLES.split(" ", -1)
end
if NODE_BOXES.kind_of?(String)
NODE_BOXES = NODE_BOXES.split(" ", -1)
end

NODE_ROLES.each_with_index do |name, i|
config.vm.define name do |node|
roles = name.split("-", -1)
role_num = roles.pop.to_i
provision(node.vm, roles, role_num, i)
end
end
end
145 changes: 145 additions & 0 deletions tests/e2e/secretsencryption/secretsencryption_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package secretsencryption

import (
"flag"
"fmt"
"os"
"strings"
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/rancher/rke2/tests/e2e"
)

// This test is desigened for the new secrets-encrypt rotate-keys command,
// Added in v1.28.0+rke2r1

// Valid nodeOS: generic/ubuntu2204, opensuse/Leap-15.3.x86_64
var nodeOS = flag.String("nodeOS", "generic/ubuntu2204", "VM operating system")
var serverCount = flag.Int("serverCount", 3, "number of server nodes")
var ci = flag.Bool("ci", false, "running on CI")

// Environment Variables Info:
// E2E_RELEASE_VERSION=v1.23.1+rke2r1 or nil for latest commit from master

func Test_E2ESecretsEncryption(t *testing.T) {
RegisterFailHandler(Fail)
flag.Parse()
suiteConfig, reporterConfig := GinkgoConfiguration()
RunSpecs(t, "Secrets Encryption Test Suite", suiteConfig, reporterConfig)
}

var (
kubeConfigFile string
serverNodeNames []string
)

var _ = ReportAfterEach(e2e.GenReport)

var _ = Describe("Verify Secrets Encryption Rotation", Ordered, func() {
Context("Secrets Keys are rotated:", func() {
It("Starts up with no issues", func() {
var err error
serverNodeNames, _, err = e2e.CreateCluster(*nodeOS, *serverCount, 0)
Expect(err).NotTo(HaveOccurred(), e2e.GetVagrantLog(err))
fmt.Println("CLUSTER CONFIG")
fmt.Println("OS:", *nodeOS)
fmt.Println("Server Nodes:", serverNodeNames)
kubeConfigFile, err = e2e.GenKubeConfigFile(serverNodeNames[0])
Expect(err).NotTo(HaveOccurred())
})

It("Checks node and pod status", func() {
fmt.Printf("\nFetching node status\n")
Eventually(func(g Gomega) {
nodes, err := e2e.ParseNodes(kubeConfigFile, false)
g.Expect(err).NotTo(HaveOccurred())
for _, node := range nodes {
g.Expect(node.Status).Should(Equal("Ready"))
}
}, "620s", "5s").Should(Succeed())
_, _ = e2e.ParseNodes(kubeConfigFile, true)

fmt.Printf("\nFetching pods status\n")
Eventually(func(g Gomega) {
pods, err := e2e.ParsePods(kubeConfigFile, false)
g.Expect(err).NotTo(HaveOccurred())
for _, pod := range pods {
if strings.Contains(pod.Name, "helm-install") {
g.Expect(pod.Status).Should(Equal("Completed"), pod.Name)
} else {
g.Expect(pod.Status).Should(Equal("Running"), pod.Name)
}
}
}, "620s", "5s").Should(Succeed())
_, _ = e2e.ParsePods(kubeConfigFile, true)
})

It("Deploys several secrets", func() {
_, err := e2e.DeployWorkload("secrets.yaml", kubeConfigFile)
Expect(err).NotTo(HaveOccurred(), "Secrets not deployed")
})

It("Verifies encryption start stage", func() {
cmd := "sudo rke2 secrets-encrypt status"
for _, nodeName := range serverNodeNames {
res, err := e2e.RunCmdOnNode(cmd, nodeName)
Expect(err).NotTo(HaveOccurred())
Expect(res).Should(ContainSubstring("Encryption Status: Enabled"))
Expect(res).Should(ContainSubstring("Current Rotation Stage: start"))
Expect(res).Should(ContainSubstring("Server Encryption Hashes: All hashes match"))
}
})

It("Rotates the Secrets-Encryption Keys", func() {
cmd := "sudo rke2 secrets-encrypt rotate-keys"
res, err := e2e.RunCmdOnNode(cmd, serverNodeNames[0])
Expect(err).NotTo(HaveOccurred(), res)
for i, nodeName := range serverNodeNames {
Eventually(func(g Gomega) {
cmd := "sudo rke2 secrets-encrypt status"
res, err := e2e.RunCmdOnNode(cmd, nodeName)
g.Expect(err).NotTo(HaveOccurred(), res)
g.Expect(res).Should(ContainSubstring("Server Encryption Hashes: hash does not match"))
if i == 0 {
g.Expect(res).Should(ContainSubstring("Current Rotation Stage: reencrypt_finished"))
} else {
g.Expect(res).Should(ContainSubstring("Current Rotation Stage: start"))
}
}, "420s", "2s").Should(Succeed())
}
})

It("Restarts RKE2 servers", func() {
Expect(e2e.RestartCluster(serverNodeNames)).To(Succeed(), e2e.GetVagrantLog(nil))
})

It("Verifies reencryption_finished stage", func() {
cmd := "sudo rke2 secrets-encrypt status"
for _, nodeName := range serverNodeNames {
Eventually(func(g Gomega) {
res, err := e2e.RunCmdOnNode(cmd, nodeName)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(res).Should(ContainSubstring("Encryption Status: Enabled"))
g.Expect(res).Should(ContainSubstring("Current Rotation Stage: reencrypt_finished"))
g.Expect(res).Should(ContainSubstring("Server Encryption Hashes: All hashes match"))
}, "420s", "2s").Should(Succeed())
}
})
})
})

var failed bool
var _ = AfterEach(func() {
failed = failed || CurrentSpecReport().Failed()
})

var _ = AfterSuite(func() {
if failed && !*ci {
fmt.Println("FAILED!")
} else {
Expect(e2e.DestroyCluster()).To(Succeed())
Expect(os.Remove(kubeConfigFile)).To(Succeed())
}
})
83 changes: 83 additions & 0 deletions tests/e2e/secretsencryption_old/Vagrantfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
ENV['VAGRANT_NO_PARALLEL'] = 'no'
NODE_ROLES = (ENV['E2E_NODE_ROLES'] || ["server-0", "server-1", "server-2"])
NODE_BOXES = (ENV['E2E_NODE_BOXES'] || ['generic/ubuntu2204', 'generic/ubuntu2204', 'generic/ubuntu2204'])
GITHUB_BRANCH = (ENV['E2E_GITHUB_BRANCH'] || "master")
RELEASE_VERSION = (ENV['E2E_RELEASE_VERSION'] || "")
NODE_CPUS = (ENV['E2E_NODE_CPUS'] || 2).to_i
NODE_MEMORY = (ENV['E2E_NODE_MEMORY'] || 3072).to_i
REGISTRY = (ENV['E2E_REGISTRY'] || "")
# Virtualbox >= 6.1.28 require `/etc/vbox/network.conf` for expanded private networks,
# see https://www.virtualbox.org/manual/ch06.html#network_hostonly
NETWORK_PREFIX = "10.10.10"

def provision(vm, roles, role_num, node_num)
vm.box = NODE_BOXES[node_num]
vm.hostname = "#{roles[0]}-#{role_num}"
# An expanded netmask is required to allow VM<-->VM communication, virtualbox defaults to /32
node_ip = "#{NETWORK_PREFIX}.#{100+node_num}"
vm.network "private_network", ip: node_ip, netmask: "255.255.255.0"

scripts_location = Dir.exists?("./scripts") ? "./scripts" : "../scripts"
vagrant_defaults = File.exists?("./vagrantdefaults.rb") ? "./vagrantdefaults.rb" : "../vagrantdefaults.rb"
load vagrant_defaults

defaultOSConfigure(vm)
install_type = getInstallType(vm, RELEASE_VERSION, GITHUB_BRANCH)

vm.provision "shell", inline: "ping -c 2 rke2.io"

if !REGISTRY.empty?
vm.provision "Set private registry", type: "shell", path: scripts_location + "/registry.sh", args: [ "#{NETWORK_PREFIX}.1" ]
end

if roles.include?("server") && role_num == 0
vm.provision :rke2, run: 'once' do |rke2|
rke2.env = %W[INSTALL_RKE2_TYPE=server #{install_type}]
rke2.config = <<~YAML
write-kubeconfig-mode: '0644'
node-external-ip: #{NETWORK_PREFIX}.100
node-ip: #{NETWORK_PREFIX}.100
token: vagrant-rke2
YAML
end
elsif roles.include?("server") && role_num != 0
vm.provision :rke2, run: 'once' do |rke2|
rke2.env = %W[INSTALL_RKE2_TYPE=server #{install_type}]
rke2.config = <<~YAML
write-kubeconfig-mode: '0644'
node-external-ip: #{node_ip}
node-ip: #{node_ip}
server: https://#{NETWORK_PREFIX}.100:9345
token: vagrant-rke2
YAML
end
end
end

Vagrant.configure("2") do |config|
config.vagrant.plugins = ["vagrant-rke2", "vagrant-reload"]
# Default provider is libvirt, virtualbox is only provided as a backup
config.vm.provider "libvirt" do |v|
v.cpus = NODE_CPUS
v.memory = NODE_MEMORY
end
config.vm.provider "virtualbox" do |v|
v.cpus = NODE_CPUS
v.memory = NODE_MEMORY
end

if NODE_ROLES.kind_of?(String)
NODE_ROLES = NODE_ROLES.split(" ", -1)
end
if NODE_BOXES.kind_of?(String)
NODE_BOXES = NODE_BOXES.split(" ", -1)
end

NODE_ROLES.each_with_index do |name, i|
config.vm.define name do |node|
roles = name.split("-", -1)
role_num = roles.pop.to_i
provision(node.vm, roles, role_num, i)
end
end
end
Loading

0 comments on commit c82cbda

Please sign in to comment.