no acl
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-10-07 14:38:18 +09:00
parent d89b989596
commit 2be752d7f8
+8 -55
View File
@@ -27,9 +27,6 @@ export type HeadscalePolicyStatus = {
raw: HeadscalePolicy | Record<string, unknown> | null raw: HeadscalePolicy | Record<string, unknown> | null
} }
const generatedPrefix = 'group:lms-platform-'
const gatewayGroup = 'group:lms-platform-gateway'
export class HeadscaleAdapter { export class HeadscaleAdapter {
async applyGrant(_grant: AccessGrant): Promise<{ applied: boolean; mode: 'noop' | 'headscale' }> { async applyGrant(_grant: AccessGrant): Promise<{ applied: boolean; mode: 'noop' | 'headscale' }> {
return { applied: true, mode: 'noop' } return { applied: true, mode: 'noop' }
@@ -76,60 +73,16 @@ export class HeadscaleAdapter {
unresolved: string[] unresolved: string[]
error?: string error?: string
}> { }> {
const nodes = await this.listNodes()
const unresolved: string[] = []
const groups: Record<string, string[]> = {
[gatewayGroup]: [...new Set(config.headscaleGatewayUsers.map((user) => this.toHeadscaleUserName(user)))],
}
const acls: HeadscalePolicy['acls'] = [
{
// The local bridge initiates discovery from the trusted gateway host.
action: 'accept',
src: [gatewayGroup],
dst: ['tag:teaching-device:*'],
},
{
// DDS discovery and topic payloads use independent UDP flows, so the
// teaching device must also be able to reach the bridge on the gateway.
action: 'accept',
src: ['tag:teaching-device'],
dst: [`${gatewayGroup}:*`],
},
]
assignments.forEach((assignment, index) => {
const users = [...new Set(assignment.studentEmails.map((email) => this.toHeadscaleUserName(email)).filter(Boolean))]
if (!users.length) return
const node = this.findPlatformNode(nodes, assignment)
const ip = node ? this.extractIpAddress(node) : null
if (!ip) {
unresolved.push(assignment.platformName || assignment.agentHostname || assignment.platformId)
return
}
const groupName = `${generatedPrefix}${this.toPolicyToken(assignment.platformId || assignment.agentHostname || String(index))}`
groups[groupName] = users
acls.push({
action: 'accept',
src: [groupName],
dst: [`${ip}:*`],
})
acls.push({
// ROS 2 DDS discovery and topic payloads also originate from the
// assigned platform, so keep the student workspace link bidirectional.
action: 'accept',
src: [ip],
dst: [`${groupName}:*`],
})
})
const policy: HeadscalePolicy = { const policy: HeadscalePolicy = {
groups, groups: {},
tagOwners: { tagOwners: {
'tag:teaching-device': [`${config.headscaleTeachingDeviceOwner}@`], 'tag:teaching-device': [`${config.headscaleTeachingDeviceOwner}@`],
}, },
acls, acls: [{
action: 'accept',
src: ['*'],
dst: ['*:*'],
}],
} }
const result = await this.applyPolicy(policy) const result = await this.applyPolicy(policy)
@@ -137,8 +90,8 @@ export class HeadscaleAdapter {
applied: result.applied, applied: result.applied,
mode: 'headscale', mode: 'headscale',
assignmentCount: assignments.length, assignmentCount: assignments.length,
aclCount: acls.length, aclCount: policy.acls.length,
unresolved, unresolved: [],
...(result.error ? { error: result.error } : {}), ...(result.error ? { error: result.error } : {}),
} }
} }