-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add launch policy and stdout/stderr redirect
- Loading branch information
Showing
5 changed files
with
327 additions
and
8 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package spec | ||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
// LaunchPolicy contains policies on starting the container. | ||
// The policy comes from the labels of the image. | ||
type LaunchPolicy struct { | ||
AllowedEnvOverride []string | ||
AllowedCmdOverride bool | ||
AllowedStdoutEcho bool | ||
AllowedStderrEcho bool | ||
} | ||
|
||
const ( | ||
envOverride = "tee.launch_policy.allow_env_override" | ||
cmdOverride = "tee.launch_policy.allow_cmd_override" | ||
stdoutEcho = "tee.launch_policy.allow_stdout_echo" | ||
stderrEcho = "tee.launch_policy.allow_stderr_echo" | ||
) | ||
|
||
// GetLaunchPolicy takes in a map[string] string which should come from image labels, | ||
// and will try to parse it into a LaunchPolicy. Extra fields will be ignored. | ||
func GetLaunchPolicy(imageLabels map[string]string) (LaunchPolicy, error) { | ||
var err error | ||
launchPolicy := LaunchPolicy{} | ||
if v, ok := imageLabels[envOverride]; ok { | ||
envs := strings.Split(v, ",") | ||
for _, env := range envs { | ||
// strip out empty env name | ||
if env != "" { | ||
launchPolicy.AllowedEnvOverride = append(launchPolicy.AllowedEnvOverride, env) | ||
} | ||
} | ||
} | ||
|
||
if v, ok := imageLabels[cmdOverride]; ok { | ||
if launchPolicy.AllowedCmdOverride, err = strconv.ParseBool(v); err != nil { | ||
return LaunchPolicy{}, fmt.Errorf("value of LABEL %s of the image is not a boolean %s", cmdOverride, v) | ||
} | ||
} else { | ||
// default is false | ||
launchPolicy.AllowedCmdOverride = false | ||
} | ||
|
||
if v, ok := imageLabels[stdoutEcho]; ok { | ||
if launchPolicy.AllowedStdoutEcho, err = strconv.ParseBool(v); err != nil { | ||
return LaunchPolicy{}, fmt.Errorf("value of LABEL %s of the image is not a boolean %s", stdoutEcho, v) | ||
} | ||
} else { | ||
launchPolicy.AllowedStdoutEcho = false | ||
} | ||
|
||
if v, ok := imageLabels[stderrEcho]; ok { | ||
if launchPolicy.AllowedStderrEcho, err = strconv.ParseBool(v); err != nil { | ||
return LaunchPolicy{}, fmt.Errorf("value of LABEL %s of the image is not a boolean %s", stderrEcho, v) | ||
} | ||
} else { | ||
launchPolicy.AllowedStderrEcho = false | ||
} | ||
|
||
return launchPolicy, nil | ||
} | ||
|
||
// Verify will use the LaunchPolicy to verify the given LauncherSpec. If the verification passed, will return nil. | ||
// If there is any issue, the function will return the error based on the first error. | ||
func (p LaunchPolicy) Verify(lp LauncherSpec) error { | ||
for _, e := range lp.Envs { | ||
if !contains(p.AllowedEnvOverride, e.Name) { | ||
return fmt.Errorf("env var %s is not allowed to be overridden on this image; allowed envs to be overridden: %v", e, p.AllowedEnvOverride) | ||
} | ||
} | ||
if !p.AllowedCmdOverride && len(lp.Cmd) > 0 { | ||
return fmt.Errorf("CMD is not allowed to be overridden on this image") | ||
} | ||
if !p.AllowedStdoutEcho && lp.ContainerStdoutEcho { | ||
return fmt.Errorf("stdout is not allowed to be redirected") | ||
} | ||
if !p.AllowedStderrEcho && lp.ContainerStderrEcho { | ||
return fmt.Errorf("stderr is not allowed to be redirected") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func contains(strs []string, target string) bool { | ||
for _, s := range strs { | ||
if s == target { | ||
return true | ||
} | ||
} | ||
return false | ||
} |
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,183 @@ | ||
package spec | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/google/go-cmp/cmp" | ||
) | ||
|
||
func TestLauncPolicy(t *testing.T) { | ||
testCases := []struct { | ||
testName string | ||
imageLables map[string]string | ||
expectedPolicy LaunchPolicy | ||
}{ | ||
{ | ||
"case 1", | ||
map[string]string{ | ||
envOverride: "foo", | ||
cmdOverride: "true", | ||
stdoutEcho: "true", | ||
stderrEcho: "True", | ||
}, | ||
LaunchPolicy{ | ||
AllowedEnvOverride: []string{"foo"}, | ||
AllowedCmdOverride: true, | ||
AllowedStdoutEcho: true, | ||
AllowedStderrEcho: true, | ||
}, | ||
}, | ||
{ | ||
"case 2", | ||
map[string]string{ | ||
envOverride: "foo,bar", | ||
}, | ||
LaunchPolicy{ | ||
AllowedEnvOverride: []string{"foo", "bar"}, | ||
AllowedCmdOverride: false, | ||
AllowedStdoutEcho: false, | ||
AllowedStderrEcho: false, | ||
}, | ||
}, | ||
{ | ||
"case 3 default case", | ||
nil, | ||
LaunchPolicy{ | ||
AllowedEnvOverride: nil, | ||
AllowedCmdOverride: false, | ||
AllowedStdoutEcho: false, | ||
AllowedStderrEcho: false, | ||
}, | ||
}, | ||
{ | ||
"case 4 empty string in env", | ||
map[string]string{ | ||
envOverride: ",,,foo", | ||
cmdOverride: "false", | ||
stdoutEcho: "true", | ||
stderrEcho: "True", | ||
}, | ||
LaunchPolicy{ | ||
AllowedEnvOverride: []string{"foo"}, | ||
AllowedCmdOverride: false, | ||
AllowedStdoutEcho: true, | ||
AllowedStderrEcho: true, | ||
}, | ||
}, | ||
} | ||
|
||
for _, testcase := range testCases { | ||
t.Run(testcase.testName, func(t *testing.T) { | ||
got, err := GetLaunchPolicy(testcase.imageLables) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
if !cmp.Equal(got, testcase.expectedPolicy) { | ||
t.Errorf("Launchspec got %+v, want %+v", got, testcase.expectedPolicy) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestVerify(t *testing.T) { | ||
testCases := []struct { | ||
testName string | ||
policy LaunchPolicy | ||
spec LauncherSpec | ||
expectErr bool | ||
}{ | ||
{ | ||
"allow everything", | ||
LaunchPolicy{ | ||
AllowedEnvOverride: []string{"foo"}, | ||
AllowedCmdOverride: true, | ||
AllowedStdoutEcho: true, | ||
AllowedStderrEcho: true, | ||
}, | ||
LauncherSpec{ | ||
Envs: []EnvVar{{Name: "foo", Value: "foo"}}, | ||
Cmd: []string{"foo"}, | ||
ContainerStdoutEcho: true, | ||
ContainerStderrEcho: true, | ||
}, | ||
false, | ||
}, | ||
{ | ||
"default case", | ||
LaunchPolicy{}, | ||
LauncherSpec{}, | ||
false, | ||
}, | ||
{ | ||
"env override violation", | ||
LaunchPolicy{ | ||
AllowedEnvOverride: []string{"foo"}, | ||
}, | ||
LauncherSpec{ | ||
Envs: []EnvVar{{Name: "bar", Value: ""}}, | ||
}, | ||
true, | ||
}, | ||
{ | ||
"cmd violation", | ||
LaunchPolicy{ | ||
AllowedCmdOverride: false, | ||
}, | ||
LauncherSpec{ | ||
Cmd: []string{"foo"}, | ||
}, | ||
true, | ||
}, | ||
{ | ||
"stdout echo violation", | ||
LaunchPolicy{ | ||
AllowedCmdOverride: false, | ||
}, | ||
LauncherSpec{ | ||
ContainerStdoutEcho: true, | ||
}, | ||
true, | ||
}, | ||
{ | ||
"stderr echo violation", | ||
LaunchPolicy{ | ||
AllowedCmdOverride: false, | ||
}, | ||
LauncherSpec{ | ||
ContainerStderrEcho: true, | ||
}, | ||
true, | ||
}, | ||
{ | ||
"allow everything", | ||
LaunchPolicy{ | ||
AllowedEnvOverride: []string{"foo"}, | ||
AllowedCmdOverride: true, | ||
AllowedStdoutEcho: true, | ||
AllowedStderrEcho: true, | ||
}, | ||
LauncherSpec{ | ||
Envs: []EnvVar{{Name: "foo", Value: "foo"}}, | ||
Cmd: []string{"foo"}, | ||
ContainerStdoutEcho: true, | ||
ContainerStderrEcho: true, | ||
}, | ||
false, | ||
}, | ||
} | ||
for _, testCase := range testCases { | ||
t.Run(testCase.testName, func(t *testing.T) { | ||
err := testCase.policy.Verify(testCase.spec) | ||
if testCase.expectErr { | ||
if err == nil { | ||
t.Errorf("expected error, but got nil") | ||
} | ||
} else { | ||
if err != nil { | ||
t.Errorf("expected no error, but got %v", err) | ||
} | ||
} | ||
}) | ||
} | ||
} |
Oops, something went wrong.