初始提交:DSH 多租户平台(dshs)

This commit is contained in:
admin committed 2026-09-13 16:18:10 +08:00
commit 43976fea6a
167 files changed
+24456

No files matched your search

+60
View File
@@ -0,0 +1,60 @@
// Minimal fake DSH for the socat-WS PoC: plain HTTP on 127.0.0.1:8080 plus a
// WebSocket echo (handshake + one text-frame round-trip) using only node built-ins.
import { createServer } from 'node:http'
import { createHash } from 'node:crypto'
const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
const acceptKey = (key) => createHash('sha1').update(key + GUID).digest('base64')
const server = createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' })
res.end('fake-dsh\n')
})
server.on('upgrade', (req, socket) => {
const key = req.headers['sec-websocket-key']
if (!key) {
socket.destroy()
return
}
socket.write(
'HTTP/1.1 101 Switching Protocols\r\n' +
'Upgrade: websocket\r\n' +
'Connection: Upgrade\r\n' +
`Sec-WebSocket-Accept: ${acceptKey(key)}\r\n\r\n`,
)
socket.on('data', (buf) => {
const opcode = buf[0] & 0x0f
if (opcode === 0x8) {
socket.end()
return
}
if (opcode !== 0x1 && opcode !== 0x2) return
const masked = (buf[1] & 0x80) !== 0
let len = buf[1] & 0x7f
let offset = 2
if (len === 126) {
len = buf.readUInt16BE(2)
offset = 4
} else if (len === 127) {
return // not exercised in the PoC
}
let maskKey
if (masked) {
maskKey = buf.subarray(offset, offset + 4)
offset += 4
}
const payload = Buffer.from(buf.subarray(offset, offset + len))
if (masked && maskKey) {
for (let i = 0; i < payload.length; i++) payload[i] ^= maskKey[i % 4]
}
// Echo back as an unmasked text frame.
const out = Buffer.alloc(2 + payload.length)
out[0] = 0x81
out[1] = payload.length
payload.copy(out, 2)
socket.write(out)
})
})
server.listen(8080, '127.0.0.1', () => console.log('fake-dsh listening on 127.0.0.1:8080'))
+38
View File
@@ -0,0 +1,38 @@
# §4.3:dsh(loopback 8080) + socat sidecar(0.0.0.0:8081 → 127.0.0.1:8080)。
# dsh 用一次性 node 镜像跑 fake-dsh.mjs(真实 DSH 不参与本 PoC)。
apiVersion: v1
kind: Pod
metadata:
name: dsh-ws-test
namespace: dsh-poc
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 65532
seccompProfile: { type: RuntimeDefault }
containers:
- name: dsh
image: node:22-alpine
command: ["node", "/app/fake-dsh.mjs"]
securityContext:
allowPrivilegeEscalation: false
capabilities: { drop: ["ALL"] }
volumeMounts:
- name: script
mountPath: /app
- name: sidecar
image: alpine/socat:1.8.0.0
args: ["TCP-LISTEN:8081,fork,reuseaddr", "TCP:127.0.0.1:8080"]
ports:
- containerPort: 8081
securityContext:
runAsNonRoot: true
runAsUser: 65532
allowPrivilegeEscalation: false
capabilities: { drop: ["ALL"] }
seccompProfile: { type: RuntimeDefault }
volumes:
- name: script
configMap:
name: fake-dsh
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -euo pipefail
NS=dsh-poc
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
pass() { echo "PASS: $*"; }
fail() { echo "FAIL: $*"; exit 1; }
kubectl create namespace "$NS" --dry-run=client -o yaml | kubectl apply -f - >/dev/null
kubectl create configmap fake-dsh --from-file="$HERE/fake-dsh.mjs" -n "$NS" --dry-run=client -o yaml | kubectl apply -f - >/dev/null
kubectl apply -f "$HERE/pod.yaml" >/dev/null
kubectl wait -n "$NS" --for=condition=Ready pod/dsh-ws-test --timeout=120s
kubectl port-forward -n "$NS" pod/dsh-ws-test 18081:8081 >/tmp/dsh-poc-pf.log 2>&1 &
PF=$!
trap 'kill $PF 2>/dev/null || true' EXIT
sleep 3
echo "== HTTP through socat (8081 -> 8080) =="
RESP="$(curl -s --max-time 5 http://127.0.0.1:18081/)"
echo "$RESP"
echo "$RESP" | grep -q 'fake-dsh' && pass "HTTP reaches fake-dsh through socat" || fail "HTTP did not reach fake-dsh"
echo "== WebSocket through socat =="
node "$HERE/ws-client.mjs" 127.0.0.1:18081
echo
echo "ALL ITEM-2 CHECKS PASSED"
+64
View File
@@ -0,0 +1,64 @@
// Minimal WebSocket client: handshake + one masked text frame + echo check.
// Usage: node ws-client.mjs <host>:<port>
import { connect } from 'node:net'
import { createHash, randomBytes } from 'node:crypto'
const [host, portStr] = process.argv[2].split(':')
const port = Number(portStr)
const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
const key = randomBytes(16).toString('base64')
const socket = connect({ host, port }, () => {
socket.write(
'GET / HTTP/1.1\r\n' +
`Host: ${host}:${port}\r\n` +
'Upgrade: websocket\r\n' +
'Connection: Upgrade\r\n' +
`Sec-WebSocket-Key: ${key}\r\n` +
'Sec-WebSocket-Version: 13\r\n\r\n',
)
})
let headBuf = Buffer.alloc(0)
let echoBuf = Buffer.alloc(0)
let phase = 'handshake'
socket.on('data', (chunk) => {
if (phase === 'handshake') {
headBuf = Buffer.concat([headBuf, chunk])
const idx = headBuf.indexOf('\r\n\r\n')
if (idx === -1) return
const head = headBuf.subarray(0, idx).toString()
if (!/^HTTP\/1\.1 101/.test(head)) return fail(`no 101 (${head.split('\r\n')[0]})`)
const m = head.match(/sec-websocket-accept:\s*(\S+)/i)
const expected = createHash('sha1').update(key + GUID).digest('base64')
if (!m || m[1] !== expected) return fail('bad Sec-WebSocket-Accept')
console.log('PASS: 101 handshake + Sec-WebSocket-Accept')
phase = 'echo'
const payload = Buffer.from('ping')
const frame = Buffer.alloc(2 + 4 + payload.length)
frame[0] = 0x81
frame[1] = 0x80 | payload.length
const mask = Buffer.from([0x12, 0x34, 0x56, 0x78])
mask.copy(frame, 2)
for (let i = 0; i < payload.length; i++) frame[2 + 4 + i] = payload[i] ^ mask[i % 4]
socket.write(frame)
return
}
echoBuf = Buffer.concat([echoBuf, chunk])
// Echoed unmasked text frame: b0=0x81, b1=len, then payload.
if (echoBuf.length < 2) return
const len = echoBuf[1] & 0x7f
if (echoBuf.length < 2 + len) return
const text = echoBuf.subarray(2, 2 + len).toString()
if (text !== 'ping') return fail(`echo mismatch (${text})`)
console.log('PASS: echo round-trip through socat')
process.exit(0)
})
function fail(msg) {
console.error('FAIL: ' + msg)
process.exit(1)
}
socket.on('error', (e) => fail(e.message))
setTimeout(() => fail('timeout'), 10000)