mirror of
https://github.com/juanfont/headscale.git
synced 2026-11-04 04:34:07 +09:00
policy/v2: name ssh-behavior-v1 a deliberate difference
SaaS stamps it on every node. Sending it would change existing users' login environment, so it stays a nodeAttr. Document that and pin it.
This commit is contained in:
1 parent
39a42e9d01
commit
1bce65a236
3 files changed
+39
-3
No files matched your search
@@ -214,6 +214,9 @@ configuration and attributes. At least the following node attributes are current
|
||||
port 41641.
|
||||
- `disable-captive-portal-detection`: [Disable automatic captive portal
|
||||
detection](https://tailscale.com/docs/integrations/captive-portals#disable-captive-portal-detection).
|
||||
- `ssh-behavior-v1`: Run Tailscale SSH sessions on a Linux node without `su`, PAM or a login environment, so `ssh -tt`
|
||||
keeps job control but `~/.profile` is not read. Tailscale's own control plane sets it on every node; Headscale leaves
|
||||
it off.
|
||||
|
||||
```json title="policy.json"
|
||||
{
|
||||
|
||||
@@ -128,9 +128,12 @@ var unmodelledTailnetStateCaps = []nodecap.Cap{
|
||||
// [tailscale.com/control/controlknobs.Knobs.UpdateFromNodeAttributes].
|
||||
nodecap.ProbeUDPLifetime,
|
||||
|
||||
// [tailcfg.NodeAttrSSHBehaviorV1]: configures the embedded SSH
|
||||
// server (no su, in-process SFTP). Internal tuning; the embedded
|
||||
// server picks Tailscale-vendored defaults without the cap.
|
||||
// [tailcfg.NodeAttrSSHBehaviorV1]: picks the embedded SSH server's
|
||||
// older path on a root-run Linux daemon: no su, in-process SFTP, no
|
||||
// PAM session or login environment. SaaS stamps it on every node;
|
||||
// headscale leaves it to a nodeAttr on purpose, as switching changes
|
||||
// existing users' login environment. It is more than tuning, so
|
||||
// TestSSHBehaviorIsAnAdminChoice pins it apart from this strip.
|
||||
nodecap.SSHBehaviorV1,
|
||||
}
|
||||
|
||||
|
||||
@@ -226,6 +226,36 @@ func TestTaildropDisabledWithholdsFileSharingCap(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// TestSSHBehaviorIsAnAdminChoice pins a deliberate difference from SaaS, which
|
||||
// stamps ssh-behavior-v1 on every node: headscale sends it only through a
|
||||
// nodeAttr, so sessions keep their login environment and PAM by default.
|
||||
func TestSSHBehaviorIsAnAdminChoice(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
srv := servertest.NewServer(t)
|
||||
user := srv.CreateUser(t, "ssh-behavior-user")
|
||||
|
||||
c := servertest.NewClient(t, srv, "ssh-behavior-node", servertest.WithUser(user))
|
||||
c.WaitForCondition(t, "baseline without ssh-behavior-v1", 10*time.Second,
|
||||
func(nm *netmap.NetworkMap) bool {
|
||||
if nm == nil || !nm.SelfNode.Valid() {
|
||||
return false
|
||||
}
|
||||
|
||||
return hasCap(nm, nodecap.SSH) && !hasCap(nm, nodecap.SSHBehaviorV1)
|
||||
})
|
||||
|
||||
reloadPolicy(t, srv, `{
|
||||
"acls": [{"action": "accept", "src": ["*"], "dst": ["*:*"]}],
|
||||
"nodeAttrs": [{"target": ["*"], "attr": ["ssh-behavior-v1"]}]
|
||||
}`)
|
||||
|
||||
c.WaitForCondition(t, "ssh-behavior-v1 through a nodeAttr", 10*time.Second,
|
||||
func(nm *netmap.NetworkMap) bool {
|
||||
return hasCap(nm, nodecap.SSHBehaviorV1)
|
||||
})
|
||||
}
|
||||
|
||||
// TestNodeAttrsAddsToBaseline verifies that policy nodeAttrs caps land on
|
||||
// nodes alongside the always-on baseline. The baseline caps remain
|
||||
// regardless of policy contents.
|
||||
|
||||
Reference in new issue
Block a user