Skip to content

Commit 972c6f1

Browse files
feat: Support injection into user-defined init containers
1 parent 796585a commit 972c6f1

File tree

11 files changed

+1265
-39
lines changed

11 files changed

+1265
-39
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,15 @@ annotations:
184184
operator.1password.io/inject: "app-example1"
185185
```
186186

187+
To inject secrets into init containers, add the `operator.1password.io/injector-init-first: "true"` annotation. This ensures the 1Password init container runs first, making secrets available to your init containers:
188+
189+
```yaml
190+
annotations:
191+
# List both regular and init containers
192+
operator.1password.io/inject: "app-example1,init-db-migration"
193+
operator.1password.io/injector-init-first: "true"
194+
```
195+
187196
### Step 5: Configure the resource's environment
188197
189198
Add an environment variable to the resource with a value referencing your 1Password item. Use the following secret reference syntax: `op://<vault>/<item>[/section]/<field>`.

deploy/kustomization.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ resources:
44
- permissions.yaml
55
- deployment.yaml
66
- service.yaml
7+
namespace: default

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.20
44

55
require (
66
github.com/golang/glog v1.1.1
7+
github.com/google/go-cmp v0.5.9
78
github.com/onsi/ginkgo/v2 v2.9.2
89
github.com/onsi/gomega v1.27.5
910
github.com/stretchr/testify v1.8.2
@@ -24,7 +25,6 @@ require (
2425
github.com/gogo/protobuf v1.3.2 // indirect
2526
github.com/golang/protobuf v1.5.3 // indirect
2627
github.com/google/gnostic v0.6.9 // indirect
27-
github.com/google/go-cmp v0.5.9 // indirect
2828
github.com/google/gofuzz v1.2.0 // indirect
2929
github.com/google/pprof v0.0.0-20230323073829-e72429f035bd // indirect
3030
github.com/google/uuid v1.3.0 // indirect

pkg/webhook/webhook.go

Lines changed: 71 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,10 @@ var (
5757
)
5858

5959
const (
60-
injectionStatus = "operator.1password.io/status"
61-
injectAnnotation = "operator.1password.io/inject"
62-
versionAnnotation = "operator.1password.io/version"
60+
injectionStatus = "operator.1password.io/status"
61+
injectAnnotation = "operator.1password.io/inject"
62+
injectorInitFirstAnnotation = "operator.1password.io/injector-init-first"
63+
versionAnnotation = "operator.1password.io/version"
6364
)
6465

6566
type SecretInjector struct {
@@ -100,21 +101,20 @@ func mutationRequired(metadata *metav1.ObjectMeta) bool {
100101
}
101102

102103
func addContainers(target, added []corev1.Container, basePath string) (patch []patchOperation) {
103-
first := len(target) == 0
104-
var value interface{}
105-
for _, add := range added {
106-
value = add
107-
path := basePath
108-
if first {
109-
first = false
110-
value = []corev1.Container{add}
111-
} else {
112-
path = path + "/-"
113-
}
104+
if len(target) == 0 {
114105
patch = append(patch, patchOperation{
115106
Op: "add",
116-
Path: path,
117-
Value: value,
107+
Path: basePath,
108+
Value: added,
109+
})
110+
return patch
111+
}
112+
113+
for _, c := range added {
114+
patch = append(patch, patchOperation{
115+
Op: "add",
116+
Path: basePath + "/-",
117+
Value: c,
118118
})
119119
}
120120
return patch
@@ -210,24 +210,40 @@ func (s *SecretInjector) mutate(ar *admissionv1.AdmissionReview) *admissionv1.Ad
210210
mutated := false
211211

212212
var patch []patchOperation
213-
for i := range pod.Spec.InitContainers {
214-
c := pod.Spec.InitContainers[i]
215-
_, mutate := containers[c.Name]
216-
if !mutate {
217-
continue
218-
}
219-
didMutate, initContainerPatch, err := s.mutateContainer(ctx, &c, i)
220-
if err != nil {
221-
return &admissionv1.AdmissionResponse{
222-
Result: &metav1.Status{
223-
Message: err.Error(),
224-
},
213+
214+
mutateInitContainers := false
215+
if value, exists := pod.Annotations[injectorInitFirstAnnotation]; exists && strings.ToLower(value) == "true" {
216+
mutateInitContainers = true
217+
}
218+
219+
if mutateInitContainers {
220+
for i := range pod.Spec.InitContainers {
221+
c := pod.Spec.InitContainers[i]
222+
// do not mutate our own init container
223+
if c.Name == "copy-op-bin" {
224+
continue
225225
}
226+
_, mutate := containers[c.Name]
227+
if !mutate {
228+
continue
229+
}
230+
didMutate, initContainerPatch, err := s.mutateContainer(ctx, &c, i)
231+
if err != nil {
232+
return &admissionv1.AdmissionResponse{
233+
Result: &metav1.Status{
234+
Message: err.Error(),
235+
},
236+
}
237+
}
238+
if didMutate {
239+
mutated = true
240+
}
241+
242+
for i := range initContainerPatch {
243+
initContainerPatch[i].Path = strings.Replace(initContainerPatch[i].Path, "/spec/containers", "/spec/initContainers", 1)
244+
}
245+
patch = append(patch, initContainerPatch...)
226246
}
227-
if didMutate {
228-
mutated = true
229-
}
230-
patch = append(patch, initContainerPatch...)
231247
}
232248

233249
for i := range pod.Spec.Containers {
@@ -297,15 +313,35 @@ func (s *SecretInjector) mutate(ar *admissionv1.AdmissionReview) *admissionv1.Ad
297313

298314
// create mutation patch for resources
299315
func createOPCLIPatch(pod *corev1.Pod, containers []corev1.Container, patch []patchOperation) ([]byte, error) {
300-
301316
annotations := map[string]string{injectionStatus: "injected"}
302317
patch = append(patch, addVolume(pod.Spec.Volumes, []corev1.Volume{binVolume}, "/spec/volumes")...)
303-
patch = append(patch, addContainers(pod.Spec.InitContainers, containers, "/spec/initContainers")...)
304-
patch = append(patch, updateAnnotation(pod.Annotations, annotations)...)
305318

319+
if value, exists := pod.Annotations[injectorInitFirstAnnotation]; exists && strings.ToLower(value) == "true" {
320+
patch = append(patch, prependContainers(pod.Spec.InitContainers, containers, "/spec/initContainers")...)
321+
} else {
322+
patch = append(patch, addContainers(pod.Spec.InitContainers, containers, "/spec/initContainers")...)
323+
}
324+
325+
patch = append(patch, updateAnnotation(pod.Annotations, annotations)...)
306326
return json.Marshal(patch)
307327
}
308328

329+
// adds containers to the beginning of the target
330+
func prependContainers(target, added []corev1.Container, basePath string) (patch []patchOperation) {
331+
if len(target) == 0 {
332+
return addContainers(target, added, basePath)
333+
}
334+
335+
for i := len(added) - 1; i >= 0; i-- {
336+
patch = append(patch, patchOperation{
337+
Op: "add",
338+
Path: basePath + "/0",
339+
Value: added[i],
340+
})
341+
}
342+
return patch
343+
}
344+
309345
func isEnvVarSetup(envVarName string) func(c *corev1.Container) bool {
310346
return func(container *corev1.Container) bool {
311347
envVar := findContainerEnvVarByName(envVarName, container)

0 commit comments

Comments
 (0)