summaryrefslogtreecommitdiffhomepage
path: root/net/currenttime
diff options
context:
space:
mode:
Diffstat (limited to 'net/currenttime')
-rw-r--r--net/currenttime/currenttime.go42
-rw-r--r--net/currenttime/currenttime_test.go14
-rw-r--r--net/currenttime/mintime.txt1
-rw-r--r--net/currenttime/update-current-time.go20
4 files changed, 77 insertions, 0 deletions
diff --git a/net/currenttime/currenttime.go b/net/currenttime/currenttime.go
new file mode 100644
index 000000000..7ef2a1c56
--- /dev/null
+++ b/net/currenttime/currenttime.go
@@ -0,0 +1,42 @@
+// Copyright (c) Tailscale Inc & AUTHORS
+// SPDX-License-Identifier: BSD-3-Clause
+
+// Package currenttime provides a fallback "current time" that can be used as
+// the minimum possible time for things like TLS certificate verification.
+//
+// This ensures that if a Tailscale client's clock is wrong, it can still
+// verify TLS certificates, assuming that the server certificate hasn't already
+// expired from the point of view of the minimum time.
+//
+// In the future, we may want to consider caching the last known current time
+// on-disk to improve the accuracy of this fallback.
+package currenttime
+
+import (
+ _ "embed"
+ "strconv"
+ "time"
+)
+
+//go:embed mintime.txt
+var minTimeUnixMs string
+
+var minCurrentTime time.Time
+
+func init() {
+ ms, err := strconv.ParseInt(minTimeUnixMs, 10, 64)
+ if err != nil {
+ panic(err)
+ }
+ minCurrentTime = time.UnixMilli(int64(ms))
+}
+
+// Now returns the current time as per [time.Now], except that if it is before
+// the baked-in "minimum current time", that value will be returned instead.
+func Now() time.Time {
+ now := time.Now()
+ if now.Before(minCurrentTime) {
+ return minCurrentTime
+ }
+ return now
+}
diff --git a/net/currenttime/currenttime_test.go b/net/currenttime/currenttime_test.go
new file mode 100644
index 000000000..958a8436e
--- /dev/null
+++ b/net/currenttime/currenttime_test.go
@@ -0,0 +1,14 @@
+package currenttime
+
+import (
+ "testing"
+ "time"
+)
+
+func TestMinTime(t *testing.T) {
+ // The baked-in time should always be before the current time.
+ now := time.Now()
+ if !minCurrentTime.Before(now) {
+ t.Fatalf("minCurrentTime is not before the current time: %v >= %v", minCurrentTime, now)
+ }
+}
diff --git a/net/currenttime/mintime.txt b/net/currenttime/mintime.txt
new file mode 100644
index 000000000..5da66af78
--- /dev/null
+++ b/net/currenttime/mintime.txt
@@ -0,0 +1 @@
+1741638824797 \ No newline at end of file
diff --git a/net/currenttime/update-current-time.go b/net/currenttime/update-current-time.go
new file mode 100644
index 000000000..717a984ce
--- /dev/null
+++ b/net/currenttime/update-current-time.go
@@ -0,0 +1,20 @@
+// Copyright (c) Tailscale Inc & AUTHORS
+// SPDX-License-Identifier: BSD-3-Clause
+
+//go:build ignore
+
+package main
+
+import (
+ "fmt"
+ "log"
+ "os"
+ "time"
+)
+
+func main() {
+ contents := fmt.Sprintf(`%d`, time.Now().UnixMilli())
+ if err := os.WriteFile("mintime.txt", []byte(contents), 0644); err != nil {
+ log.Fatal(err)
+ }
+}