mirror of
https://github.com/netbirdio/netbird.git
synced 2026-05-30 20:49:57 +00:00
Adds a new "private" service mode for the reverse proxy: services reachable exclusively over the embedded WireGuard tunnel, gated by per-peer group membership instead of operator auth schemes. Wire contract - ProxyMapping.private (field 13): the proxy MUST call ValidateTunnelPeer and fail closed; operator schemes are bypassed. - ProxyCapabilities.private (4) + supports_private_service (5): capability gate. Management never streams private mappings to proxies that don't claim the capability; the broadcast path applies the same filter via filterMappingsForProxy. - ValidateTunnelPeer RPC: resolves an inbound tunnel IP to a peer, checks the peer's groups against service.AccessGroups, and mints a session JWT on success. checkPeerGroupAccess fails closed when a private service has empty AccessGroups. - ValidateSession/ValidateTunnelPeer responses now carry peer_group_ids + peer_group_names so the proxy can authorise policy-aware middlewares without an extra management round-trip. - ProxyInboundListener + SendStatusUpdate.inbound_listener: per-account inbound listener state surfaced to dashboards. - PathTargetOptions.direct_upstream (11): bypass the embedded NetBird client and dial the target via the proxy host's network stack for upstreams reachable without WireGuard. Data model - Service.Private (bool) + Service.AccessGroups ([]string, JSON- serialised). Validate() rejects bearer auth on private services. Copy() deep-copies AccessGroups. pgx getServices loads the columns. - DomainConfig.Private threaded into the proxy auth middleware. Request handler routes private services through forwardWithTunnelPeer and returns 403 on validation failure. - Account-level SynthesizePrivateServiceZones (synthetic DNS) and injectPrivateServicePolicies (synthetic ACL) gate on len(svc.AccessGroups) > 0. Proxy - /netbird proxy --private (embedded mode) flag; Config.Private in proxy/lifecycle.go. - Per-account inbound listener (proxy/inbound.go) binding HTTP/HTTPS on the embedded NetBird client's WireGuard tunnel netstack. - proxy/internal/auth/tunnel_cache: ValidateTunnelPeer response cache with single-flight de-duplication and per-account eviction. - Local peerstore short-circuit: when the inbound IP isn't in the account roster, deny fast without an RPC. - proxy/server.go reports SupportsPrivateService=true and redacts the full ProxyMapping JSON from info logs (auth_token + header-auth hashed values now only at debug level). Identity forwarding - ValidateSessionJWT returns user_id, email, method, groups, group_names. sessionkey.Claims carries Email + Groups + GroupNames so the proxy can stamp identity onto upstream requests without an extra management round-trip on every cookie-bearing request. - CapturedData carries userEmail / userGroups / userGroupNames; the proxy stamps X-NetBird-User and X-NetBird-Groups on r.Out from the authenticated identity (strips client-supplied values first to prevent spoofing). - AccessLog.UserGroups: access-log enrichment captures the user's group memberships at write time so the dashboard can render group context without reverse-resolving stale memberships. OpenAPI/dashboard surface - ReverseProxyService gains private + access_groups; ReverseProxyCluster gains private + supports_private. ReverseProxyTarget target_type enum gains "cluster". ServiceTargetOptions gains direct_upstream. ProxyAccessLog gains user_groups.
95 lines
3.3 KiB
YAML
95 lines
3.3 KiB
YAML
name: Proto Version Check
|
|
|
|
on:
|
|
pull_request:
|
|
paths:
|
|
- "**/*.pb.go"
|
|
|
|
jobs:
|
|
check-proto-versions:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Check for proto tool version changes
|
|
uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
const files = await github.paginate(github.rest.pulls.listFiles, {
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
pull_number: context.issue.number,
|
|
per_page: 100,
|
|
});
|
|
|
|
const modifiedPbFiles = files.filter(
|
|
f => f.filename.endsWith('.pb.go') && f.status === 'modified'
|
|
);
|
|
if (modifiedPbFiles.length === 0) {
|
|
console.log('No modified .pb.go files to check');
|
|
return;
|
|
}
|
|
|
|
const versionPattern = /^\s*\/\/\s+protoc(?:-gen-go)?\s+v[\d.]+/;
|
|
const baseSha = context.payload.pull_request.base.sha;
|
|
const headSha = context.payload.pull_request.head.sha;
|
|
|
|
async function getVersionHeader(path, ref) {
|
|
try {
|
|
const res = await github.rest.repos.getContent({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
path,
|
|
ref,
|
|
});
|
|
if (!res.data.content) {
|
|
return { ok: false, reason: 'no inline content (file too large)' };
|
|
}
|
|
const content = Buffer.from(res.data.content, 'base64').toString('utf8');
|
|
const lines = content
|
|
.split('\n')
|
|
.slice(0, 20)
|
|
.filter(line => versionPattern.test(line));
|
|
return { ok: true, lines };
|
|
} catch (e) {
|
|
return { ok: false, reason: e.message };
|
|
}
|
|
}
|
|
|
|
const violations = [];
|
|
for (const file of modifiedPbFiles) {
|
|
const [base, head] = await Promise.all([
|
|
getVersionHeader(file.filename, baseSha),
|
|
getVersionHeader(file.filename, headSha),
|
|
]);
|
|
if (!base.ok || !head.ok) {
|
|
core.warning(
|
|
`Skipping ${file.filename}: base=${base.ok ? 'ok' : base.reason}, head=${head.ok ? 'ok' : head.reason}`
|
|
);
|
|
continue;
|
|
}
|
|
if (base.lines.join('\n') !== head.lines.join('\n')) {
|
|
violations.push({
|
|
file: file.filename,
|
|
base: base.lines,
|
|
head: head.lines,
|
|
});
|
|
}
|
|
}
|
|
|
|
if (violations.length > 0) {
|
|
const details = violations.map(v =>
|
|
`${v.file}:\n` +
|
|
` base:\n${v.base.map(l => ' ' + l).join('\n') || ' (none)'}\n` +
|
|
` head:\n${v.head.map(l => ' ' + l).join('\n') || ' (none)'}`
|
|
).join('\n\n');
|
|
|
|
core.setFailed(
|
|
`Proto version strings changed in generated files.\n` +
|
|
`This usually means the wrong protoc or protoc-gen-go version was used.\n` +
|
|
`Regenerate with the matching tool versions.\n\n` +
|
|
details
|
|
);
|
|
return;
|
|
}
|
|
|
|
console.log('No proto version string changes detected');
|