Skip to content

Commit a879b4c

Browse files
feat(ci): Bump to upstream v1.78.3 (#19)
https://tailscale.com/changelog#2024-12-13
2 parents 36854d1 + 38bbe01 commit a879b4c

File tree

289 files changed

+14213
-3262
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

289 files changed

+14213
-3262
lines changed

.github/workflows/publish-image.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ jobs:
5757
- name: Publish k8s-operator
5858
shell: bash
5959
run: |
60-
REPOS="ghcr.io/${{ github.repository }}/k8s-operator" TARGET="operator" ./build_docker.sh
60+
REPOS="ghcr.io/${{ github.repository }}/k8s-operator" TARGET="k8s-operator" ./build_docker.sh
6161
6262
- name: Publish k8s-nameserver
6363
shell: bash

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ publishdevoperator: ## Build and publish k8s-operator image to location specifie
100100
@test "${REPO}" != "ghcr.io/tailscale/tailscale" || (echo "REPO=... must not be ghcr.io/tailscale/tailscale" && exit 1)
101101
@test "${REPO}" != "tailscale/k8s-operator" || (echo "REPO=... must not be tailscale/k8s-operator" && exit 1)
102102
@test "${REPO}" != "ghcr.io/tailscale/k8s-operator" || (echo "REPO=... must not be ghcr.io/tailscale/k8s-operator" && exit 1)
103-
TAGS="${TAGS}" REPOS=${REPO} PLATFORM=${PLATFORM} PUSH=true TARGET=operator ./build_docker.sh
103+
TAGS="${TAGS}" REPOS=${REPO} PLATFORM=${PLATFORM} PUSH=true TARGET=k8s-operator ./build_docker.sh
104104

105105
publishdevnameserver: ## Build and publish k8s-nameserver image to location specified by ${REPO}
106106
@test -n "${REPO}" || (echo "REPO=... required; e.g. REPO=ghcr.io/${USER}/tailscale" && exit 1)

VERSION.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.76.6
1+
1.78.3

build_docker.sh

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,20 @@ eval "$(./build_dist.sh shellvars)"
1717
DEFAULT_TARGET="client"
1818
DEFAULT_TAGS="v${VERSION_SHORT},v${VERSION_MINOR}"
1919
DEFAULT_BASE="tailscale/alpine-base:3.18"
20+
# Set a few pre-defined OCI annotations. The source annotation is used by tools such as Renovate that scan the linked
21+
# Github repo to find release notes for any new image tags. Note that for official Tailscale images the default
22+
# annotations defined here will be overriden by release scripts that call this script.
23+
# https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
24+
DEFAULT_ANNOTATIONS="org.opencontainers.image.source=https://github.com/tailscale/tailscale/blob/main/build_docker.sh,org.opencontainers.image.vendor=Tailscale"
2025

2126
PUSH="${PUSH:-false}"
2227
TARGET="${TARGET:-${DEFAULT_TARGET}}"
2328
TAGS="${TAGS:-${DEFAULT_TAGS}}"
2429
BASE="${BASE:-${DEFAULT_BASE}}"
2530
PLATFORM="${PLATFORM:-}" # default to all platforms
31+
# OCI annotations that will be added to the image.
32+
# https://github.com/opencontainers/image-spec/blob/main/annotations.md
33+
ANNOTATIONS="${ANNOTATIONS:-${DEFAULT_ANNOTATIONS}}"
2634

2735
case "$TARGET" in
2836
client)
@@ -43,9 +51,10 @@ case "$TARGET" in
4351
--repos="${REPOS}" \
4452
--push="${PUSH}" \
4553
--target="${PLATFORM}" \
54+
--annotations="${ANNOTATIONS}" \
4655
/usr/local/bin/containerboot
4756
;;
48-
operator)
57+
k8s-operator)
4958
DEFAULT_REPOS="tailscale/k8s-operator"
5059
REPOS="${REPOS:-${DEFAULT_REPOS}}"
5160
go run github.com/tailscale/mkctr \
@@ -56,9 +65,11 @@ case "$TARGET" in
5665
-X tailscale.com/version.gitCommitStamp=${VERSION_GIT_HASH}" \
5766
--base="${BASE}" \
5867
--tags="${TAGS}" \
68+
--gotags="ts_kube,ts_package_container" \
5969
--repos="${REPOS}" \
6070
--push="${PUSH}" \
6171
--target="${PLATFORM}" \
72+
--annotations="${ANNOTATIONS}" \
6273
/usr/local/bin/operator
6374
;;
6475
k8s-nameserver)
@@ -72,9 +83,11 @@ case "$TARGET" in
7283
-X tailscale.com/version.gitCommitStamp=${VERSION_GIT_HASH}" \
7384
--base="${BASE}" \
7485
--tags="${TAGS}" \
86+
--gotags="ts_kube,ts_package_container" \
7587
--repos="${REPOS}" \
7688
--push="${PUSH}" \
7789
--target="${PLATFORM}" \
90+
--annotations="${ANNOTATIONS}" \
7891
/usr/local/bin/k8s-nameserver
7992
;;
8093
*)

client/tailscale/localclient.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import (
4040
"tailscale.com/types/dnstype"
4141
"tailscale.com/types/key"
4242
"tailscale.com/types/tkatype"
43+
"tailscale.com/util/syspolicy/setting"
4344
)
4445

4546
// defaultLocalClient is the default LocalClient when using the legacy
@@ -492,6 +493,17 @@ func (lc *LocalClient) DebugAction(ctx context.Context, action string) error {
492493
return nil
493494
}
494495

496+
// DebugActionBody invokes a debug action with a body parameter, such as
497+
// "debug-force-prefer-derp".
498+
// These are development tools and subject to change or removal over time.
499+
func (lc *LocalClient) DebugActionBody(ctx context.Context, action string, rbody io.Reader) error {
500+
body, err := lc.send(ctx, "POST", "/localapi/v0/debug?action="+url.QueryEscape(action), 200, rbody)
501+
if err != nil {
502+
return fmt.Errorf("error %w: %s", err, body)
503+
}
504+
return nil
505+
}
506+
495507
// DebugResultJSON invokes a debug action and returns its result as something JSON-able.
496508
// These are development tools and subject to change or removal over time.
497509
func (lc *LocalClient) DebugResultJSON(ctx context.Context, action string) (any, error) {
@@ -814,6 +826,33 @@ func (lc *LocalClient) EditPrefs(ctx context.Context, mp *ipn.MaskedPrefs) (*ipn
814826
return decodeJSON[*ipn.Prefs](body)
815827
}
816828

829+
// GetEffectivePolicy returns the effective policy for the specified scope.
830+
func (lc *LocalClient) GetEffectivePolicy(ctx context.Context, scope setting.PolicyScope) (*setting.Snapshot, error) {
831+
scopeID, err := scope.MarshalText()
832+
if err != nil {
833+
return nil, err
834+
}
835+
body, err := lc.get200(ctx, "/localapi/v0/policy/"+string(scopeID))
836+
if err != nil {
837+
return nil, err
838+
}
839+
return decodeJSON[*setting.Snapshot](body)
840+
}
841+
842+
// ReloadEffectivePolicy reloads the effective policy for the specified scope
843+
// by reading and merging policy settings from all applicable policy sources.
844+
func (lc *LocalClient) ReloadEffectivePolicy(ctx context.Context, scope setting.PolicyScope) (*setting.Snapshot, error) {
845+
scopeID, err := scope.MarshalText()
846+
if err != nil {
847+
return nil, err
848+
}
849+
body, err := lc.send(ctx, "POST", "/localapi/v0/policy/"+string(scopeID), 200, http.NoBody)
850+
if err != nil {
851+
return nil, err
852+
}
853+
return decodeJSON[*setting.Snapshot](body)
854+
}
855+
817856
// GetDNSOSConfig returns the system DNS configuration for the current device.
818857
// That is, it returns the DNS configuration that the system would use if Tailscale weren't being used.
819858
func (lc *LocalClient) GetDNSOSConfig(ctx context.Context) (*apitype.DNSOSConfig, error) {
@@ -1299,6 +1338,17 @@ func (lc *LocalClient) SetServeConfig(ctx context.Context, config *ipn.ServeConf
12991338
return nil
13001339
}
13011340

1341+
// DisconnectControl shuts down all connections to control, thus making control consider this node inactive. This can be
1342+
// run on HA subnet router or app connector replicas before shutting them down to ensure peers get told to switch over
1343+
// to another replica whilst there is still some grace period for the existing connections to terminate.
1344+
func (lc *LocalClient) DisconnectControl(ctx context.Context) error {
1345+
_, _, err := lc.sendWithHeaders(ctx, "POST", "/localapi/v0/disconnect-control", 200, nil, nil)
1346+
if err != nil {
1347+
return fmt.Errorf("error disconnecting control: %w", err)
1348+
}
1349+
return nil
1350+
}
1351+
13021352
// NetworkLockDisable shuts down network-lock across the tailnet.
13031353
func (lc *LocalClient) NetworkLockDisable(ctx context.Context, secret []byte) error {
13041354
if _, err := lc.send(ctx, "POST", "/localapi/v0/tka/disable", 200, bytes.NewReader(secret)); err != nil {

client/tailscale/tailscale.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ type Client struct {
5151
// HTTPClient optionally specifies an alternate HTTP client to use.
5252
// If nil, http.DefaultClient is used.
5353
HTTPClient *http.Client
54+
55+
// UserAgent optionally specifies an alternate User-Agent header
56+
UserAgent string
5457
}
5558

5659
func (c *Client) httpClient() *http.Client {
@@ -97,8 +100,9 @@ func (c *Client) setAuth(r *http.Request) {
97100
// and can be changed manually by the user.
98101
func NewClient(tailnet string, auth AuthMethod) *Client {
99102
return &Client{
100-
tailnet: tailnet,
101-
auth: auth,
103+
tailnet: tailnet,
104+
auth: auth,
105+
UserAgent: "tailscale-client-oss",
102106
}
103107
}
104108

@@ -110,17 +114,16 @@ func (c *Client) Do(req *http.Request) (*http.Response, error) {
110114
return nil, errors.New("use of Client without setting I_Acknowledge_This_API_Is_Unstable")
111115
}
112116
c.setAuth(req)
117+
if c.UserAgent != "" {
118+
req.Header.Set("User-Agent", c.UserAgent)
119+
}
113120
return c.httpClient().Do(req)
114121
}
115122

116123
// sendRequest add the authentication key to the request and sends it. It
117124
// receives the response and reads up to 10MB of it.
118125
func (c *Client) sendRequest(req *http.Request) ([]byte, *http.Response, error) {
119-
if !I_Acknowledge_This_API_Is_Unstable {
120-
return nil, nil, errors.New("use of Client without setting I_Acknowledge_This_API_Is_Unstable")
121-
}
122-
c.setAuth(req)
123-
resp, err := c.httpClient().Do(req)
126+
resp, err := c.Do(req)
124127
if err != nil {
125128
return nil, resp, err
126129
}

client/web/web.go

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"tailscale.com/client/tailscale/apitype"
2727
"tailscale.com/clientupdate"
2828
"tailscale.com/envknob"
29+
"tailscale.com/envknob/featureknob"
2930
"tailscale.com/hostinfo"
3031
"tailscale.com/ipn"
3132
"tailscale.com/ipn/ipnstate"
@@ -960,37 +961,16 @@ func (s *Server) serveGetNodeData(w http.ResponseWriter, r *http.Request) {
960961
}
961962

962963
func availableFeatures() map[string]bool {
963-
env := hostinfo.GetEnvType()
964964
features := map[string]bool{
965965
"advertise-exit-node": true, // available on all platforms
966966
"advertise-routes": true, // available on all platforms
967-
"use-exit-node": canUseExitNode(env) == nil,
968-
"ssh": envknob.CanRunTailscaleSSH() == nil,
967+
"use-exit-node": featureknob.CanUseExitNode() == nil,
968+
"ssh": featureknob.CanRunTailscaleSSH() == nil,
969969
"auto-update": version.IsUnstableBuild() && clientupdate.CanAutoUpdate(),
970970
}
971-
if env == hostinfo.HomeAssistantAddOn {
972-
// Setting SSH on Home Assistant causes trouble on startup
973-
// (since the flag is not being passed to `tailscale up`).
974-
// Although Tailscale SSH does work here,
975-
// it's not terribly useful since it's running in a separate container.
976-
features["ssh"] = false
977-
}
978971
return features
979972
}
980973

981-
func canUseExitNode(env hostinfo.EnvType) error {
982-
switch dist := distro.Get(); dist {
983-
case distro.Synology, // see https://github.com/tailscale/tailscale/issues/1995
984-
distro.QNAP,
985-
distro.Unraid:
986-
return fmt.Errorf("Tailscale exit nodes cannot be used on %s.", dist)
987-
}
988-
if env == hostinfo.HomeAssistantAddOn {
989-
return errors.New("Tailscale exit nodes cannot be used on Home Assistant.")
990-
}
991-
return nil
992-
}
993-
994974
// aclsAllowAccess returns whether tailnet ACLs (as expressed in the provided filter rules)
995975
// permit any devices to access the local web client.
996976
// This does not currently check whether a specific device can connect, just any device.

cmd/checkmetrics/checkmetrics.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// Copyright (c) Tailscale Inc & AUTHORS
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
4+
// checkmetrics validates that all metrics in the tailscale client-metrics
5+
// are documented in a given path or URL.
6+
package main
7+
8+
import (
9+
"context"
10+
"flag"
11+
"fmt"
12+
"io"
13+
"log"
14+
"net/http"
15+
"net/http/httptest"
16+
"os"
17+
"strings"
18+
"time"
19+
20+
"tailscale.com/ipn/store/mem"
21+
"tailscale.com/tsnet"
22+
"tailscale.com/tstest/integration/testcontrol"
23+
"tailscale.com/util/httpm"
24+
)
25+
26+
var (
27+
kbPath = flag.String("kb-path", "", "filepath to the client-metrics knowledge base")
28+
kbUrl = flag.String("kb-url", "", "URL to the client-metrics knowledge base page")
29+
)
30+
31+
func main() {
32+
flag.Parse()
33+
if *kbPath == "" && *kbUrl == "" {
34+
log.Fatalf("either -kb-path or -kb-url must be set")
35+
}
36+
37+
var control testcontrol.Server
38+
ts := httptest.NewServer(&control)
39+
defer ts.Close()
40+
41+
td, err := os.MkdirTemp("", "testcontrol")
42+
if err != nil {
43+
log.Fatal(err)
44+
}
45+
defer os.RemoveAll(td)
46+
47+
// tsnet is used not used as a Tailscale client, but as a way to
48+
// boot up Tailscale, have all the metrics registered, and then
49+
// verifiy that all the metrics are documented.
50+
tsn := &tsnet.Server{
51+
Dir: td,
52+
Store: new(mem.Store),
53+
UserLogf: log.Printf,
54+
Ephemeral: true,
55+
ControlURL: ts.URL,
56+
}
57+
if err := tsn.Start(); err != nil {
58+
log.Fatal(err)
59+
}
60+
defer tsn.Close()
61+
62+
log.Printf("checking that all metrics are documented, looking for: %s", tsn.Sys().UserMetricsRegistry().MetricNames())
63+
64+
if *kbPath != "" {
65+
kb, err := readKB(*kbPath)
66+
if err != nil {
67+
log.Fatalf("reading kb: %v", err)
68+
}
69+
missing := undocumentedMetrics(kb, tsn.Sys().UserMetricsRegistry().MetricNames())
70+
71+
if len(missing) > 0 {
72+
log.Fatalf("found undocumented metrics in %q: %v", *kbPath, missing)
73+
}
74+
}
75+
76+
if *kbUrl != "" {
77+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
78+
defer cancel()
79+
80+
kb, err := getKB(ctx, *kbUrl)
81+
if err != nil {
82+
log.Fatalf("getting kb: %v", err)
83+
}
84+
missing := undocumentedMetrics(kb, tsn.Sys().UserMetricsRegistry().MetricNames())
85+
86+
if len(missing) > 0 {
87+
log.Fatalf("found undocumented metrics in %q: %v", *kbUrl, missing)
88+
}
89+
}
90+
}
91+
92+
func readKB(path string) (string, error) {
93+
b, err := os.ReadFile(path)
94+
if err != nil {
95+
return "", fmt.Errorf("reading file: %w", err)
96+
}
97+
98+
return string(b), nil
99+
}
100+
101+
func getKB(ctx context.Context, url string) (string, error) {
102+
req, err := http.NewRequestWithContext(ctx, httpm.GET, url, nil)
103+
if err != nil {
104+
return "", fmt.Errorf("creating request: %w", err)
105+
}
106+
107+
resp, err := http.DefaultClient.Do(req)
108+
if err != nil {
109+
return "", fmt.Errorf("getting kb page: %w", err)
110+
}
111+
112+
if resp.StatusCode != http.StatusOK {
113+
return "", fmt.Errorf("unexpected status code: %d", resp.StatusCode)
114+
}
115+
116+
b, err := io.ReadAll(resp.Body)
117+
if err != nil {
118+
return "", fmt.Errorf("reading body: %w", err)
119+
}
120+
return string(b), nil
121+
}
122+
123+
func undocumentedMetrics(b string, metrics []string) []string {
124+
var missing []string
125+
for _, metric := range metrics {
126+
if !strings.Contains(b, metric) {
127+
missing = append(missing, metric)
128+
}
129+
}
130+
return missing
131+
}

0 commit comments

Comments
 (0)