HAMMERHEAD_SELF_DOMAIN creates an edge for this machine, writes its token to <data>/self-edge.env and adds an https route for the dashboard to that edge's config, so no reverse proxy is needed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
72 lines
2.5 KiB
Go
72 lines
2.5 KiB
Go
package hammerhead
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"git.veillax.com/Veillax/reef/model"
|
|
)
|
|
|
|
const selfMappingID = "hammerhead-dashboard"
|
|
|
|
// ensureSelfEdge makes sure this server has an edge of its own, for a Blacktip on the same
|
|
// machine, and writes that edge's token to <data>/self-edge.env for the installer. The edge
|
|
// also carries a built-in https route for the dashboard (see selfMapping), so no separate
|
|
// reverse proxy is needed in front of Hammerhead.
|
|
func (s *Server) ensureSelfEdge() error {
|
|
o := s.opts
|
|
if o.SelfDomain == "" {
|
|
return nil
|
|
}
|
|
host, port, err := net.SplitHostPort(o.SelfTarget)
|
|
if err != nil {
|
|
return fmt.Errorf("HAMMERHEAD_SELF_DOMAIN needs a listen address like 127.0.0.1:8080: %w", err)
|
|
}
|
|
if n, _ := strconv.Atoi(port); n < 1 || host == "" {
|
|
return errors.New("the dashboard's listen address needs a host and a port")
|
|
}
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
e := s.findEdge(s.st.Settings.SelfEdgeID)
|
|
if e == nil {
|
|
e = &model.Edge{ID: newID(), Name: "Hammerhead server", Token: randHex(24), AuthKey: randHex(32),
|
|
TunnelAddr: o.SelfTunnel, TunnelIP: s.allocIPLocked(), OrgIDs: []string{}, IPs: []string{}}
|
|
s.st.Edges = append(s.st.Edges, e)
|
|
s.st.Sites = append(s.st.Sites, &model.Site{ID: newID(), Name: e.Name + " (this machine)",
|
|
Local: true, EdgeID: e.ID, Connected: []string{}})
|
|
s.st.Settings.SelfEdgeID = e.ID
|
|
s.bump()
|
|
}
|
|
body := "# written by Hammerhead for the Blacktip on this machine; keep it private\nBLACKTIP_TOKEN=" + e.Token + "\n"
|
|
return os.WriteFile(filepath.Join(s.dir, "self-edge.env"), []byte(body), 0o600)
|
|
}
|
|
|
|
// selfMapping is the dashboard's own route on the self edge: https on the configured name,
|
|
// forwarded to Hammerhead's listen address through the edge's local site. It is added to that
|
|
// edge's config and never stored, so it can't be edited or deleted by mistake in the UI.
|
|
// Caller holds s.mu.
|
|
func (s *Server) selfMapping(e *model.Edge) *model.Mapping {
|
|
o := s.opts
|
|
if o.SelfDomain == "" || e.ID != s.st.Settings.SelfEdgeID {
|
|
return nil
|
|
}
|
|
host, port, err := net.SplitHostPort(o.SelfTarget)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
n, _ := strconv.Atoi(port)
|
|
for _, st := range s.st.Sites {
|
|
if st.Local && st.EdgeID == e.ID {
|
|
return &model.Mapping{ID: selfMappingID, Name: "Hammerhead dashboard", Proto: model.ProtoHTTPS, EdgeID: e.ID,
|
|
BindIP: "0.0.0.0", Port: 443, Hostname: strings.ToLower(o.SelfDomain), Enabled: true, RedirectHTTP: true,
|
|
HealthSeconds: -1, Targets: []model.Target{{SiteID: st.ID, Host: host, Port: n}}}
|
|
}
|
|
}
|
|
return nil
|
|
}
|