Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(helm): create prototype for networkc chart #70

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Ignore helm chart templates.
charts/**/*.yaml
23 changes: 23 additions & 0 deletions charts/networkc/.helmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
6 changes: 6 additions & 0 deletions charts/networkc/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v2
name: networkc
description: A software defined routing stack built on top of Kubernetes.
type: application
version: "0.1.0"
appVersion: "0.1.0"
18 changes: 18 additions & 0 deletions charts/networkc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# 📡 networkc

`networkc` is a software-defined routing stack meant to be deployed to a Kubernetes cluster.

## 🚀 Quickstart

```bash
helm upgrade --install networkc charts/networkc --namespace networkc
```

## 📦 Components

For now the implementation is naive and simple. It consists of the following components:

- `CronJob` for updating the firewall configuration via `nftables`
<!-- - `CronJob` to update the `isc-dhcp-server` configuration -->
<!-- - `CronJob` to update the `netplan` configuration -->
<!-- - `CronJob` to run `ddclient` -->
105 changes: 105 additions & 0 deletions charts/networkc/config/nftables.conf.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/sbin/nft -f

# This setup is based on the Red Hat Documentation of nftables:
# https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/security_guide/sec-configuring_nat_using_nftables

# Reset firewall flushing the ruleset.
flush ruleset

# Declare variables.
{{- if not (.Values.interfaces.wan) }}
{{- fail "failed to find WAN interface: .interfaces.wan" }}
{{- end }}
define iface_wan = "{{ .Values.interfaces.wan }}"
{{- if not (.Values.interfaces.lan) }}
{{- fail "failed to find LAN interface: .interfaces.lan" }}
{{- end }}
define iface_lan = "{{ .Values.interfaces.lan }}"
{{- if not (.Values.subnets.kubernetes.pods) }}
{{- fail "failed to find Kubernetes pod subnet: .subnets.kubernetes.pods" }}
{{- end }}
{{- if not (.Values.subnets.kubernetes.services) }}
{{- fail "failed to find Kubernetes service subnet: .subnets.kubernetes.services" }}
{{- end }}
define cidr_kubernetes = "{ {{ .Values.subnets.kubernetes.pods }}, {{ .Values.subnets.kubernetes.services }} }"

# Create a table for firewalling.
table inet firewall {
# Handle incoming traffic for host processes.
chain input {
# Drop all traffic by default.
type filter hook input priority filter; policy drop;

# Let invalid connections time out, while accepting established
# or related connections.
ct state invalid counter drop comment "invalid input packets"
ct state established,related counter accept comment "accepted input packets"

# Accept connections from the loopback interface to itself while
# protecting it from external traffic.
iif lo accept
iif != lo ip daddr 127.0.0.1/8 counter drop comment "dropped lo packets"
iif != lo ip6 daddr ::1/128 counter drop comment "dropped lo packets"

# Allow ICMP and IGMP traffic for correct network operation.
# Reference: http://shouldiblockicmp.com/
ip protocol icmp counter accept comment "icmp input packets"
ip protocol igmp counter accept comment "igmp input packets"
ip6 nexthdr icmpv6 counter accept comment "icmpv6 input packets"

# Allow SSH, HTTP and HTTPS.
tcp dport { ssh, 80, 443, 6443, 7443 } counter accept comment "accepted input packets"

# Allow wireguard traffic.
udp dport { 5800, 5801, 5802, 5803 } counter accept comment "accepted wireguard packets"

# Ensure that Kubernetes node metrics can be collected and BGP routes can be advertised.
iif != $iface_wan tcp dport { 179, 9100, 10250 } counter accept comment "accepted input packets"

# Ensure that VXLAN works.
iif != $iface_wan udp dport { 8472 } counter accept comment "accepted input packets"

# Count dropped packets.
counter comment "dropped input packets"
}

# Handle forwarded traffic between network interfaces.
chain forward {
# Drop all traffic by default.
type filter hook forward priority 0; policy drop;

# Clamp MSS to MTU based on routing cache via PMTUD. Without this
# TCP traffic might not work if your ISP uses PPPoE or FTTH.
tcp flags syn tcp option maxseg size set rt mtu

# Allow forwarded traffic only from internal network interfaces
# or if packets are established or related.
ct state established,related counter accept comment "accepted related packets"

# Allow forwarded traffic to Kubernetes services and pods.
ip daddr $cidr_kubernetes counter accept comment "accepted kubernetes packets"
ip saddr $cidr_kubernetes counter accept comment "accepted kubernetes packets"

# Allow forwarded traffic towards the internet.
iif != $iface_wan counter accept comment "accepted internet packets"

# Count dropped packets.
counter comment "dropped alien packets"
}

# Handle outgoing traffic from host processes.
chain output {
# Allows all outgoing traffic by default.
type filter hook output priority 0; policy accept;
counter comment "accepted output packets"
}

# Handle outgoing packets before leaving the system.
chain postrouting {
# Register a hook to in the postrouting chain.
type nat hook postrouting priority srcnat; policy accept;

# Rewrite the source address with the address of the outgoing interface.
oifname $iface_wan masquerade
}
}
19 changes: 19 additions & 0 deletions charts/networkc/scripts/nftables-reconcile.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash

apk add --no-cache nftables

if [[ $# -ne 2 ]]; then
echo "usage: $0 <old-config> <new-config>"
exit 1
fi
old_config="$1"
new_config="$2"

if ! diff -q "$old_config" "$new_config"; then
echo "Detected changes in nftables configuration. Reconciling..."
cp "$new_config" "$old_config"
nft -f "$old_config"
fi

# TODO: Add connectivity check to ensure that
# the nftables rules are working as expected.
4 changes: 4 additions & 0 deletions charts/networkc/templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Your interfaces are configured and ready to use.

- wan: {{ .Values.interfaces.wan }}
- lan: {{ .Values.interfaces.lan }}
62 changes: 62 additions & 0 deletions charts/networkc/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "networkc.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "networkc.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "networkc.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "networkc.labels" -}}
helm.sh/chart: {{ include "networkc.chart" . }}
{{ include "networkc.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "networkc.selectorLabels" -}}
app.kubernetes.io/name: {{ include "networkc.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Create the name of the service account to use
*/}}
{{- define "networkc.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "networkc.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
12 changes: 12 additions & 0 deletions charts/networkc/templates/configmap-nftables.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "networkc.fullname" . }}-nftables
labels:
{{- include "networkc.labels" . | nindent 4 }}
app.kubernetes.io/component: firewall
data:
nftables.conf: |-
{{- tpl (.Files.Get "config/nftables.conf.tpl") . | printf "\n%s" | indent 4 }}
nftables-reconcile.sh: |-
{{- tpl (.Files.Get "config/nftables-reconcile.sh") . | printf "\n%s" | indent 4 }}
35 changes: 35 additions & 0 deletions charts/networkc/templates/cronjob-nftables.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
apiVersion: batch/v1
kind: CronJob
metadata:
name: {{ include "networkc.fullname" . }}-nftables
labels:
{{- include "networkc.labels" . | nindent 4 }}
app.kubernetes.io/component: firewall
spec:
schedule: "*/1 * * * *"
historyLimit: 5
jobTemplate:
spec:
template:
spec:
containers:
# The container should run as root and on the root filesystem.
# It should use a bash container with the diff command installed via apk.
- name: nftables
image: bash
command: ["/bin/bash", "/app/nftables-reconcile.sh", "/etc/nftables.conf", "/app/nftables.conf"]
volumeMounts:
- name: etc
mountPath: /etc
- name: nftables
mountPath: /app
volumes:
- name: nftables
configMap:
name: {{ include "networkc.fullname" . }}-nftables
- name: etc
hostPath:
path: /etc
restartPolicy: Always
# The container should run as root and on the root filesystem.
privileged: true
10 changes: 10 additions & 0 deletions charts/networkc/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Configure the interfaces.
interfaces:
wan: wan
lan: br0

# Configure the subnet allocations.
subnets:
kubernetes:
pods: 172.28.0.0/16
services: 172.29.0.0/16