2 Commits

Author SHA1 Message Date
chyoo 0032085139 fix: allow gateway access with open ACL policy
ci/woodpecker/push/woodpecker Pipeline was successful
2026-10-07 14:55:49 +09:00
chyoo 6570ebb109 no acl
ci/woodpecker/push/woodpecker Pipeline was successful
2026-10-07 14:38:18 +09:00
+23 -48
View File
@@ -27,9 +27,6 @@ export type HeadscalePolicyStatus = {
raw: HeadscalePolicy | Record<string, unknown> | null
}
const generatedPrefix = 'group:lms-platform-'
const gatewayGroup = 'group:lms-platform-gateway'
export class HeadscaleAdapter {
async applyGrant(_grant: AccessGrant): Promise<{ applied: boolean; mode: 'noop' | 'headscale' }> {
return { applied: true, mode: 'noop' }
@@ -76,53 +73,31 @@ export class HeadscaleAdapter {
unresolved: 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}:*`],
})
})
const gatewaySources = [...new Set(
config.headscaleGatewayUsers.map((user) => this.toHeadscaleUserName(user)),
)]
const policy: HeadscalePolicy = {
groups,
groups: {},
tagOwners: {
'tag:teaching-device': [`${config.headscaleTeachingDeviceOwner}@`],
},
acls,
acls: [
{
action: 'accept',
src: ['*'],
dst: ['*:*'],
},
{
action: 'accept',
src: gatewaySources,
dst: ['*:*'],
},
{
action: 'accept',
src: ['tag:teaching-device'],
dst: ['*:*'],
},
],
}
const result = await this.applyPolicy(policy)
@@ -130,8 +105,8 @@ export class HeadscaleAdapter {
applied: result.applied,
mode: 'headscale',
assignmentCount: assignments.length,
aclCount: acls.length,
unresolved,
aclCount: policy.acls.length,
unresolved: [],
...(result.error ? { error: result.error } : {}),
}
}