penguin/monok8s

k8s image for Mono Gateway Dev Kit

clitools/pkg/render/sshd.go

raw ยท 7110 bytes

package render

import (
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/intstr"

	monov1alpha1 "example.com/monok8s/pkg/apis/monok8s/v1alpha1"
	"example.com/monok8s/pkg/templates"
)

const (
	sshdName       = "sshd"
	sshdConfigName = "sshd-authorized-keys"
	sshdNodePort   = int32(30022)
)

func RenderSSHDDeployments(namespace, authKeys string) (string, error) {
	vals := templates.LoadTemplateValuesFromEnv()

	labels := map[string]string{
		"app.kubernetes.io/name":       sshdName,
		"app.kubernetes.io/component":  "host-access",
		"app.kubernetes.io/part-of":    "monok8s",
		"app.kubernetes.io/managed-by": monov1alpha1.NodeControlName,
	}

	objs := []runtime.Object{
		buildSSHDConfigMap(authKeys, namespace, labels),
		buildSSHDService(vals, namespace, labels),
		buildSSHDDeployment(vals, namespace, labels),
	}

	return renderObjects(objs)
}

func buildSSHDConfigMap(
	authorizedKeys string,
	namespace string,
	labels map[string]string,
) *corev1.ConfigMap {

	return &corev1.ConfigMap{
		TypeMeta: metav1.TypeMeta{
			APIVersion: "v1",
			Kind:       "ConfigMap",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      sshdConfigName,
			Namespace: namespace,
			Labels:    labels,
		},
		Data: map[string]string{
			"authorized_keys": authorizedKeys,
		},
	}
}

func buildSSHDService(
	tVals templates.TemplateValues,
	namespace string,
	labels map[string]string,
) *corev1.Service {
	selectorLabels := map[string]string{
		monov1alpha1.NodeControlKey: "true",
		"kubernetes.io/hostname":    tVals.NodeName,
	}

	return &corev1.Service{
		TypeMeta: metav1.TypeMeta{
			APIVersion: "v1",
			Kind:       "Service",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      sshdName,
			Namespace: namespace,
			Labels:    labels,
		},
		Spec: corev1.ServiceSpec{
			Type:     corev1.ServiceTypeNodePort,
			Selector: selectorLabels,
			Ports: []corev1.ServicePort{
				{
					Name:       "ssh",
					Protocol:   corev1.ProtocolTCP,
					Port:       22,
					TargetPort: intstr.FromInt32(22),
					NodePort:   sshdNodePort,
				},
			},
		},
	}
}

func buildSSHDDeployment(
	tVals templates.TemplateValues,
	namespace string,
	labels map[string]string,
) *appsv1.Deployment {
	replicas := int32(1)

	selectorLabels := map[string]string{
		monov1alpha1.NodeControlKey: "true",
		"kubernetes.io/hostname":    tVals.NodeName,
	}

	podLabels := mergeStringMaps(labels, selectorLabels)

	runAsUser := int64(0)
	runAsNonRoot := false
	privileged := true
	allowPrivilegeEscalation := true
	readOnlyRootFilesystem := false

	return &appsv1.Deployment{
		TypeMeta: metav1.TypeMeta{
			APIVersion: "apps/v1",
			Kind:       "Deployment",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      sshdName,
			Namespace: namespace,
			Labels:    labels,
		},
		Spec: appsv1.DeploymentSpec{
			Replicas: &replicas,
			Selector: &metav1.LabelSelector{
				MatchLabels: selectorLabels,
			},
			Template: corev1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: podLabels,
				},
				Spec: corev1.PodSpec{
					HostPID:      true,
					NodeSelector: selectorLabels,
					Containers: []corev1.Container{
						{
							Name:  sshdName,
							Image: "alpine:latest",
							Command: []string{
								"/bin/sh",
								"-ceu",
								`
apk add --no-cache openssh-server

mkdir -p /run/sshd
mkdir -p /root/.ssh

cp /authorized-keys/authorized_keys /root/.ssh/authorized_keys
chmod 700 /root/.ssh
chmod 600 /root/.ssh/authorized_keys

ssh-keygen -A

exec /usr/sbin/sshd \
  -D \
  -e \
  -p 22 \
  -o PermitRootLogin=prohibit-password \
  -o PasswordAuthentication=no \
  -o KbdInteractiveAuthentication=no \
  -o PubkeyAuthentication=yes \
  -o AuthorizedKeysFile=/root/.ssh/authorized_keys
`,
							},
							Ports: []corev1.ContainerPort{
								{
									Name:          "ssh",
									ContainerPort: 22,
									Protocol:      corev1.ProtocolTCP,
								},
							},
							SecurityContext: &corev1.SecurityContext{
								RunAsUser:                &runAsUser,
								RunAsNonRoot:             &runAsNonRoot,
								Privileged:               &privileged,
								AllowPrivilegeEscalation: &allowPrivilegeEscalation,
								ReadOnlyRootFilesystem:   &readOnlyRootFilesystem,
							},
							Resources: corev1.ResourceRequirements{
								Requests: corev1.ResourceList{
									corev1.ResourceCPU:    resource.MustParse("10m"),
									corev1.ResourceMemory: resource.MustParse("32Mi"),
								},
								Limits: corev1.ResourceList{
									corev1.ResourceCPU:    resource.MustParse("200m"),
									corev1.ResourceMemory: resource.MustParse("128Mi"),
								},
							},
							VolumeMounts: append(
								[]corev1.VolumeMount{
									{
										Name:      "authorized-keys",
										MountPath: "/authorized-keys",
										ReadOnly:  true,
									},
								},
								buildHostRootVolumeMounts()...,
							),
						},
					},
					Volumes: append(
						[]corev1.Volume{
							{
								Name: "authorized-keys",
								VolumeSource: corev1.VolumeSource{
									ConfigMap: &corev1.ConfigMapVolumeSource{
										LocalObjectReference: corev1.LocalObjectReference{
											Name: sshdConfigName,
										},
										DefaultMode: ptrInt32(0600),
									},
								},
							},
						},
						buildHostRootVolumes()...,
					),
				},
			},
		},
	}
}

func buildHostRootVolumeMounts() []corev1.VolumeMount {
	paths := []struct {
		name      string
		mountPath string
		readOnly  bool
	}{
		{"host-bin", "/host/bin", true},
		{"host-sbin", "/host/sbin", true},
		{"host-lib", "/host/lib", true},
		{"host-usr", "/host/usr", true},
		{"host-etc", "/host/etc", false},
		{"host-run", "/host/run", false},
		{"host-proc", "/host/proc", false},
		{"host-sys", "/host/sys", false},
		{"host-dev", "/host/dev", false},
		{"host-var", "/host/var", false},
	}

	mounts := make([]corev1.VolumeMount, 0, len(paths))

	for _, p := range paths {
		mounts = append(mounts, corev1.VolumeMount{
			Name:      p.name,
			MountPath: p.mountPath,
			ReadOnly:  p.readOnly,
		})
	}

	return mounts
}

func buildHostRootVolumes() []corev1.Volume {
	hostPathDir := corev1.HostPathDirectory

	paths := []struct {
		name string
		path string
	}{
		{"host-bin", "/bin"},
		{"host-sbin", "/sbin"},
		{"host-lib", "/lib"},
		{"host-usr", "/usr"},
		{"host-etc", "/etc"},
		{"host-run", "/run"},
		{"host-proc", "/proc"},
		{"host-sys", "/sys"},
		{"host-dev", "/dev"},

		// /var is an rbind mount in monok8s and may be private.
		// Mount the real backing path instead.
		{"host-var", "/data/var"},
	}

	volumes := make([]corev1.Volume, 0, len(paths))

	for _, p := range paths {
		volumes = append(volumes, corev1.Volume{
			Name: p.name,
			VolumeSource: corev1.VolumeSource{
				HostPath: &corev1.HostPathVolumeSource{
					Path: p.path,
					Type: &hostPathDir,
				},
			},
		})
	}

	return volumes
}

func ptrInt32(v int32) *int32 {
	return &v
}

func ptrHostPathType(v corev1.HostPathType) *corev1.HostPathType {
	return &v
}