game-stream: add Wolf portal access controls
This commit is contained in:
parent
1332b611a3
commit
d89fec8ae5
@ -4,9 +4,15 @@ kind: Kustomization
|
|||||||
resources:
|
resources:
|
||||||
- namespace.yaml
|
- namespace.yaml
|
||||||
- vault-serviceaccount.yaml
|
- vault-serviceaccount.yaml
|
||||||
|
- wolf-api-proxy-configmap.yaml
|
||||||
|
- wolf-gatekeeper-configmap.yaml
|
||||||
- wolf-service.yaml
|
- wolf-service.yaml
|
||||||
|
- wolf-api-service.yaml
|
||||||
|
- wolf-gatekeeper-service.yaml
|
||||||
|
- wolfmanager-service.yaml
|
||||||
- wolf-moonlight-service.yaml
|
- wolf-moonlight-service.yaml
|
||||||
- wolf-statefulset.yaml
|
- wolf-statefulset.yaml
|
||||||
|
- wolf-gatekeeper-daemonset.yaml
|
||||||
- oauth2-proxy-wolf.yaml
|
- oauth2-proxy-wolf.yaml
|
||||||
- certificate.yaml
|
- certificate.yaml
|
||||||
- ingress.yaml
|
- ingress.yaml
|
||||||
|
|||||||
@ -77,8 +77,6 @@ spec:
|
|||||||
- --oidc-issuer-url=https://sso.bstein.dev/realms/atlas
|
- --oidc-issuer-url=https://sso.bstein.dev/realms/atlas
|
||||||
- --scope=openid profile email groups
|
- --scope=openid profile email groups
|
||||||
- --email-domain=*
|
- --email-domain=*
|
||||||
- --allowed-group=game-stream-users
|
|
||||||
- --allowed-group=/game-stream-users
|
|
||||||
- --allowed-group=admin
|
- --allowed-group=admin
|
||||||
- --allowed-group=/admin
|
- --allowed-group=/admin
|
||||||
- --set-xauthrequest=true
|
- --set-xauthrequest=true
|
||||||
@ -90,7 +88,7 @@ spec:
|
|||||||
- --cookie-refresh=20m
|
- --cookie-refresh=20m
|
||||||
- --cookie-expire=24h
|
- --cookie-expire=24h
|
||||||
- --insecure-oidc-allow-unverified-email=true
|
- --insecure-oidc-allow-unverified-email=true
|
||||||
- --upstream=http://ariadne.maintenance.svc.cluster.local
|
- --upstream=http://wolfmanager.game-stream.svc.cluster.local:8080
|
||||||
- --http-address=0.0.0.0:4180
|
- --http-address=0.0.0.0:4180
|
||||||
- --skip-provider-button=true
|
- --skip-provider-button=true
|
||||||
- --approval-prompt=auto
|
- --approval-prompt=auto
|
||||||
|
|||||||
87
services/game-stream/wolf-api-proxy-configmap.yaml
Normal file
87
services/game-stream/wolf-api-proxy-configmap.yaml
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# services/game-stream/wolf-api-proxy-configmap.yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: wolf-api-proxy
|
||||||
|
namespace: game-stream
|
||||||
|
data:
|
||||||
|
wolf_api_proxy.py: |
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
|
||||||
|
SOCKET_PATH = os.environ.get("WOLF_SOCKET_PATH", "/run/user/wolf/wolf.sock")
|
||||||
|
LISTEN = ("0.0.0.0", int(os.environ.get("WOLF_API_PROXY_PORT", "8088")))
|
||||||
|
|
||||||
|
|
||||||
|
def _response(handler, status, payload):
|
||||||
|
body = json.dumps(payload).encode("utf-8")
|
||||||
|
handler.send_response(status)
|
||||||
|
handler.send_header("content-type", "application/json")
|
||||||
|
handler.send_header("content-length", str(len(body)))
|
||||||
|
handler.end_headers()
|
||||||
|
handler.wfile.write(body)
|
||||||
|
|
||||||
|
|
||||||
|
def _forward(method, path, body):
|
||||||
|
if not path.startswith("/api/v1/"):
|
||||||
|
return 404, {"success": False, "error": "unsupported path"}
|
||||||
|
if method not in {"GET", "POST"}:
|
||||||
|
return 405, {"success": False, "error": "unsupported method"}
|
||||||
|
if not os.path.exists(SOCKET_PATH):
|
||||||
|
return 503, {"success": False, "error": "wolf socket is not ready"}
|
||||||
|
|
||||||
|
payload = body or b""
|
||||||
|
request = [
|
||||||
|
f"{method} {path} HTTP/1.1",
|
||||||
|
"Host: wolf.local",
|
||||||
|
"Connection: close",
|
||||||
|
f"Content-Length: {len(payload)}",
|
||||||
|
]
|
||||||
|
if payload:
|
||||||
|
request.append("Content-Type: application/json")
|
||||||
|
request_bytes = ("\r\n".join(request) + "\r\n\r\n").encode("utf-8") + payload
|
||||||
|
|
||||||
|
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
|
||||||
|
sock.settimeout(5)
|
||||||
|
sock.connect(SOCKET_PATH)
|
||||||
|
sock.sendall(request_bytes)
|
||||||
|
chunks = []
|
||||||
|
while True:
|
||||||
|
chunk = sock.recv(65536)
|
||||||
|
if not chunk:
|
||||||
|
break
|
||||||
|
chunks.append(chunk)
|
||||||
|
|
||||||
|
raw = b"".join(chunks)
|
||||||
|
header_bytes, _, body_bytes = raw.partition(b"\r\n\r\n")
|
||||||
|
status_line = header_bytes.splitlines()[0].decode("utf-8", "replace") if header_bytes else ""
|
||||||
|
try:
|
||||||
|
status = int(status_line.split()[1])
|
||||||
|
except Exception:
|
||||||
|
status = 502
|
||||||
|
try:
|
||||||
|
return status, json.loads(body_bytes.decode("utf-8") or "{}")
|
||||||
|
except Exception:
|
||||||
|
return status, {"success": False, "error": body_bytes.decode("utf-8", "replace")}
|
||||||
|
|
||||||
|
|
||||||
|
class Handler(BaseHTTPRequestHandler):
|
||||||
|
def log_message(self, _format, *args):
|
||||||
|
return
|
||||||
|
|
||||||
|
def do_GET(self):
|
||||||
|
status, payload = _forward("GET", self.path, b"")
|
||||||
|
_response(self, status, payload)
|
||||||
|
|
||||||
|
def do_POST(self):
|
||||||
|
length = int(self.headers.get("content-length") or "0")
|
||||||
|
body = self.rfile.read(length) if length else b""
|
||||||
|
status, payload = _forward("POST", self.path, body)
|
||||||
|
_response(self, status, payload)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
ThreadingHTTPServer(LISTEN, Handler).serve_forever()
|
||||||
17
services/game-stream/wolf-api-service.yaml
Normal file
17
services/game-stream/wolf-api-service.yaml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# services/game-stream/wolf-api-service.yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: wolf-api
|
||||||
|
namespace: game-stream
|
||||||
|
labels:
|
||||||
|
app: wolf
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app: wolf
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 8088
|
||||||
|
targetPort: 8088
|
||||||
|
protocol: TCP
|
||||||
176
services/game-stream/wolf-gatekeeper-configmap.yaml
Normal file
176
services/game-stream/wolf-gatekeeper-configmap.yaml
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
# services/game-stream/wolf-gatekeeper-configmap.yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: wolf-gatekeeper
|
||||||
|
namespace: game-stream
|
||||||
|
data:
|
||||||
|
wolf_gatekeeper.py: |
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||||
|
import ipaddress
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
LISTEN = ("0.0.0.0", int(os.environ.get("WOLF_GATEKEEPER_PORT", "8087")))
|
||||||
|
HOST_ROOT = os.environ.get("HOST_ROOT", "/host")
|
||||||
|
NFT_PATH = os.environ.get("NFT_PATH", "/sbin/nft")
|
||||||
|
MAX_TTL_SECONDS = int(os.environ.get("MAX_TTL_SECONDS", "28800"))
|
||||||
|
TCP_PORTS = ["47984", "47989", "48010"]
|
||||||
|
UDP_PORTS = ["47999", "48100", "48200"]
|
||||||
|
PRIVATE_V4 = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "127.0.0.0/8"]
|
||||||
|
PRIVATE_V6 = ["::1/128", "fc00::/7", "fe80::/10"]
|
||||||
|
|
||||||
|
|
||||||
|
def _json(handler, status, payload):
|
||||||
|
body = json.dumps(payload).encode("utf-8")
|
||||||
|
handler.send_response(status)
|
||||||
|
handler.send_header("content-type", "application/json")
|
||||||
|
handler.send_header("content-length", str(len(body)))
|
||||||
|
handler.end_headers()
|
||||||
|
handler.wfile.write(body)
|
||||||
|
|
||||||
|
|
||||||
|
def _nft(args, check=True):
|
||||||
|
command = ["chroot", HOST_ROOT, NFT_PATH, *args]
|
||||||
|
proc = subprocess.run(command, check=False, capture_output=True, text=True)
|
||||||
|
if check and proc.returncode != 0:
|
||||||
|
raise RuntimeError(proc.stderr.strip() or proc.stdout.strip() or "nft failed")
|
||||||
|
return proc
|
||||||
|
|
||||||
|
|
||||||
|
def _port_set(ports):
|
||||||
|
return ["{", *[f"{port}," for port in ports[:-1]], ports[-1], "}"]
|
||||||
|
|
||||||
|
|
||||||
|
def _ensure_rules():
|
||||||
|
_nft(["add", "table", "inet", "wolf_gatekeeper"], check=False)
|
||||||
|
_nft(["add", "set", "inet", "wolf_gatekeeper", "allowed_v4", "{", "type", "ipv4_addr;", "flags", "timeout;", "}"], check=False)
|
||||||
|
_nft(["add", "set", "inet", "wolf_gatekeeper", "allowed_v6", "{", "type", "ipv6_addr;", "flags", "timeout;", "}"], check=False)
|
||||||
|
_nft(
|
||||||
|
[
|
||||||
|
"add",
|
||||||
|
"chain",
|
||||||
|
"inet",
|
||||||
|
"wolf_gatekeeper",
|
||||||
|
"input",
|
||||||
|
"{",
|
||||||
|
"type",
|
||||||
|
"filter",
|
||||||
|
"hook",
|
||||||
|
"input",
|
||||||
|
"priority",
|
||||||
|
"-90;",
|
||||||
|
"policy",
|
||||||
|
"accept;",
|
||||||
|
"}",
|
||||||
|
],
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
_nft(["flush", "chain", "inet", "wolf_gatekeeper", "input"], check=False)
|
||||||
|
_nft(["add", "rule", "inet", "wolf_gatekeeper", "input", "iifname", "lo", "accept"])
|
||||||
|
for cidr in PRIVATE_V4:
|
||||||
|
_nft(["add", "rule", "inet", "wolf_gatekeeper", "input", "ip", "saddr", cidr, "accept"])
|
||||||
|
for cidr in PRIVATE_V6:
|
||||||
|
_nft(["add", "rule", "inet", "wolf_gatekeeper", "input", "ip6", "saddr", cidr, "accept"])
|
||||||
|
_nft(["add", "rule", "inet", "wolf_gatekeeper", "input", "ip", "saddr", "@allowed_v4", "tcp", "dport", *_port_set(TCP_PORTS), "accept"])
|
||||||
|
_nft(["add", "rule", "inet", "wolf_gatekeeper", "input", "ip", "saddr", "@allowed_v4", "udp", "dport", *_port_set(UDP_PORTS), "accept"])
|
||||||
|
_nft(["add", "rule", "inet", "wolf_gatekeeper", "input", "ip6", "saddr", "@allowed_v6", "tcp", "dport", *_port_set(TCP_PORTS), "accept"])
|
||||||
|
_nft(["add", "rule", "inet", "wolf_gatekeeper", "input", "ip6", "saddr", "@allowed_v6", "udp", "dport", *_port_set(UDP_PORTS), "accept"])
|
||||||
|
_nft(["add", "rule", "inet", "wolf_gatekeeper", "input", "tcp", "dport", *_port_set(TCP_PORTS), "drop"])
|
||||||
|
_nft(["add", "rule", "inet", "wolf_gatekeeper", "input", "udp", "dport", *_port_set(UDP_PORTS), "drop"])
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_ip(value):
|
||||||
|
ip = ipaddress.ip_address(str(value or "").strip())
|
||||||
|
return str(ip), ip.version
|
||||||
|
|
||||||
|
|
||||||
|
def _set_name(version):
|
||||||
|
return "allowed_v4" if version == 4 else "allowed_v6"
|
||||||
|
|
||||||
|
|
||||||
|
def _unlock(value, ttl_seconds):
|
||||||
|
ip, version = _validate_ip(value)
|
||||||
|
ttl = max(60, min(int(ttl_seconds or MAX_TTL_SECONDS), MAX_TTL_SECONDS))
|
||||||
|
set_name = _set_name(version)
|
||||||
|
_nft(["delete", "element", "inet", "wolf_gatekeeper", set_name, "{", ip, "}"], check=False)
|
||||||
|
_nft(["add", "element", "inet", "wolf_gatekeeper", set_name, "{", ip, "timeout", f"{ttl}s", "}"])
|
||||||
|
return {"ip": ip, "ttl_seconds": ttl}
|
||||||
|
|
||||||
|
|
||||||
|
def _revoke(value):
|
||||||
|
ip, version = _validate_ip(value)
|
||||||
|
_nft(["delete", "element", "inet", "wolf_gatekeeper", _set_name(version), "{", ip, "}"], check=False)
|
||||||
|
return {"ip": ip}
|
||||||
|
|
||||||
|
|
||||||
|
def _entries(set_name, pattern):
|
||||||
|
proc = _nft(["list", "set", "inet", "wolf_gatekeeper", set_name], check=False)
|
||||||
|
if proc.returncode != 0:
|
||||||
|
return []
|
||||||
|
values = []
|
||||||
|
for match in re.finditer(pattern, proc.stdout):
|
||||||
|
value = match.group(0)
|
||||||
|
if value not in values:
|
||||||
|
values.append(value)
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
def _status():
|
||||||
|
_ensure_rules()
|
||||||
|
v4 = _entries("allowed_v4", r"\b(?:\d{1,3}\.){3}\d{1,3}\b")
|
||||||
|
v6 = _entries("allowed_v6", r"\b[0-9a-fA-F:]{2,}\b")
|
||||||
|
return {"success": True, "active_unlocks": [{"ip": ip} for ip in [*v4, *v6]], "tcp_ports": TCP_PORTS, "udp_ports": UDP_PORTS}
|
||||||
|
|
||||||
|
|
||||||
|
class Handler(BaseHTTPRequestHandler):
|
||||||
|
def log_message(self, _format, *args):
|
||||||
|
return
|
||||||
|
|
||||||
|
def _payload(self):
|
||||||
|
length = int(self.headers.get("content-length") or "0")
|
||||||
|
if not length:
|
||||||
|
return {}
|
||||||
|
try:
|
||||||
|
data = json.loads(self.rfile.read(length).decode("utf-8"))
|
||||||
|
return data if isinstance(data, dict) else {}
|
||||||
|
except Exception:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def do_GET(self):
|
||||||
|
try:
|
||||||
|
if self.path == "/healthz":
|
||||||
|
_ensure_rules()
|
||||||
|
_json(self, 200, {"ok": True})
|
||||||
|
elif self.path == "/status":
|
||||||
|
_json(self, 200, _status())
|
||||||
|
else:
|
||||||
|
_json(self, 404, {"success": False, "error": "not found"})
|
||||||
|
except Exception as exc:
|
||||||
|
_json(self, 500, {"success": False, "error": str(exc)})
|
||||||
|
|
||||||
|
def do_POST(self):
|
||||||
|
try:
|
||||||
|
payload = self._payload()
|
||||||
|
if self.path == "/unlock":
|
||||||
|
_ensure_rules()
|
||||||
|
result = _unlock(payload.get("ip"), payload.get("ttl_seconds"))
|
||||||
|
result.update({"success": True, "actor": payload.get("actor") or "", "target_user": payload.get("target_user") or ""})
|
||||||
|
_json(self, 200, result)
|
||||||
|
elif self.path == "/revoke":
|
||||||
|
_ensure_rules()
|
||||||
|
result = _revoke(payload.get("ip"))
|
||||||
|
result.update({"success": True})
|
||||||
|
_json(self, 200, result)
|
||||||
|
else:
|
||||||
|
_json(self, 404, {"success": False, "error": "not found"})
|
||||||
|
except Exception as exc:
|
||||||
|
_json(self, 400, {"success": False, "error": str(exc)})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
_ensure_rules()
|
||||||
|
ThreadingHTTPServer(LISTEN, Handler).serve_forever()
|
||||||
77
services/game-stream/wolf-gatekeeper-daemonset.yaml
Normal file
77
services/game-stream/wolf-gatekeeper-daemonset.yaml
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
# services/game-stream/wolf-gatekeeper-daemonset.yaml
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
name: wolf-gatekeeper
|
||||||
|
namespace: game-stream
|
||||||
|
labels:
|
||||||
|
app: wolf-gatekeeper
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: wolf-gatekeeper
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: wolf-gatekeeper
|
||||||
|
spec:
|
||||||
|
hostNetwork: true
|
||||||
|
dnsPolicy: ClusterFirstWithHostNet
|
||||||
|
nodeSelector:
|
||||||
|
kubernetes.io/hostname: titan-24
|
||||||
|
tolerations:
|
||||||
|
- key: nvidia.com/gpu
|
||||||
|
operator: Exists
|
||||||
|
effect: NoSchedule
|
||||||
|
containers:
|
||||||
|
- name: gatekeeper
|
||||||
|
image: ghcr.io/games-on-whales/wolf:stable
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command: ["/usr/bin/python3", "/opt/wolf-gatekeeper/wolf_gatekeeper.py"]
|
||||||
|
env:
|
||||||
|
- name: HOST_ROOT
|
||||||
|
value: /host
|
||||||
|
- name: NFT_PATH
|
||||||
|
value: /sbin/nft
|
||||||
|
- name: MAX_TTL_SECONDS
|
||||||
|
value: "28800"
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 8087
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 8087
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 8087
|
||||||
|
initialDelaySeconds: 20
|
||||||
|
periodSeconds: 20
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 25m
|
||||||
|
memory: 64Mi
|
||||||
|
limits:
|
||||||
|
cpu: 250m
|
||||||
|
memory: 256Mi
|
||||||
|
volumeMounts:
|
||||||
|
- name: script
|
||||||
|
mountPath: /opt/wolf-gatekeeper
|
||||||
|
readOnly: true
|
||||||
|
- name: host-root
|
||||||
|
mountPath: /host
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: script
|
||||||
|
configMap:
|
||||||
|
name: wolf-gatekeeper
|
||||||
|
defaultMode: 0555
|
||||||
|
- name: host-root
|
||||||
|
hostPath:
|
||||||
|
path: /
|
||||||
|
type: Directory
|
||||||
17
services/game-stream/wolf-gatekeeper-service.yaml
Normal file
17
services/game-stream/wolf-gatekeeper-service.yaml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# services/game-stream/wolf-gatekeeper-service.yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: wolf-gatekeeper
|
||||||
|
namespace: game-stream
|
||||||
|
labels:
|
||||||
|
app: wolf-gatekeeper
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app: wolf-gatekeeper
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 8087
|
||||||
|
targetPort: 8087
|
||||||
|
protocol: TCP
|
||||||
@ -21,6 +21,15 @@ spec:
|
|||||||
hostNetwork: true
|
hostNetwork: true
|
||||||
dnsPolicy: ClusterFirstWithHostNet
|
dnsPolicy: ClusterFirstWithHostNet
|
||||||
runtimeClassName: nvidia
|
runtimeClassName: nvidia
|
||||||
|
securityContext:
|
||||||
|
fsGroup: 1000
|
||||||
|
initContainers:
|
||||||
|
- name: wolfmanager-data-permissions
|
||||||
|
image: busybox:1.36
|
||||||
|
command: ["sh", "-c", "mkdir -p /app/data && chown -R 1000:1000 /app/data"]
|
||||||
|
volumeMounts:
|
||||||
|
- name: wolfmanager-data
|
||||||
|
mountPath: /app/data
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
kubernetes.io/hostname: titan-24
|
kubernetes.io/hostname: titan-24
|
||||||
tolerations:
|
tolerations:
|
||||||
@ -54,17 +63,114 @@ spec:
|
|||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: wolf-state
|
- name: wolf-state
|
||||||
mountPath: /etc/wolf
|
mountPath: /etc/wolf
|
||||||
|
- name: wolf-runtime
|
||||||
|
mountPath: /run/user/wolf
|
||||||
- name: docker-socket
|
- name: docker-socket
|
||||||
mountPath: /var/run/docker.sock
|
mountPath: /var/run/docker.sock
|
||||||
- name: dev
|
- name: dev
|
||||||
mountPath: /dev
|
mountPath: /dev
|
||||||
- name: udev
|
- name: udev
|
||||||
mountPath: /run/udev
|
mountPath: /run/udev
|
||||||
|
- name: wolf-api-proxy
|
||||||
|
image: ghcr.io/games-on-whales/wolf:stable
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command: ["/usr/bin/python3", "/opt/wolf-api-proxy/wolf_api_proxy.py"]
|
||||||
|
ports:
|
||||||
|
- name: api-proxy
|
||||||
|
containerPort: 8088
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 25m
|
||||||
|
memory: 64Mi
|
||||||
|
limits:
|
||||||
|
cpu: 250m
|
||||||
|
memory: 256Mi
|
||||||
|
volumeMounts:
|
||||||
|
- name: wolf-runtime
|
||||||
|
mountPath: /run/user/wolf
|
||||||
|
- name: wolf-api-proxy
|
||||||
|
mountPath: /opt/wolf-api-proxy
|
||||||
|
readOnly: true
|
||||||
|
- name: wolfmanager
|
||||||
|
image: ghcr.io/salty2011/wolfmanager:latest
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command: ["/bin/sh", "-ec"]
|
||||||
|
args:
|
||||||
|
- |
|
||||||
|
umask 077
|
||||||
|
mkdir -p /app/data
|
||||||
|
if [ ! -s /app/data/jwt_secret ]; then
|
||||||
|
head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n' > /app/data/jwt_secret
|
||||||
|
fi
|
||||||
|
if [ ! -s /app/data/admin_password ]; then
|
||||||
|
printf 'Wm%s1a\n' "$(head -c 18 /dev/urandom | od -An -tx1 | tr -d ' \n')" > /app/data/admin_password
|
||||||
|
fi
|
||||||
|
export Jwt__SecretKey="$(cat /app/data/jwt_secret)"
|
||||||
|
export Admin__Password="$(cat /app/data/admin_password)"
|
||||||
|
exec dotnet WolfManager.Api.dll
|
||||||
|
env:
|
||||||
|
- name: ASPNETCORE_URLS
|
||||||
|
value: http://+:8080
|
||||||
|
- name: ASPNETCORE_ENVIRONMENT
|
||||||
|
value: Production
|
||||||
|
- name: ConnectionStrings__DefaultConnection
|
||||||
|
value: Data Source=/app/data/wolfmanager.db
|
||||||
|
- name: Jobs__Storage
|
||||||
|
value: Memory
|
||||||
|
- name: Jobs__DashboardEnabled
|
||||||
|
value: "true"
|
||||||
|
- name: Wolf__UseUnixSocket
|
||||||
|
value: "true"
|
||||||
|
- name: Wolf__UnixSocketPath
|
||||||
|
value: /run/user/wolf/wolf.sock
|
||||||
|
- name: OpenTelemetry__ServiceName
|
||||||
|
value: WolfManager
|
||||||
|
- name: OpenTelemetry__ConsoleExporter
|
||||||
|
value: "false"
|
||||||
|
- name: OpenTelemetry__OtlpExporter
|
||||||
|
value: "false"
|
||||||
|
ports:
|
||||||
|
- name: wolfmanager
|
||||||
|
containerPort: 8080
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health/ready
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health/live
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 20
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
cpu: "1"
|
||||||
|
memory: 1Gi
|
||||||
|
volumeMounts:
|
||||||
|
- name: wolf-runtime
|
||||||
|
mountPath: /run/user/wolf
|
||||||
|
- name: wolfmanager-data
|
||||||
|
mountPath: /app/data
|
||||||
volumes:
|
volumes:
|
||||||
- name: wolf-state
|
- name: wolf-state
|
||||||
hostPath:
|
hostPath:
|
||||||
path: /etc/wolf
|
path: /etc/wolf
|
||||||
type: DirectoryOrCreate
|
type: DirectoryOrCreate
|
||||||
|
- name: wolf-runtime
|
||||||
|
emptyDir: {}
|
||||||
|
- name: wolf-api-proxy
|
||||||
|
configMap:
|
||||||
|
name: wolf-api-proxy
|
||||||
|
defaultMode: 0555
|
||||||
|
- name: wolfmanager-data
|
||||||
|
hostPath:
|
||||||
|
path: /etc/wolfmanager
|
||||||
|
type: DirectoryOrCreate
|
||||||
- name: docker-socket
|
- name: docker-socket
|
||||||
hostPath:
|
hostPath:
|
||||||
path: /var/run/docker.sock
|
path: /var/run/docker.sock
|
||||||
|
|||||||
17
services/game-stream/wolfmanager-service.yaml
Normal file
17
services/game-stream/wolfmanager-service.yaml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# services/game-stream/wolfmanager-service.yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: wolfmanager
|
||||||
|
namespace: game-stream
|
||||||
|
labels:
|
||||||
|
app: wolf
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app: wolf
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 8080
|
||||||
|
targetPort: 8080
|
||||||
|
protocol: TCP
|
||||||
@ -295,6 +295,14 @@ spec:
|
|||||||
value: game-stream/wolf-oidc
|
value: game-stream/wolf-oidc
|
||||||
- name: ARIADNE_SCHEDULE_WOLF_OIDC
|
- name: ARIADNE_SCHEDULE_WOLF_OIDC
|
||||||
value: "17 */6 * * *"
|
value: "17 */6 * * *"
|
||||||
|
- name: WOLF_API_URL
|
||||||
|
value: http://wolf-api.game-stream.svc.cluster.local:8088
|
||||||
|
- name: WOLF_GATEKEEPER_URL
|
||||||
|
value: http://wolf-gatekeeper.game-stream.svc.cluster.local:8087
|
||||||
|
- name: GAME_STREAM_FIREWALL_UNLOCK_TTL_SEC
|
||||||
|
value: "28800"
|
||||||
|
- name: GAME_STREAM_MOONLIGHT_HOST
|
||||||
|
value: moonlight.bstein.dev
|
||||||
- name: GAME_STREAM_USER_GROUP
|
- name: GAME_STREAM_USER_GROUP
|
||||||
value: game-stream-users
|
value: game-stream-users
|
||||||
- name: GAME_STREAM_ADMIN_GROUP
|
- name: GAME_STREAM_ADMIN_GROUP
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user