kube.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build linux
  4. package main
  5. import (
  6. "context"
  7. "encoding/json"
  8. "fmt"
  9. "log"
  10. "net/http"
  11. "net/netip"
  12. "os"
  13. "tailscale.com/kube"
  14. "tailscale.com/tailcfg"
  15. )
  16. // findKeyInKubeSecret inspects the kube secret secretName for a data
  17. // field called "authkey", and returns its value if present.
  18. func findKeyInKubeSecret(ctx context.Context, secretName string) (string, error) {
  19. s, err := kc.GetSecret(ctx, secretName)
  20. if err != nil {
  21. return "", err
  22. }
  23. ak, ok := s.Data["authkey"]
  24. if !ok {
  25. return "", nil
  26. }
  27. return string(ak), nil
  28. }
  29. // storeDeviceInfo writes deviceID into the "device_id" data field of the kube
  30. // secret secretName.
  31. func storeDeviceInfo(ctx context.Context, secretName string, deviceID tailcfg.StableNodeID, fqdn string, addresses []netip.Prefix) error {
  32. // First check if the secret exists at all. Even if running on
  33. // kubernetes, we do not necessarily store state in a k8s secret.
  34. if _, err := kc.GetSecret(ctx, secretName); err != nil {
  35. if s, ok := err.(*kube.Status); ok {
  36. if s.Code >= 400 && s.Code <= 499 {
  37. // Assume the secret doesn't exist, or we don't have
  38. // permission to access it.
  39. return nil
  40. }
  41. }
  42. return err
  43. }
  44. var ips []string
  45. for _, addr := range addresses {
  46. ips = append(ips, addr.Addr().String())
  47. }
  48. deviceIPs, err := json.Marshal(ips)
  49. if err != nil {
  50. return err
  51. }
  52. m := &kube.Secret{
  53. Data: map[string][]byte{
  54. "device_id": []byte(deviceID),
  55. "device_fqdn": []byte(fqdn),
  56. "device_ips": deviceIPs,
  57. },
  58. }
  59. return kc.StrategicMergePatchSecret(ctx, secretName, m, "tailscale-container")
  60. }
  61. // deleteAuthKey deletes the 'authkey' field of the given kube
  62. // secret. No-op if there is no authkey in the secret.
  63. func deleteAuthKey(ctx context.Context, secretName string) error {
  64. // m is a JSON Patch data structure, see https://jsonpatch.com/ or RFC 6902.
  65. m := []kube.JSONPatch{
  66. {
  67. Op: "remove",
  68. Path: "/data/authkey",
  69. },
  70. }
  71. if err := kc.JSONPatchSecret(ctx, secretName, m); err != nil {
  72. if s, ok := err.(*kube.Status); ok && s.Code == http.StatusUnprocessableEntity {
  73. // This is kubernetes-ese for "the field you asked to
  74. // delete already doesn't exist", aka no-op.
  75. return nil
  76. }
  77. return err
  78. }
  79. return nil
  80. }
  81. var kc *kube.Client
  82. func initKube(root string) {
  83. if root != "/" {
  84. // If we are running in a test, we need to set the root path to the fake
  85. // service account directory.
  86. kube.SetRootPathForTesting(root)
  87. }
  88. var err error
  89. kc, err = kube.New()
  90. if err != nil {
  91. log.Fatalf("Error creating kube client: %v", err)
  92. }
  93. if root != "/" {
  94. // If we are running in a test, we need to set the URL to the
  95. // httptest server.
  96. kc.SetURL(fmt.Sprintf("https://%s:%s", os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT_HTTPS")))
  97. }
  98. }