diff --git a/Makefile b/Makefile index 30a1b05..8a60843 100644 --- a/Makefile +++ b/Makefile @@ -20,10 +20,10 @@ appcat-apiserver: vshnpostgresql ## Install appcat-apiserver dependencies vshnall: vshnpostgresql vshnredis .PHONY: vshnpostgresql -vshnpostgresql: certmanager-setup stackgres-setup prometheus-setup minio-setup metallb-setup ## Install vshn postgres dependencies +vshnpostgresql: certmanager-setup stackgres-setup prometheus-setup minio-setup metallb-setup netpols-setup ## Install vshn postgres dependencies .PHONY: vshnredis -vshnredis: certmanager-setup k8up-setup ## Install vshn redis dependencies +vshnredis: certmanager-setup k8up-setup netpols-setup ## Install vshn redis dependencies .PHONY: help help: ## Show this help @@ -128,6 +128,7 @@ $(prometheus_sentinel): kind-setup-ingress --values prometheus/values.yaml \ prometheus-community/kube-prometheus-stack kubectl -n prometheus-system wait --for condition=Available deployment/kube-prometheus-kube-prome-operator --timeout 120s + kubectl apply -f prometheus/netpol.yaml @echo -e "***\n*** Installed Prometheus in http://prometheus.127.0.0.1.nip.io:8088/ and AlertManager in http://alertmanager.127.0.0.1.nip.io:8088/.\n***" @touch $@ @@ -175,3 +176,11 @@ unset-default-sc: for sc in $$(kubectl get sc -o name) ; do \ kubectl patch $$sc -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'; \ done + +netpols-setup: $(netpols_sentinel) ## Install netpols to simulate appuio's netpols + +$(netpols_sentinel): export KUBECONFIG = $(KIND_KUBECONFIG) +$(netpols_sentinel): + kubectl apply -f netpols/sync.appuio.ch_syncconfigs.yaml + kubectl apply -f netpols + touch $@ diff --git a/Makefile.vars.mk b/Makefile.vars.mk index 31554fc..f53cd1a 100644 --- a/Makefile.vars.mk +++ b/Makefile.vars.mk @@ -6,6 +6,7 @@ local_pv_sentinel = $(kind_dir)/local_pv csi_sentinel = $(kind_dir)/csi_provider metallb_sentinel = $(kind_dir)/metallb komoplane_sentinel = $(kind_dir)/komoplane +netpols_sentinel = $(kind_dir)/netpols enable_xfn = true PROJECT_ROOT_DIR = . @@ -18,7 +19,7 @@ DOCKER_CMD ?= docker ## KIND:setup # https://hub.docker.com/r/kindest/node/tags -KIND_NODE_VERSION ?= v1.28.9 +KIND_NODE_VERSION ?= v1.29.7 KIND_IMAGE ?= docker.io/kindest/node:$(KIND_NODE_VERSION) KIND_CMD ?= go run sigs.k8s.io/kind KIND_KUBECONFIG ?= $(kind_dir)/kind-kubeconfig-$(KIND_NODE_VERSION) diff --git a/netpols/00_namespace.yaml b/netpols/00_namespace.yaml new file mode 100644 index 0000000..8b55c3c --- /dev/null +++ b/netpols/00_namespace.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: system diff --git a/netpols/leader_election_role.yaml b/netpols/leader_election_role.yaml new file mode 100644 index 0000000..6539ed2 --- /dev/null +++ b/netpols/leader_election_role.yaml @@ -0,0 +1,31 @@ +# permissions to do leader election. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: leader-election-role +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/finalizers + verbs: + - update +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch diff --git a/netpols/leader_election_role_binding.yaml b/netpols/leader_election_role_binding.yaml new file mode 100644 index 0000000..eed1690 --- /dev/null +++ b/netpols/leader_election_role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: leader-election-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/netpols/manager.yaml b/netpols/manager.yaml new file mode 100644 index 0000000..ea1cbad --- /dev/null +++ b/netpols/manager.yaml @@ -0,0 +1,35 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: espejo + namespace: system +spec: + selector: + matchLabels: + control-plane: controller-manager + replicas: 1 + template: + metadata: + labels: + control-plane: controller-manager + spec: + securityContext: + runAsUser: 65532 + containers: + - name: operator + image: quay.io/vshn/espejo:v0.5.0 + args: + - --enable-leader-election + resources: + limits: + cpu: 300m + memory: 100Mi + requests: + cpu: 20m + memory: 50Mi + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + terminationGracePeriodSeconds: 10 diff --git a/netpols/netpol-controller.yaml b/netpols/netpol-controller.yaml new file mode 100644 index 0000000..900e8fb --- /dev/null +++ b/netpols/netpol-controller.yaml @@ -0,0 +1,98 @@ +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: kube-network-policies +rules: + - apiGroups: + - "" + resources: + - pods + - namespaces + verbs: + - list + - watch + - apiGroups: + - "networking.k8s.io" + resources: + - networkpolicies + verbs: + - list + - watch +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: kube-network-policies +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: kube-network-policies +subjects: +- kind: ServiceAccount + name: kube-network-policies + namespace: kube-system +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kube-network-policies + namespace: kube-system +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: kube-network-policies + namespace: kube-system + labels: + tier: node + app: kube-network-policies + k8s-app: kube-network-policies +spec: + selector: + matchLabels: + app: kube-network-policies + template: + metadata: + labels: + tier: node + app: kube-network-policies + k8s-app: kube-network-policies + spec: + hostNetwork: true + dnsPolicy: ClusterFirst + nodeSelector: + kubernetes.io/os: linux + tolerations: + - operator: Exists + effect: NoSchedule + serviceAccountName: kube-network-policies + containers: + - name: kube-network-policies + image: registry.k8s.io/networking/kube-network-policies:v0.6.0 + args: + - /bin/netpol + - --hostname-override=$(MY_NODE_NAME) + - --v=2 + - --nfqueue-id=98 + volumeMounts: + - name: lib-modules + mountPath: /lib/modules + readOnly: true + resources: + requests: + cpu: "100m" + memory: "50Mi" + securityContext: + privileged: true + capabilities: + add: ["NET_ADMIN"] + env: + - name: MY_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + volumes: + - name: lib-modules + hostPath: + path: /lib/modules diff --git a/netpols/role.yaml b/netpols/role.yaml new file mode 100644 index 0000000..ccb435f --- /dev/null +++ b/netpols/role.yaml @@ -0,0 +1,73 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: manager-role +rules: +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - list + - update +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - namespaces/status + verbs: + - get +- apiGroups: + - "" + resources: + - events + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - sync.appuio.ch + resources: + - syncconfigs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - sync.appuio.ch + resources: + - syncconfigs/status + verbs: + - get + - patch + - update +- apiGroups: + - networking.k8s.io + resources: + - networkpolicies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch diff --git a/netpols/role_binding.yaml b/netpols/role_binding.yaml new file mode 100644 index 0000000..8f26587 --- /dev/null +++ b/netpols/role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/netpols/sync.appuio.ch_syncconfigs.yaml b/netpols/sync.appuio.ch_syncconfigs.yaml new file mode 100644 index 0000000..3a2c072 --- /dev/null +++ b/netpols/sync.appuio.ch_syncconfigs.yaml @@ -0,0 +1,254 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: syncconfigs.sync.appuio.ch +spec: + group: sync.appuio.ch + names: + kind: SyncConfig + listKind: SyncConfigList + plural: syncconfigs + singular: syncconfig + preserveUnknownFields: false + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.synchronizedItemCount + name: Synced + type: integer + - jsonPath: .status.deletedItemCount + name: Deleted + type: integer + - jsonPath: .status.failedItemCount + name: Failed + type: integer + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: SyncConfig is the Schema for the syncconfigs API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: SyncConfigSpec defines the desired state of SyncConfig + properties: + deleteItems: + description: DeleteItems lists items to be deleted from targeted namespaces + items: + description: DeleteMeta defines an object by name, kind and version + properties: + apiVersion: + description: APIVersion of the item to be deleted + type: string + kind: + description: Kind of the item to be deleted + type: string + name: + description: Name of the item to be deleted + type: string + type: object + type: array + forceRecreate: + description: ForceRecreate defines if objects should be deleted and + recreated if updates fails + type: boolean + namespaceSelector: + description: NamespaceSelector defines which namespaces should be + targeted + properties: + ignoreNames: + description: |- + IgnoreNames lists namespace names to be ignored. Each entry can be a Regex pattern and if they match + the namespaces will be excluded from the sync even if matching in "matchNames" or via LabelSelector. + A namespace is ignored if at least one pattern matches. + Invalid patterns will cause the sync to be cancelled and the status conditions will contain the error message. + items: + type: string + type: array + labelSelector: + description: LabelSelector of namespaces to be targeted. Can be + combined with MatchNames to include unlabelled namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchNames: + description: |- + MatchNames lists namespace names to be targeted. Each entry can be a Regex pattern. + A namespace is included if at least one pattern matches. + Invalid patterns will cause the sync to be cancelled and the status conditions will contain the error message. + items: + type: string + type: array + type: object + syncItems: + description: SyncItems lists items to be synced to targeted namespaces + items: + description: Manifest is an unstructured kubernetes object with + kube-builder validation and pruning settings applied. + type: object + x-kubernetes-embedded-resource: true + x-kubernetes-preserve-unknown-fields: true + type: array + type: object + status: + description: SyncConfigStatus defines the observed state of SyncConfig + properties: + conditions: + description: Conditions contain the states of the SyncConfig. A SyncConfig + is considered Ready when at least one item has been synced. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + deletedItemCount: + description: DeletedItemCount holds the accumulated number of deleted + objects from targeted namespaces. Inexisting items do not get counted. + format: int64 + type: integer + failedItemCount: + description: FailedItemCount holds the accumulated number of objects + that could not be created, updated or deleted. Inexisting items + do not get counted. + format: int64 + type: integer + synchronizedItemCount: + description: SynchronizedItemCount holds the accumulated number of + created or updated objects in the targeted namespaces. + format: int64 + type: integer + required: + - deletedItemCount + - failedItemCount + - synchronizedItemCount + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/netpols/syncconfig_editor_role.yaml b/netpols/syncconfig_editor_role.yaml new file mode 100644 index 0000000..9240c89 --- /dev/null +++ b/netpols/syncconfig_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit syncconfigs. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: syncconfig-editor-role +rules: +- apiGroups: + - sync.appuio.ch + resources: + - syncconfigs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - sync.appuio.ch + resources: + - syncconfigs/status + verbs: + - get diff --git a/netpols/syncconfig_viewer_role.yaml b/netpols/syncconfig_viewer_role.yaml new file mode 100644 index 0000000..47b68b5 --- /dev/null +++ b/netpols/syncconfig_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view syncconfigs. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: syncconfig-viewer-role +rules: +- apiGroups: + - sync.appuio.ch + resources: + - syncconfigs + verbs: + - get + - list + - watch +- apiGroups: + - sync.appuio.ch + resources: + - syncconfigs/status + verbs: + - get diff --git a/netpols/zz_netpol-mirroring.yaml b/netpols/zz_netpol-mirroring.yaml new file mode 100644 index 0000000..aa17b04 --- /dev/null +++ b/netpols/zz_netpol-mirroring.yaml @@ -0,0 +1,39 @@ +apiVersion: sync.appuio.ch/v1alpha1 +kind: SyncConfig +metadata: + name: netpol-mirroring + namespace: system +spec: + forceRecreate: true + namespaceSelector: + labelSelector: + matchExpressions: + - key: appcat.vshn.io/servicename + operator: Exists + ignoreNames: + - system + syncItems: + - apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: allow-from-same-namespace + spec: + ingress: + - from: + - podSelector: {} + podSelector: {} + policyTypes: + - Ingress + - apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: allow-from-other-namespaces + spec: + ingress: + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: prometheus-system + podSelector: {} + policyTypes: + - Ingress diff --git a/prometheus/netpol.yaml b/prometheus/netpol.yaml new file mode 100644 index 0000000..dc7a928 --- /dev/null +++ b/prometheus/netpol.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-all-ingress + namespace: prometheus-system +spec: + podSelector: {} + ingress: + - {} + policyTypes: + - Ingress