mirror of
https://github.com/tailscale/tailscale.git
synced 2025-01-05 23:07:44 +00:00
ipn/{ipnlocal,localapi}, control/controlclient: add SetDNS localapi
Updates #1235 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
8236464252
commit
e29cec759a
@ -716,3 +716,9 @@ func (c *Auto) TestOnlySetAuthKey(authkey string) {
|
||||
func (c *Auto) TestOnlyTimeNow() time.Time {
|
||||
return c.timeNow()
|
||||
}
|
||||
|
||||
// SetDNS sends the SetDNSRequest request to the control plane server,
|
||||
// requesting a DNS record be created or updated.
|
||||
func (c *Auto) SetDNS(ctx context.Context, req *tailcfg.SetDNSRequest) error {
|
||||
return c.direct.SetDNS(ctx, req)
|
||||
}
|
||||
|
@ -74,4 +74,7 @@ type Client interface {
|
||||
// in a separate http request. It has nothing to do with the rest of
|
||||
// the state machine.
|
||||
UpdateEndpoints(localPort uint16, endpoints []tailcfg.Endpoint)
|
||||
// SetDNS sends the SetDNSRequest request to the control plane server,
|
||||
// requesting a DNS record be created or updated.
|
||||
SetDNS(context.Context, *tailcfg.SetDNSRequest) error
|
||||
}
|
||||
|
@ -1211,3 +1211,50 @@ func sleepAsRequested(ctx context.Context, logf logger.Logf, timeoutReset chan<-
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetDNS sends the SetDNSRequest request to the control plane server,
|
||||
// requesting a DNS record be created or updated.
|
||||
func (c *Direct) SetDNS(ctx context.Context, req *tailcfg.SetDNSRequest) error {
|
||||
c.mu.Lock()
|
||||
serverKey := c.serverKey
|
||||
c.mu.Unlock()
|
||||
|
||||
if serverKey.IsZero() {
|
||||
return errors.New("zero serverKey")
|
||||
}
|
||||
machinePrivKey, err := c.getMachinePrivKey()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getMachinePrivKey: %w", err)
|
||||
}
|
||||
if machinePrivKey.IsZero() {
|
||||
return errors.New("getMachinePrivKey returned zero key")
|
||||
}
|
||||
|
||||
bodyData, err := encode(req, &serverKey, &machinePrivKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body := bytes.NewReader(bodyData)
|
||||
|
||||
u := fmt.Sprintf("%s/machine/%s/set-dns", c.serverURL, machinePrivKey.Public().HexString())
|
||||
hreq, err := http.NewRequestWithContext(ctx, "POST", u, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := c.httpc.Do(hreq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != 200 {
|
||||
msg, _ := ioutil.ReadAll(res.Body)
|
||||
return fmt.Errorf("sign-dns response: %v, %.200s", res.Status, strings.TrimSpace(string(msg)))
|
||||
}
|
||||
var setDNSRes struct{} // no fields yet
|
||||
if err := decode(res, &setDNSRes, &serverKey, &machinePrivKey); err != nil {
|
||||
c.logf("error decoding SetDNSResponse with server key %s and machine key %s: %v", serverKey, machinePrivKey.Public(), err)
|
||||
return fmt.Errorf("set-dns-response: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -2577,6 +2577,42 @@ func (b *LocalBackend) FileTargets() ([]*apitype.FileTarget, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// SetDNS adds a DNS record for the given domain name & TXT record
|
||||
// value.
|
||||
//
|
||||
// It's meant for use with dns-01 ACME (LetsEncrypt) challenges.
|
||||
//
|
||||
// This is the low-level interface. Other layers will provide more
|
||||
// friendly options to get HTTPS certs.
|
||||
func (b *LocalBackend) SetDNS(ctx context.Context, name, value string) error {
|
||||
req := &tailcfg.SetDNSRequest{
|
||||
Version: 1,
|
||||
Type: "TXT",
|
||||
Name: name,
|
||||
Value: value,
|
||||
}
|
||||
|
||||
b.mu.Lock()
|
||||
cc := b.cc
|
||||
if prefs := b.prefs; prefs != nil {
|
||||
req.NodeKey = tailcfg.NodeKey(prefs.Persist.PrivateNodeKey.Public())
|
||||
}
|
||||
b.mu.Unlock()
|
||||
if cc == nil {
|
||||
return errors.New("not connected")
|
||||
}
|
||||
if req.NodeKey.IsZero() {
|
||||
return errors.New("no nodekey")
|
||||
}
|
||||
if name == "" {
|
||||
return errors.New("missing 'name'")
|
||||
}
|
||||
if value == "" {
|
||||
return errors.New("missing 'value'")
|
||||
}
|
||||
return cc.SetDNS(ctx, req)
|
||||
}
|
||||
|
||||
func (b *LocalBackend) registerIncomingFile(inf *incomingFile, active bool) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
@ -248,6 +248,10 @@ func (cc *mockControl) UpdateEndpoints(localPort uint16, endpoints []tailcfg.End
|
||||
cc.called("UpdateEndpoints")
|
||||
}
|
||||
|
||||
func (*mockControl) SetDNS(context.Context, *tailcfg.SetDNSRequest) error {
|
||||
panic("unexpected SetDNS call")
|
||||
}
|
||||
|
||||
// A very precise test of the sequence of function calls generated by
|
||||
// ipnlocal.Local into its controlclient instance, and the events it
|
||||
// produces upstream into the UI.
|
||||
|
@ -100,6 +100,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
h.serveBugReport(w, r)
|
||||
case "/localapi/v0/file-targets":
|
||||
h.serveFileTargets(w, r)
|
||||
case "/localapi/v0/set-dns":
|
||||
h.serveSetDNS(w, r)
|
||||
case "/":
|
||||
io.WriteString(w, "tailscaled\n")
|
||||
default:
|
||||
@ -382,6 +384,25 @@ func (h *Handler) serveFilePut(w http.ResponseWriter, r *http.Request) {
|
||||
rp.ServeHTTP(w, outReq)
|
||||
}
|
||||
|
||||
func (h *Handler) serveSetDNS(w http.ResponseWriter, r *http.Request) {
|
||||
if !h.PermitWrite {
|
||||
http.Error(w, "access denied", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if r.Method != "POST" {
|
||||
http.Error(w, "want POST", 400)
|
||||
return
|
||||
}
|
||||
ctx := r.Context()
|
||||
err := h.b.SetDNS(ctx, r.FormValue("name"), r.FormValue("value"))
|
||||
if err != nil {
|
||||
writeErrorJSON(w, err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(struct{}{})
|
||||
}
|
||||
|
||||
var dialPeerTransportOnce struct {
|
||||
sync.Once
|
||||
v *http.Transport
|
||||
|
Loading…
x
Reference in New Issue
Block a user