diff --git a/CHANGELOG.md b/CHANGELOG.md
index c45db6a770d3d..3369289d75676 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -72,6 +72,12 @@ by `insecure-drop`, which still creates temporary users but does not create a
home directory. Users who need home directory creation should either wrap `useradd`/`userdel`
or use PAM.
+#### Remove restricted sessions for SSH
+
+The restricted session feature for SSH has been deprecated since Teleport 14 and
+has been removed in Teleport 15. We recommend implementing network restrictions
+outside of Teleport (iptables, security groups, etc).
+
#### Packages no longer published to legacy Debian and RPM repos
`deb.releases.teleport.dev` and `rpm.releases.teleport.dev` were deprecated in
diff --git a/Makefile b/Makefile
index 9808e4cca0260..8a7122b12ff8e 100644
--- a/Makefile
+++ b/Makefile
@@ -321,27 +321,16 @@ ifeq ("$(with_bpf)","yes")
$(ER_BPF_BUILDDIR):
mkdir -p $(ER_BPF_BUILDDIR)
-$(RS_BPF_BUILDDIR):
- mkdir -p $(RS_BPF_BUILDDIR)
-
# Build BPF code
$(ER_BPF_BUILDDIR)/%.bpf.o: bpf/enhancedrecording/%.bpf.c $(wildcard bpf/*.h) | $(ER_BPF_BUILDDIR)
$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(KERNEL_ARCH) -I/usr/libbpf-${LIBBPF_VER}/include $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) -c $(filter %.c,$^) -o $@
$(LLVM_STRIP) -g $@ # strip useless DWARF info
-# Build BPF code
-$(RS_BPF_BUILDDIR)/%.bpf.o: bpf/restrictedsession/%.bpf.c $(wildcard bpf/*.h) | $(RS_BPF_BUILDDIR)
- $(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(KERNEL_ARCH) -I/usr/libbpf-${LIBBPF_VER}/include $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) -c $(filter %.c,$^) -o $@
- $(LLVM_STRIP) -g $@ # strip useless DWARF info
-
-.PHONY: bpf-rs-bytecode
-bpf-rs-bytecode: $(RS_BPF_BUILDDIR)/restricted.bpf.o
-
.PHONY: bpf-er-bytecode
bpf-er-bytecode: $(ER_BPF_BUILDDIR)/command.bpf.o $(ER_BPF_BUILDDIR)/disk.bpf.o $(ER_BPF_BUILDDIR)/network.bpf.o $(ER_BPF_BUILDDIR)/counter_test.bpf.o
.PHONY: bpf-bytecode
-bpf-bytecode: bpf-er-bytecode bpf-rs-bytecode
+bpf-bytecode: bpf-er-bytecode
# Generate vmlinux.h based on the installed kernel
.PHONY: update-vmlinux-h
@@ -410,9 +399,6 @@ clean-build:
# Check if the variable is set to prevent calling remove on the root directory.
ifneq ($(ER_BPF_BUILDDIR),)
rm -f $(ER_BPF_BUILDDIR)/*.o
-endif
-ifneq ($(RS_BPF_BUILDDIR),)
- rm -f $(RS_BPF_BUILDDIR)/*.o
endif
-cargo clean
-go clean -cache
diff --git a/common.mk b/common.mk
index 2088e9971fd86..1f9f9ec20f413 100644
--- a/common.mk
+++ b/common.mk
@@ -28,7 +28,6 @@ LLVM_STRIP ?= $(shell which llvm-strip || which llvm-strip-12)
KERNEL_ARCH := $(shell uname -m | sed 's/x86_64/x86/g; s/aarch64/arm64/g')
INCLUDES :=
ER_BPF_BUILDDIR := lib/bpf/bytecode
-RS_BPF_BUILDDIR := lib/restrictedsession/bytecode
# Get Clang's default includes on this system. We'll explicitly add these dirs
# to the includes list when compiling with `-target bpf` because otherwise some
diff --git a/constants.go b/constants.go
index fc05d3ae29081..d1a47d9de45e5 100644
--- a/constants.go
+++ b/constants.go
@@ -240,9 +240,6 @@ const (
// ComponentBPF is the eBPF packagae.
ComponentBPF = "bpf"
- // ComponentRestrictedSession is restriction of user access to kernel objects
- ComponentRestrictedSession = "restrictedsess"
-
// ComponentCgroup is the cgroup package.
ComponentCgroup = "cgroups"
diff --git a/fuzz/oss-fuzz-build.sh b/fuzz/oss-fuzz-build.sh
index 2ac1f954c0956..34001912c9cdc 100755
--- a/fuzz/oss-fuzz-build.sh
+++ b/fuzz/oss-fuzz-build.sh
@@ -26,9 +26,6 @@ build_teleport_fuzzers() {
compile_native_go_fuzzer $TELEPORT_PREFIX/lib/srv/desktop/tdp \
FuzzDecode fuzz_decode
- compile_native_go_fuzzer $TELEPORT_PREFIX/lib/restrictedsession \
- FuzzParseIPSpec fuzz_parse_ip_spec
-
compile_native_go_fuzzer $TELEPORT_PREFIX/lib/services \
FuzzParseRefs fuzz_parse_refs
diff --git a/integration/utmp_integration_test.go b/integration/utmp_integration_test.go
index 6b72407f7e503..c5d420987ce2f 100644
--- a/integration/utmp_integration_test.go
+++ b/integration/utmp_integration_test.go
@@ -42,7 +42,6 @@ import (
"github.com/gravitational/teleport/lib/auth/testauthority"
"github.com/gravitational/teleport/lib/authz"
"github.com/gravitational/teleport/lib/bpf"
- restricted "github.com/gravitational/teleport/lib/restrictedsession"
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/srv"
@@ -336,7 +335,6 @@ func newSrvCtx(ctx context.Context, t *testing.T) *SrvCtx {
}, nil,
),
regular.SetBPF(&bpf.NOP{}),
- regular.SetRestrictedSessionManager(&restricted.NOP{}),
regular.SetClock(s.clock),
regular.SetUserAccountingPaths(utmpPath, wtmpPath, btmpPath),
regular.SetLockWatcher(lockWatcher),
diff --git a/lib/bpf/bpf.go b/lib/bpf/bpf.go
index 6bdfbd170fb8a..7eea97c5cba4e 100644
--- a/lib/bpf/bpf.go
+++ b/lib/bpf/bpf.go
@@ -117,15 +117,11 @@ type Service struct {
}
// New creates a BPF service.
-func New(config *servicecfg.BPFConfig, restrictedSession *servicecfg.RestrictedSessionConfig) (BPF, error) {
+func New(config *servicecfg.BPFConfig) (BPF, error) {
if err := config.CheckAndSetDefaults(); err != nil {
return nil, trace.Wrap(err)
}
- if err := restrictedSession.CheckAndSetDefaults(); err != nil {
- return nil, trace.Wrap(err)
- }
-
// If BPF-based auditing is not enabled, don't configure anything return
// right away.
if !config.Enabled {
@@ -185,10 +181,8 @@ func New(config *servicecfg.BPFConfig, restrictedSession *servicecfg.RestrictedS
}
log.Debugf("Started enhanced session recording with buffer sizes (command=%v, "+
- "disk=%v, network=%v), restricted session (bufferSize=%v) "+
- "and cgroup mount path: %v. Took %v.",
+ "disk=%v, network=%v) and cgroup mount path: %v. Took %v.",
*s.CommandBufferSize, *s.DiskBufferSize, *s.NetworkBufferSize,
- *restrictedSession.EventsBufferSize,
s.CgroupPath, time.Since(start))
go s.processNetworkEvents()
@@ -590,7 +584,7 @@ func (s *Service) emit6NetworkEvent(eventBytes []byte) {
func ipv4HostToIP(addr uint32) net.IP {
val := make([]byte, 4)
binary.LittleEndian.PutUint32(val, addr)
- return net.IP(val)
+ return val
}
func ipv6HostToIP(addr [4]uint32) net.IP {
@@ -599,7 +593,7 @@ func ipv6HostToIP(addr [4]uint32) net.IP {
binary.LittleEndian.PutUint32(val[4:], addr[1])
binary.LittleEndian.PutUint32(val[8:], addr[2])
binary.LittleEndian.PutUint32(val[12:], addr[3])
- return net.IP(val)
+ return val
}
// unmarshalEvent will unmarshal the perf event.
diff --git a/lib/bpf/bpf_nop.go b/lib/bpf/bpf_nop.go
index 95a4cb14cfbd2..1866b46a32fd5 100644
--- a/lib/bpf/bpf_nop.go
+++ b/lib/bpf/bpf_nop.go
@@ -30,7 +30,7 @@ type Service struct {
}
// New returns a new NOP service. Note this function does nothing.
-func New(_ *servicecfg.BPFConfig, _ *servicecfg.RestrictedSessionConfig) (BPF, error) {
+func New(_ *servicecfg.BPFConfig) (BPF, error) {
return &NOP{}, nil
}
diff --git a/lib/bpf/bpf_test.go b/lib/bpf/bpf_test.go
index 320a11f2d0457..69d2abf208f4f 100644
--- a/lib/bpf/bpf_test.go
+++ b/lib/bpf/bpf_test.go
@@ -154,7 +154,7 @@ func TestRootWatch(t *testing.T) {
service, err := New(&servicecfg.BPFConfig{
Enabled: true,
CgroupPath: cgroupPath,
- }, &servicecfg.RestrictedSessionConfig{})
+ })
require.NoError(t, err)
t.Cleanup(func() {
diff --git a/lib/config/configuration.go b/lib/config/configuration.go
index f80d0bdf24281..d4f68c4afce53 100644
--- a/lib/config/configuration.go
+++ b/lib/config/configuration.go
@@ -1458,14 +1458,7 @@ func applySSHConfig(fc *FileConfig, cfg *servicecfg.Config) (err error) {
cfg.SSH.BPF = fc.SSH.BPF.Parse()
}
if fc.SSH.RestrictedSession != nil {
- rs, err := fc.SSH.RestrictedSession.Parse()
- if err != nil {
- return trace.Wrap(err)
- }
- cfg.SSH.RestrictedSession = rs
-
- log.Warnf("Restricted Sessions for SSH were deprecated in Teleport 14 " +
- "and will be removed in Teleport 15.")
+ log.Error("Restricted Sessions for SSH were removed in Teleport 15.")
}
cfg.SSH.AllowTCPForwarding = fc.SSH.AllowTCPForwarding()
diff --git a/lib/config/fileconf.go b/lib/config/fileconf.go
index b70a9aec9f557..081724fcec6d8 100644
--- a/lib/config/fileconf.go
+++ b/lib/config/fileconf.go
@@ -1524,19 +1524,6 @@ type RestrictedSession struct {
EventsBufferSize *int `yaml:"events_buffer_size,omitempty"`
}
-// Parse will parse the enhanced session recording configuration.
-func (r *RestrictedSession) Parse() (*servicecfg.RestrictedSessionConfig, error) {
- enabled, err := apiutils.ParseBool(r.Enabled)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- return &servicecfg.RestrictedSessionConfig{
- Enabled: enabled,
- EventsBufferSize: r.EventsBufferSize,
- }, nil
-}
-
// X11 is a configuration for X11 forwarding
type X11 struct {
// Enabled controls whether X11 forwarding requests can be granted by the server.
diff --git a/lib/restrictedsession/audit.go b/lib/restrictedsession/audit.go
deleted file mode 100644
index 2bef185f7b21d..0000000000000
--- a/lib/restrictedsession/audit.go
+++ /dev/null
@@ -1,160 +0,0 @@
-//go:build bpf && !386
-// +build bpf,!386
-
-/*
- * Teleport
- * Copyright (C) 2023 Gravitational, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package restrictedsession
-
-import (
- "bytes"
- "encoding/binary"
- "fmt"
- "net"
- "unsafe"
-
- "github.com/gravitational/trace"
-
- "github.com/gravitational/teleport/api/types/events"
- "github.com/gravitational/teleport/lib/bpf"
- api "github.com/gravitational/teleport/lib/events"
-)
-
-const (
- BlockedIP4 = iota
- BlockedIP6
-)
-
-//
-// Audit Event types communicated between the kernel and userspace
-//
-
-// auditEventHeader matches audit_event_header in the C file
-type auditEventHeader struct {
- CGroupID uint64
- PID uint32
- EventType int32
- Command [bpf.CommMax]byte
-}
-
-// auditEventBlockedIPv4 matches audit_event_blocked_ipv4 in the C file
-type auditEventBlockedIPv4 struct {
- SrcIP [4]byte
- DstIP [4]byte
- DstPort uint16
- Op uint8
-}
-
-// auditEventBlockedIPv6 matches audit_event_blocked_ipv6 in the C file
-type auditEventBlockedIPv6 struct {
- SrcIP [16]byte
- DstIP [16]byte
- DstPort uint16
- Op uint8
-}
-
-// newNetworkAuditEvent creates events.SessionNetwork, filling in common fields
-// from the SessionContext
-func newNetworkAuditEvent(ctx *bpf.SessionContext, hdr *auditEventHeader) events.SessionNetwork {
- return events.SessionNetwork{
- Metadata: events.Metadata{
- Type: api.SessionNetworkEvent,
- Code: api.SessionNetworkCode,
- },
- ServerMetadata: events.ServerMetadata{
- ServerID: ctx.ServerID,
- ServerHostname: ctx.ServerHostname,
- ServerNamespace: ctx.Namespace,
- },
- SessionMetadata: events.SessionMetadata{
- SessionID: ctx.SessionID,
- },
- UserMetadata: events.UserMetadata{
- User: ctx.User,
- Login: ctx.Login,
- },
- BPFMetadata: events.BPFMetadata{
- CgroupID: hdr.CGroupID,
- Program: bpf.ConvertString(unsafe.Pointer(&hdr.Command)),
- PID: uint64(hdr.PID),
- },
- }
-}
-
-// parseAuditEventHeader parse the header portion of the event.
-// buf is consumed so that only body bytes remain.
-func parseAuditEventHeader(buf *bytes.Buffer) (auditEventHeader, error) {
- var hdr auditEventHeader
- err := binary.Read(buf, binary.LittleEndian, &hdr)
- if err != nil {
- return auditEventHeader{}, trace.BadParameter("corrupt event header: %v", err)
- }
- return hdr, nil
-}
-
-// ip6String is similar to IP.String but retains mapped addresses
-// in IPv6 form.
-func ip6String(ip net.IP) string {
- var prefix string
-
- if ip.To4() != nil {
- // IP4 mapped address
- prefix = "::ffff:"
- }
-
- return prefix + ip.String()
-}
-
-// parseAuditEvent parses the body of the audit event
-func parseAuditEvent(buf *bytes.Buffer, hdr *auditEventHeader, ctx *bpf.SessionContext) (events.AuditEvent, error) {
- switch hdr.EventType {
- case BlockedIP4:
- var body auditEventBlockedIPv4
- if err := binary.Read(buf, binary.LittleEndian, &body); err != nil {
- return nil, trace.Wrap(err)
- }
-
- event := newNetworkAuditEvent(ctx, hdr)
- event.DstPort = int32(body.DstPort)
- event.DstAddr = net.IP(body.DstIP[:]).String()
- event.SrcAddr = net.IP(body.SrcIP[:]).String()
- event.TCPVersion = 4
- event.Operation = events.SessionNetwork_NetworkOperation(body.Op)
- event.Action = events.EventAction_DENIED
-
- return &event, nil
-
- case BlockedIP6:
- var body auditEventBlockedIPv6
- if err := binary.Read(buf, binary.LittleEndian, &body); err != nil {
- return nil, trace.Wrap(err)
- }
-
- event := newNetworkAuditEvent(ctx, hdr)
- event.DstPort = int32(body.DstPort)
- event.DstAddr = ip6String(net.IP(body.DstIP[:]))
- event.SrcAddr = ip6String(net.IP(body.SrcIP[:]))
- event.TCPVersion = 6
- event.Operation = events.SessionNetwork_NetworkOperation(body.Op)
- event.Action = events.EventAction_DENIED
-
- return &event, nil
- }
-
- return nil, fmt.Errorf("received unknown event type: %v", hdr.EventType)
-}
diff --git a/lib/restrictedsession/bytecode/README.md b/lib/restrictedsession/bytecode/README.md
deleted file mode 100644
index a71a135de3b5f..0000000000000
--- a/lib/restrictedsession/bytecode/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-## BPF Bytecode
-
-After builds, this directory contains CO-RE BFP bytecode that is embedded within Teleport.
diff --git a/lib/restrictedsession/config.go b/lib/restrictedsession/config.go
deleted file mode 100644
index 53b5f00f6a6ad..0000000000000
--- a/lib/restrictedsession/config.go
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Teleport
- * Copyright (C) 2023 Gravitational, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package restrictedsession
-
-import (
- "net"
-
- "github.com/gravitational/trace"
-)
-
-func compactIP(ip net.IP) net.IP {
- if ipv4 := ip.To4(); ipv4 != nil {
- return ipv4
- }
- return ip
-}
-
-// ParseIPSpec takes in either a CIDR format (e.g. 192.168.1.2/16 or fe::/8)
-// or a single IP address (e.g. 10.1.2.3 or fe::1) and returns *net.IPNet.
-// In case of a single IP address, the associated network length is either
-// /32 for IPv4 or /128 for IPv6.
-func ParseIPSpec(cidr string) (*net.IPNet, error) {
- _, ipnet, err := net.ParseCIDR(cidr)
- if err == nil {
- return ipnet, nil
- }
-
- // not in CIDR format, try as a plain IP
- ip := net.ParseIP(cidr)
- if ip == nil {
- return nil, trace.BadParameter("%q is not an IP nor CIDR", cidr)
- }
-
- ip = compactIP(ip)
- bits := len(ip) * 8
- return &net.IPNet{
- IP: ip,
- Mask: net.CIDRMask(bits, bits),
- }, nil
-}
-
-// NetworkRestrictions specifies which addresses should be blocked.
-type NetworkRestrictions struct {
- // Enabled controls if restrictions are enforced.
- Enabled bool
-
- // Allow holds a list of IPs (with masks) to allow, overriding deny list
- Allow []net.IPNet
-
- // Deny holds a list of IPs (with masks) to deny (block)
- Deny []net.IPNet
-}
diff --git a/lib/restrictedsession/fuzz_test.go b/lib/restrictedsession/fuzz_test.go
deleted file mode 100644
index d1c3b15737288..0000000000000
--- a/lib/restrictedsession/fuzz_test.go
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Teleport
- * Copyright (C) 2023 Gravitational, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package restrictedsession
-
-import (
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func FuzzParseIPSpec(f *testing.F) {
- f.Add("127.0.0.111")
- f.Add("127.0.0.111/8")
- f.Add("192.168.0.0/16")
- f.Add("2001:0db8:85a3:0000:0000:8a2e:0370:7334")
- f.Add("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64")
- f.Add("2001:db8::ff00:42:8329")
- f.Add("2001:db8::ff00:42:8329/48")
-
- f.Fuzz(func(t *testing.T, cidr string) {
- require.NotPanics(t, func() {
- ParseIPSpec(cidr)
- })
- })
-}
diff --git a/lib/restrictedsession/manager.go b/lib/restrictedsession/manager.go
deleted file mode 100644
index 9965b9dcaa523..0000000000000
--- a/lib/restrictedsession/manager.go
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Teleport
- * Copyright (C) 2023 Gravitational, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package restrictedsession
-
-import (
- "github.com/gravitational/teleport/api/types"
- "github.com/gravitational/teleport/lib/bpf"
- "github.com/gravitational/teleport/lib/services"
-)
-
-// RestrictionsWatcherClient is used by changeset to fetch a list
-// of proxies and subscribe to updates
-type RestrictionsWatcherClient interface {
- services.Restrictions
- types.Events
-}
-
-// Manager starts and stop enforcing restrictions for a given session.
-type Manager interface {
- // OpenSession starts enforcing restrictions for a cgroup with cgroupID
- OpenSession(ctx *bpf.SessionContext, cgroupID uint64)
- // CloseSession stops enforcing restrictions for a cgroup with cgroupID
- CloseSession(ctx *bpf.SessionContext, cgroupID uint64)
- // Close stops the manager, cleaning up any resources
- Close()
-}
-
-// Stubbed out Manager interface for cases where the real thing is not used.
-type NOP struct{}
-
-func (NOP) OpenSession(ctx *bpf.SessionContext, cgroupID uint64) {
-}
-
-func (NOP) CloseSession(ctx *bpf.SessionContext, cgroupID uint64) {
-}
-
-func (NOP) UpdateNetworkRestrictions(r *NetworkRestrictions) error {
- return nil
-}
-
-func (NOP) Close() {
-}
diff --git a/lib/restrictedsession/network.go b/lib/restrictedsession/network.go
deleted file mode 100644
index 336d85abd128e..0000000000000
--- a/lib/restrictedsession/network.go
+++ /dev/null
@@ -1,342 +0,0 @@
-//go:build bpf && !386
-// +build bpf,!386
-
-/*
- * Teleport
- * Copyright (C) 2023 Gravitational, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package restrictedsession
-
-import (
- "bytes"
- "encoding/binary"
- "fmt"
- "net"
- "sort"
- "strings"
- "sync"
- "unsafe"
-
- "github.com/aquasecurity/libbpfgo"
- "github.com/gravitational/trace"
-)
-
-var (
- wildcard4 = net.IPNet{
- IP: net.IP{0, 0, 0, 0},
- Mask: net.IPMask{0, 0, 0, 0},
- }
-
- wildcard6 = net.IPNet{
- IP: net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
- Mask: net.IPMask{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
- }
-)
-
-// ipTrie wraps BPF LSM map to work with net.IPNet types
-type ipTrie struct {
- bpfMap *libbpfgo.BPFMap
-}
-
-func newIPTrie(m *libbpfgo.Module, name string) (ipTrie, error) {
- t, err := m.GetMap(name)
- if err != nil {
- return ipTrie{}, trace.Wrap(err)
- }
-
- return ipTrie{
- bpfMap: t,
- }, nil
-}
-
-func (t *ipTrie) toKey(n net.IPNet) []byte {
- prefixLen, _ := n.Mask.Size()
-
- // Key format: Prefix length (4 bytes) followed by prefix
- key := make([]byte, 4+len(n.IP))
-
- binary.LittleEndian.PutUint32(key[0:4], uint32(prefixLen))
- copy(key[4:], n.IP)
-
- return key
-}
-
-// Add upserts (prefixLen, prefix) -> value entry in BPF trie
-func (t *ipTrie) add(n net.IPNet) error {
- key := t.toKey(n)
- return t.bpfMap.Update(unsafe.Pointer(&key[0]), unsafe.Pointer(&unit[0]))
-}
-
-// Remove removes the entry for the given network
-func (t *ipTrie) remove(n net.IPNet) error {
- key := t.toKey(n)
- return t.bpfMap.DeleteKey(unsafe.Pointer(&key[0]))
-}
-
-// cmp is 3-way integral compare
-func cmp(x, y int) int {
- switch {
- case x < y:
- return -1
- case x > y:
- return 1
- default:
- return 0
- }
-}
-
-// prefixLen returns the length of network prefix
-// based on IPv6 encoding
-func prefixLen(m net.IPMask) int {
- ones, bits := m.Size()
- if bits == 32 {
- ones += (128 - 32)
- }
- return ones
-}
-
-// compareIPNets performs a 3-way compare of two IPNet
-// objects. This induces a total order but it doesn't
-// matter what order that is.
-func compareIPNets(x, y *net.IPNet) int {
- x.IP = x.IP.To16()
- y.IP = y.IP.To16()
-
- if ret := bytes.Compare(x.IP, y.IP); ret != 0 {
- return ret
- }
-
- xPrefix := prefixLen(x.Mask)
- yPrefix := prefixLen(y.Mask)
-
- return cmp(xPrefix, yPrefix)
-}
-
-// ipNets is sort.Interface impl to sort []net.IPNet
-type ipNets []net.IPNet
-
-func (s ipNets) Len() int {
- return len(s)
-}
-
-func (s ipNets) Less(i, j int) bool {
- x := &s[i]
- y := &s[j]
- return compareIPNets(x, y) < 0
-}
-
-func (s ipNets) Swap(i, j int) {
- s[i], s[j] = s[j], s[i]
-}
-
-// diffIPNets computes the differences between prev
-// and next sets of net.IPNet objects. The diffs are
-// returned as a tuple of (additions, deletions)
-func diffIPNets(prev, next ipNets) (ipNets, ipNets) {
- // Sorts []net.IPNet according to some
- // criteria. The ordering used is immaterial, as long
- // as total order is induced.
- sort.Sort(prev)
- sort.Sort(next)
-
- adds := ipNets{}
- dels := ipNets{}
-
- i := 0
- j := 0
- for i < len(prev) && j < len(next) {
- switch compareIPNets(&prev[i], &next[j]) {
- case -1:
- dels = append(dels, prev[i])
- i++
- case 0:
- i++
- j++
- case 1:
- adds = append(adds, next[j])
- j++
- }
- }
-
- // handle the tails (at most one of the lists still has a tail)
- dels = append(dels, prev[i:]...)
- adds = append(adds, next[j:]...)
-
- return adds, dels
-}
-
-// network restricts IPv4 and IPv6 related operations.
-type network struct {
- mu sync.Mutex
- mod *libbpfgo.Module
- deny4 ipTrie
- allow4 ipTrie
- deny6 ipTrie
- allow6 ipTrie
- restrictions *NetworkRestrictions
-}
-
-func newNetwork(mod *libbpfgo.Module) (*network, error) {
- deny4, err := newIPTrie(mod, "ip4_denylist")
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- allow4, err := newIPTrie(mod, "ip4_allowlist")
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- deny6, err := newIPTrie(mod, "ip6_denylist")
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- allow6, err := newIPTrie(mod, "ip6_allowlist")
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- n := network{
- mod: mod,
- deny4: deny4,
- allow4: allow4,
- deny6: deny6,
- allow6: allow6,
- restrictions: &NetworkRestrictions{},
- }
-
- if err = n.start(); err != nil {
- return nil, trace.Wrap(err)
- }
-
- return &n, err
-}
-
-func (n *network) start() error {
- hooks := []string{"socket_connect", "socket_sendmsg"}
-
- for _, hook := range hooks {
- if err := attachLSM(n.mod, hook); err != nil {
- return trace.Wrap(err)
- }
- }
-
- return nil
-}
-
-func ipv4MappedIPNet(ipnet net.IPNet) net.IPNet {
- ipnet.IP = ipnet.IP.To16()
- ones, _ := ipnet.Mask.Size()
- // IPv4 mapped address has a 96-bit fixed prefix
- ipnet.Mask = net.CIDRMask(96+ones, 128)
- return ipnet
-}
-
-func (n *network) apply(nets []net.IPNet, fn4, fn6 func(net.IPNet) error) error {
- for _, ipnet := range nets {
- ip := ipnet.IP.To4()
- if ip != nil {
- // IPv4 address
- ipnet.IP = ip
- if err := fn4(ipnet); err != nil {
- return trace.Wrap(err)
- }
-
- // Also add it to IPv6 trie as a mapped address.
- // Needed in case an AF_INET6 socket is used with
- // IPv4 translated address. The IPv6 stack will forward
- // it to IPv4 stack but that happens much lower than
- // the LSM hook.
- ipnet = ipv4MappedIPNet(ipnet)
- if err := fn6(ipnet); err != nil {
- return trace.Wrap(err)
- }
- } else {
- ip = ipnet.IP.To16()
- if ip == nil {
- return fmt.Errorf("%q is not an IPv4 or IPv6 address", ip.String())
- }
-
- if err := fn6(ipnet); err != nil {
- return trace.Wrap(err)
- }
- }
- }
-
- return nil
-}
-
-func (n *network) update(newRestrictions *NetworkRestrictions) error {
- n.mu.Lock()
- defer n.mu.Unlock()
-
- if !newRestrictions.Enabled {
- newRestrictions.Allow = []net.IPNet{wildcard4, wildcard6}
- newRestrictions.Deny = nil
- }
-
- // Compute the diff between the previous and new configs
- // as a set of additions and deletions. Then apply these
- // changes to the BPF maps.
-
- // The deny list
- denyAdds, denyDels := diffIPNets(n.restrictions.Deny, newRestrictions.Deny)
- // Do the deletions
- if err := n.apply(denyDels, n.deny4.remove, n.deny6.remove); err != nil {
- return trace.Wrap(err)
- }
- // Do the additions
- if err := n.apply(denyAdds, n.deny4.add, n.deny6.add); err != nil {
- return trace.Wrap(err)
- }
-
- // The allow list
- allowAdds, allowDels := diffIPNets(n.restrictions.Allow, newRestrictions.Allow)
- // Do the deletions
- if err := n.apply(allowDels, n.allow4.remove, n.allow6.remove); err != nil {
- return trace.Wrap(err)
- }
- // Do the additions
- if err := n.apply(allowAdds, n.allow4.add, n.allow6.add); err != nil {
- return trace.Wrap(err)
- }
-
- n.restrictions = newRestrictions
-
- log.Infof("New network restrictions applied: allow=[%v], deny=[%v]",
- ipNetsToString(n.restrictions.Allow),
- ipNetsToString(n.restrictions.Deny))
-
- return nil
-}
-
-func (n *network) close() {
-}
-
-func ipNetsToString(ns []net.IPNet) string {
- b := strings.Builder{}
- for i, n := range ns {
- if i != 0 {
- b.WriteString(", ")
- }
-
- b.WriteString(n.String())
- }
-
- return b.String()
-}
diff --git a/lib/restrictedsession/nop.go b/lib/restrictedsession/nop.go
deleted file mode 100644
index 020fbc8cde040..0000000000000
--- a/lib/restrictedsession/nop.go
+++ /dev/null
@@ -1,29 +0,0 @@
-//go:build !bpf || 386
-// +build !bpf 386
-
-/*
- * Teleport
- * Copyright (C) 2023 Gravitational, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package restrictedsession
-
-import "github.com/gravitational/teleport/lib/service/servicecfg"
-
-// New returns a new NOP service. Note this function does nothing.
-func New(config *servicecfg.RestrictedSessionConfig, wc RestrictionsWatcherClient) (Manager, error) {
- return &NOP{}, nil
-}
diff --git a/lib/restrictedsession/restricted.go b/lib/restrictedsession/restricted.go
deleted file mode 100644
index 5c0264a72d7e9..0000000000000
--- a/lib/restrictedsession/restricted.go
+++ /dev/null
@@ -1,346 +0,0 @@
-//go:build bpf && !386
-// +build bpf,!386
-
-/*
- * Teleport
- * Copyright (C) 2023 Gravitational, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package restrictedsession
-
-import (
- "bytes"
- "embed"
- "encoding/binary"
- "os"
- "sync"
- "unsafe"
-
- "github.com/aquasecurity/libbpfgo"
- "github.com/gravitational/trace"
- "github.com/prometheus/client_golang/prometheus"
- "github.com/sirupsen/logrus"
-
- "github.com/gravitational/teleport"
- "github.com/gravitational/teleport/lib/bpf"
- "github.com/gravitational/teleport/lib/service/servicecfg"
-)
-
-var log = logrus.WithFields(logrus.Fields{
- trace.Component: teleport.ComponentRestrictedSession,
-})
-
-var (
- lostRestrictedEvents = prometheus.NewCounter(
- prometheus.CounterOpts{
- Name: teleport.MetricLostRestrictedEvents,
- Help: "Number of lost restricted events.",
- },
- )
-)
-
-//go:embed bytecode
-var embedFS embed.FS
-
-func init() {
- prometheus.MustRegister(lostRestrictedEvents)
-}
-
-var unit = make([]byte, 1)
-
-// sessionMgr implements restrctedsession.Manager interface
-// by enforcing the rules via LSM BPF hooks
-type sessionMgr struct {
- // mod is the handle to the BPF loaded module
- mod *libbpfgo.Module
-
- // watch keeps the set of cgroups being enforced
- watch bpf.SessionWatch
-
- // cgroups for which enforcement is active
- restrictedCGroups *libbpfgo.BPFMap
-
- // network blocking subsystem
- nw *network
-
- // eventLoop pumps the audit messages from the kernel
- // to the audit subsystem
- eventLoop *auditEventLoop
-
- // updateLoop listens for restriction updates and applies them
- // to the audit subsystem
- updateLoop *restrictionsUpdateLoop
-}
-
-// New creates a RestrictedSession service.
-func New(config *servicecfg.RestrictedSessionConfig, wc RestrictionsWatcherClient) (Manager, error) {
- err := config.CheckAndSetDefaults()
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- // If BPF-based auditing is not enabled, don't configure anything
- // right away.
- if !config.Enabled {
- log.Debugf("Restricted session is not enabled, skipping.")
- return &NOP{}, nil
- }
-
- // Before proceeding, check that eBPF based LSM is enabled in the kernel
- if err = checkBpfLsm(); err != nil {
- return nil, trace.Wrap(err)
- }
-
- log.Debugf("Starting restricted session.")
-
- restrictedBPF, err := embedFS.ReadFile("bytecode/restricted.bpf.o")
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- mod, err := libbpfgo.NewModuleFromBuffer(restrictedBPF, "restricted")
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- // Load into the kernel
- if err = mod.BPFLoadObject(); err != nil {
- return nil, trace.Wrap(err)
- }
-
- nw, err := newNetwork(mod)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- cgroups, err := mod.GetMap("restricted_cgroups")
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- m := &sessionMgr{
- mod: mod,
- watch: bpf.NewSessionWatch(),
- restrictedCGroups: cgroups,
- nw: nw,
- }
-
- m.eventLoop, err = newAuditEventLoop(mod, &m.watch)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- m.updateLoop, err = newRestrictionsUpdateLoop(nw, wc)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- log.Info("Started restricted session management")
-
- return m, nil
-}
-
-// Close will stop any running BPF programs. Note this is only for a graceful
-// shutdown, from the man page for BPF: "Generally, eBPF programs are loaded
-// by the user process and automatically unloaded when the process exits."
-func (m *sessionMgr) Close() {
- // Close the updater loop
- m.updateLoop.close()
-
- // Signal the loop pulling events off the perf buffer to shutdown.
- m.eventLoop.close()
-}
-
-// OpenSession inserts the cgroupID into the BPF hash map to enable
-// enforcement by the kernel
-func (m *sessionMgr) OpenSession(ctx *bpf.SessionContext, cgroupID uint64) {
- m.watch.Add(cgroupID, ctx)
-
- key := make([]byte, 8)
- binary.LittleEndian.PutUint64(key, cgroupID)
-
- m.restrictedCGroups.Update(unsafe.Pointer(&key[0]), unsafe.Pointer(&unit[0]))
-
- log.Debugf("CGroup %v registered", cgroupID)
-}
-
-// CloseSession removes the cgroupID from the BPF hash map to enable
-// enforcement by the kernel
-func (m *sessionMgr) CloseSession(ctx *bpf.SessionContext, cgroupID uint64) {
- key := make([]byte, 8)
- binary.LittleEndian.PutUint64(key, cgroupID)
-
- m.restrictedCGroups.DeleteKey(unsafe.Pointer(&key[0]))
-
- m.watch.Remove(cgroupID)
-
- log.Debugf("CGroup %v unregistered", cgroupID)
-}
-
-type restrictionsUpdateLoop struct {
- nw *network
-
- watcher *RestrictionsWatcher
-
- // Notifies that loop goroutine is done
- wg sync.WaitGroup
-}
-
-func newRestrictionsUpdateLoop(nw *network, wc RestrictionsWatcherClient) (*restrictionsUpdateLoop, error) {
- w, err := NewRestrictionsWatcher(RestrictionsWatcherConfig{
- Client: wc,
- RestrictionsC: make(chan *NetworkRestrictions, 10),
- })
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- l := &restrictionsUpdateLoop{
- nw: nw,
- watcher: w,
- }
-
- l.wg.Add(1)
- go l.loop()
-
- return l, nil
-}
-
-func (l *restrictionsUpdateLoop) close() {
- l.watcher.Close()
- l.wg.Wait()
-}
-
-func (l *restrictionsUpdateLoop) loop() {
- defer l.wg.Done()
-
- for r := range l.watcher.RestrictionsC {
- l.nw.update(r)
- }
-}
-
-type auditEventLoop struct {
- // Maps the cgroup to the session
- watch *bpf.SessionWatch
-
- // BPF ring buffer for reported audit (blocked) events
- events *bpf.RingBuffer
-
- // Keeps track of the number of lost audit events
- lost *bpf.Counter
-
- // Notifies that loop goroutine is done
- wg sync.WaitGroup
-}
-
-// loop pulls events off the perf ring buffer, parses them, and emits them to
-// the audit log.
-func newAuditEventLoop(mod *libbpfgo.Module, w *bpf.SessionWatch) (*auditEventLoop, error) {
- events, err := bpf.NewRingBuffer(mod, "audit_events")
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- lost, err := bpf.NewCounter(mod, "lost", lostRestrictedEvents)
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- l := &auditEventLoop{
- watch: w,
- events: events,
- lost: lost,
- }
-
- l.wg.Add(1)
- go l.loop()
-
- return l, nil
-}
-
-func (l *auditEventLoop) loop() {
- defer l.wg.Done()
-
- for eventBytes := range l.events.EventCh {
- buf := bytes.NewBuffer(eventBytes)
- hdr, err := parseAuditEventHeader(buf)
- if err != nil {
- log.Error(err.Error())
- continue
- }
-
- ctx, ok := l.watch.Get(hdr.CGroupID)
- if !ok {
- log.Errorf("Blocked event for unknown cgroup ID (%v)", hdr.CGroupID)
- continue
- }
-
- event, err := parseAuditEvent(buf, &hdr, ctx)
- if err != nil {
- log.WithError(err).Error("Failed to parse network event.")
- continue
- }
-
- if err = ctx.Emitter.EmitAuditEvent(ctx.Context, event); err != nil {
- log.WithError(err).Warn("Failed to emit network event.")
- }
- }
-}
-
-func (l *auditEventLoop) close() {
- // Cleanup
- l.events.Close()
- l.lost.Close()
-
- l.wg.Wait()
-}
-
-// checkBpfLsm checks that eBPF is one of the enabled
-// LSM "modules".
-func checkBpfLsm() error {
- const lsmInfo = "/sys/kernel/security/lsm"
-
- csv, err := os.ReadFile(lsmInfo)
- if err != nil {
- return trace.Wrap(err)
- }
-
- for _, mod := range bytes.Split(csv, []byte(",")) {
- if bytes.Equal(mod, []byte("bpf")) {
- return nil
- }
- }
-
- return trace.Errorf(`%s does not contain bpf entry, indicating that the kernel
-is not enabled for eBPF based LSM enforcement. Make sure the kernel is compiled with
-CONFIG_BPF_LSM=y and enabled via CONFIG_LSM or lsm= boot option`, lsmInfo)
-}
-
-// attachLSM attaches the LSM programs in the module to
-// kernel hook points.
-func attachLSM(mod *libbpfgo.Module, name string) error {
- prog, err := mod.GetProgram(name)
- if err != nil {
- return trace.Wrap(err)
- }
-
- _, err = prog.AttachLSM()
- if err != nil {
- return trace.Wrap(err)
- }
-
- return nil
-}
diff --git a/lib/restrictedsession/restricted_test.go b/lib/restrictedsession/restricted_test.go
deleted file mode 100644
index f01866602c60c..0000000000000
--- a/lib/restrictedsession/restricted_test.go
+++ /dev/null
@@ -1,515 +0,0 @@
-//go:build bpf && !386
-// +build bpf,!386
-
-/*
- * Teleport
- * Copyright (C) 2023 Gravitational, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package restrictedsession
-
-import (
- "context"
- "errors"
- "fmt"
- "net"
- "os"
- "regexp"
- "syscall"
- "testing"
- "time"
-
- gocmp "github.com/google/go-cmp/cmp"
- "github.com/google/uuid"
- "github.com/stretchr/testify/require"
-
- apidefaults "github.com/gravitational/teleport/api/defaults"
- api "github.com/gravitational/teleport/api/types"
- apievents "github.com/gravitational/teleport/api/types/events"
- "github.com/gravitational/teleport/lib/bpf"
- "github.com/gravitational/teleport/lib/events"
- "github.com/gravitational/teleport/lib/events/eventstest"
- "github.com/gravitational/teleport/lib/service/servicecfg"
- "github.com/gravitational/teleport/lib/services"
- "github.com/gravitational/teleport/lib/utils"
-)
-
-type blockAction int
-
-const (
- allowed = iota
- denied
-)
-
-type blockedRange struct {
- ver int // 4 or 6
- deny string // Denied IP range in CIDR format or a lone IP
- allow string // Allowed IP range in CIDR format or a lone IP
- probe map[string]blockAction // IP to test the blocked range (needs to be within range)
-}
-
-const (
- testPort = 8888
-)
-
-var testRanges = []blockedRange{
- {
- ver: 4,
- allow: "39.156.69.70/28",
- deny: "39.156.69.71",
- probe: map[string]blockAction{
- "39.156.69.64": allowed,
- "39.156.69.79": allowed,
- "39.156.69.71": denied,
- "39.156.69.63": denied,
- "39.156.69.80": denied,
- "72.156.69.80": denied,
- },
- },
- {
- ver: 4,
- allow: "77.88.55.88",
- probe: map[string]blockAction{
- "77.88.55.88": allowed,
- "77.88.55.87": denied,
- "77.88.55.86": denied,
- "67.88.55.86": denied,
- },
- },
- {
- ver: 6,
- allow: "39.156.68.48/28",
- deny: "39.156.68.48/31",
- probe: map[string]blockAction{
- "::ffff:39.156.68.48": denied,
- "::ffff:39.156.68.49": denied,
- "::ffff:39.156.68.50": allowed,
- "::ffff:39.156.68.63": allowed,
- "::ffff:39.156.68.47": denied,
- "::ffff:39.156.68.64": denied,
- "::ffff:72.156.68.80": denied,
- },
- },
- {
- ver: 6,
- allow: "fc80::/64",
- deny: "fc80::10/124",
- probe: map[string]blockAction{
- "fc80::": allowed,
- "fc80::ffff:ffff:ffff:ffff": allowed,
- "fc80::10": denied,
- "fc80::1f": denied,
- "fc7f:ffff:ffff:ffff:ffff:ffff:ffff:ffff": denied,
- "fc60:0:0:1::": denied,
- },
- },
- {
- ver: 6,
- allow: "2607:f8b0:4005:80a::200e",
- probe: map[string]blockAction{
- "2607:f8b0:4005:80a::200e": allowed,
- "2607:f8b0:4005:80a::200d": denied,
- "2607:f8b0:4005:80a::200f": denied,
- "2607:f8b0:4005:80a::300f": denied,
- },
- },
-}
-
-type bpfContext struct {
- cgroupDir string
- cgroupID uint64
- ctx *bpf.SessionContext
- enhancedRecorder bpf.BPF
- restrictedMgr Manager
- srcAddrs map[int]string
-
- // Audit events emitted by us
- emitter eventstest.MockRecorderEmitter
- expectedAuditEvents []apievents.AuditEvent
-}
-
-func setupBPFContext(t *testing.T) *bpfContext {
- utils.InitLoggerForTests()
-
- // This test must be run as root and the host has to be capable of running
- // BPF programs.
- if !isRoot() {
- t.Skip("Tests for package restrictedsession can only be run as root.")
- }
-
- // Create temporary directory where cgroup2 hierarchy will be mounted.
- // DO NOT MOVE THIS LINE.
- // t.TempDir() creates t.Cleanup() action. If this hook is called before
- // we mount cgroup in Close() this test will fail.
- cgroupDir := t.TempDir()
-
- bpfCtx := bpfContext{
- cgroupDir: cgroupDir,
- }
- t.Cleanup(func() { bpfCtx.Close(t) })
-
- bpfCtx.srcAddrs = map[int]string{
- 4: "0.0.0.0",
- 6: "::",
- }
-
- config := &servicecfg.RestrictedSessionConfig{
- Enabled: true,
- }
-
- var err error
- // Create BPF service since we piggyback on it
- bpfCtx.enhancedRecorder, err = bpf.New(&servicecfg.BPFConfig{
- Enabled: true,
- CgroupPath: bpfCtx.cgroupDir,
- }, config)
- require.NoError(t, err)
-
- // Create the SessionContext used by both enhanced recording and us (restricted session)
- bpfCtx.ctx = &bpf.SessionContext{
- Namespace: apidefaults.Namespace,
- SessionID: uuid.New().String(),
- ServerID: uuid.New().String(),
- ServerHostname: "ip-172-31-11-148",
- Login: "foo",
- User: "foo@example.com",
- PID: os.Getpid(),
- Emitter: &bpfCtx.emitter,
- Events: map[string]bool{},
- }
-
- // Create enhanced recording session to piggyback on.
- bpfCtx.cgroupID, err = bpfCtx.enhancedRecorder.OpenSession(bpfCtx.ctx)
- require.NoError(t, err)
- require.Equal(t, bpfCtx.cgroupID > 0, true)
-
- deny := []api.AddressCondition{}
- allow := []api.AddressCondition{}
- for _, r := range testRanges {
- if len(r.deny) > 0 {
- deny = append(deny, api.AddressCondition{CIDR: r.deny})
- }
-
- if len(r.allow) > 0 {
- allow = append(allow, api.AddressCondition{CIDR: r.allow})
- }
- }
-
- restrictions := api.NewNetworkRestrictions()
- restrictions.SetAllow(allow)
- restrictions.SetDeny(deny)
-
- client := &mockClient{
- restrictions: restrictions,
- Fanout: *services.NewFanout(),
- }
-
- bpfCtx.restrictedMgr, err = New(config, client)
- require.NoError(t, err)
-
- client.Fanout.SetInit([]api.WatchKind{{Kind: api.KindNetworkRestrictions}})
-
- time.Sleep(100 * time.Millisecond)
-
- return &bpfCtx
-}
-
-func (tt *bpfContext) Close(t *testing.T) {
- if tt.cgroupID > 0 {
- tt.restrictedMgr.CloseSession(tt.ctx, tt.cgroupID)
- }
-
- if tt.restrictedMgr != nil {
- tt.restrictedMgr.Close()
- }
-
- if tt.enhancedRecorder != nil && tt.ctx != nil {
- err := tt.enhancedRecorder.CloseSession(tt.ctx)
- require.NoError(t, err)
- const restarting = false
- err = tt.enhancedRecorder.Close(restarting)
- require.NoError(t, err)
- }
-
- if tt.cgroupDir != "" {
- err := os.RemoveAll(tt.cgroupDir)
- require.NoError(t, err)
- }
-}
-
-func (tt *bpfContext) openSession(t *testing.T) {
- // Create the restricted session
- tt.restrictedMgr.OpenSession(tt.ctx, tt.cgroupID)
-}
-
-func (tt *bpfContext) closeSession(t *testing.T) {
- // Close the restricted session
- tt.restrictedMgr.CloseSession(tt.ctx, tt.cgroupID)
-}
-
-func (tt *bpfContext) dialExpectAllow(t *testing.T, ver int, ip string) {
- if err := dialTCP(ver, mustParseIP(ip)); err != nil {
- // Other than EPERM or EINVAL is OK
- if errors.Is(err, syscall.EPERM) || errors.Is(err, syscall.EINVAL) {
- t.Fatalf("Dial %v was not allowed: %v", ip, err)
- }
- }
-}
-
-func (tt *bpfContext) sendExpectAllow(t *testing.T, ver int, ip string) {
- err := sendUDP(ver, mustParseIP(ip))
-
- // Other than EPERM or EINVAL is OK
- if errors.Is(err, syscall.EPERM) || errors.Is(err, syscall.EINVAL) {
- t.Fatalf("Send %v: failed with %v", ip, err)
- }
-}
-
-func (tt *bpfContext) dialExpectDeny(t *testing.T, ver int, ip string) {
- // Only EPERM is expected
- err := dialTCP(ver, mustParseIP(ip))
- if err == nil {
- t.Fatalf("Dial %v: did not expect to succeed", ip)
- }
-
- if !errors.Is(err, syscall.EPERM) {
- t.Fatalf("Dial %v: EPERM expected, got: %v", ip, err)
- }
-
- ev := tt.expectedAuditEvent(ver, ip, apievents.SessionNetwork_CONNECT)
- tt.expectedAuditEvents = append(tt.expectedAuditEvents, ev)
-}
-
-func (tt *bpfContext) sendExpectDeny(t *testing.T, ver int, ip string) {
- err := sendUDP(ver, mustParseIP(ip))
- if !errors.Is(err, syscall.EPERM) {
- t.Fatalf("Send %v: was not denied: %v", ip, err)
- }
-
- ev := tt.expectedAuditEvent(ver, ip, apievents.SessionNetwork_SEND)
- tt.expectedAuditEvents = append(tt.expectedAuditEvents, ev)
-}
-
-func (tt *bpfContext) expectedAuditEvent(ver int, ip string, op apievents.SessionNetwork_NetworkOperation) apievents.AuditEvent {
- return &apievents.SessionNetwork{
- Metadata: apievents.Metadata{
- Type: events.SessionNetworkEvent,
- Code: events.SessionNetworkCode,
- },
- ServerMetadata: apievents.ServerMetadata{
- ServerID: tt.ctx.ServerID,
- ServerHostname: tt.ctx.ServerHostname,
- ServerNamespace: tt.ctx.Namespace,
- },
- SessionMetadata: apievents.SessionMetadata{
- SessionID: tt.ctx.SessionID,
- },
- UserMetadata: apievents.UserMetadata{
- User: tt.ctx.User,
- Login: tt.ctx.Login,
- },
- BPFMetadata: apievents.BPFMetadata{
- CgroupID: tt.cgroupID,
- Program: "restrictedsessi",
- PID: uint64(tt.ctx.PID),
- },
- DstPort: testPort,
- DstAddr: ip,
- SrcAddr: tt.srcAddrs[ver],
- TCPVersion: int32(ver),
- Operation: op,
- Action: apievents.EventAction_DENIED,
- }
-}
-
-func TestRootNetwork(t *testing.T) {
- if !bpfTestEnabled() {
- t.Skip("BPF testing is disabled")
- }
-
- tt := setupBPFContext(t)
-
- type testCase struct {
- ver int
- ip string
- expected blockAction
- }
-
- tests := []testCase{}
- for _, r := range testRanges {
- for ip, expected := range r.probe {
- tests = append(tests, testCase{
- ver: r.ver,
- ip: ip,
- expected: expected,
- })
- }
- }
-
- // Restricted session is not yet open, all these should be allowed
- for _, tc := range tests {
- tt.dialExpectAllow(t, tc.ver, tc.ip)
- tt.sendExpectAllow(t, tc.ver, tc.ip)
- }
-
- // Nothing should be reported to the audit log
- time.Sleep(100 * time.Millisecond)
- require.Empty(t, tt.emitter.Events())
-
- // Open the restricted session
- tt.openSession(t)
-
- // Now the policy should be enforced
- for _, tc := range tests {
- if tc.expected == denied {
- tt.dialExpectDeny(t, tc.ver, tc.ip)
- tt.sendExpectDeny(t, tc.ver, tc.ip)
- } else {
- tt.dialExpectAllow(t, tc.ver, tc.ip)
- tt.sendExpectAllow(t, tc.ver, tc.ip)
- }
- }
-
- time.Sleep(100 * time.Millisecond)
-
- // Close the restricted session
- tt.closeSession(t)
-
- // Check that the emitted audit events are correct
- actualAuditEvents := tt.emitter.Events()
- require.Empty(t, gocmp.Diff(tt.expectedAuditEvents, actualAuditEvents),
- "Audit events mismatch (-want +got)")
-
- // Clear out the expected and actual events
- tt.expectedAuditEvents = nil
- tt.emitter.Reset()
-
- // Restricted session is now closed, all these should be allowed
- for _, tc := range tests {
- tt.dialExpectAllow(t, tc.ver, tc.ip)
- tt.sendExpectAllow(t, tc.ver, tc.ip)
- }
-
- // Nothing should be reported to the audit log
- time.Sleep(100 * time.Millisecond)
- require.Empty(t, tt.emitter.Events())
-}
-
-type mockClient struct {
- restrictions api.NetworkRestrictions
- services.Fanout
-}
-
-func (mc *mockClient) GetNetworkRestrictions(context.Context) (api.NetworkRestrictions, error) {
- return mc.restrictions, nil
-}
-
-func (_ *mockClient) SetNetworkRestrictions(context.Context, api.NetworkRestrictions) error {
- return nil
-}
-
-func (_ *mockClient) DeleteNetworkRestrictions(context.Context) error {
- return nil
-}
-
-var ip4Regex = regexp.MustCompile(`^\d+\.\d+\.\d+\.\d+$`)
-
-// mustParseIP parses the IP and also converts IPv4 addresses
-// to 4 byte representation. IPv4 mapped (into IPv6) addresses
-// are kept in 16 byte encoding
-func mustParseIP(addr string) net.IP {
- is4 := ip4Regex.MatchString(addr)
-
- ip := net.ParseIP(addr)
- if is4 {
- return ip.To4()
- }
- return ip.To16()
-}
-
-func testSocket(ver, typ int, ip net.IP) (int, syscall.Sockaddr, error) {
- var domain int
- var src syscall.Sockaddr
- var dst syscall.Sockaddr
- if ver == 4 {
- domain = syscall.AF_INET
- src = &syscall.SockaddrInet4{}
- dst4 := &syscall.SockaddrInet4{
- Port: testPort,
- }
- copy(dst4.Addr[:], ip)
- dst = dst4
- } else {
- domain = syscall.AF_INET6
- src = &syscall.SockaddrInet6{}
- dst6 := &syscall.SockaddrInet6{
- Port: testPort,
- }
- copy(dst6.Addr[:], ip)
- dst = dst6
- }
-
- fd, err := syscall.Socket(domain, typ, 0)
- if err != nil {
- return 0, nil, fmt.Errorf("socket() failed: %v", err)
- }
-
- if err = syscall.Bind(fd, src); err != nil {
- return 0, nil, fmt.Errorf("bind() failed: %v", err)
- }
-
- return fd, dst, nil
-}
-
-func dialTCP(ver int, ip net.IP) error {
- fd, dst, err := testSocket(ver, syscall.SOCK_STREAM, ip)
- if err != nil {
- return err
- }
- defer syscall.Close(fd)
-
- tv := syscall.Timeval{
- Usec: 1000,
- }
- err = syscall.SetsockoptTimeval(fd, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &tv)
- if err != nil {
- return fmt.Errorf("setsockopt(SO_SNDTIMEO) failed: %v", err)
- }
-
- return syscall.Connect(fd, dst)
-}
-
-func sendUDP(ver int, ip net.IP) error {
- fd, dst, err := testSocket(ver, syscall.SOCK_DGRAM, ip)
- if err != nil {
- return err
- }
- defer syscall.Close(fd)
-
- return syscall.Sendto(fd, []byte("abc"), 0, dst)
-}
-
-// isRoot returns a boolean if the test is being run as root or not. Tests
-// for this package must be run as root.
-func isRoot() bool {
- return os.Geteuid() == 0
-}
-
-// bpfTestEnabled returns true if BPF/LSM tests should run. Tests can be enabled by
-// setting TELEPORT_BPF_LSM_TEST environment variable to any value.
-func bpfTestEnabled() bool {
- return os.Getenv("TELEPORT_BPF_LSM_TEST") != ""
-}
diff --git a/lib/restrictedsession/watcher.go b/lib/restrictedsession/watcher.go
deleted file mode 100644
index 38c31a0cb6d9c..0000000000000
--- a/lib/restrictedsession/watcher.go
+++ /dev/null
@@ -1,305 +0,0 @@
-//go:build bpf && !386
-// +build bpf,!386
-
-/*
- * Teleport
- * Copyright (C) 2023 Gravitational, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package restrictedsession
-
-import (
- "context"
- "net"
- "sync"
- "time"
-
- "github.com/gravitational/trace"
-
- "github.com/gravitational/teleport"
- "github.com/gravitational/teleport/api/types"
- "github.com/gravitational/teleport/api/utils/retryutils"
- "github.com/gravitational/teleport/lib/defaults"
- "github.com/gravitational/teleport/lib/utils"
-)
-
-// NewRestrictionsWatcher returns a new instance of changeset
-func NewRestrictionsWatcher(cfg RestrictionsWatcherConfig) (*RestrictionsWatcher, error) {
- if err := cfg.CheckAndSetDefaults(); err != nil {
- return nil, trace.Wrap(err)
- }
- retry, err := retryutils.NewLinear(retryutils.LinearConfig{
- First: utils.HalfJitter(cfg.MaxRetryPeriod / 10),
- Step: cfg.MaxRetryPeriod / 5,
- Max: cfg.MaxRetryPeriod,
- Jitter: retryutils.NewHalfJitter(),
- })
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- ctx, cancelFn := context.WithCancel(context.Background())
-
- w := &RestrictionsWatcher{
- retry: retry,
- resetC: make(chan struct{}),
- cancelFn: cancelFn,
- RestrictionsWatcherConfig: cfg,
- }
- w.wg.Add(1)
- go w.watchRestrictions(ctx)
- return w, nil
-}
-
-// RestrictionsWatcher is a resource built on top of the events,
-// it monitors the changes to restrictions
-type RestrictionsWatcher struct {
- RestrictionsWatcherConfig
-
- resetC chan struct{}
-
- // retry is used to manage backoff logic for watches
- retry retryutils.Retry
-
- wg sync.WaitGroup
- cancelFn context.CancelFunc
-}
-
-// RestrictionsWatcherConfig configures restrictions watcher
-type RestrictionsWatcherConfig struct {
- // MaxRetryPeriod is the maximum retry period on failed watchers
- MaxRetryPeriod time.Duration
- // ReloadPeriod is a failed period on failed watches
- ReloadPeriod time.Duration
- // Client is used by changeset to monitor restrictions updates
- Client RestrictionsWatcherClient
- // RestrictionsC is a channel that will be used
- // by the watcher to push updated list,
- // it will always receive a fresh list on the start
- // and the subsequent list of new values
- // whenever an addition or deletion to the list is detected
- RestrictionsC chan *NetworkRestrictions
-}
-
-// CheckAndSetDefaults checks parameters and sets default values
-func (cfg *RestrictionsWatcherConfig) CheckAndSetDefaults() error {
- if cfg.Client == nil {
- return trace.BadParameter("missing parameter Client")
- }
- if cfg.RestrictionsC == nil {
- return trace.BadParameter("missing parameter RestrictionsC")
- }
- if cfg.MaxRetryPeriod == 0 {
- cfg.MaxRetryPeriod = defaults.MaxWatcherBackoff
- }
- if cfg.ReloadPeriod == 0 {
- cfg.ReloadPeriod = defaults.LowResPollingPeriod
- }
- return nil
-}
-
-// Reset returns a channel which notifies of internal
-// watcher resets (used in tests).
-func (w *RestrictionsWatcher) Reset() <-chan struct{} {
- return w.resetC
-}
-
-// Close closes proxy watcher and cancels all the functions
-func (w *RestrictionsWatcher) Close() error {
- w.cancelFn()
- w.wg.Wait()
- close(w.RestrictionsC)
- return nil
-}
-
-// watchProxies watches new proxies added and removed to the cluster
-// and when this happens, notifies all connected agents
-// about the proxy set change via discovery requests
-func (w *RestrictionsWatcher) watchRestrictions(ctx context.Context) {
- defer w.wg.Done()
-
- for {
- // Reload period is here to protect against
- // unknown cache going out of sync problems
- // that we did not predict.
- if err := w.watch(ctx); err != nil {
- log.Warningf("Re-init the watcher on error: %v.", trace.Unwrap(err))
- }
- log.Debugf("Reloading %v.", w.retry)
- select {
- case w.resetC <- struct{}{}:
- default:
- }
- select {
- case <-w.retry.After():
- w.retry.Inc()
- case <-ctx.Done():
- log.Debugf("Closed, returning from update loop.")
- return
- }
- }
-}
-
-func (w *RestrictionsWatcher) getNetworkRestrictions(ctx context.Context) (*NetworkRestrictions, error) {
- resource, err := w.Client.GetNetworkRestrictions(ctx)
- if err != nil {
- if !trace.IsNotFound(err) {
- return nil, trace.Wrap(err)
- }
- return &NetworkRestrictions{}, nil
- }
-
- restrictions, err := protoToNetworkRestrictions(resource)
- if err != nil {
- return nil, trace.Wrap(err)
- }
- return restrictions, nil
-}
-
-// watch sets up the watch on proxies
-func (w *RestrictionsWatcher) watch(ctx context.Context) error {
- watcher, err := w.Client.NewWatcher(ctx, types.Watch{
- Name: teleport.ComponentRestrictedSession,
- Kinds: []types.WatchKind{
- {
- Kind: types.KindNetworkRestrictions,
- },
- },
- MetricComponent: teleport.ComponentRestrictedSession,
- })
- if err != nil {
- return trace.Wrap(err)
- }
- defer watcher.Close()
- reloadC := time.After(w.ReloadPeriod)
- // before fetch, make sure watcher is synced by receiving init event,
- // to avoid the scenario:
- // 1. Cache process: w = NewWatcher()
- // 2. Cache process: c.fetch()
- // 3. Backend process: addItem()
- // 4. Cache process: <- w.Events()
- //
- // If there is a way that NewWatcher() on line 1 could
- // return without subscription established first,
- // Code line 3 could execute and line 4 could miss event,
- // wrapping up with out of sync replica.
- // To avoid this, before doing fetch,
- // cache process makes sure the connection is established
- // by receiving init event first.
- select {
- case <-reloadC:
- log.Debugf("Triggering scheduled reload.")
- return nil
- case <-ctx.Done():
- return nil
- case event := <-watcher.Events():
- if event.Type != types.OpInit {
- return trace.BadParameter("expected init event, got %v instead", event.Type)
- }
- }
-
- restrictions, err := w.getNetworkRestrictions(ctx)
- if err != nil {
- return trace.Wrap(err)
- }
-
- w.retry.Reset()
-
- select {
- case w.RestrictionsC <- restrictions:
- case <-ctx.Done():
- return nil
- }
-
- for {
- select {
- case <-reloadC:
- log.Debugf("Triggering scheduled reload.")
- return nil
- case <-ctx.Done():
- return nil
- case event := <-watcher.Events():
- if restrictions := w.processEvent(event); restrictions != nil {
- select {
- case w.RestrictionsC <- restrictions:
- case <-ctx.Done():
- return nil
- }
- }
- }
- }
-}
-
-// processEvent updates proxy map and returns true if the proxies list have been modified -
-// the proxy has been either added or deleted
-func (w *RestrictionsWatcher) processEvent(event types.Event) *NetworkRestrictions {
- if event.Resource.GetKind() != types.KindNetworkRestrictions {
- log.Warningf("Unexpected event: %v.", event.Resource.GetKind())
- return nil
- }
-
- switch event.Type {
- case types.OpDelete:
- return &NetworkRestrictions{}
-
- case types.OpPut:
- resource, ok := event.Resource.(types.NetworkRestrictions)
- if !ok {
- log.Warningf("unexpected type %T", event.Resource)
- return nil
- }
- restrictions, err := protoToNetworkRestrictions(resource)
- if err != nil {
- log.Warningf("Bad network restrictions %#v.", resource)
- return nil
- }
- return restrictions
-
- default:
- log.Warningf("Skipping unsupported event type %v.", event.Type)
- return nil
- }
-}
-
-func protoToIPNets(protoAddrs []types.AddressCondition) ([]net.IPNet, error) {
- nets := []net.IPNet{}
- for _, a := range protoAddrs {
- n, err := ParseIPSpec(a.CIDR)
- if err != nil {
- return nil, trace.Wrap(err)
- }
- nets = append(nets, *n)
- }
- return nets, nil
-}
-
-func protoToNetworkRestrictions(proto types.NetworkRestrictions) (*NetworkRestrictions, error) {
- deny, err := protoToIPNets(proto.GetDeny())
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- allow, err := protoToIPNets(proto.GetAllow())
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- return &NetworkRestrictions{
- Enabled: true,
- Deny: deny,
- Allow: allow,
- }, nil
-}
diff --git a/lib/service/service.go b/lib/service/service.go
index 0c4cfc0dceb15..f033d42792f7c 100644
--- a/lib/service/service.go
+++ b/lib/service/service.go
@@ -121,7 +121,6 @@ import (
"github.com/gravitational/teleport/lib/proxy"
"github.com/gravitational/teleport/lib/proxy/clusterdial"
"github.com/gravitational/teleport/lib/proxy/peer"
- restricted "github.com/gravitational/teleport/lib/restrictedsession"
"github.com/gravitational/teleport/lib/reversetunnel"
"github.com/gravitational/teleport/lib/reversetunnelclient"
"github.com/gravitational/teleport/lib/service/servicecfg"
@@ -2505,12 +2504,6 @@ func (process *TeleportProcess) initSSH() error {
"the cluster level, then restart Teleport.")
}
- // Restricted session requires BPF (enhanced recording)
- if cfg.SSH.RestrictedSession.Enabled && !cfg.SSH.BPF.Enabled {
- return trace.BadParameter("restricted_session requires enhanced_recording " +
- "to be enabled")
- }
-
// If BPF is enabled in file configuration, but the operating system does
// not support enhanced session recording (like macOS), exit right away.
if cfg.SSH.BPF.Enabled && !bpf.SystemHasBPF() {
@@ -2522,21 +2515,12 @@ func (process *TeleportProcess) initSSH() error {
// Start BPF programs. This is blocking and if the BPF programs fail to
// load, the node will not start. If BPF is not enabled, this will simply
// return a NOP struct that can be used to discard BPF data.
- ebpf, err := bpf.New(cfg.SSH.BPF, cfg.SSH.RestrictedSession)
+ ebpf, err := bpf.New(cfg.SSH.BPF)
if err != nil {
return trace.Wrap(err)
}
defer func() { warnOnErr(ebpf.Close(restartingOnGracefulShutdown), log) }()
- // Start access control programs. This is blocking and if the BPF programs fail to
- // load, the node will not start. If access control is not enabled, this will simply
- // return a NOP struct.
- rm, err := restricted.New(cfg.SSH.RestrictedSession, conn.Client)
- if err != nil {
- return trace.Wrap(err)
- }
- // TODO: are we missing rm.Close()
-
// make sure the default namespace is used
if ns := cfg.SSH.Namespace; ns != "" && ns != apidefaults.Namespace {
return trace.BadParameter("cannot start with custom namespace %q, custom namespaces are deprecated. "+
@@ -2640,7 +2624,6 @@ func (process *TeleportProcess) initSSH() error {
regular.SetUseTunnel(conn.UseTunnel()),
regular.SetFIPS(cfg.FIPS),
regular.SetBPF(ebpf),
- regular.SetRestrictedSessionManager(rm),
regular.SetOnHeartbeat(process.OnHeartbeat(teleport.ComponentNode)),
regular.SetAllowTCPForwarding(cfg.SSH.AllowTCPForwarding),
regular.SetLockWatcher(lockWatcher),
diff --git a/lib/service/servicecfg/bpf.go b/lib/service/servicecfg/bpf.go
index 1969a97d74033..c7ec8713ba678 100644
--- a/lib/service/servicecfg/bpf.go
+++ b/lib/service/servicecfg/bpf.go
@@ -58,23 +58,3 @@ func (c *BPFConfig) CheckAndSetDefaults() error {
return nil
}
-
-// RestrictedSessionConfig holds configuration for the RestrictedSession service.
-type RestrictedSessionConfig struct {
- // Enabled if this service will try and install BPF programs on this system.
- Enabled bool
-
- // EventsBufferSize is the size (in pages) of the perf buffer for events.
- EventsBufferSize *int
-}
-
-// CheckAndSetDefaults checks BPF configuration.
-func (c *RestrictedSessionConfig) CheckAndSetDefaults() error {
- var perfBufferPageCount = defaults.PerfBufferPageCount
-
- if c.EventsBufferSize == nil {
- c.EventsBufferSize = &perfBufferPageCount
- }
-
- return nil
-}
diff --git a/lib/service/servicecfg/config.go b/lib/service/servicecfg/config.go
index df0cd8391d7e6..48c1ce673c9e4 100644
--- a/lib/service/servicecfg/config.go
+++ b/lib/service/servicecfg/config.go
@@ -568,7 +568,6 @@ func ApplyDefaults(cfg *Config) {
defaults.ConfigureLimiter(&cfg.SSH.Limiter)
cfg.SSH.PAM = &PAMConfig{Enabled: false}
cfg.SSH.BPF = &BPFConfig{Enabled: false}
- cfg.SSH.RestrictedSession = &RestrictedSessionConfig{Enabled: false}
cfg.SSH.AllowTCPForwarding = true
cfg.SSH.AllowFileCopying = true
diff --git a/lib/service/servicecfg/ssh.go b/lib/service/servicecfg/ssh.go
index 94998cd37cab6..2b98d6fe802dc 100644
--- a/lib/service/servicecfg/ssh.go
+++ b/lib/service/servicecfg/ssh.go
@@ -45,9 +45,6 @@ type SSHConfig struct {
// BPF holds BPF configuration for Teleport.
BPF *BPFConfig
- // RestrictedSession holds kernel objects restrictions for Teleport.
- RestrictedSession *RestrictedSessionConfig
-
// AllowTCPForwarding indicates that TCP port forwarding is allowed on this node
AllowTCPForwarding bool
diff --git a/lib/srv/ctx.go b/lib/srv/ctx.go
index 161b164d3ed74..dd2939209a4e1 100644
--- a/lib/srv/ctx.go
+++ b/lib/srv/ctx.go
@@ -46,7 +46,6 @@ import (
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/bpf"
"github.com/gravitational/teleport/lib/events"
- restricted "github.com/gravitational/teleport/lib/restrictedsession"
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/services"
rsession "github.com/gravitational/teleport/lib/session"
@@ -163,9 +162,6 @@ type Server interface {
// GetBPF returns the BPF service used for enhanced session recording.
GetBPF() bpf.BPF
- // GetRestrictedSessionManager returns the manager for restricting user activity
- GetRestrictedSessionManager() restricted.Manager
-
// Context returns server shutdown context
Context() context.Context
diff --git a/lib/srv/forward/sshserver.go b/lib/srv/forward/sshserver.go
index a39ceff08b9ad..8de88aad84757 100644
--- a/lib/srv/forward/sshserver.go
+++ b/lib/srv/forward/sshserver.go
@@ -47,7 +47,6 @@ import (
"github.com/gravitational/teleport/lib/bpf"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/integrations/awsoidc"
- restricted "github.com/gravitational/teleport/lib/restrictedsession"
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/srv"
@@ -505,13 +504,6 @@ func (s *Server) GetHostSudoers() srv.HostSudoers {
return &srv.HostSudoersNotImplemented{}
}
-// GetRestrictedSessionManager returns a NOP manager since for a
-// forwarding server it makes no sense (it has to run on the actual
-// node).
-func (s *Server) GetRestrictedSessionManager() restricted.Manager {
- return &restricted.NOP{}
-}
-
// GetInfo returns a services.Server that represents this server.
func (s *Server) GetInfo() types.Server {
return &types.ServerV2{
diff --git a/lib/srv/mock.go b/lib/srv/mock.go
index 9b90104df0168..fa0859cdded69 100644
--- a/lib/srv/mock.go
+++ b/lib/srv/mock.go
@@ -44,7 +44,6 @@ import (
"github.com/gravitational/teleport/lib/bpf"
"github.com/gravitational/teleport/lib/events/eventstest"
"github.com/gravitational/teleport/lib/fixtures"
- restricted "github.com/gravitational/teleport/lib/restrictedsession"
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/sshutils"
@@ -255,11 +254,6 @@ func (m *mockServer) GetBPF() bpf.BPF {
return &bpf.NOP{}
}
-// GetRestrictedSessionManager returns the manager for restricting user activity
-func (m *mockServer) GetRestrictedSessionManager() restricted.Manager {
- return &restricted.NOP{}
-}
-
// Context returns server shutdown context
func (m *mockServer) Context() context.Context {
return context.Background()
diff --git a/lib/srv/regular/sshserver.go b/lib/srv/regular/sshserver.go
index ed6998e9c0133..4ef41c2467709 100644
--- a/lib/srv/regular/sshserver.go
+++ b/lib/srv/regular/sshserver.go
@@ -60,7 +60,6 @@ import (
"github.com/gravitational/teleport/lib/labels"
"github.com/gravitational/teleport/lib/limiter"
"github.com/gravitational/teleport/lib/proxy"
- restricted "github.com/gravitational/teleport/lib/restrictedsession"
"github.com/gravitational/teleport/lib/reversetunnel"
"github.com/gravitational/teleport/lib/reversetunnelclient"
"github.com/gravitational/teleport/lib/service/servicecfg"
@@ -178,9 +177,6 @@ type Server struct {
// ebpf is the service used for enhanced session recording.
ebpf bpf.BPF
- // restrictedMgr is the service used for restricting access to kernel objects
- restrictedMgr restricted.Manager
-
// onHeartbeat is a callback for heartbeat status.
onHeartbeat func(error)
@@ -298,11 +294,6 @@ func (s *Server) GetBPF() bpf.BPF {
return s.ebpf
}
-// GetRestrictedSessionManager returns the manager for restricting user activity.
-func (s *Server) GetRestrictedSessionManager() restricted.Manager {
- return s.restrictedMgr
-}
-
// GetLockWatcher gets the server's lock watcher.
func (s *Server) GetLockWatcher() *services.LockWatcher {
return s.lockWatcher
@@ -607,13 +598,6 @@ func SetBPF(ebpf bpf.BPF) ServerOption {
}
}
-func SetRestrictedSessionManager(m restricted.Manager) ServerOption {
- return func(s *Server) error {
- s.restrictedMgr = m
- return nil
- }
-}
-
func SetOnHeartbeat(fn func(error)) ServerOption {
return func(s *Server) error {
s.onHeartbeat = fn
diff --git a/lib/srv/regular/sshserver_test.go b/lib/srv/regular/sshserver_test.go
index 20103409f1dfa..7c135796c5e4b 100644
--- a/lib/srv/regular/sshserver_test.go
+++ b/lib/srv/regular/sshserver_test.go
@@ -66,7 +66,6 @@ import (
"github.com/gravitational/teleport/lib/limiter"
"github.com/gravitational/teleport/lib/observability/tracing"
libproxy "github.com/gravitational/teleport/lib/proxy"
- restricted "github.com/gravitational/teleport/lib/restrictedsession"
"github.com/gravitational/teleport/lib/reversetunnel"
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/services"
@@ -229,7 +228,6 @@ func newCustomFixture(t *testing.T, mutateCfg func(*auth.TestServerConfig), sshO
}, nil,
),
SetBPF(&bpf.NOP{}),
- SetRestrictedSessionManager(&restricted.NOP{}),
SetClock(clock),
SetLockWatcher(lockWatcher),
SetX11ForwardingConfig(&x11.ServerConfig{}),
@@ -1480,7 +1478,6 @@ func TestProxyRoundRobin(t *testing.T) {
SetNamespace(apidefaults.Namespace),
SetPAMConfig(&servicecfg.PAMConfig{Enabled: false}),
SetBPF(&bpf.NOP{}),
- SetRestrictedSessionManager(&restricted.NOP{}),
SetClock(f.clock),
SetLockWatcher(lockWatcher),
SetNodeWatcher(nodeWatcher),
@@ -1621,7 +1618,6 @@ func TestProxyDirectAccess(t *testing.T) {
SetNamespace(apidefaults.Namespace),
SetPAMConfig(&servicecfg.PAMConfig{Enabled: false}),
SetBPF(&bpf.NOP{}),
- SetRestrictedSessionManager(&restricted.NOP{}),
SetClock(f.clock),
SetLockWatcher(lockWatcher),
SetNodeWatcher(nodeWatcher),
@@ -1862,7 +1858,6 @@ func TestLimiter(t *testing.T) {
SetNamespace(apidefaults.Namespace),
SetPAMConfig(&servicecfg.PAMConfig{Enabled: false}),
SetBPF(&bpf.NOP{}),
- SetRestrictedSessionManager(&restricted.NOP{}),
SetClock(f.clock),
SetLockWatcher(lockWatcher),
SetSessionController(sessionController),
@@ -2341,7 +2336,6 @@ func TestParseSubsystemRequest(t *testing.T) {
SetNamespace(apidefaults.Namespace),
SetPAMConfig(&servicecfg.PAMConfig{Enabled: false}),
SetBPF(&bpf.NOP{}),
- SetRestrictedSessionManager(&restricted.NOP{}),
SetClock(f.clock),
SetLockWatcher(lockWatcher),
SetNodeWatcher(nodeWatcher),
@@ -2603,7 +2597,6 @@ func TestIgnorePuTTYSimpleChannel(t *testing.T) {
SetNamespace(apidefaults.Namespace),
SetPAMConfig(&servicecfg.PAMConfig{Enabled: false}),
SetBPF(&bpf.NOP{}),
- SetRestrictedSessionManager(&restricted.NOP{}),
SetClock(f.clock),
SetLockWatcher(lockWatcher),
SetNodeWatcher(nodeWatcher),
@@ -2758,7 +2751,6 @@ func TestTargetMetadata(t *testing.T) {
}, nil,
),
SetBPF(&bpf.NOP{}),
- SetRestrictedSessionManager(&restricted.NOP{}),
SetLockWatcher(lockWatcher),
SetX11ForwardingConfig(&x11.ServerConfig{}),
SetSessionController(sessionController),
diff --git a/lib/srv/sess.go b/lib/srv/sess.go
index 22533b402cec5..d942c9ad97dcd 100644
--- a/lib/srv/sess.go
+++ b/lib/srv/sess.go
@@ -1286,11 +1286,9 @@ func (s *session) startInteractive(ctx context.Context, scx *ServerContext, p *p
} else if cgroupID > 0 {
// If a cgroup ID was assigned then enhanced session recording was enabled.
s.setHasEnhancedRecording(true)
- scx.srv.GetRestrictedSessionManager().OpenSession(sessionContext, cgroupID)
go func() {
// Close the BPF recording session once the session is closed
<-s.stopC
- scx.srv.GetRestrictedSessionManager().CloseSession(sessionContext, cgroupID)
err = scx.srv.GetBPF().CloseSession(sessionContext)
if err != nil {
s.log.WithError(err).Error("Failed to close enhanced recording (interactive) session")
@@ -1459,7 +1457,6 @@ func (s *session) startExec(ctx context.Context, channel ssh.Channel, scx *Serve
// If a cgroup ID was assigned then enhanced session recording was enabled.
if cgroupID > 0 {
s.setHasEnhancedRecording(true)
- scx.srv.GetRestrictedSessionManager().OpenSession(sessionContext, cgroupID)
}
// Process has been placed in a cgroup, continue execution.
@@ -1480,8 +1477,6 @@ func (s *session) startExec(ctx context.Context, channel ssh.Channel, scx *Serve
// BPF session so everything can be recorded.
time.Sleep(2 * time.Second)
- scx.srv.GetRestrictedSessionManager().CloseSession(sessionContext, cgroupID)
-
// Close the BPF recording session. If BPF was not configured, not available,
// or running in a recording proxy, this is simply a NOP.
err = scx.srv.GetBPF().CloseSession(sessionContext)
diff --git a/lib/web/apiserver_test.go b/lib/web/apiserver_test.go
index 7b3f49a94eb01..c3abf02251e09 100644
--- a/lib/web/apiserver_test.go
+++ b/lib/web/apiserver_test.go
@@ -120,7 +120,6 @@ import (
"github.com/gravitational/teleport/lib/multiplexer"
"github.com/gravitational/teleport/lib/observability/tracing"
"github.com/gravitational/teleport/lib/proxy"
- restricted "github.com/gravitational/teleport/lib/restrictedsession"
"github.com/gravitational/teleport/lib/reversetunnel"
"github.com/gravitational/teleport/lib/reversetunnelclient"
"github.com/gravitational/teleport/lib/secret"
@@ -341,7 +340,6 @@ func newWebSuiteWithConfig(t *testing.T, cfg webSuiteConfig) *WebSuite {
regular.SetEmitter(nodeClient),
regular.SetPAMConfig(&servicecfg.PAMConfig{Enabled: false}),
regular.SetBPF(&bpf.NOP{}),
- regular.SetRestrictedSessionManager(&restricted.NOP{}),
regular.SetClock(s.clock),
regular.SetLockWatcher(nodeLockWatcher),
regular.SetSessionController(nodeSessionController),
@@ -446,7 +444,6 @@ func newWebSuiteWithConfig(t *testing.T, cfg webSuiteConfig) *WebSuite {
regular.SetEmitter(s.proxyClient),
regular.SetNamespace(apidefaults.Namespace),
regular.SetBPF(&bpf.NOP{}),
- regular.SetRestrictedSessionManager(&restricted.NOP{}),
regular.SetClock(s.clock),
regular.SetLockWatcher(proxyLockWatcher),
regular.SetNodeWatcher(proxyNodeWatcher),
@@ -613,7 +610,6 @@ func (s *WebSuite) addNode(t *testing.T, uuid string, hostname string, address s
regular.SetEmitter(nodeClient),
regular.SetPAMConfig(&servicecfg.PAMConfig{Enabled: false}),
regular.SetBPF(&bpf.NOP{}),
- regular.SetRestrictedSessionManager(&restricted.NOP{}),
regular.SetClock(s.clock),
regular.SetLockWatcher(nodeLockWatcher),
regular.SetSessionController(nodeSessionController),
@@ -7625,7 +7621,6 @@ func newWebPack(t *testing.T, numProxies int, opts ...proxyOption) *webPack {
regular.SetEmitter(nodeClient),
regular.SetPAMConfig(&servicecfg.PAMConfig{Enabled: false}),
regular.SetBPF(&bpf.NOP{}),
- regular.SetRestrictedSessionManager(&restricted.NOP{}),
regular.SetClock(clock),
regular.SetLockWatcher(nodeLockWatcher),
regular.SetSessionController(nodeSessionController),
@@ -7870,7 +7865,6 @@ func createProxy(ctx context.Context, t *testing.T, proxyID string, node *regula
regular.SetEmitter(client),
regular.SetNamespace(apidefaults.Namespace),
regular.SetBPF(&bpf.NOP{}),
- regular.SetRestrictedSessionManager(&restricted.NOP{}),
regular.SetClock(clock),
regular.SetLockWatcher(proxyLockWatcher),
regular.SetNodeWatcher(proxyNodeWatcher),