mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-11 21:27:31 +00:00
tailcfg,cmd/k8s-operator,kube: move Kubernetes cap to a location that can be shared with control (#12236)
This PR is in prep of adding logic to control to be able to parse tailscale.com/cap/kubernetes grants in control: - moves the type definition of PeerCapabilityKubernetes cap to a location shared with control. - update the Kubernetes cap rule definition with fields for granting kubectl exec session recording capabilities. - adds a convenience function to produce tailcfg.RawMessage from an arbitrary cap rule and a test for it. An example grant defined via ACLs: "grants": [{ "src": ["tag:eng"], "dst": ["tag:k8s-operator"], "app": { "tailscale.com/cap/kubernetes": [{ "recorder": ["tag:my-recorder"] “enforceRecorder”: true }], }, } ] This grant enforces `kubectl exec` sessions from tailnet clients, matching `tag:eng` via API server proxy matching `tag:k8s-operator` to be recorded and recording to be sent to a tsrecorder instance, matching `tag:my-recorder`. The type needs to be shared with control because we want control to parse this cap and resolve tags to peer IPs. Updates tailscale/corp#19821 Signed-off-by: Irbe Krumina <irbe@tailscale.com>
This commit is contained in:
@@ -258,6 +258,15 @@ func (m *RawMessage) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalCapJSON returns a capability rule in RawMessage string format.
|
||||
func MarshalCapJSON[T any](capRule T) (RawMessage, error) {
|
||||
bs, err := json.Marshal(capRule)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error marshalling capability rule: %w", err)
|
||||
}
|
||||
return RawMessage(string(bs)), nil
|
||||
}
|
||||
|
||||
type Node struct {
|
||||
ID NodeID
|
||||
StableID StableNodeID
|
||||
|
@@ -854,6 +854,55 @@ func TestRawMessage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalToRawMessageAndBack(t *testing.T) {
|
||||
type inner struct {
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
}
|
||||
type testRule struct {
|
||||
Ports []int `json:"ports,omitempty"`
|
||||
ToggleOn bool `json:"toggleOn,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Groups inner `json:"groups,omitempty"`
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
capType PeerCapability
|
||||
val testRule
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
val: testRule{},
|
||||
capType: PeerCapability("foo"),
|
||||
},
|
||||
{
|
||||
name: "some values",
|
||||
val: testRule{Ports: []int{80, 443}, Name: "foo"},
|
||||
capType: PeerCapability("foo"),
|
||||
},
|
||||
{
|
||||
name: "all values",
|
||||
val: testRule{Ports: []int{80, 443}, Name: "foo", ToggleOn: true, Groups: inner{Groups: []string{"foo", "bar"}}},
|
||||
capType: PeerCapability("foo"),
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
raw, err := MarshalCapJSON(tc.val)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error marshalling raw message: %v", err)
|
||||
}
|
||||
cap := PeerCapMap{tc.capType: []RawMessage{raw}}
|
||||
after, err := UnmarshalCapJSON[testRule](cap, tc.capType)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error unmarshaling raw message: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual([]testRule{tc.val}, after) {
|
||||
t.Errorf("got %#v; want %#v", after, []testRule{tc.val})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeps(t *testing.T) {
|
||||
deptest.DepChecker{
|
||||
BadDeps: map[string]string{
|
||||
|
Reference in New Issue
Block a user