mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-11 21:27:31 +00:00
cmd/tailscaled, ipn/conffile: support ec2 user-data config file
Updates #1412 Updates #1866 Change-Id: I4d08fb233b80c2078b3b28ffc18559baabb4a081 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:

committed by
Brad Fitzpatrick

parent
2d2b62c400
commit
1ea100e2e5
59
ipn/conffile/cloudconf.go
Normal file
59
ipn/conffile/cloudconf.go
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package conffile
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"tailscale.com/omit"
|
||||
)
|
||||
|
||||
func getEC2MetadataToken() (string, error) {
|
||||
if omit.AWS {
|
||||
return "", omit.Err
|
||||
}
|
||||
req, _ := http.NewRequest("PUT", "http://169.254.169.254/latest/api/token", nil)
|
||||
req.Header.Add("X-aws-ec2-metadata-token-ttl-seconds", "300")
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get metadata token: %w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != 200 {
|
||||
return "", fmt.Errorf("failed to get metadata token: %v", res.Status)
|
||||
}
|
||||
all, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read metadata token: %w", err)
|
||||
}
|
||||
return strings.TrimSpace(string(all)), nil
|
||||
}
|
||||
|
||||
func readVMUserData() ([]byte, error) {
|
||||
// TODO(bradfitz): support GCP, Azure, Proxmox/cloud-init
|
||||
// (NoCloud/ConfigDrive ISO), etc.
|
||||
|
||||
if omit.AWS {
|
||||
return nil, omit.Err
|
||||
}
|
||||
token, tokErr := getEC2MetadataToken()
|
||||
req, _ := http.NewRequest("GET", "http://169.254.169.254/latest/user-data", nil)
|
||||
req.Header.Add("X-aws-ec2-metadata-token", token)
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != 200 {
|
||||
if tokErr != nil {
|
||||
return nil, fmt.Errorf("failed to get VM user data: %v; also failed to get metadata token: %v", res.Status, tokErr)
|
||||
}
|
||||
return nil, errors.New(res.Status)
|
||||
}
|
||||
return io.ReadAll(res.Body)
|
||||
}
|
@@ -17,7 +17,7 @@ import (
|
||||
|
||||
// Config describes a config file.
|
||||
type Config struct {
|
||||
Path string // disk path of HuJSON
|
||||
Path string // disk path of HuJSON, or VMUserDataPath
|
||||
Raw []byte // raw bytes from disk, in HuJSON form
|
||||
Std []byte // standardized JSON form
|
||||
Version string // "alpha0" for now
|
||||
@@ -35,13 +35,22 @@ func (c *Config) WantRunning() bool {
|
||||
return c != nil && !c.Parsed.Enabled.EqualBool(false)
|
||||
}
|
||||
|
||||
// VMUserDataPath is a sentinel value for Load to use to get the data
|
||||
// from the VM's metadata service's user-data field.
|
||||
const VMUserDataPath = "vm:user-data"
|
||||
|
||||
// Load reads and parses the config file at the provided path on disk.
|
||||
func Load(path string) (*Config, error) {
|
||||
var c Config
|
||||
c.Path = path
|
||||
|
||||
var err error
|
||||
c.Raw, err = os.ReadFile(path)
|
||||
|
||||
switch path {
|
||||
case VMUserDataPath:
|
||||
c.Raw, err = readVMUserData()
|
||||
default:
|
||||
c.Raw, err = os.ReadFile(path)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
Reference in New Issue
Block a user