mirror of
https://github.com/tailscale/tailscale.git
synced 2025-01-07 08:07:42 +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 {
|
func (c *Auto) TestOnlyTimeNow() time.Time {
|
||||||
return c.timeNow()
|
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
|
// in a separate http request. It has nothing to do with the rest of
|
||||||
// the state machine.
|
// the state machine.
|
||||||
UpdateEndpoints(localPort uint16, endpoints []tailcfg.Endpoint)
|
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
|
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) {
|
func (b *LocalBackend) registerIncomingFile(inf *incomingFile, active bool) {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
@ -248,6 +248,10 @@ func (cc *mockControl) UpdateEndpoints(localPort uint16, endpoints []tailcfg.End
|
|||||||
cc.called("UpdateEndpoints")
|
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
|
// A very precise test of the sequence of function calls generated by
|
||||||
// ipnlocal.Local into its controlclient instance, and the events it
|
// ipnlocal.Local into its controlclient instance, and the events it
|
||||||
// produces upstream into the UI.
|
// produces upstream into the UI.
|
||||||
|
@ -100,6 +100,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
h.serveBugReport(w, r)
|
h.serveBugReport(w, r)
|
||||||
case "/localapi/v0/file-targets":
|
case "/localapi/v0/file-targets":
|
||||||
h.serveFileTargets(w, r)
|
h.serveFileTargets(w, r)
|
||||||
|
case "/localapi/v0/set-dns":
|
||||||
|
h.serveSetDNS(w, r)
|
||||||
case "/":
|
case "/":
|
||||||
io.WriteString(w, "tailscaled\n")
|
io.WriteString(w, "tailscaled\n")
|
||||||
default:
|
default:
|
||||||
@ -382,6 +384,25 @@ func (h *Handler) serveFilePut(w http.ResponseWriter, r *http.Request) {
|
|||||||
rp.ServeHTTP(w, outReq)
|
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 {
|
var dialPeerTransportOnce struct {
|
||||||
sync.Once
|
sync.Once
|
||||||
v *http.Transport
|
v *http.Transport
|
||||||
|
Loading…
x
Reference in New Issue
Block a user