-
Notifications
You must be signed in to change notification settings - Fork 4
/
main.go
144 lines (125 loc) · 4.22 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/*
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
limitations under the License.
*/
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"path/filepath"
"gopkg.in/yaml.v2"
admission "k8s.io/api/admission/v1beta1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
const (
tlsDir = `/run/secrets/tls`
tlsCertFile = `tls.crt`
tlsKeyFile = `tls.key`
)
var (
podResource = metav1.GroupVersionResource{Version: "v1", Resource: "pods"}
config Config
)
// Config program
type Config struct {
VerboseLogs bool `yaml:"verboseLogs"`
ContainersAllowed map[string]bool `yaml:"containersAllowed"`
}
// Enable debug logging
func init() {
filename, err := filepath.Abs("/run/config/config.yml")
yamlFile, err := ioutil.ReadFile(filename)
err = yaml.Unmarshal(yamlFile, &config)
if err != nil {
panic(err)
}
}
// In cluster config
func kubeClient() kubernetes.Clientset {
// Create the in-cluster config
config, err := rest.InClusterConfig()
if err != nil {
panic(err)
}
// Create the client set
ks, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err)
}
return *ks
}
// applyEnvPatching adds environment variables from a secret that is created later in the pods/binding event.
// The admission controller will patch every pod that is created outside of Kubernetes namespaces with an envFrom
// reference that is appended to existing configurations, checking that the container is allowed to receive the updates.
// The secret name must be created upfront, and we store it in a pod's metadata label.
func mutateDispatch(req *admission.AdmissionRequest) ([]patchOperation, error) {
// If this gets invoked on an object of a different kind, then log a message and return an empty patch, allowing the
// request to pass through unchanged.
if req.Resource != podResource {
log.Printf("pass the buck on %s, only admit %s ATM.", req.Resource, podResource)
return nil, nil
}
var err error
var patches []patchOperation
// Parse the request object and patch the pod with secret reference at pod creation event
if (req.RequestKind.Kind == "Pod") && (req.Operation == "CREATE" || req.Operation == "UPDATE") {
raw := req.Object.Raw
pod := corev1.Pod{}
if _, _, err := universalDeserializer.Decode(raw, nil, &pod); err != nil {
return nil, fmt.Errorf("could not deserialize pod object: %v", err)
}
patches = patchPod(pod)
}
// Parse the request object and create secret that holds the environment variables at pod binding event
if (req.RequestKind.Kind == "Binding") && (req.Operation == "CREATE") {
raw := req.Object.Raw
binding := corev1.Binding{}
if _, _, err := universalDeserializer.Decode(raw, nil, &binding); err != nil {
return nil, fmt.Errorf("could not deserialize binding object: %v", err)
}
err = createSecret(binding)
if err != nil {
return nil, err
}
}
// Parse the request old object and delete envs secret at pod delete event
if (req.RequestKind.Kind == "Pod") && (req.Operation == "DELETE") {
pod := corev1.Pod{}
raw := req.OldObject.Raw
if _, _, err := universalDeserializer.Decode(raw, nil, &pod); err != nil {
return nil, fmt.Errorf("could not deserialize pod object: %v", err)
}
err = deleteSecret(pod)
if err != nil {
return nil, err
}
}
return patches, err
}
func main() {
certPath := filepath.Join(tlsDir, tlsCertFile)
keyPath := filepath.Join(tlsDir, tlsKeyFile)
log.Println("starting admission controller ...")
mux := http.NewServeMux()
mux.Handle("/mutate", admitFuncHandler(mutateDispatch))
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("ok"))
})
server := &http.Server{
Addr: ":8443",
Handler: mux,
}
log.Fatal(server.ListenAndServeTLS(certPath, keyPath))
}