2016-07-13 21:10:10 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2016-07-25 14:24:22 +00:00
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"strings"
|
2017-11-09 10:10:35 +00:00
|
|
|
"time"
|
2017-09-05 20:10:42 +00:00
|
|
|
|
|
|
|
"github.com/cri-o/ocicni/pkg/ocicni"
|
2017-11-30 15:46:11 +00:00
|
|
|
"github.com/kubernetes-incubator/cri-o/lib/sandbox"
|
2017-11-09 10:10:35 +00:00
|
|
|
"github.com/kubernetes-incubator/cri-o/server/metrics"
|
2017-11-30 10:52:30 +00:00
|
|
|
"github.com/opencontainers/image-spec/specs-go/v1"
|
2017-09-06 11:25:19 +00:00
|
|
|
"github.com/opencontainers/runtime-tools/validate"
|
|
|
|
"github.com/syndtr/gocapability/capability"
|
2018-02-12 20:13:07 +00:00
|
|
|
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
2016-07-13 21:10:10 +00:00
|
|
|
)
|
|
|
|
|
2016-09-19 07:21:14 +00:00
|
|
|
const (
|
|
|
|
// According to http://man7.org/linux/man-pages/man5/resolv.conf.5.html:
|
|
|
|
// "The search list is currently limited to six domains with a total of 256 characters."
|
|
|
|
maxDNSSearches = 6
|
2017-11-11 11:00:48 +00:00
|
|
|
|
|
|
|
maxLabelSize = 4096
|
2016-09-19 07:21:14 +00:00
|
|
|
)
|
|
|
|
|
2016-07-25 14:24:22 +00:00
|
|
|
func copyFile(src, dest string) error {
|
|
|
|
in, err := os.Open(src)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer in.Close()
|
|
|
|
|
|
|
|
out, err := os.Create(dest)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer out.Close()
|
|
|
|
|
2016-09-19 07:21:14 +00:00
|
|
|
_, err = io.Copy(out, in)
|
|
|
|
return err
|
2016-07-25 14:24:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func removeFile(path string) error {
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
|
|
if err := os.Remove(path); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-10-24 14:57:40 +00:00
|
|
|
func parseDNSOptions(servers, searches, options []string, path string) error {
|
2016-07-25 14:24:22 +00:00
|
|
|
nServers := len(servers)
|
|
|
|
nSearches := len(searches)
|
2016-10-24 14:57:40 +00:00
|
|
|
nOptions := len(options)
|
|
|
|
if nServers == 0 && nSearches == 0 && nOptions == 0 {
|
2016-07-25 14:24:22 +00:00
|
|
|
return copyFile("/etc/resolv.conf", path)
|
|
|
|
}
|
|
|
|
|
|
|
|
if nSearches > maxDNSSearches {
|
|
|
|
return fmt.Errorf("DNSOption.Searches has more than 6 domains")
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.Create(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
if nSearches > 0 {
|
|
|
|
data := fmt.Sprintf("search %s\n", strings.Join(searches, " "))
|
|
|
|
_, err = f.Write([]byte(data))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if nServers > 0 {
|
|
|
|
data := fmt.Sprintf("nameserver %s\n", strings.Join(servers, "\nnameserver "))
|
|
|
|
_, err = f.Write([]byte(data))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-24 14:57:40 +00:00
|
|
|
if nOptions > 0 {
|
|
|
|
data := fmt.Sprintf("options %s\n", strings.Join(options, " "))
|
|
|
|
_, err = f.Write([]byte(data))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-25 14:24:22 +00:00
|
|
|
return nil
|
|
|
|
}
|
2016-11-19 02:16:50 +00:00
|
|
|
|
|
|
|
// TODO: remove sysctl extraction related code here, instead we import from k8s directly.
|
|
|
|
|
|
|
|
const (
|
|
|
|
// SysctlsPodAnnotationKey represents the key of sysctls which are set for the infrastructure
|
|
|
|
// container of a pod. The annotation value is a comma separated list of sysctl_name=value
|
|
|
|
// key-value pairs. Only a limited set of whitelisted and isolated sysctls is supported by
|
|
|
|
// the kubelet. Pods with other sysctls will fail to launch.
|
|
|
|
SysctlsPodAnnotationKey string = "security.alpha.kubernetes.io/sysctls"
|
|
|
|
|
|
|
|
// UnsafeSysctlsPodAnnotationKey represents the key of sysctls which are set for the infrastructure
|
|
|
|
// container of a pod. The annotation value is a comma separated list of sysctl_name=value
|
|
|
|
// key-value pairs. Unsafe sysctls must be explicitly enabled for a kubelet. They are properly
|
|
|
|
// namespaced to a pod or a container, but their isolation is usually unclear or weak. Their use
|
|
|
|
// is at-your-own-risk. Pods that attempt to set an unsafe sysctl that is not enabled for a kubelet
|
|
|
|
// will fail to launch.
|
|
|
|
UnsafeSysctlsPodAnnotationKey string = "security.alpha.kubernetes.io/unsafe-sysctls"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Sysctl defines a kernel parameter to be set
|
|
|
|
type Sysctl struct {
|
|
|
|
// Name of a property to set
|
|
|
|
Name string `json:"name"`
|
|
|
|
// Value of a property to set
|
|
|
|
Value string `json:"value"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// SysctlsFromPodAnnotations parses the sysctl annotations into a slice of safe Sysctls
|
|
|
|
// and a slice of unsafe Sysctls. This is only a convenience wrapper around
|
|
|
|
// SysctlsFromPodAnnotation.
|
|
|
|
func SysctlsFromPodAnnotations(a map[string]string) ([]Sysctl, []Sysctl, error) {
|
|
|
|
safe, err := SysctlsFromPodAnnotation(a[SysctlsPodAnnotationKey])
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
unsafe, err := SysctlsFromPodAnnotation(a[UnsafeSysctlsPodAnnotationKey])
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return safe, unsafe, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SysctlsFromPodAnnotation parses an annotation value into a slice of Sysctls.
|
|
|
|
func SysctlsFromPodAnnotation(annotation string) ([]Sysctl, error) {
|
|
|
|
if len(annotation) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
kvs := strings.Split(annotation, ",")
|
|
|
|
sysctls := make([]Sysctl, len(kvs))
|
|
|
|
for i, kv := range kvs {
|
|
|
|
cs := strings.Split(kv, "=")
|
|
|
|
if len(cs) != 2 || len(cs[0]) == 0 {
|
|
|
|
return nil, fmt.Errorf("sysctl %q not of the format sysctl_name=value", kv)
|
|
|
|
}
|
|
|
|
sysctls[i].Name = cs[0]
|
|
|
|
sysctls[i].Value = cs[1]
|
|
|
|
}
|
|
|
|
return sysctls, nil
|
|
|
|
}
|
2017-09-05 20:10:42 +00:00
|
|
|
|
2017-09-21 08:17:39 +00:00
|
|
|
func newPodNetwork(sb *sandbox.Sandbox) ocicni.PodNetwork {
|
2017-09-05 20:10:42 +00:00
|
|
|
return ocicni.PodNetwork{
|
2017-09-21 08:17:39 +00:00
|
|
|
Name: sb.KubeName(),
|
|
|
|
Namespace: sb.Namespace(),
|
|
|
|
ID: sb.ID(),
|
|
|
|
NetNS: sb.NetNsPath(),
|
2017-09-05 20:10:42 +00:00
|
|
|
}
|
|
|
|
}
|
2017-09-06 11:25:19 +00:00
|
|
|
|
|
|
|
// inStringSlice checks whether a string is inside a string slice.
|
|
|
|
// Comparison is case insensitive.
|
|
|
|
func inStringSlice(ss []string, str string) bool {
|
|
|
|
for _, s := range ss {
|
|
|
|
if strings.ToLower(s) == strings.ToLower(str) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// getOCICapabilitiesList returns a list of all available capabilities.
|
|
|
|
func getOCICapabilitiesList() []string {
|
|
|
|
var caps []string
|
|
|
|
for _, cap := range capability.List() {
|
|
|
|
if cap > validate.LastCap() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
caps = append(caps, "CAP_"+strings.ToUpper(cap.String()))
|
|
|
|
}
|
|
|
|
return caps
|
|
|
|
}
|
2017-11-09 10:10:35 +00:00
|
|
|
|
|
|
|
func recordOperation(operation string, start time.Time) {
|
|
|
|
metrics.CRIOOperations.WithLabelValues(operation).Inc()
|
|
|
|
metrics.CRIOOperationsLatency.WithLabelValues(operation).Observe(metrics.SinceInMicroseconds(start))
|
|
|
|
}
|
|
|
|
|
|
|
|
// recordError records error for metric if an error occurred.
|
|
|
|
func recordError(operation string, err error) {
|
|
|
|
if err != nil {
|
|
|
|
// TODO(runcom): handle timeout from ctx as well
|
|
|
|
metrics.CRIOOperationsErrors.WithLabelValues(operation).Inc()
|
|
|
|
}
|
|
|
|
}
|
2017-11-11 11:00:48 +00:00
|
|
|
|
|
|
|
func validateLabels(labels map[string]string) error {
|
|
|
|
for k, v := range labels {
|
|
|
|
if (len(k) + len(v)) > maxLabelSize {
|
|
|
|
if len(k) > 10 {
|
|
|
|
k = k[:10]
|
|
|
|
}
|
|
|
|
return fmt.Errorf("label key and value greater than maximum size (%d bytes), key: %s", maxLabelSize, k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2017-11-30 10:52:30 +00:00
|
|
|
|
|
|
|
func mergeEnvs(imageConfig *v1.Image, kubeEnvs []*pb.KeyValue) []string {
|
|
|
|
envs := []string{}
|
|
|
|
if kubeEnvs == nil && imageConfig != nil {
|
|
|
|
envs = imageConfig.Config.Env
|
|
|
|
} else {
|
|
|
|
for _, item := range kubeEnvs {
|
|
|
|
if item.GetKey() == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
envs = append(envs, item.GetKey()+"="+item.GetValue())
|
|
|
|
}
|
|
|
|
if imageConfig != nil {
|
|
|
|
for _, imageEnv := range imageConfig.Config.Env {
|
|
|
|
var found bool
|
|
|
|
parts := strings.SplitN(imageEnv, "=", 2)
|
|
|
|
if len(parts) != 2 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
imageEnvKey := parts[0]
|
|
|
|
if imageEnvKey == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, kubeEnv := range envs {
|
|
|
|
kubeEnvKey := strings.SplitN(kubeEnv, "=", 2)[0]
|
|
|
|
if kubeEnvKey == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if imageEnvKey == kubeEnvKey {
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
|
|
|
envs = append(envs, imageEnv)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return envs
|
|
|
|
}
|