Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/lima-guestagent/daemon_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func daemonAction(cmd *cobra.Command, _ []string) error {
tickerInst = ticker.NewCompoundTicker(simpleTicker, ebpfTicker)
}

agent, err := guestagent.New(ctx, tickerInst, tick*20)
agent, err := guestagent.New(ctx, tickerInst)
if err != nil {
return err
}
Expand Down
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ require (
github.com/digitalocean/go-qemu v0.0.0-20221209210016-f035778c97f7
github.com/diskfs/go-diskfs v1.7.0 // gomodjail:unconfined
github.com/docker/go-units v0.5.0
github.com/elastic/go-libaudit/v2 v2.6.2
github.com/foxcpp/go-mockdns v1.1.0
github.com/goccy/go-yaml v1.18.0
github.com/google/go-cmp v0.7.0
Expand Down
4 changes: 0 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,6 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/elastic/go-libaudit/v2 v2.6.2 h1:1PM6wVBTJHJQYsKl8jfA9/Aw9pFty5uUezPiUfKtOI4=
github.com/elastic/go-libaudit/v2 v2.6.2/go.mod h1:8205nkf2oSrXFlO4H5j8/cyVMoSF3Y7jt+FjgS4ubQU=
github.com/elastic/go-licenser v0.4.1 h1:1xDURsc8pL5zYT9R29425J3vkHdt4RT5TNEMeRN48x4=
github.com/elastic/go-licenser v0.4.1/go.mod h1:V56wHMpmdURfibNBggaSBfqgPxyT1Tldns1i87iTEvU=
github.com/elliotchance/orderedmap v1.8.0 h1:TrOREecvh3JbS+NCgwposXG5ZTFHtEsQiCGOhPElnMw=
github.com/elliotchance/orderedmap v1.8.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys=
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab h1:h1UgjJdAAhj+uPL68n7XASS6bU+07ZX1WJvVS2eyoeY=
Expand Down
150 changes: 3 additions & 147 deletions pkg/guestagent/guestagent_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,91 +5,30 @@ package guestagent

import (
"context"
"errors"
"os"
"reflect"
"sync"
"syscall"
"time"

"github.com/elastic/go-libaudit/v2"
"github.com/elastic/go-libaudit/v2/auparse"
"github.com/sirupsen/logrus"
"google.golang.org/protobuf/types/known/timestamppb"

"github.com/lima-vm/lima/v2/pkg/guestagent/api"
"github.com/lima-vm/lima/v2/pkg/guestagent/iptables"
"github.com/lima-vm/lima/v2/pkg/guestagent/kubernetesservice"
"github.com/lima-vm/lima/v2/pkg/guestagent/sockets"
"github.com/lima-vm/lima/v2/pkg/guestagent/ticker"
"github.com/lima-vm/lima/v2/pkg/guestagent/timesync"
)

func New(ctx context.Context, ticker ticker.Ticker, iptablesIdle time.Duration) (Agent, error) {
func New(ctx context.Context, ticker ticker.Ticker) (Agent, error) {
a := &agent{
ticker: ticker,
kubernetesServiceWatcher: kubernetesservice.NewServiceWatcher(),
}

auditClient, err := libaudit.NewMulticastAuditClient(nil)
if err != nil {
// syscall.EPROTONOSUPPORT or syscall.EAFNOSUPPORT is returned when calling attempting to connect to NETLINK_AUDIT
// on a kernel built without auditing support.
// https://github.com/elastic/go-libaudit/blob/ec298e53a6841a1f7715abbc7122635622f349bd/audit.go#L112-L115
if !errors.Is(err, syscall.EPROTONOSUPPORT) && !errors.Is(err, syscall.EAFNOSUPPORT) {
return nil, err
}
logrus.Infof("Auditing is not available: %s", err)
return startGuestAgentRoutines(ctx, a, false), nil
}

auditStatus, err := auditClient.GetStatus()
if err != nil {
// syscall.EPERM is returned when using audit from a non-initial namespace
// https://github.com/torvalds/linux/blob/633b47cb009d09dc8f4ba9cdb3a0ca138809c7c7/kernel/audit.c#L1054-L1057
if !errors.Is(err, syscall.EPERM) {
return nil, err
}
logrus.Infof("Auditing is not permitted: %s", err)
return startGuestAgentRoutines(ctx, a, false), nil
}

if auditStatus.Enabled == 0 {
logrus.Info("Enabling auditing")
if err = auditClient.SetEnabled(true, libaudit.WaitForReply); err != nil {
return nil, err
}
auditStatus, err := auditClient.GetStatus()
if err != nil {
return nil, err
}
if auditStatus.Enabled == 0 {
if err = auditClient.SetEnabled(true, libaudit.WaitForReply); err != nil {
return nil, err
}
}
}

a.worthCheckingIPTables = true // allow initial iptables scan
go a.setWorthCheckingIPTablesRoutine(auditClient, iptablesIdle)

logrus.Infof("Auditing enabled (%d)", auditStatus.Enabled)
return startGuestAgentRoutines(ctx, a, true), nil
}

// startGuestAgentRoutines sets worthCheckingIPTables to true if auditing is not supported,
// instead of using setWorthCheckingIPTablesRoutine to dynamically set the value.
//
// Auditing is not supported in a kernels and is not currently supported outside of the initial namespace, so does not work
// from inside a container or WSL2 instance, for example.
func startGuestAgentRoutines(ctx context.Context, a *agent, supportsAuditing bool) *agent {
if !supportsAuditing {
a.worthCheckingIPTables = true
}
go a.kubernetesServiceWatcher.Start(ctx)
go a.fixSystemTimeSkew()

return a
return a, nil
}

type agent struct {
Expand All @@ -98,51 +37,9 @@ type agent struct {
// reload /proc/net/tcp.
ticker ticker.Ticker

worthCheckingIPTables bool
worthCheckingIPTablesMu sync.RWMutex
latestIPTables []iptables.Entry
latestIPTablesMu sync.RWMutex
kubernetesServiceWatcher *kubernetesservice.ServiceWatcher
}

// setWorthCheckingIPTablesRoutine sets worthCheckingIPTables to be true
// when received NETFILTER_CFG audit message.
//
// setWorthCheckingIPTablesRoutine sets worthCheckingIPTables to be false
// when no NETFILTER_CFG audit message was received for the iptablesIdle time.
func (a *agent) setWorthCheckingIPTablesRoutine(auditClient *libaudit.AuditClient, iptablesIdle time.Duration) {
logrus.Info("setWorthCheckingIPTablesRoutine(): monitoring netfilter audit events")
// Initialize to now so the first sleeper loop does not immediately mark it false.
latestTrue := time.Now()
go func() {
for {
time.Sleep(iptablesIdle)
a.worthCheckingIPTablesMu.Lock()
// time is monotonic, see https://pkg.go.dev/time#hdr-Monotonic_Clocks
elapsedSinceLastTrue := time.Since(latestTrue)
if elapsedSinceLastTrue >= iptablesIdle {
logrus.Debug("setWorthCheckingIPTablesRoutine(): setting to false")
a.worthCheckingIPTables = false
}
a.worthCheckingIPTablesMu.Unlock()
}
}()
for {
msg, err := auditClient.Receive(false)
if err != nil {
logrus.Error(err)
continue
}
if msg.Type == auparse.AUDIT_NETFILTER_CFG {
a.worthCheckingIPTablesMu.Lock()
logrus.Debug("setWorthCheckingIPTablesRoutine(): setting to true")
a.worthCheckingIPTables = true
latestTrue = time.Now()
a.worthCheckingIPTablesMu.Unlock()
}
}
}

type eventState struct {
ports []*api.IPPort
}
Expand Down Expand Up @@ -220,7 +117,7 @@ func (a *agent) Events(ctx context.Context, ch chan *api.Event) {
}
}

func (a *agent) LocalPorts(ctx context.Context) ([]*api.IPPort, error) {
func (a *agent) LocalPorts(_ context.Context) ([]*api.IPPort, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _ is only needed when you mix named and unnamed parameters.

Suggested change
func (a *agent) LocalPorts(_ context.Context) ([]*api.IPPort, error) {
func (a *agent) LocalPorts(context.Context) ([]*api.IPPort, error) {

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linter doesn't allow that

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure?

GOOS=linux golangci-lint run pkg/guestagent/...
0 issues.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC CI failed without that, but I could be wrong

var res []*api.IPPort
socketsList, err := sockets.List()
if err != nil {
Expand Down Expand Up @@ -252,47 +149,6 @@ func (a *agent) LocalPorts(ctx context.Context) ([]*api.IPPort, error) {
}
}

a.worthCheckingIPTablesMu.RLock()
worthCheckingIPTables := a.worthCheckingIPTables
a.worthCheckingIPTablesMu.RUnlock()
logrus.Debugf("LocalPorts(): worthCheckingIPTables=%v", worthCheckingIPTables)

var ipts []iptables.Entry
if a.worthCheckingIPTables {
ipts, err = iptables.GetPorts(ctx)
if err != nil {
return res, err
}
a.latestIPTablesMu.Lock()
a.latestIPTables = ipts
a.latestIPTablesMu.Unlock()
} else {
a.latestIPTablesMu.RLock()
ipts = a.latestIPTables
a.latestIPTablesMu.RUnlock()
}

for _, ipt := range ipts {
port := int32(ipt.AddrPort.Port())
// Make sure the port isn't already listed from sockets
found := false
for _, re := range res {
if re.Port == port {
found = true
}
}
if !found {
if ipt.TCP {
res = append(res,
&api.IPPort{
Ip: ipt.AddrPort.Addr().String(),
Port: port,
Protocol: "tcp",
})
}
}
}

kubernetesEntries := a.kubernetesServiceWatcher.GetPorts()
for _, entry := range kubernetesEntries {
found := false
Expand Down
147 changes: 0 additions & 147 deletions pkg/guestagent/iptables/iptables.go

This file was deleted.

Loading