1
0
forked from wrenn/wrenn

Replace one-shot clock_settime with chrony for continuous guest time sync

Switch from the envd /init endpoint pushing host time via syscall to
chronyd reading the KVM PTP hardware clock (/dev/ptp0) continuously.
This fixes clock drift between init calls and handles snapshot resume
gracefully.

Changes:
- Add clocksource=kvm-clock kernel boot arg
- Start chronyd in wrenn-init.sh before tini (PHC /dev/ptp0, makestep 1.0 -1)
- Remove clock_settime logic from envd SetData and shouldSetSystemTime
- Remove client.Init() clock sync calls from sandbox manager (3 sites)
- Remove Init() method from envdclient (no longer needed)
- Simplify rootfs scripts: socat/chrony now come from apt in the container
  image, only envd/wrenn-init/tini are injected by build scripts
This commit is contained in:
2026-03-26 04:47:44 +06:00
parent 12d1e356fa
commit 6898528096
8 changed files with 37 additions and 234 deletions

View File

@ -17,8 +17,6 @@ import (
"github.com/awnumar/memguard"
"github.com/rs/zerolog"
"github.com/txn2/txeh"
"golang.org/x/sys/unix"
"git.omukk.dev/wrenn/sandbox/envd/internal/host"
"git.omukk.dev/wrenn/sandbox/envd/internal/logs"
"git.omukk.dev/wrenn/sandbox/envd/internal/shared/keys"
@ -29,11 +27,6 @@ var (
ErrAccessTokenResetNotAuthorized = errors.New("access token reset not authorized")
)
const (
maxTimeInPast = 50 * time.Millisecond
maxTimeInFuture = 5 * time.Second
)
// validateInitAccessToken validates the access token for /init requests.
// Token is valid if it matches the existing token OR the MMDS hash.
// If neither exists, first-time setup is allowed.
@ -172,20 +165,6 @@ func (a *API) SetData(ctx context.Context, logger zerolog.Logger, data PostInitJ
return err
}
if data.Timestamp != nil {
// Check if current time differs significantly from the received timestamp
if shouldSetSystemTime(time.Now(), *data.Timestamp) {
logger.Debug().Msgf("Setting sandbox start time to: %v", *data.Timestamp)
ts := unix.NsecToTimespec(data.Timestamp.UnixNano())
err := unix.ClockSettime(unix.CLOCK_REALTIME, &ts)
if err != nil {
logger.Error().Msgf("Failed to set system time: %v", err)
}
} else {
logger.Debug().Msgf("Current time is within acceptable range of timestamp %v, not setting system time", *data.Timestamp)
}
}
if data.EnvVars != nil {
logger.Debug().Msg(fmt.Sprintf("Setting %d env vars", len(*data.EnvVars)))
@ -309,9 +288,3 @@ func getIPFamily(address string) (txeh.IPFamily, error) {
}
}
// shouldSetSystemTime returns true if the current time differs significantly from the received timestamp,
// indicating the system clock should be adjusted. Returns true when the sandboxTime is more than
// maxTimeInPast before the hostTime or more than maxTimeInFuture after the hostTime.
func shouldSetSystemTime(sandboxTime, hostTime time.Time) bool {
return sandboxTime.Before(hostTime.Add(-maxTimeInPast)) || sandboxTime.After(hostTime.Add(maxTimeInFuture))
}

View File

@ -9,7 +9,6 @@ import (
"path/filepath"
"strings"
"testing"
"time"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
@ -59,71 +58,6 @@ func TestSimpleCases(t *testing.T) {
}
}
func TestShouldSetSystemTime(t *testing.T) {
t.Parallel()
sandboxTime := time.Now()
tests := []struct {
name string
hostTime time.Time
want bool
}{
{
name: "sandbox time far ahead of host time (should set)",
hostTime: sandboxTime.Add(-10 * time.Second),
want: true,
},
{
name: "sandbox time at maxTimeInPast boundary ahead of host time (should not set)",
hostTime: sandboxTime.Add(-50 * time.Millisecond),
want: false,
},
{
name: "sandbox time just within maxTimeInPast ahead of host time (should not set)",
hostTime: sandboxTime.Add(-40 * time.Millisecond),
want: false,
},
{
name: "sandbox time slightly ahead of host time (should not set)",
hostTime: sandboxTime.Add(-10 * time.Millisecond),
want: false,
},
{
name: "sandbox time equals host time (should not set)",
hostTime: sandboxTime,
want: false,
},
{
name: "sandbox time slightly behind host time (should not set)",
hostTime: sandboxTime.Add(1 * time.Second),
want: false,
},
{
name: "sandbox time just within maxTimeInFuture behind host time (should not set)",
hostTime: sandboxTime.Add(4 * time.Second),
want: false,
},
{
name: "sandbox time at maxTimeInFuture boundary behind host time (should not set)",
hostTime: sandboxTime.Add(5 * time.Second),
want: false,
},
{
name: "sandbox time far behind host time (should set)",
hostTime: sandboxTime.Add(1 * time.Minute),
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := shouldSetSystemTime(tt.hostTime, sandboxTime)
assert.Equal(t, tt.want, got)
})
}
}
func secureTokenPtr(s string) *SecureToken {
token := &SecureToken{}
_ = token.Set([]byte(s))