Skip to content

Commit

Permalink
[v9] Backport #12530, #12531, #12405, #12615, and #12684 (#14012)
Browse files Browse the repository at this point in the history
* Restructure `rdpdr.rs` into a multi file module (#12530)

* Adds go build flags for directory sharing and some basic scaffolding for handling them (#12531)

* TDP Shared Directory Announce and Acknowledge (#12405)

* RDP <--> TDP Translation Architecture (#12615)

* RBAC for directory sharing (#12684)
  • Loading branch information
Isaiah Becker-Mayer authored Jul 11, 2022
1 parent 418cd3e commit 2eb1ff1
Show file tree
Hide file tree
Showing 25 changed files with 3,855 additions and 1,689 deletions.
14 changes: 12 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions api/types/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,9 @@ func (r *RoleV5) CheckAndSetDefaults() error {
if r.Spec.Options.DesktopClipboard == nil {
r.Spec.Options.DesktopClipboard = NewBoolOption(true)
}
if r.Spec.Options.DesktopDirectorySharing == nil {
r.Spec.Options.DesktopDirectorySharing = NewBoolOption(true)
}

switch r.Version {
case V3:
Expand Down
1,712 changes: 886 additions & 826 deletions api/types/types.pb.go

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions api/types/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1613,6 +1613,15 @@ message RoleOptions {
// Kubernetes sessions a user may hold.
int64 MaxKubernetesConnections = 18
[ (gogoproto.jsontag) = "max_kubernetes_connections,omitempty" ];

// DesktopDirectorySharing indicates whether directory sharing is allowed between the user's
// workstation and the remote desktop. It defaults to false unless explicitly set to
// true.
BoolValue DesktopDirectorySharing = 19 [
(gogoproto.nullable) = true,
(gogoproto.jsontag) = "desktop_directory_sharing",
(gogoproto.customtype) = "BoolOption"
];
}

message RecordSession {
Expand Down
16 changes: 16 additions & 0 deletions lib/services/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,10 @@ type AccessChecker interface {
// RecordDesktopSession returns true if a role in the role set has enabled
// desktop session recoring.
RecordDesktopSession() bool
// DesktopDirectorySharing returns true if the role set has directory sharing
// enabled. This setting is enabled if one or more of the roles in the set has
// enabled it.
DesktopDirectorySharing() bool

// MaybeCanReviewRequests attempts to guess if this RoleSet belongs
// to a user who should be submitting access reviews. Because not all rolesets
Expand Down Expand Up @@ -2110,6 +2114,18 @@ func (set RoleSet) DesktopClipboard() bool {
return true
}

// DesktopDirectorySharing returns true if the role set has directory sharing
// enabled. This setting is enabled if one or more of the roles in the set has
// enabled it.
func (set RoleSet) DesktopDirectorySharing() bool {
for _, role := range set {
if !types.BoolDefaultTrue(role.GetOptions().DesktopDirectorySharing) {
return false
}
}
return true
}

// MaybeCanReviewRequests attempts to guess if this RoleSet belongs
// to a user who should be submitting access reviews. Because not all rolesets
// are derived from statically assigned roles, this may return false positives.
Expand Down
210 changes: 138 additions & 72 deletions lib/services/role_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,13 @@ func TestRoleParse(t *testing.T) {
},
Spec: types.RoleSpecV5{
Options: types.RoleOptions{
CertificateFormat: constants.CertificateFormatStandard,
MaxSessionTTL: types.NewDuration(apidefaults.MaxCertDuration),
PortForwarding: types.NewBoolOption(true),
RecordSession: &types.RecordSession{Desktop: types.NewBoolOption(true)},
BPF: apidefaults.EnhancedEvents(),
DesktopClipboard: types.NewBoolOption(true),
CertificateFormat: constants.CertificateFormatStandard,
MaxSessionTTL: types.NewDuration(apidefaults.MaxCertDuration),
PortForwarding: types.NewBoolOption(true),
RecordSession: &types.RecordSession{Desktop: types.NewBoolOption(true)},
BPF: apidefaults.EnhancedEvents(),
DesktopClipboard: types.NewBoolOption(true),
DesktopDirectorySharing: types.NewBoolOption(true),
},
Allow: types.RoleConditions{
NodeLabels: types.Labels{},
Expand Down Expand Up @@ -223,12 +224,13 @@ func TestRoleParse(t *testing.T) {
},
Spec: types.RoleSpecV5{
Options: types.RoleOptions{
CertificateFormat: constants.CertificateFormatStandard,
MaxSessionTTL: types.NewDuration(apidefaults.MaxCertDuration),
PortForwarding: types.NewBoolOption(true),
RecordSession: &types.RecordSession{Desktop: types.NewBoolOption(true)},
BPF: apidefaults.EnhancedEvents(),
DesktopClipboard: types.NewBoolOption(true),
CertificateFormat: constants.CertificateFormatStandard,
MaxSessionTTL: types.NewDuration(apidefaults.MaxCertDuration),
PortForwarding: types.NewBoolOption(true),
RecordSession: &types.RecordSession{Desktop: types.NewBoolOption(true)},
BPF: apidefaults.EnhancedEvents(),
DesktopClipboard: types.NewBoolOption(true),
DesktopDirectorySharing: types.NewBoolOption(true),
},
Allow: types.RoleConditions{
Namespaces: []string{apidefaults.Namespace},
Expand All @@ -254,7 +256,8 @@ func TestRoleParse(t *testing.T) {
"client_idle_timeout": "17m",
"disconnect_expired_cert": "yes",
"enhanced_recording": ["command", "network"],
"desktop_clipboard": true
"desktop_clipboard": true,
"desktop_directory_sharing": true
},
"allow": {
"node_labels": {"a": "b", "c-d": "e"},
Expand Down Expand Up @@ -290,14 +293,15 @@ func TestRoleParse(t *testing.T) {
},
Spec: types.RoleSpecV5{
Options: types.RoleOptions{
CertificateFormat: constants.CertificateFormatStandard,
MaxSessionTTL: types.NewDuration(20 * time.Hour),
PortForwarding: types.NewBoolOption(true),
RecordSession: &types.RecordSession{Desktop: types.NewBoolOption(true)},
ClientIdleTimeout: types.NewDuration(17 * time.Minute),
DisconnectExpiredCert: types.NewBool(true),
BPF: apidefaults.EnhancedEvents(),
DesktopClipboard: types.NewBoolOption(true),
CertificateFormat: constants.CertificateFormatStandard,
MaxSessionTTL: types.NewDuration(20 * time.Hour),
PortForwarding: types.NewBoolOption(true),
RecordSession: &types.RecordSession{Desktop: types.NewBoolOption(true)},
ClientIdleTimeout: types.NewDuration(17 * time.Minute),
DisconnectExpiredCert: types.NewBool(true),
BPF: apidefaults.EnhancedEvents(),
DesktopClipboard: types.NewBoolOption(true),
DesktopDirectorySharing: types.NewBoolOption(true),
},
Allow: types.RoleConditions{
NodeLabels: types.Labels{"a": []string{"b"}, "c-d": []string{"e"}},
Expand Down Expand Up @@ -341,7 +345,8 @@ func TestRoleParse(t *testing.T) {
"client_idle_timeout": "never",
"disconnect_expired_cert": "no",
"enhanced_recording": ["command", "network"],
"desktop_clipboard": true
"desktop_clipboard": true,
"desktop_directory_sharing": true
},
"allow": {
"node_labels": {"a": "b"},
Expand Down Expand Up @@ -374,15 +379,16 @@ func TestRoleParse(t *testing.T) {
},
Spec: types.RoleSpecV5{
Options: types.RoleOptions{
CertificateFormat: constants.CertificateFormatStandard,
ForwardAgent: types.NewBool(true),
MaxSessionTTL: types.NewDuration(20 * time.Hour),
PortForwarding: types.NewBoolOption(true),
RecordSession: &types.RecordSession{Desktop: types.NewBoolOption(true)},
ClientIdleTimeout: types.NewDuration(0),
DisconnectExpiredCert: types.NewBool(false),
BPF: apidefaults.EnhancedEvents(),
DesktopClipboard: types.NewBoolOption(true),
CertificateFormat: constants.CertificateFormatStandard,
ForwardAgent: types.NewBool(true),
MaxSessionTTL: types.NewDuration(20 * time.Hour),
PortForwarding: types.NewBoolOption(true),
RecordSession: &types.RecordSession{Desktop: types.NewBoolOption(true)},
ClientIdleTimeout: types.NewDuration(0),
DisconnectExpiredCert: types.NewBool(false),
BPF: apidefaults.EnhancedEvents(),
DesktopClipboard: types.NewBoolOption(true),
DesktopDirectorySharing: types.NewBoolOption(true),
},
Allow: types.RoleConditions{
NodeLabels: types.Labels{"a": []string{"b"}},
Expand Down Expand Up @@ -424,7 +430,8 @@ func TestRoleParse(t *testing.T) {
"client_idle_timeout": "never",
"disconnect_expired_cert": "no",
"enhanced_recording": ["command", "network"],
"desktop_clipboard": true
"desktop_clipboard": true,
"desktop_directory_sharing": true
},
"allow": {
"node_labels": {"a": "b", "key": ["val"], "key2": ["val2", "val3"]},
Expand All @@ -446,15 +453,16 @@ func TestRoleParse(t *testing.T) {
},
Spec: types.RoleSpecV5{
Options: types.RoleOptions{
CertificateFormat: constants.CertificateFormatStandard,
ForwardAgent: types.NewBool(true),
MaxSessionTTL: types.NewDuration(20 * time.Hour),
PortForwarding: types.NewBoolOption(true),
RecordSession: &types.RecordSession{Desktop: types.NewBoolOption(true)},
ClientIdleTimeout: types.NewDuration(0),
DisconnectExpiredCert: types.NewBool(false),
BPF: apidefaults.EnhancedEvents(),
DesktopClipboard: types.NewBoolOption(true),
CertificateFormat: constants.CertificateFormatStandard,
ForwardAgent: types.NewBool(true),
MaxSessionTTL: types.NewDuration(20 * time.Hour),
PortForwarding: types.NewBoolOption(true),
RecordSession: &types.RecordSession{Desktop: types.NewBoolOption(true)},
ClientIdleTimeout: types.NewDuration(0),
DisconnectExpiredCert: types.NewBool(false),
BPF: apidefaults.EnhancedEvents(),
DesktopClipboard: types.NewBoolOption(true),
DesktopDirectorySharing: types.NewBoolOption(true),
},
Allow: types.RoleConditions{
NodeLabels: types.Labels{
Expand Down Expand Up @@ -2380,48 +2388,55 @@ func TestExtractFrom(t *testing.T) {
// port forwarding) can be disabled in a role.
func TestBoolOptions(t *testing.T) {
var tests = []struct {
inOptions types.RoleOptions
outCanPortForward bool
outCanForwardAgents bool
outRecordDesktopSessions bool
outDesktopClipboard bool
inOptions types.RoleOptions
outCanPortForward bool
outCanForwardAgents bool
outRecordDesktopSessions bool
outDesktopClipboard bool
outDesktopDirectorySharing bool
}{
// Setting options explicitly off should remain off.
{
inOptions: types.RoleOptions{
ForwardAgent: types.NewBool(false),
PortForwarding: types.NewBoolOption(false),
RecordSession: &types.RecordSession{Desktop: types.NewBoolOption(false)},
DesktopClipboard: types.NewBoolOption(false),
},
outCanPortForward: false,
outCanForwardAgents: false,
outRecordDesktopSessions: false,
outDesktopClipboard: false,
ForwardAgent: types.NewBool(false),
PortForwarding: types.NewBoolOption(false),
RecordSession: &types.RecordSession{Desktop: types.NewBoolOption(false)},
DesktopClipboard: types.NewBoolOption(false),
DesktopDirectorySharing: types.NewBoolOption(false),
},
outCanPortForward: false,
outCanForwardAgents: false,
outRecordDesktopSessions: false,
outDesktopClipboard: false,
outDesktopDirectorySharing: false,
},
// Not setting options should set port forwarding to true (default enabled),
// agent forwarding false (default disabled),
// desktop session recording to true (default enabled),
// and desktop clipboard sharing to true (default enabled).
// desktop clipboard sharing to true (default enabled),
// and desktop directory sharing to true (default enabled).
{
inOptions: types.RoleOptions{},
outCanPortForward: true,
outCanForwardAgents: false,
outRecordDesktopSessions: true,
outDesktopClipboard: true,
inOptions: types.RoleOptions{},
outCanPortForward: true,
outCanForwardAgents: false,
outRecordDesktopSessions: true,
outDesktopClipboard: true,
outDesktopDirectorySharing: true,
},
// Explicitly enabling should enable them.
{
inOptions: types.RoleOptions{
ForwardAgent: types.NewBool(true),
PortForwarding: types.NewBoolOption(true),
RecordSession: &types.RecordSession{Desktop: types.NewBoolOption(true)},
DesktopClipboard: types.NewBoolOption(true),
},
outCanPortForward: true,
outCanForwardAgents: true,
outRecordDesktopSessions: true,
outDesktopClipboard: true,
ForwardAgent: types.NewBool(true),
PortForwarding: types.NewBoolOption(true),
RecordSession: &types.RecordSession{Desktop: types.NewBoolOption(true)},
DesktopClipboard: types.NewBoolOption(true),
DesktopDirectorySharing: types.NewBoolOption(true),
},
outCanPortForward: true,
outCanForwardAgents: true,
outRecordDesktopSessions: true,
outDesktopClipboard: true,
outDesktopDirectorySharing: true,
},
}
for _, tt := range tests {
Expand All @@ -2440,6 +2455,7 @@ func TestBoolOptions(t *testing.T) {
require.Equal(t, tt.outCanForwardAgents, set.CanForwardAgents())
require.Equal(t, tt.outRecordDesktopSessions, set.RecordDesktopSession())
require.Equal(t, tt.outDesktopClipboard, set.DesktopClipboard())
require.Equal(t, tt.outDesktopDirectorySharing, set.DesktopDirectorySharing())
}
}

Expand Down Expand Up @@ -3691,15 +3707,65 @@ func TestDesktopClipboard(t *testing.T) {
} {
t.Run(test.desc, func(t *testing.T) {
var roles []types.Role
for _, r := range test.roles {
roles = append(roles, &r)
for i := range test.roles {
roles = append(roles, &test.roles[i])
}
rs := NewRoleSet(roles...)
require.Equal(t, test.hasClipboard, rs.DesktopClipboard())
})
}
}

func TestDesktopDirectorySharing(t *testing.T) {
for _, test := range []struct {
desc string
roles []types.RoleV5
hasDirectorySharing bool
}{
{
desc: "single role, unspecified, defaults true",
roles: []types.RoleV5{newRole(func(r *types.RoleV5) {})},
hasDirectorySharing: true,
},
{
desc: "single role, explicitly disabled",
roles: []types.RoleV5{
newRole(func(r *types.RoleV5) {
r.SetOptions(types.RoleOptions{
DesktopDirectorySharing: types.NewBoolOption(false),
})
}),
},
hasDirectorySharing: false,
},
{
desc: "multiple conflicting roles, disable wins",
roles: []types.RoleV5{
newRole(func(r *types.RoleV5) {
r.SetOptions(types.RoleOptions{
DesktopDirectorySharing: types.NewBoolOption(false),
})
}),
newRole(func(r *types.RoleV5) {
r.SetOptions(types.RoleOptions{
DesktopDirectorySharing: types.NewBoolOption(true),
})
}),
},
hasDirectorySharing: false,
},
} {
t.Run(test.desc, func(t *testing.T) {
roles := []types.Role{}
for i := range test.roles {
roles = append(roles, &test.roles[i])
}
rs := NewRoleSet(roles...)
require.Equal(t, test.hasDirectorySharing, rs.DesktopDirectorySharing())
})
}
}

func TestCheckAccessToWindowsDesktop(t *testing.T) {
desktopNoLabels := &types.WindowsDesktopV3{
ResourceHeader: types.ResourceHeader{
Expand Down
Loading

0 comments on commit 2eb1ff1

Please sign in to comment.