blob: f57e8921e0c414fded04d2141d0989b665baf2be [file] [log] [blame]
// Package time implements a supervisor runnable which is responsible for
// keeping both the system clock and the RTC accurate.
// Metropolis nodes need accurate time both for themselves (for log
// timestamping, validating certain certificates, ...) as well as workloads
// running on top of it expecting accurate time.
// This initial implementation is very minimalistic, running just a stateless
// NTP client per node for the whole lifecycle of it.
// This implementation is simple, but is fairly unsafe as NTP by itself does
// not offer any cryptography, so it's easy to tamper with the responses.
// See #73 for further work in that direction.
package time
import (
"context"
"fmt"
"os/exec"
"strconv"
"strings"
"source.monogon.dev/metropolis/node"
"source.monogon.dev/metropolis/pkg/fileargs"
"source.monogon.dev/metropolis/pkg/supervisor"
)
// Service implements the time service. See package documentation for further
// information.
type Service struct{}
func New() *Service {
return &Service{}
}
func (s *Service) Run(ctx context.Context) error {
// TODO(#72): Apply for a NTP pool vendor zone
config := strings.Join([]string{
"pool ntp.monogon.dev iburst",
"bindcmdaddress /",
"stratumweight 0.01",
"leapsecmode slew",
"maxslewrate 10000",
"makestep 2.0 3",
"rtconutc",
"rtcsync",
}, "\n")
args, err := fileargs.New()
if err != nil {
return fmt.Errorf("cannot create fileargs: %w", err)
}
defer args.Close()
cmd := exec.Command(
"/time/chrony",
"-d",
"-i", strconv.Itoa(node.TimeUid),
"-g", strconv.Itoa(node.TimeUid),
"-f", args.ArgPath("chrony.conf", []byte(config)),
)
cmd.Stdout = supervisor.RawLogger(ctx)
cmd.Stderr = supervisor.RawLogger(ctx)
return supervisor.RunCommand(ctx, cmd)
}