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 all Auth Methods via annotations #213

Merged
merged 6 commits into from
Feb 25, 2021
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
18 changes: 16 additions & 2 deletions agent-inject/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

const (
DefaultVaultImage = "vault:1.6.2"
DefaultVaultAuthType = "kubernetes"
DefaultVaultAuthPath = "auth/kubernetes"
DefaultAgentRunAsUser = 100
DefaultAgentRunAsGroup = 1000
Expand Down Expand Up @@ -163,9 +164,15 @@ type Vault struct {
// ProxyAddress is the proxy service address to use when talking to the Vault service.
ProxyAddress string

// AuthPath is the Mount Path of Vault Kubernetes Auth Method.
// AuthType is type of Vault Auth Method to use.
AuthType string

// AuthPath is the Mount Path of Vault Auth Method.
AuthPath string

// AuthConfig is the Auto Auth Method configuration.
AuthConfig map[string]interface{}

// CACert is the name of the Certificate Authority certificate
// to use when validating Vault's server certificates.
CACert string
Expand Down Expand Up @@ -248,6 +255,7 @@ func New(pod *corev1.Pod, patches []*jsonpatch.JsonPatchOperation) (*Agent, erro
Vault: Vault{
Address: pod.Annotations[AnnotationVaultService],
ProxyAddress: pod.Annotations[AnnotationProxyAddress],
AuthType: pod.Annotations[AnnotationVaultAuthType],
AuthPath: pod.Annotations[AnnotationVaultAuthPath],
CACert: pod.Annotations[AnnotationVaultCACert],
CAKey: pod.Annotations[AnnotationVaultCAKey],
Expand All @@ -266,6 +274,7 @@ func New(pod *corev1.Pod, patches []*jsonpatch.JsonPatchOperation) (*Agent, erro

var err error
agent.Secrets = agent.secrets()
agent.Vault.AuthConfig = agent.authConfig()
agent.Inject, err = agent.inject()
if err != nil {
return agent, err
Expand Down Expand Up @@ -508,7 +517,12 @@ func (a *Agent) Validate() error {
}

if a.ConfigMapName == "" {
if a.Vault.Role == "" {
if a.Vault.AuthType == "" {
return errors.New("no Vault Auth Type found")
}

if a.Vault.AuthType == DefaultVaultAuthType &&
a.Vault.Role == "" && a.Annotations[fmt.Sprintf("%s-role", AnnotationVaultAuthConfig)] == "" {
return errors.New("no Vault role found")
}

tvoran marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
26 changes: 22 additions & 4 deletions agent-inject/agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func TestValidate(t *testing.T) {
Role: "test",
Address: "https://foobar.com:8200",
AuthPath: "test",
AuthType: "kubernetes",
},
}, true,
},
Expand Down Expand Up @@ -129,8 +130,9 @@ func TestValidate(t *testing.T) {
ServiceAccountName: "foobar",
ImageName: "test",
Vault: Vault{
Role: "",
Address: "https://foobar.com:8200",
Role: "",
Address: "https://foobar.com:8200",
AuthType: "kubernetes",
},
}, false,
},
Expand All @@ -141,8 +143,9 @@ func TestValidate(t *testing.T) {
ServiceAccountName: "foobar",
ImageName: "test",
Vault: Vault{
Role: "test",
Address: "",
Role: "test",
Address: "",
AuthType: "kubernetes",
},
}, false,
},
Expand All @@ -156,6 +159,21 @@ func TestValidate(t *testing.T) {
Role: "test",
Address: "https://foobar.com:8200",
AuthPath: "",
AuthType: "kubernetes",
},
}, false,
},
{
Agent{
Namespace: "test",
ServiceAccountPath: "foobar",
ServiceAccountName: "foobar",
ImageName: "test",
Vault: Vault{
Role: "test",
Address: "https://foobar.com:8200",
AuthPath: "test",
AuthType: "",
},
}, false,
},
Expand Down
37 changes: 35 additions & 2 deletions agent-inject/agent/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,17 @@ const (
// method.
AnnotationVaultRole = "vault.hashicorp.com/role"

// AnnotationVaultAuthPath specifies the mount path to be used for the Kubernetes auto-auth
// method.
// AnnotationVaultAuthType specifies the auto-auth method type to be used.
AnnotationVaultAuthType = "vault.hashicorp.com/auth-type"

// AnnotationVaultAuthPath specifies the mount path to be used for the auto-auth method.
AnnotationVaultAuthPath = "vault.hashicorp.com/auth-path"

// AnnotationVaultAuthConfig specifies the Auto Auth Method configuration parameters.
// The name of the parameter is any unique string after "vault.hashicorp.com/auth-config-",
// such as "vault.hashicorp.com/auth-config-foobar".
AnnotationVaultAuthConfig = "vault.hashicorp.com/auth-config"

// AnnotationVaultSecretVolumePath specifies where the secrets are to be
// Mounted after fetching.
AnnotationVaultSecretVolumePath = "vault.hashicorp.com/secret-volume-path"
Expand Down Expand Up @@ -207,6 +214,7 @@ const (
type AgentConfig struct {
Image string
Address string
AuthType string
AuthPath string
Namespace string
RevokeOnShutdown bool
Expand Down Expand Up @@ -250,6 +258,13 @@ func Init(pod *corev1.Pod, cfg AgentConfig) error {
pod.ObjectMeta.Annotations[AnnotationVaultService] = cfg.Address
}

if _, ok := pod.ObjectMeta.Annotations[AnnotationVaultAuthType]; !ok {
if cfg.AuthType == "" {
cfg.AuthType = DefaultVaultAuthType
}
pod.ObjectMeta.Annotations[AnnotationVaultAuthType] = cfg.AuthType
}

if _, ok := pod.ObjectMeta.Annotations[AnnotationVaultAuthPath]; !ok {
pod.ObjectMeta.Annotations[AnnotationVaultAuthPath] = cfg.AuthPath
}
Expand Down Expand Up @@ -534,3 +549,21 @@ func (a *Agent) agentCacheEnable() (bool, error) {

return strconv.ParseBool(raw)
}

func (a *Agent) authConfig() map[string]interface{} {
authConfig := make(map[string]interface{})

prefix := fmt.Sprintf("%s-", AnnotationVaultAuthConfig)
for annotation, value := range a.Annotations {
if strings.HasPrefix(annotation, prefix) {
param := strings.TrimPrefix(annotation, prefix)
param = strings.ReplaceAll(param, "-", "_")
authConfig[param] = value
}
}
if a.Vault.Role != "" {
authConfig["role"] = a.Vault.Role
}

return authConfig
}
86 changes: 74 additions & 12 deletions agent-inject/agent/annotations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func TestInitCanSet(t *testing.T) {
pod := testPod(annotations)

agentConfig := AgentConfig{
"foobar-image", "http://foobar:8200", "test", "test", true, "100", "1000",
"foobar-image", "http://foobar:8200", DefaultVaultAuthType, "test", "test", true, "100", "1000",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "http://proxy:3128",
}
err := Init(pod, agentConfig)
Expand Down Expand Up @@ -56,7 +56,7 @@ func TestInitDefaults(t *testing.T) {
pod := testPod(annotations)

agentConfig := AgentConfig{
"", "http://foobar:8200", "test", "test", true, "", "",
"", "http://foobar:8200", DefaultVaultAuthType, "test", "test", true, "", "",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "",
}
err := Init(pod, agentConfig)
Expand Down Expand Up @@ -90,7 +90,7 @@ func TestInitError(t *testing.T) {
pod := testPod(annotations)

agentConfig := AgentConfig{
"image", "", "authPath", "namespace", true, "100", "1000",
"image", "", DefaultVaultAuthType, "authPath", "namespace", true, "100", "1000",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "",
}
err := Init(pod, agentConfig)
Expand Down Expand Up @@ -154,7 +154,7 @@ func TestSecretAnnotationsWithPreserveCaseSensitivityFlagOff(t *testing.T) {
var patches []*jsonpatch.JsonPatchOperation

agentConfig := AgentConfig{
"", "http://foobar:8200", "test", "test", true, "100", "1000",
"", "http://foobar:8200", DefaultVaultAuthType, "test", "test", true, "100", "1000",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "",
}
err := Init(pod, agentConfig)
Expand Down Expand Up @@ -205,7 +205,7 @@ func TestSecretAnnotationsWithPreserveCaseSensitivityFlagOn(t *testing.T) {
var patches []*jsonpatch.JsonPatchOperation

agentConfig := AgentConfig{
"", "http://foobar:8200", "test", "test", true, "100", "1000",
"", "http://foobar:8200", DefaultVaultAuthType, "test", "test", true, "100", "1000",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "",
}
err := Init(pod, agentConfig)
Expand Down Expand Up @@ -292,7 +292,7 @@ func TestSecretLocationFileAnnotations(t *testing.T) {
var patches []*jsonpatch.JsonPatchOperation

agentConfig := AgentConfig{
"", "http://foobar:8200", "test", "test", true, "100", "1000",
"", "http://foobar:8200", DefaultVaultAuthType, "test", "test", true, "100", "1000",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "",
}
err := Init(pod, agentConfig)
Expand Down Expand Up @@ -377,7 +377,7 @@ func TestSecretTemplateAnnotations(t *testing.T) {
var patches []*jsonpatch.JsonPatchOperation

agentConfig := AgentConfig{
"", "http://foobar:8200", "test", "test", true, "100", "1000",
"", "http://foobar:8200", DefaultVaultAuthType, "test", "test", true, "100", "1000",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "",
}
err := Init(pod, agentConfig)
Expand Down Expand Up @@ -437,7 +437,7 @@ func TestTemplateShortcuts(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
pod := testPod(tt.annotations)
agentConfig := AgentConfig{
"", "http://foobar:8200", "test", "test", true, "100", "1000",
"", "http://foobar:8200", DefaultVaultAuthType, "test", "test", true, "100", "1000",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "",
}
err := Init(pod, agentConfig)
Expand Down Expand Up @@ -497,7 +497,7 @@ func TestSecretCommandAnnotations(t *testing.T) {
for _, tt := range tests {
pod := testPod(tt.annotations)
agentConfig := AgentConfig{
"", "http://foobar:8200", "test", "test", true, "100", "1000",
"", "http://foobar:8200", DefaultVaultAuthType, "test", "test", true, "100", "1000",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "",
}
err := Init(pod, agentConfig)
Expand Down Expand Up @@ -630,7 +630,7 @@ func TestCouldErrorAnnotations(t *testing.T) {
var patches []*jsonpatch.JsonPatchOperation

agentConfig := AgentConfig{
"", "http://foobar:8200", "test", "test", true, "100", "1000",
"", "http://foobar:8200", DefaultVaultAuthType, "test", "test", true, "100", "1000",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "",
}
err := Init(pod, agentConfig)
Expand All @@ -651,7 +651,7 @@ func TestInitEmptyPod(t *testing.T) {
var pod *corev1.Pod

agentConfig := AgentConfig{
"foobar-image", "http://foobar:8200", "test", "test", true, "100", "1000",
"foobar-image", "http://foobar:8200", DefaultVaultAuthType, "test", "test", true, "100", "1000",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "",
}
err := Init(pod, agentConfig)
Expand Down Expand Up @@ -680,7 +680,7 @@ func TestVaultNamespaceAnnotation(t *testing.T) {
var patches []*jsonpatch.JsonPatchOperation

agentConfig := AgentConfig{
"foobar-image", "http://foobar:8200", "test", "test", true, "100", "1000",
"foobar-image", "http://foobar:8200", DefaultVaultAuthType, "test", "test", true, "100", "1000",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "",
}
err := Init(pod, agentConfig)
Expand Down Expand Up @@ -794,3 +794,65 @@ func Test_runAsSameID(t *testing.T) {
})
}
}

func TestAuthConfigAnnotations(t *testing.T) {
tests := []struct {
annotations map[string]string
expectedAuthConfig map[string]interface{}
}{
{
map[string]string{
"vault.hashicorp.com/role": "backwardscompat",
},
map[string]interface{}{
"role": "backwardscompat",
},
},
{
map[string]string{
"vault.hashicorp.com/role": "backwardscompat",
"vault.hashicorp.com/auth-config-role": "lowerprio",
},
map[string]interface{}{
"role": "backwardscompat",
},
},
{
map[string]string{
"vault.hashicorp.com/auth-config-name": "foo",
"vault.hashicorp.com/auth-config-ca-cert": "bar",
"vault.hashicorp.com/auth-config-client_cert": "baz",
"vault.hashicorp.com/auth-config-credential_poll_interval": "1",
"vault.hashicorp.com/auth-config-remove_secret_id_file_after_reading": "false",
},
map[string]interface{}{
"name": "foo",
"ca_cert": "bar", // param name dashes converted to underscores for ease
"client_cert": "baz",
"credential_poll_interval": "1", // string->int conversion left up to consuming app HCL parser
"remove_secret_id_file_after_reading": "false", // string->bool, same as above
},
},
}

for _, tt := range tests {
pod := testPod(tt.annotations)
var patches []*jsonpatch.JsonPatchOperation

agentConfig := AgentConfig{
"", "http://foobar:8200", DefaultVaultAuthType, "test", "test", true, "100", "1000",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "",
}
err := Init(pod, agentConfig)
if err != nil {
t.Errorf("got error, shouldn't have: %s", err)
}

agent, err := New(pod, patches)
if err != nil {
t.Errorf("got error, shouldn't have: %s", err)
}

require.Equal(t, agent.Vault.AuthConfig, tt.expectedAuthConfig, "expected AuthConfig %v, got %v", tt.expectedAuthConfig, agent.Vault.AuthConfig)
}
}
6 changes: 2 additions & 4 deletions agent-inject/agent/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,10 @@ func (a *Agent) newConfig(init bool) ([]byte, error) {
},
AutoAuth: &AutoAuth{
Method: &Method{
Type: "kubernetes",
Type: a.Vault.AuthType,
Namespace: a.Vault.Namespace,
MountPath: a.Vault.AuthPath,
Config: map[string]interface{}{
"role": a.Vault.Role,
},
Config: a.Vault.AuthConfig,
},
Sinks: []*Sink{
{
Expand Down
Loading