-
Notifications
You must be signed in to change notification settings - Fork 275
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[E2E] Secrets Encryption test (#5234)
* 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
Showing
10 changed files
with
600 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.