Sync the self-hosting stack
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
# The headless browser Jarvis is allowed to use, and the policy that keeps it away from everything
|
||||
# else.
|
||||
#
|
||||
# WHY IT IS ITS OWN POD. A browser's entire purpose is to fetch code written by strangers and run it.
|
||||
# The api pod holds VAULT_MASTER_KEY, the database URL and a live connection to Postgres and Redis;
|
||||
# putting Chromium in it would let any page the assistant is pointed at reach all three — and reach
|
||||
# the node's metadata endpoint on 169.254.169.254, which is how instance credentials get read out
|
||||
# loud. PowerShell is in the api image and this is not, and the difference is exactly that: the
|
||||
# Exchange module talks to Microsoft, and a browser talks to whoever the model was told to look at.
|
||||
#
|
||||
# THE NETWORK POLICY BELOW IS THE REAL CONTROL. `browser.client.ts` refuses private addresses before
|
||||
# it asks, which is the half that can EXPLAIN itself to the model — but it cannot survive a redirect
|
||||
# and it cannot survive DNS rebinding, because by then the browser is already connecting. The policy
|
||||
# can, because it is enforced on the packet.
|
||||
#
|
||||
# DELETE THIS FILE (and its line in kustomization.yaml, and BROWSER_URL from 02-config.yaml) if you
|
||||
# would rather Jarvis had no web access. Nothing else stops working: the two web tools report
|
||||
# themselves unavailable and every other tool is unaffected.
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: jarvis-browser
|
||||
labels:
|
||||
app.kubernetes.io/name: jarvis
|
||||
app.kubernetes.io/component: browser
|
||||
spec:
|
||||
# One is enough and more is not better: each replica is a Chromium, and a render is short. Scale
|
||||
# this only if the assistant is genuinely browsing concurrently across many conversations.
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: jarvis
|
||||
app.kubernetes.io/component: browser
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: jarvis
|
||||
app.kubernetes.io/component: browser
|
||||
spec:
|
||||
# No service account token. This pod has no business talking to the Kubernetes API, and a
|
||||
# mounted token is the first thing anything escaping a renderer would look for.
|
||||
automountServiceAccountToken: false
|
||||
containers:
|
||||
- name: browser
|
||||
image: git.luxit.be/luxit/jarvis-browser:stable
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
name: http
|
||||
# No envFrom, no secretRef, no volume. Deliberately: this container should hold nothing
|
||||
# worth stealing, so that escaping the renderer is not the same as reaching anything.
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
# Chromium will use whatever it is given on a heavy page. The limit is what stops one
|
||||
# bad page evicting the api from the same node.
|
||||
memory: 1Gi
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
runAsNonRoot: true
|
||||
# `pwuser` in the Playwright image.
|
||||
runAsUser: 1000
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
# NOT read-only: Chromium writes its profile, its cache and its shared memory. The
|
||||
# writable surface is an emptyDir that dies with the pod rather than the image.
|
||||
volumeMounts:
|
||||
- name: tmp
|
||||
mountPath: /tmp
|
||||
- name: home
|
||||
mountPath: /home/pwuser
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: http
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 30
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: http
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
volumes:
|
||||
- name: tmp
|
||||
emptyDir:
|
||||
# /dev/shm is small by default and Chromium is the classic victim of it; the server
|
||||
# passes --disable-dev-shm-usage and writes here instead.
|
||||
sizeLimit: 512Mi
|
||||
- name: home
|
||||
emptyDir:
|
||||
sizeLimit: 512Mi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: jarvis-browser
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: jarvis
|
||||
app.kubernetes.io/component: browser
|
||||
ports:
|
||||
- port: 3000
|
||||
targetPort: http
|
||||
name: http
|
||||
---
|
||||
# WHAT THE BROWSER MAY TALK TO, and it is the file's reason for existing.
|
||||
#
|
||||
# Ingress: the api pods, and nothing else. Nothing in this namespace should be calling a browser.
|
||||
#
|
||||
# Egress: DNS, and then the internet MINUS every private range. The exception list is the whole
|
||||
# point — 10/8, 172.16/12 and 192.168/16 are where the cluster and the customer's estate live, and
|
||||
# 169.254.0.0/16 is the metadata endpoint. Without these `except` clauses a page could ask the
|
||||
# browser to fetch http://169.254.169.254/ and read the node's cloud credentials back as page text.
|
||||
#
|
||||
# NOTE THIS NEEDS A CNI THAT ENFORCES NetworkPolicy. Calico, Cilium and most managed clusters do;
|
||||
# plain kube-proxy with flannel does NOT, and on such a cluster this file is documentation rather
|
||||
# than a control. Check before relying on it — `browser.client.ts` still refuses private addresses
|
||||
# on the near side either way, but that half cannot survive a redirect.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: jarvis-browser
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: jarvis
|
||||
app.kubernetes.io/component: browser
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: jarvis
|
||||
app.kubernetes.io/component: api
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 3000
|
||||
egress:
|
||||
# DNS, to the cluster resolver. Without this nothing resolves and every page fails.
|
||||
- to:
|
||||
- namespaceSelector: {}
|
||||
podSelector:
|
||||
matchLabels:
|
||||
k8s-app: kube-dns
|
||||
ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
- protocol: TCP
|
||||
port: 53
|
||||
# The public internet, and nothing that is not.
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 0.0.0.0/0
|
||||
except:
|
||||
- 10.0.0.0/8
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
- 169.254.0.0/16
|
||||
- 127.0.0.0/8
|
||||
- 100.64.0.0/10
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
- protocol: TCP
|
||||
port: 443
|
||||
Reference in New Issue
Block a user