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 istio.io docs test for security/authn-policy #16140
Changes from all commits
b536c8e
a88bf27
2409508
88c5733
b4c144b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,12 +56,26 @@ func New(t *testing.T, name string) Example { | |
|
||
// AddScript adds a directive to run a script | ||
func (example *Example) AddScript(namespace string, script string, output outputType) { | ||
example.t.Helper() | ||
|
||
//fullPath := getFullPath(istioPath + script) | ||
example.steps = append(example.steps, newStepScript("./"+script, output)) | ||
fullPath := "./"+script | ||
stats, err := os.Stat(fullPath) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we move this validation to a common function and use it for files as well? |
||
if os.IsNotExist(err) { | ||
example.t.Fatalf("Script %q was not found", script) | ||
} | ||
if !stats.Mode().IsRegular() || stats.Mode().Perm() & 0x1 == 0 { | ||
example.t.Fatalf("Script %q is not executable (mode: %s)", | ||
script, stats.Mode().Perm().String()) | ||
} | ||
|
||
example.steps = append(example.steps, newStepScript(fullPath, output)) | ||
} | ||
|
||
// AddFile adds an existing file | ||
func (example *Example) AddFile(namespace string, path string) { | ||
example.t.Helper() | ||
|
||
fullPath := getFullPath(istioPath + path) | ||
example.steps = append(example.steps, newStepFile(namespace, fullPath)) | ||
} | ||
|
@@ -72,6 +86,8 @@ type testFunc func(t *testing.T) | |
// Exec registers a callback to be invoked synchronously. This is typically used for | ||
// validation logic to ensure command-lines worked as intended | ||
func (example *Example) Exec(testFunction testFunc) { | ||
example.t.Helper() | ||
|
||
example.steps = append(example.steps, newStepFunction(testFunction)) | ||
} | ||
|
||
|
@@ -80,12 +96,12 @@ func (example *Example) Exec(testFunction testFunc) { | |
func getFullPath(path string) string { | ||
gopath := os.Getenv("GOPATH") | ||
return gopath + "/src/" + path | ||
|
||
} | ||
|
||
// Run runs the scripts and capture output | ||
// Note that this overrides os.Stdout/os.Stderr and is not thread-safe | ||
func (example *Example) Run() { | ||
example.t.Helper() | ||
|
||
//override stdout and stderr for test. Is there a better way of doing this? | ||
|
||
|
@@ -99,7 +115,7 @@ func (example *Example) Run() { | |
//f, err := os.Create( | ||
//os.StdOut = | ||
|
||
example.t.Log(fmt.Sprintf("Executing test %s (%d steps)", example.name, len(example.steps))) | ||
example.t.Log(fmt.Sprintf("Executing example %s (%d steps)", example.name, len(example.steps))) | ||
|
||
//create directory if it doesn't exist | ||
if _, err := os.Stat(example.name); os.IsNotExist(err) { | ||
|
@@ -112,6 +128,8 @@ func (example *Example) Run() { | |
framework. | ||
NewTest(example.t). | ||
Run(func(ctx framework.TestContext) { | ||
example.t.Helper() | ||
|
||
kubeEnv, ok := ctx.Environment().(*kube.Environment) | ||
if !ok { | ||
example.t.Fatalf("test framework unable to get Kubernetes environment") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
// Copyright 2019 Istio Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
package tests | ||
|
||
import ( | ||
"testing" | ||
|
||
"istio.io/istio/pkg/test/framework" | ||
"istio.io/istio/pkg/test/framework/components/environment" | ||
"istio.io/istio/pkg/test/framework/components/istio" | ||
"istio.io/istio/pkg/test/istio.io/examples" | ||
) | ||
|
||
var ( | ||
ist istio.Instance | ||
) | ||
|
||
func TestMain(m *testing.M) { | ||
framework.NewSuite("authn-policy", m). | ||
SetupOnEnv(environment.Kube, istio.Setup(&ist, setupConfig)). | ||
RequireEnvironment(environment.Kube). | ||
Run() | ||
} | ||
|
||
func setupConfig(cfg *istio.Config) { | ||
if cfg == nil { | ||
return | ||
} | ||
// This is redundant, but setting it explicitly to match the docs as it's explicitly required | ||
// in the docs. | ||
cfg.Values["global.mtls.enabled"] = "false" | ||
} | ||
|
||
// https://preliminary.istio.io/docs/tasks/security/authn-policy/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These links are likely to change. Not sure we want to be adding them to the code. That said, it would be nice to have a convention to map between the test and the doc. |
||
// https://github.com/istio/istio.io/blob/master/content/docs/tasks/security/authn-policy/index.md | ||
func TestAuthnPolicy(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps it would be better to create separate test functions for each example? |
||
ex := examples.New(t, "Setup") | ||
|
||
ex.AddScript("", "create-namespaces.sh", examples.TextOutput) | ||
ex.AddFile("foo", "samples/httpbin/httpbin.yaml") | ||
ex.AddFile("foo", "samples/sleep/sleep.yaml") | ||
ex.AddFile("bar", "samples/httpbin/httpbin.yaml") | ||
ex.AddFile("bar", "samples/sleep/sleep.yaml") | ||
ex.AddFile("legacy", "samples/httpbin/httpbin.yaml") | ||
ex.AddFile("legacy", "samples/sleep/sleep.yaml") | ||
|
||
// This is missing from the docs, but it is necessary before continuing. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea of having a validation function for scripts (i.e. should return error, should not return error, should have text...) was suggested yesterday. Might be a nice to have that idea for files as well. Waiting for containers to be ready and verifying reachability are likely common activities. |
||
ex.AddScript("", "wait-for-containers.sh", examples.TextOutput) | ||
ex.AddScript("", "verify-reachability.sh", examples.TextOutput) | ||
|
||
// TODO: Update the docs to use commands that succeed or fail, to check the authentication | ||
// policies and destination rules, and use the same commands here. | ||
ex.Run() | ||
|
||
ex = examples.New(t, "Globally enabling Istio mutual TLS") | ||
|
||
ex.AddScript("", "part1-configure-authentication-meshpolicy.sh", examples.TextOutput) | ||
// TODO: Update the docs to add instructions to wait until the policy has been propagated, | ||
// and use the same commands here. | ||
|
||
// TODO: Check the output of the command. Fail if curl doesn't fail. | ||
ex.AddScript("", "part1-verify-reachability-from-istio.sh", examples.TextOutput) | ||
ex.AddScript("", "part1-configure-destinationrule-default.sh", examples.TextOutput) | ||
// TODO: Fail if curl fails. | ||
ex.AddScript("", "part1-verify-reachability-from-istio.sh", examples.TextOutput) | ||
// TODO: Fail if curl doesn't fail. | ||
ex.AddScript("", "part1-verify-reachability-from-non-istio.sh", examples.TextOutput) | ||
|
||
// TODO: Fail if curl doesn't fail. | ||
ex.AddScript("", "part1-verify-reachability-to-legacy.sh", examples.TextOutput) | ||
ex.AddScript("", "part1-configure-destinationrule-httpbin-legacy.sh", examples.TextOutput) | ||
// TODO: Fail if curl fails. | ||
ex.AddScript("", "part1-verify-reachability-to-legacy.sh", examples.TextOutput) | ||
|
||
// TODO: Fail if curl doesn't fail. | ||
ex.AddScript("", "part1-verify-reachability-to-k8s-api.sh", examples.TextOutput) | ||
ex.AddScript("", "part1-configure-destinationrule-api-server.sh", examples.TextOutput) | ||
// TODO: Fail if curl fails. | ||
ex.AddScript("", "part1-verify-reachability-to-k8s-api.sh", examples.TextOutput) | ||
|
||
ex.AddScript("", "part1-cleanup.sh", examples.TextOutput) | ||
|
||
ex.Run() | ||
|
||
ex = examples.New(t, "Enable mutual TLS per namespace or service") | ||
|
||
ex.AddScript("", "part2-configure-authentication-policy-default.sh", examples.TextOutput) | ||
ex.AddScript("", "part2-configure-destinationrule-default.sh", examples.TextOutput) | ||
// TODO: Update the docs to add instructions to wait until the policy has been propagated, | ||
// and use the same commands here. | ||
|
||
// TODO: Fail if curl from foo or bar to any other namespace fails. | ||
// TODO: Fail if curl from legacy to foo succeeds. | ||
ex.AddScript("", "part2-verify-reachability.sh", examples.TextOutput) | ||
ex.AddScript("", "part2-configure-authentication-policy-httpbin.sh", examples.TextOutput) | ||
ex.AddScript("", "part2-configure-destinationrule-httpbin.sh", examples.TextOutput) | ||
// TODO: Fail if curl from foo or bar to any other namespace fails. | ||
// TODO: Fail if curl from legacy to foo OR bar succeeds. | ||
ex.AddScript("", "part2-verify-reachability.sh", examples.TextOutput) | ||
|
||
ex.AddScript("", "part2-configure-authentication-policy-httpbin-port.sh", examples.TextOutput) | ||
ex.AddScript("", "part2-configure-destinationrule-httpbin-port.sh", examples.TextOutput) | ||
// TODO: Fail if curl fails. | ||
ex.AddScript("", "part2-verify-reachability-to-bar-port-8000.sh", examples.TextOutput) | ||
|
||
ex.AddScript("", "part2-configure-authentication-policy-overwrite-example.sh", examples.TextOutput) | ||
ex.AddScript("", "part2-configure-destinationrule-overwrite-example.sh", examples.TextOutput) | ||
// TODO: Fail if curl fails. | ||
ex.AddScript("", "part2-verify-reachability-to-foo-port-8000.sh", examples.TextOutput) | ||
|
||
ex.AddScript("", "part2-cleanup.sh", examples.TextOutput) | ||
|
||
ex.Run() | ||
|
||
ex = examples.New(t, "End-user authentication") | ||
|
||
ex.AddScript("", "part3-configure-gateway-httpbin.sh", examples.TextOutput) | ||
ex.AddScript("", "part3-configure-virtualservice-httpbin.sh", examples.TextOutput) | ||
// TODO: Update the docs to add instructions to wait until the gateway is ready, | ||
// and use the same commands here. | ||
|
||
// TODO: Fail if curl fails. | ||
ex.AddScript("", "part3-verify-reachability-headers-without-token.sh", examples.TextOutput) | ||
ex.AddScript("", "part3-configure-authentication-policy-jwt-example.sh", examples.TextOutput) | ||
// TODO: Fail if curl succeeds. | ||
ex.AddScript("", "part3-verify-reachability-headers-without-token.sh", examples.TextOutput) | ||
// TODO: Fail if curl fails. | ||
ex.AddScript("", "part3-verify-reachability-headers-with-token.sh", examples.TextOutput) | ||
|
||
// TODO: Add the test that runs security/tools/jwt/samples/gen-jwt.py against | ||
// security/tools/jwt/samples/key.pem. | ||
// This requires having Python and the jwcrypto library installed locally. | ||
|
||
ex.AddScript("", "part3-configure-authentication-policy-jwt-example-exclude.sh", examples.TextOutput) | ||
// TODO: Fail if curl fails. | ||
ex.AddScript("", "part3-verify-reachability-useragent-without-token.sh", examples.TextOutput) | ||
// TODO: Fail if curl succeeds. | ||
ex.AddScript("", "part3-verify-reachability-headers-without-token.sh", examples.TextOutput) | ||
|
||
ex.AddScript("", "part3-configure-authentication-policy-jwt-example-include.sh", examples.TextOutput) | ||
// TODO: Fail if curl fails. | ||
ex.AddScript("", "part3-verify-reachability-useragent-without-token.sh", examples.TextOutput) | ||
// TODO: Fail if curl succeeds. | ||
ex.AddScript("", "part3-verify-reachability-ip-without-token.sh", examples.TextOutput) | ||
// TODO: Fail if curl fails. | ||
ex.AddScript("", "part3-verify-reachability-ip-with-token.sh", examples.TextOutput) | ||
|
||
ex.AddScript("", "part3-configure-authentication-policy-jwt-mtls.sh", examples.TextOutput) | ||
ex.AddScript("", "part3-configure-destinationrule-httpbin.sh", examples.TextOutput) | ||
// TODO: Fail if curl fails. | ||
ex.AddScript("", "part3-verify-reachability-from-istio-with-token.sh", examples.TextOutput) | ||
// TODO: Fail if curl succeeds. | ||
ex.AddScript("", "part3-verify-reachability-from-non-istio-with-token.sh", examples.TextOutput) | ||
|
||
ex.AddScript("", "part3-cleanup.sh", examples.TextOutput) | ||
|
||
ex.Run() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#!/bin/bash | ||
set -e | ||
kubectl create ns foo | ||
kubectl create ns bar | ||
kubectl create ns legacy |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#!/bin/bash | ||
kubectl delete meshpolicy default | ||
kubectl delete destinationrules httpbin-legacy -n legacy | ||
kubectl delete destinationrules api-server -n istio-system | ||
kubectl delete destinationrules default -n istio-system |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#!/bin/bash | ||
set -e | ||
kubectl apply -f - <<EOF | ||
apiVersion: "authentication.istio.io/v1alpha1" | ||
kind: "MeshPolicy" | ||
metadata: | ||
name: "default" | ||
spec: | ||
peers: | ||
- mtls: {} | ||
EOF |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
#!/bin/bash | ||
set -e | ||
kubectl apply -f - <<EOF | ||
apiVersion: networking.istio.io/v1alpha3 | ||
kind: DestinationRule | ||
metadata: | ||
name: "api-server" | ||
namespace: istio-system | ||
spec: | ||
host: "kubernetes.default.svc.cluster.local" | ||
trafficPolicy: | ||
tls: | ||
mode: DISABLE | ||
EOF |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
#!/bin/bash | ||
set -e | ||
kubectl apply -f - <<EOF | ||
apiVersion: "networking.istio.io/v1alpha3" | ||
kind: "DestinationRule" | ||
metadata: | ||
name: "default" | ||
namespace: "istio-system" | ||
spec: | ||
host: "*.local" | ||
trafficPolicy: | ||
tls: | ||
mode: ISTIO_MUTUAL | ||
EOF |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
#!/bin/bash | ||
set -e | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we need some mechanism to not have these first two lines show up in the output that's generated for use on istio.io. One option is to not include the lines in these files, and instead have the test framework insert them autoamtically before executing the script. The alternative is to automatically remove these lines when producing the final output. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this particular case, I can replace this with bash's
We could then filter out all comments (any line matching We also need a way to capture the output of commands, both for asserting on it in tests, and for documentation. Many examples in the docs show the output of
This doesn't allow capturing the output per command, but it's close to what we need. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Having the test automatically insert them sounds good to me. |
||
kubectl apply -f - <<EOF | ||
apiVersion: networking.istio.io/v1alpha3 | ||
kind: DestinationRule | ||
metadata: | ||
name: "httpbin-legacy" | ||
namespace: "legacy" | ||
spec: | ||
host: "httpbin.legacy.svc.cluster.local" | ||
trafficPolicy: | ||
tls: | ||
mode: DISABLE | ||
EOF |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#!/bin/bash | ||
for from in "foo" "bar"; do for to in "foo" "bar"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#!/bin/bash | ||
rlenglet marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for from in "legacy"; do for to in "foo" "bar"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
#!/bin/bash | ||
TOKEN=$(kubectl describe secret $(kubectl get secrets | grep default-token | cut -f1 -d ' ' | head -1) | grep -E '^token' | cut -f2 -d':' | tr -d '\t') | ||
kubectl exec $(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name}) -c sleep -n foo -- curl https://kubernetes.default/api --header "Authorization: Bearer $TOKEN" --insecure -s -o /dev/null -w "%{http_code}\n" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#!/bin/bash | ||
for from in "foo" "bar"; do for to in "legacy"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#!/bin/bash | ||
kubectl delete policy default overwrite-example -n foo | ||
kubectl delete policy httpbin -n bar | ||
kubectl delete destinationrules default overwrite-example -n foo | ||
kubectl delete destinationrules httpbin -n bar |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line can be removed.