Refactored app code with Node

This commit is contained in:
Juan Font 2023-05-01 14:52:03 +00:00
parent 89fffeab31
commit 83b4389090
17 changed files with 416 additions and 416 deletions

90
acls.go
View file

@ -119,7 +119,7 @@ func (h *Headscale) LoadACLPolicy(path string) error {
}
func (h *Headscale) UpdateACLRules() error {
machines, err := h.ListMachines()
nodes, err := h.ListNodes()
if err != nil {
return err
}
@ -128,7 +128,7 @@ func (h *Headscale) UpdateACLRules() error {
return errEmptyPolicy
}
rules, err := generateACLRules(machines, *h.aclPolicy, h.cfg.OIDC.StripEmaildomain)
rules, err := generateACLRules(nodes, *h.aclPolicy, h.cfg.OIDC.StripEmaildomain)
if err != nil {
return err
}
@ -225,7 +225,7 @@ func expandACLPeerAddr(srcIP string) []string {
}
func generateACLRules(
machines []Machine,
nodes []Node,
aclPolicy ACLPolicy,
stripEmaildomain bool,
) ([]tailcfg.FilterRule, error) {
@ -238,7 +238,7 @@ func generateACLRules(
srcIPs := []string{}
for innerIndex, src := range acl.Sources {
srcs, err := generateACLPolicySrc(machines, aclPolicy, src, stripEmaildomain)
srcs, err := generateACLPolicySrc(nodes, aclPolicy, src, stripEmaildomain)
if err != nil {
log.Error().
Msgf("Error parsing ACL %d, Source %d", index, innerIndex)
@ -259,7 +259,7 @@ func generateACLRules(
destPorts := []tailcfg.NetPortRange{}
for innerIndex, dest := range acl.Destinations {
dests, err := generateACLPolicyDest(
machines,
nodes,
aclPolicy,
dest,
needsWildcard,
@ -291,7 +291,7 @@ func (h *Headscale) generateSSHRules() ([]*tailcfg.SSHRule, error) {
return nil, errEmptyPolicy
}
machines, err := h.ListMachines()
nodes, err := h.ListNodes()
if err != nil {
return nil, err
}
@ -339,7 +339,7 @@ func (h *Headscale) generateSSHRules() ([]*tailcfg.SSHRule, error) {
principals := make([]*tailcfg.SSHPrincipal, 0, len(sshACL.Sources))
for innerIndex, rawSrc := range sshACL.Sources {
expandedSrcs, err := expandAlias(
machines,
nodes,
*h.aclPolicy,
rawSrc,
h.cfg.OIDC.StripEmaildomain,
@ -390,16 +390,16 @@ func sshCheckAction(duration string) (*tailcfg.SSHAction, error) {
}
func generateACLPolicySrc(
machines []Machine,
nodes []Node,
aclPolicy ACLPolicy,
src string,
stripEmaildomain bool,
) ([]string, error) {
return expandAlias(machines, aclPolicy, src, stripEmaildomain)
return expandAlias(nodes, aclPolicy, src, stripEmaildomain)
}
func generateACLPolicyDest(
machines []Machine,
nodes []Node,
aclPolicy ACLPolicy,
dest string,
needsWildcard bool,
@ -449,7 +449,7 @@ func generateACLPolicyDest(
}
expanded, err := expandAlias(
machines,
nodes,
aclPolicy,
alias,
stripEmaildomain,
@ -535,7 +535,7 @@ func parseProtocol(protocol string) ([]int, bool, error) {
// - a cidr
// and transform these in IPAddresses.
func expandAlias(
machines Machines,
nodes Nodes,
aclPolicy ACLPolicy,
alias string,
stripEmailDomain bool,
@ -555,7 +555,7 @@ func expandAlias(
return ips, err
}
for _, n := range users {
nodes := filterMachinesByUser(machines, n)
nodes := filterNodesByUser(nodes, n)
for _, node := range nodes {
ips = append(ips, node.IPAddresses.ToStringSlice()...)
}
@ -566,9 +566,9 @@ func expandAlias(
if strings.HasPrefix(alias, "tag:") {
// check for forced tags
for _, machine := range machines {
if contains(machine.ForcedTags, alias) {
ips = append(ips, machine.IPAddresses.ToStringSlice()...)
for _, node := range nodes {
if contains(node.ForcedTags, alias) {
ips = append(ips, node.IPAddresses.ToStringSlice()...)
}
}
@ -590,13 +590,13 @@ func expandAlias(
}
}
// filter out machines per tag owner
// filter out nodes per tag owner
for _, user := range owners {
machines := filterMachinesByUser(machines, user)
for _, machine := range machines {
hi := machine.GetHostInfo()
nodes := filterNodesByUser(nodes, user)
for _, node := range nodes {
hi := node.GetHostInfo()
if contains(hi.RequestTags, alias) {
ips = append(ips, machine.IPAddresses.ToStringSlice()...)
ips = append(ips, node.IPAddresses.ToStringSlice()...)
}
}
}
@ -605,10 +605,10 @@ func expandAlias(
}
// if alias is a user
nodes := filterMachinesByUser(machines, alias)
nodes = excludeCorrectlyTaggedNodes(aclPolicy, nodes, alias, stripEmailDomain)
filteredNodes := filterNodesByUser(nodes, alias)
filteredNodes = excludeCorrectlyTaggedNodes(aclPolicy, filteredNodes, alias, stripEmailDomain)
for _, n := range nodes {
for _, n := range filteredNodes {
ips = append(ips, n.IPAddresses.ToStringSlice()...)
}
if len(ips) > 0 {
@ -619,17 +619,17 @@ func expandAlias(
if h, ok := aclPolicy.Hosts[alias]; ok {
log.Trace().Str("host", h.String()).Msg("expandAlias got hosts entry")
return expandAlias(machines, aclPolicy, h.String(), stripEmailDomain)
return expandAlias(filteredNodes, aclPolicy, h.String(), stripEmailDomain)
}
// if alias is an IP
if ip, err := netip.ParseAddr(alias); err == nil {
log.Trace().Str("ip", ip.String()).Msg("expandAlias got ip")
ips := []string{ip.String()}
matches := machines.FilterByIP(ip)
matches := nodes.FilterByIP(ip)
for _, machine := range matches {
ips = append(ips, machine.IPAddresses.ToStringSlice()...)
for _, node := range matches {
ips = append(ips, node.IPAddresses.ToStringSlice()...)
}
return lo.Uniq(ips), nil
@ -640,12 +640,12 @@ func expandAlias(
val := []string{cidr.String()}
// This is suboptimal and quite expensive, but if we only add the cidr, we will miss all the relevant IPv6
// addresses for the hosts that belong to tailscale. This doesnt really affect stuff like subnet routers.
for _, machine := range machines {
for _, ip := range machine.IPAddresses {
for _, node := range nodes {
for _, ip := range node.IPAddresses {
// log.Trace().
// Msgf("checking if machine ip (%s) is part of cidr (%s): %v, is single ip cidr (%v), addr: %s", ip.String(), cidr.String(), cidr.Contains(ip), cidr.IsSingleIP(), cidr.Addr().String())
// Msgf("checking if node ip (%s) is part of cidr (%s): %v, is single ip cidr (%v), addr: %s", ip.String(), cidr.String(), cidr.Contains(ip), cidr.IsSingleIP(), cidr.Addr().String())
if cidr.Contains(ip) {
val = append(val, machine.IPAddresses.ToStringSlice()...)
val = append(val, node.IPAddresses.ToStringSlice()...)
}
}
}
@ -663,11 +663,11 @@ func expandAlias(
// we assume in this function that we only have nodes from 1 user.
func excludeCorrectlyTaggedNodes(
aclPolicy ACLPolicy,
nodes []Machine,
nodes []Node,
user string,
stripEmailDomain bool,
) []Machine {
out := []Machine{}
) []Node {
out := []Node{}
tags := []string{}
for tag := range aclPolicy.TagOwners {
owners, _ := expandTagOwners(aclPolicy, user, stripEmailDomain)
@ -676,9 +676,9 @@ func excludeCorrectlyTaggedNodes(
tags = append(tags, tag)
}
}
// for each machine if tag is in tags list, don't append it.
for _, machine := range nodes {
hi := machine.GetHostInfo()
// for each node if tag is in tags list, don't append it.
for _, node := range nodes {
hi := node.GetHostInfo()
found := false
for _, t := range hi.RequestTags {
@ -688,11 +688,11 @@ func excludeCorrectlyTaggedNodes(
break
}
}
if len(machine.ForcedTags) > 0 {
if len(node.ForcedTags) > 0 {
found = true
}
if !found {
out = append(out, machine)
out = append(out, node)
}
}
@ -747,11 +747,11 @@ func expandPorts(portsStr string, needsWildcard bool) (*[]tailcfg.PortRange, err
return &ports, nil
}
func filterMachinesByUser(machines []Machine, user string) []Machine {
out := []Machine{}
for _, machine := range machines {
if machine.User.Name == user {
out = append(out, machine)
func filterNodesByUser(nodes []Node, user string) []Node {
out := []Node{}
for _, node := range nodes {
if node.User.Name == user {
out = append(out, node)
}
}

6
api.go
View file

@ -20,7 +20,7 @@ const (
RegisterMethodOIDC = "oidc"
RegisterMethodCLI = "cli"
ErrRegisterMethodCLIDoesNotSupportExpire = Error(
"machines registered with CLI does not support expire",
"node registered with CLI does not support expire",
)
)
@ -74,9 +74,9 @@ var registerWebAPITemplate = template.Must(
</head>
<body>
<h1>headscale</h1>
<h2>Machine registration</h2>
<h2>Node registration</h2>
<p>
Run the command below in the headscale server to add this machine to your network:
Run the command below in the headscale server to add this node to your network:
</p>
<pre><code>headscale nodes register --user USERNAME --key {{.Key}}</code></pre>
</body>

View file

@ -9,13 +9,13 @@ import (
func (h *Headscale) generateMapResponse(
mapRequest tailcfg.MapRequest,
machine *Machine,
node *Node,
) (*tailcfg.MapResponse, error) {
log.Trace().
Str("func", "generateMapResponse").
Str("machine", mapRequest.Hostinfo.Hostname).
Str("node", mapRequest.Hostinfo.Hostname).
Msg("Creating Map response")
node, err := h.toNode(*machine, h.cfg.BaseDomain, h.cfg.DNSConfig)
tailNode, err := h.toNode(*node, h.cfg.BaseDomain, h.cfg.DNSConfig)
if err != nil {
log.Error().
Caller().
@ -26,7 +26,7 @@ func (h *Headscale) generateMapResponse(
return nil, err
}
peers, err := h.getValidPeers(machine)
peers, err := h.getValidPeers(node)
if err != nil {
log.Error().
Caller().
@ -37,7 +37,7 @@ func (h *Headscale) generateMapResponse(
return nil, err
}
profiles := h.getMapResponseUserProfiles(*machine, peers)
profiles := h.getMapResponseUserProfiles(*node, peers)
nodePeers, err := h.toNodes(peers, h.cfg.BaseDomain, h.cfg.DNSConfig)
if err != nil {
@ -53,7 +53,7 @@ func (h *Headscale) generateMapResponse(
dnsConfig := getMapResponseDNSConfig(
h.cfg.DNSConfig,
h.cfg.BaseDomain,
*machine,
*node,
peers,
)
@ -61,7 +61,7 @@ func (h *Headscale) generateMapResponse(
resp := tailcfg.MapResponse{
KeepAlive: false,
Node: node,
Node: tailNode,
// TODO: Only send if updated
DERPMap: h.DERPMap,
@ -105,7 +105,7 @@ func (h *Headscale) generateMapResponse(
log.Trace().
Str("func", "generateMapResponse").
Str("machine", mapRequest.Hostinfo.Hostname).
Str("node", mapRequest.Hostinfo.Hostname).
// Interface("payload", resp).
Msgf("Generated map response: %s", tailMapResponseToString(resp))

54
app.go
View file

@ -211,7 +211,7 @@ func (h *Headscale) redirect(w http.ResponseWriter, req *http.Request) {
http.Redirect(w, req, target, http.StatusFound)
}
// expireEphemeralNodes deletes ephemeral machine records that have not been
// expireEphemeralNodes deletes ephemeral node records that have not been
// seen for longer than h.cfg.EphemeralNodeInactivityTimeout.
func (h *Headscale) expireEphemeralNodes(milliSeconds int64) {
ticker := time.NewTicker(time.Duration(milliSeconds) * time.Millisecond)
@ -220,12 +220,12 @@ func (h *Headscale) expireEphemeralNodes(milliSeconds int64) {
}
}
// expireExpiredMachines expires machines that have an explicit expiry set
// expireExpiredNodes expires node that have an explicit expiry set
// after that expiry time has passed.
func (h *Headscale) expireExpiredMachines(milliSeconds int64) {
func (h *Headscale) expireExpiredNodes(milliSeconds int64) {
ticker := time.NewTicker(time.Duration(milliSeconds) * time.Millisecond)
for range ticker.C {
h.expireExpiredMachinesWorker()
h.expireExpiredNodesWorker()
}
}
@ -248,32 +248,32 @@ func (h *Headscale) expireEphemeralNodesWorker() {
}
for _, user := range users {
machines, err := h.ListMachinesByUser(user.Name)
nodes, err := h.ListNodesByUser(user.Name)
if err != nil {
log.Error().
Err(err).
Str("user", user.Name).
Msg("Error listing machines in user")
Msg("Error listing nodes in user")
return
}
expiredFound := false
for _, machine := range machines {
if machine.isEphemeral() && machine.LastSeen != nil &&
for _, node := range nodes {
if node.isEphemeral() && node.LastSeen != nil &&
time.Now().
After(machine.LastSeen.Add(h.cfg.EphemeralNodeInactivityTimeout)) {
After(node.LastSeen.Add(h.cfg.EphemeralNodeInactivityTimeout)) {
expiredFound = true
log.Info().
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Msg("Ephemeral client removed from database")
err = h.db.Unscoped().Delete(machine).Error
err = h.db.Unscoped().Delete(node).Error
if err != nil {
log.Error().
Err(err).
Str("machine", machine.Hostname).
Msg("🤮 Cannot delete ephemeral machine from the database")
Str("node", node.Hostname).
Msg("Cannot delete ephemeral node from the database")
}
}
}
@ -284,7 +284,7 @@ func (h *Headscale) expireEphemeralNodesWorker() {
}
}
func (h *Headscale) expireExpiredMachinesWorker() {
func (h *Headscale) expireExpiredNodesWorker() {
users, err := h.ListUsers()
if err != nil {
log.Error().Err(err).Msg("Error listing users")
@ -293,34 +293,34 @@ func (h *Headscale) expireExpiredMachinesWorker() {
}
for _, user := range users {
machines, err := h.ListMachinesByUser(user.Name)
nodes, err := h.ListNodesByUser(user.Name)
if err != nil {
log.Error().
Err(err).
Str("user", user.Name).
Msg("Error listing machines in user")
Msg("Error listing nodes in user")
return
}
expiredFound := false
for index, machine := range machines {
if machine.isExpired() &&
machine.Expiry.After(h.getLastStateChange(user)) {
for index, node := range nodes {
if node.isExpired() &&
node.Expiry.After(h.getLastStateChange(user)) {
expiredFound = true
err := h.ExpireMachine(&machines[index])
err := h.ExpireNode(&nodes[index])
if err != nil {
log.Error().
Err(err).
Str("machine", machine.Hostname).
Str("name", machine.GivenName).
Msg("🤮 Cannot expire machine")
Str("node", node.Hostname).
Str("name", node.GivenName).
Msg("Cannot expire node")
} else {
log.Info().
Str("machine", machine.Hostname).
Str("name", machine.GivenName).
Msg("Machine successfully expired")
Str("node", node.Hostname).
Str("name", node.GivenName).
Msg("Node successfully expired")
}
}
}
@ -552,7 +552,7 @@ func (h *Headscale) Serve() error {
}
go h.expireEphemeralNodes(updateInterval)
go h.expireExpiredMachines(updateInterval)
go h.expireExpiredNodes(updateInterval)
go h.failoverSubnetRoutes(updateInterval)

22
dns.go
View file

@ -159,22 +159,22 @@ func generateIPv6DNSRootDomain(ipPrefix netip.Prefix) []dnsname.FQDN {
}
// If any nextdns DoH resolvers are present in the list of resolvers it will
// take metadata from the machine metadata and instruct tailscale to add it
// take metadata from the node metadata and instruct tailscale to add it
// to the requests. This makes it possible to identify from which device the
// requests come in the NextDNS dashboard.
//
// This will produce a resolver like:
// `https://dns.nextdns.io/<nextdns-id>?device_name=node-name&device_model=linux&device_ip=100.64.0.1`
func addNextDNSMetadata(resolvers []*dnstype.Resolver, machine Machine) {
func addNextDNSMetadata(resolvers []*dnstype.Resolver, node Node) {
for _, resolver := range resolvers {
if strings.HasPrefix(resolver.Addr, nextDNSDoHPrefix) {
attrs := url.Values{
"device_name": []string{machine.Hostname},
"device_model": []string{machine.HostInfo.OS},
"device_name": []string{node.Hostname},
"device_model": []string{node.HostInfo.OS},
}
if len(machine.IPAddresses) > 0 {
attrs.Add("device_ip", machine.IPAddresses[0].String())
if len(node.IPAddresses) > 0 {
attrs.Add("device_ip", node.IPAddresses[0].String())
}
resolver.Addr = fmt.Sprintf("%s?%s", resolver.Addr, attrs.Encode())
@ -185,8 +185,8 @@ func addNextDNSMetadata(resolvers []*dnstype.Resolver, machine Machine) {
func getMapResponseDNSConfig(
dnsConfigOrig *tailcfg.DNSConfig,
baseDomain string,
machine Machine,
peers Machines,
node Node,
peers Nodes,
) *tailcfg.DNSConfig {
var dnsConfig *tailcfg.DNSConfig = dnsConfigOrig.Clone()
if dnsConfigOrig != nil && dnsConfigOrig.Proxied { // if MagicDNS is enabled
@ -195,13 +195,13 @@ func getMapResponseDNSConfig(
dnsConfig.Domains,
fmt.Sprintf(
"%s.%s",
machine.User.Name,
node.User.Name,
baseDomain,
),
)
userSet := mapset.NewSet[User]()
userSet.Add(machine.User)
userSet.Add(node.User)
for _, p := range peers {
userSet.Add(p.User)
}
@ -213,7 +213,7 @@ func getMapResponseDNSConfig(
dnsConfig = dnsConfigOrig
}
addNextDNSMetadata(dnsConfig.Resolvers, machine)
addNextDNSMetadata(dnsConfig.Resolvers, node)
return dnsConfig
}

View file

@ -8,34 +8,34 @@ import (
const prometheusNamespace = "headscale"
var (
// This is a high cardinality metric (user x machines), we might want to make this
// This is a high cardinality metric (user x nodes), we might want to make this
// configurable/opt-in in the future.
lastStateUpdate = promauto.NewGaugeVec(prometheus.GaugeOpts{
Namespace: prometheusNamespace,
Name: "last_update_seconds",
Help: "Time stamp in unix time when a machine or headscale was updated",
}, []string{"user", "machine"})
Help: "Time stamp in unix time when a node or headscale was updated",
}, []string{"user", "nodes"})
machineRegistrations = promauto.NewCounterVec(prometheus.CounterOpts{
nodeRegistrations = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: prometheusNamespace,
Name: "machine_registrations_total",
Help: "The total amount of registered machine attempts",
Name: "node_registrations_total",
Help: "The total amount of registered node attempts",
}, []string{"action", "auth", "status", "user"})
updateRequestsFromNode = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: prometheusNamespace,
Name: "update_request_from_node_total",
Help: "The number of updates requested by a node/update function",
}, []string{"user", "machine", "state"})
}, []string{"user", "node", "state"})
updateRequestsSentToNode = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: prometheusNamespace,
Name: "update_request_sent_to_node_total",
Help: "The number of calls/messages issued on a specific nodes update channel",
}, []string{"user", "machine", "status"})
}, []string{"user", "node", "status"})
// TODO(kradalby): This is very debugging, we might want to remove it.
updateRequestsReceivedOnChannel = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: prometheusNamespace,
Name: "update_request_received_on_channel_total",
Help: "The number of update requests received on an update channel",
}, []string{"user", "machine"})
}, []string{"user", "node"})
)

64
oidc.go
View file

@ -27,8 +27,8 @@ const (
errOIDCAllowedDomains = Error("authenticated principal does not match any allowed domain")
errOIDCAllowedGroups = Error("authenticated principal is not in any allowed group")
errOIDCAllowedUsers = Error("authenticated principal does not match any allowed user")
errOIDCInvalidMachineState = Error(
"requested machine state key expired before authorisation completed",
errOIDCInvalidNodeState = Error(
"requested node state key expired before authorisation completed",
)
errOIDCNodeKeyMissing = Error("could not get node key from cache")
)
@ -181,9 +181,9 @@ var oidcCallbackTemplate = template.Must(
)
// OIDCCallback handles the callback from the OIDC endpoint
// Retrieves the nkey from the state cache and adds the machine to the users email user
// TODO: A confirmation page for new machines should be added to avoid phishing vulnerabilities
// TODO: Add groups information from OIDC tokens into machine HostInfo
// Retrieves the nkey from the state cache and adds the node to the users email user
// TODO: A confirmation page for new nodes should be added to avoid phishing vulnerabilities
// TODO: Add groups information from OIDC tokens into node HostInfo
// Listens in /oidc/callback.
func (h *Headscale) OIDCCallback(
writer http.ResponseWriter,
@ -229,13 +229,13 @@ func (h *Headscale) OIDCCallback(
return
}
nodeKey, machineExists, err := h.validateMachineForOIDCCallback(
nodeKey, nodeExists, err := h.validateNodeForOIDCCallback(
writer,
state,
claims,
idTokenExpiry,
)
if err != nil || machineExists {
if err != nil || nodeExists {
return
}
@ -244,15 +244,15 @@ func (h *Headscale) OIDCCallback(
return
}
// register the machine if it's new
log.Debug().Msg("Registering new machine after successful callback")
// register the node if it's new
log.Debug().Msg("Registering new node after successful callback")
user, err := h.findOrCreateNewUserForOIDCCallback(writer, userName)
if err != nil {
return
}
if err := h.registerMachineForOIDCCallback(writer, user, nodeKey, idTokenExpiry); err != nil {
if err := h.registerNodeForOIDCCallback(writer, user, nodeKey, idTokenExpiry); err != nil {
return
}
@ -484,21 +484,21 @@ func validateOIDCAllowedUsers(
return nil
}
// validateMachine retrieves machine information if it exist
// validateNode retrieves node information if it exist
// The error is not important, because if it does not
// exist, then this is a new machine and we will move
// exist, then this is a new node and we will move
// on to registration.
func (h *Headscale) validateMachineForOIDCCallback(
func (h *Headscale) validateNodeForOIDCCallback(
writer http.ResponseWriter,
state string,
claims *IDTokenClaims,
expiry time.Time,
) (*key.NodePublic, bool, error) {
// retrieve machinekey from state cache
// retrieve nodekey from state cache
nodeKeyIf, nodeKeyFound := h.registrationCache.Get(state)
if !nodeKeyFound {
log.Error().
Msg("requested machine state key expired before authorisation completed")
Msg("requested node state key expired before authorisation completed")
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
writer.WriteHeader(http.StatusBadRequest)
_, err := writer.Write([]byte("state has expired"))
@ -516,7 +516,7 @@ func (h *Headscale) validateMachineForOIDCCallback(
nodeKeyFromCache, nodeKeyOK := nodeKeyIf.(string)
if !nodeKeyOK {
log.Error().
Msg("requested machine state key is not a string")
Msg("requested node state key is not a string")
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
writer.WriteHeader(http.StatusBadRequest)
_, err := writer.Write([]byte("state is invalid"))
@ -527,7 +527,7 @@ func (h *Headscale) validateMachineForOIDCCallback(
Msg("Failed to write response")
}
return nil, false, errOIDCInvalidMachineState
return nil, false, errOIDCInvalidNodeState
}
err := nodeKey.UnmarshalText(
@ -551,36 +551,36 @@ func (h *Headscale) validateMachineForOIDCCallback(
return nil, false, err
}
// retrieve machine information if it exist
// retrieve node information if it exist
// The error is not important, because if it does not
// exist, then this is a new machine and we will move
// exist, then this is a new node and we will move
// on to registration.
machine, _ := h.GetMachineByNodeKey(nodeKey)
node, _ := h.GetNodeByNodeKey(nodeKey)
if machine != nil {
if node != nil {
log.Trace().
Caller().
Str("machine", machine.Hostname).
Msg("machine already registered, reauthenticating")
Str("node", node.Hostname).
Msg("node already registered, reauthenticating")
err := h.RefreshMachine(machine, expiry)
err := h.RefreshNode(node, expiry)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to refresh machine")
Msg("Failed to refresh node")
http.Error(
writer,
"Failed to refresh machine",
"Failed to refresh node",
http.StatusInternalServerError,
)
return nil, true, err
}
log.Debug().
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("expiresAt", fmt.Sprintf("%v", expiry)).
Msg("successfully refreshed machine")
Msg("successfully refreshed node")
var content bytes.Buffer
if err := oidcCallbackTemplate.Execute(&content, oidcCallbackTemplateConfig{
@ -696,13 +696,13 @@ func (h *Headscale) findOrCreateNewUserForOIDCCallback(
return user, nil
}
func (h *Headscale) registerMachineForOIDCCallback(
func (h *Headscale) registerNodeForOIDCCallback(
writer http.ResponseWriter,
user *User,
nodeKey *key.NodePublic,
expiry time.Time,
) error {
if _, err := h.RegisterMachineFromAuthCallback(
if _, err := h.RegisterNodeFromAuthCallback(
nodeKey.String(),
user.Name,
&expiry,
@ -711,10 +711,10 @@ func (h *Headscale) registerMachineForOIDCCallback(
log.Error().
Caller().
Err(err).
Msg("could not register machine")
Msg("could not register node")
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
writer.WriteHeader(http.StatusInternalServerError)
_, werr := writer.Write([]byte("could not register machine"))
_, werr := writer.Write([]byte("could not register node"))
if werr != nil {
log.Error().
Caller().

View file

@ -193,12 +193,12 @@ func (h *Headscale) checkKeyValidity(k string) (*PreAuthKey, error) {
return &pak, nil
}
machines := []Machine{}
if err := h.db.Preload("AuthKey").Where(&Machine{AuthKeyID: uint(pak.ID)}).Find(&machines).Error; err != nil {
nodes := []Node{}
if err := h.db.Preload("AuthKey").Where(&Node{AuthKeyID: uint(pak.ID)}).Find(&nodes).Error; err != nil {
return nil, err
}
if len(machines) != 0 || pak.Used {
if len(nodes) != 0 || pak.Used {
return nil, ErrSingleUseAuthKeyHasBeenUsed
}

View file

@ -102,9 +102,9 @@ func (h *Headscale) handleRegisterCommon(
isNoise bool,
) {
now := time.Now().UTC()
machine, err := h.GetMachineByAnyKey(machineKey, registerRequest.NodeKey, registerRequest.OldNodeKey)
node, err := h.GetNodeByAnyKey(machineKey, registerRequest.NodeKey, registerRequest.OldNodeKey)
if errors.Is(err, gorm.ErrRecordNotFound) {
// If the machine has AuthKey set, handle registration via PreAuthKeys
// If the node has AuthKey set, handle registration via PreAuthKeys
if registerRequest.Auth.AuthKey != "" {
h.handleAuthKeyCommon(writer, registerRequest, machineKey, isNoise)
@ -115,7 +115,7 @@ func (h *Headscale) handleRegisterCommon(
//
// TODO(juan): We could use this field to improve our protocol implementation,
// and hold the request until the client closes it, or the interactive
// login is completed (i.e., the user registers the machine).
// login is completed (i.e., the user registers the node).
// This is not implemented yet, as it is no strictly required. The only side-effect
// is that the client will hammer headscale with requests until it gets a
// successful RegisterResponse.
@ -123,19 +123,19 @@ func (h *Headscale) handleRegisterCommon(
if _, ok := h.registrationCache.Get(NodePublicKeyStripPrefix(registerRequest.NodeKey)); ok {
log.Debug().
Caller().
Str("machine", registerRequest.Hostinfo.Hostname).
Str("node", registerRequest.Hostinfo.Hostname).
Str("machine_key", machineKey.ShortString()).
Str("node_key", registerRequest.NodeKey.ShortString()).
Str("node_key_old", registerRequest.OldNodeKey.ShortString()).
Str("follow_up", registerRequest.Followup).
Bool("noise", isNoise).
Msg("Machine is waiting for interactive login")
Msg("Node is waiting for interactive login")
select {
case <-req.Context().Done():
return
case <-time.After(registrationHoldoff):
h.handleNewMachineCommon(writer, registerRequest, machineKey, isNoise)
h.handleNewNodeCommon(writer, registerRequest, machineKey, isNoise)
return
}
@ -144,13 +144,13 @@ func (h *Headscale) handleRegisterCommon(
log.Info().
Caller().
Str("machine", registerRequest.Hostinfo.Hostname).
Str("node", registerRequest.Hostinfo.Hostname).
Str("machine_key", machineKey.ShortString()).
Str("node_key", registerRequest.NodeKey.ShortString()).
Str("node_key_old", registerRequest.OldNodeKey.ShortString()).
Str("follow_up", registerRequest.Followup).
Bool("noise", isNoise).
Msg("New machine not yet in the database")
Msg("New node not yet in the database")
givenName, err := h.GenerateGivenName(
machineKey.String(),
@ -166,11 +166,11 @@ func (h *Headscale) handleRegisterCommon(
return
}
// The machine did not have a key to authenticate, which means
// The node did not have a key to authenticate, which means
// that we rely on a method that calls back some how (OpenID or CLI)
// We create the machine and then keep it around until a callback
// We create the node and then keep it around until a callback
// happens
newMachine := Machine{
newNode := Node{
MachineKey: MachinePublicKeyStripPrefix(machineKey),
Hostname: registerRequest.Hostinfo.Hostname,
GivenName: givenName,
@ -183,42 +183,42 @@ func (h *Headscale) handleRegisterCommon(
log.Trace().
Caller().
Bool("noise", isNoise).
Str("machine", registerRequest.Hostinfo.Hostname).
Str("node", registerRequest.Hostinfo.Hostname).
Time("expiry", registerRequest.Expiry).
Msg("Non-zero expiry time requested")
newMachine.Expiry = &registerRequest.Expiry
newNode.Expiry = &registerRequest.Expiry
}
h.registrationCache.Set(
newMachine.NodeKey,
newMachine,
newNode.NodeKey,
newNode,
registerCacheExpiration,
)
h.handleNewMachineCommon(writer, registerRequest, machineKey, isNoise)
h.handleNewNodeCommon(writer, registerRequest, machineKey, isNoise)
return
}
// The machine is already in the DB. This could mean one of the following:
// - The machine is authenticated and ready to /map
// The node is already in the DB. This could mean one of the following:
// - The node is authenticated and ready to /map
// - We are doing a key refresh
// - The machine is logged out (or expired) and pending to be authorized. TODO(juan): We need to keep alive the connection here
if machine != nil {
// - The node is logged out (or expired) and pending to be authorized. TODO(juan): We need to keep alive the connection here
if node != nil {
// (juan): For a while we had a bug where we were not storing the MachineKey for the nodes using the TS2021,
// due to a misunderstanding of the protocol https://github.com/juanfont/headscale/issues/1054
// So if we have a not valid MachineKey (but we were able to fetch the machine with the NodeKeys), we update it.
// So if we have a not valid MachineKey (but we were able to fetch the node with the NodeKeys), we update it.
var storedMachineKey key.MachinePublic
err = storedMachineKey.UnmarshalText(
[]byte(MachinePublicKeyEnsurePrefix(machine.MachineKey)),
[]byte(MachinePublicKeyEnsurePrefix(node.MachineKey)),
)
if err != nil || storedMachineKey.IsZero() {
machine.MachineKey = MachinePublicKeyStripPrefix(machineKey)
if err := h.db.Save(&machine).Error; err != nil {
node.MachineKey = MachinePublicKeyStripPrefix(machineKey)
if err := h.db.Save(&node).Error; err != nil {
log.Error().
Caller().
Str("func", "RegistrationHandler").
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Err(err).
Msg("Error saving machine key to database")
@ -229,34 +229,34 @@ func (h *Headscale) handleRegisterCommon(
// If the NodeKey stored in headscale is the same as the key presented in a registration
// request, then we have a node that is either:
// - Trying to log out (sending a expiry in the past)
// - A valid, registered machine, looking for /map
// - Expired machine wanting to reauthenticate
if machine.NodeKey == NodePublicKeyStripPrefix(registerRequest.NodeKey) {
// - A valid, registered node, looking for /map
// - Expired node wanting to reauthenticate
if node.NodeKey == NodePublicKeyStripPrefix(registerRequest.NodeKey) {
// The client sends an Expiry in the past if the client is requesting to expire the key (aka logout)
// https://github.com/tailscale/tailscale/blob/main/tailcfg/tailcfg.go#L648
if !registerRequest.Expiry.IsZero() &&
registerRequest.Expiry.UTC().Before(now) {
h.handleMachineLogOutCommon(writer, *machine, machineKey, isNoise)
h.handleNodeLogOutCommon(writer, *node, machineKey, isNoise)
return
}
// If machine is not expired, and it is register, we have a already accepted this machine,
// If node is not expired, and it is register, we have a already accepted this node,
// let it proceed with a valid registration
if !machine.isExpired() {
h.handleMachineValidRegistrationCommon(writer, *machine, machineKey, isNoise)
if !node.isExpired() {
h.handleNodeValidRegistrationCommon(writer, *node, machineKey, isNoise)
return
}
}
// The NodeKey we have matches OldNodeKey, which means this is a refresh after a key expiration
if machine.NodeKey == NodePublicKeyStripPrefix(registerRequest.OldNodeKey) &&
!machine.isExpired() {
h.handleMachineRefreshKeyCommon(
if node.NodeKey == NodePublicKeyStripPrefix(registerRequest.OldNodeKey) &&
!node.isExpired() {
h.handleNodeRefreshKeyCommon(
writer,
registerRequest,
*machine,
*node,
machineKey,
isNoise,
)
@ -272,20 +272,20 @@ func (h *Headscale) handleRegisterCommon(
}
}
// The machine has expired or it is logged out
h.handleMachineExpiredOrLoggedOutCommon(writer, registerRequest, *machine, machineKey, isNoise)
// The node has expired or it is logged out
h.handleNodeExpiredOrLoggedOutCommon(writer, registerRequest, *node, machineKey, isNoise)
// TODO(juan): RegisterRequest includes an Expiry time, that we could optionally use
machine.Expiry = &time.Time{}
node.Expiry = &time.Time{}
// If we are here it means the client needs to be reauthorized,
// we need to make sure the NodeKey matches the one in the request
// TODO(juan): What happens when using fast user switching between two
// headscale-managed tailnets?
machine.NodeKey = NodePublicKeyStripPrefix(registerRequest.NodeKey)
node.NodeKey = NodePublicKeyStripPrefix(registerRequest.NodeKey)
h.registrationCache.Set(
NodePublicKeyStripPrefix(registerRequest.NodeKey),
*machine,
*node,
registerCacheExpiration,
)
@ -306,7 +306,7 @@ func (h *Headscale) handleAuthKeyCommon(
) {
log.Debug().
Str("func", "handleAuthKeyCommon").
Str("machine", registerRequest.Hostinfo.Hostname).
Str("node", registerRequest.Hostinfo.Hostname).
Bool("noise", isNoise).
Msgf("Processing auth key for %s", registerRequest.Hostinfo.Hostname)
resp := tailcfg.RegisterResponse{}
@ -317,7 +317,7 @@ func (h *Headscale) handleAuthKeyCommon(
Caller().
Str("func", "handleAuthKeyCommon").
Bool("noise", isNoise).
Str("machine", registerRequest.Hostinfo.Hostname).
Str("node", registerRequest.Hostinfo.Hostname).
Err(err).
Msg("Failed authentication via AuthKey")
resp.MachineAuthorized = false
@ -328,11 +328,11 @@ func (h *Headscale) handleAuthKeyCommon(
Caller().
Str("func", "handleAuthKeyCommon").
Bool("noise", isNoise).
Str("machine", registerRequest.Hostinfo.Hostname).
Str("node", registerRequest.Hostinfo.Hostname).
Err(err).
Msg("Cannot encode message")
http.Error(writer, "Internal server error", http.StatusInternalServerError)
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.User.Name).
nodeRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.User.Name).
Inc()
return
@ -353,14 +353,14 @@ func (h *Headscale) handleAuthKeyCommon(
Caller().
Str("func", "handleAuthKeyCommon").
Bool("noise", isNoise).
Str("machine", registerRequest.Hostinfo.Hostname).
Str("node", registerRequest.Hostinfo.Hostname).
Msg("Failed authentication via AuthKey")
if pak != nil {
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.User.Name).
nodeRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.User.Name).
Inc()
} else {
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", "unknown").Inc()
nodeRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", "unknown").Inc()
}
return
@ -369,33 +369,33 @@ func (h *Headscale) handleAuthKeyCommon(
log.Debug().
Str("func", "handleAuthKeyCommon").
Bool("noise", isNoise).
Str("machine", registerRequest.Hostinfo.Hostname).
Str("node", registerRequest.Hostinfo.Hostname).
Msg("Authentication key was valid, proceeding to acquire IP addresses")
nodeKey := NodePublicKeyStripPrefix(registerRequest.NodeKey)
// retrieve machine information if it exist
// retrieve node information if it exist
// The error is not important, because if it does not
// exist, then this is a new machine and we will move
// exist, then this is a new node and we will move
// on to registration.
machine, _ := h.GetMachineByAnyKey(machineKey, registerRequest.NodeKey, registerRequest.OldNodeKey)
if machine != nil {
node, _ := h.GetNodeByAnyKey(machineKey, registerRequest.NodeKey, registerRequest.OldNodeKey)
if node != nil {
log.Trace().
Caller().
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Msg("machine was already registered before, refreshing with new auth key")
Str("node", node.Hostname).
Msg("node was already registered before, refreshing with new auth key")
machine.NodeKey = nodeKey
machine.AuthKeyID = uint(pak.ID)
err := h.RefreshMachine(machine, registerRequest.Expiry)
node.NodeKey = nodeKey
node.AuthKeyID = uint(pak.ID)
err := h.RefreshNode(node, registerRequest.Expiry)
if err != nil {
log.Error().
Caller().
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Err(err).
Msg("Failed to refresh machine")
Msg("Failed to refresh node")
return
}
@ -403,16 +403,16 @@ func (h *Headscale) handleAuthKeyCommon(
aclTags := pak.toProto().AclTags
if len(aclTags) > 0 {
// This conditional preserves the existing behaviour, although SaaS would reset the tags on auth-key login
err = h.SetTags(machine, aclTags)
err = h.SetTags(node, aclTags)
if err != nil {
log.Error().
Caller().
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Strs("aclTags", aclTags).
Err(err).
Msg("Failed to set tags after refreshing machine")
Msg("Failed to set tags after refreshing node")
return
}
@ -432,7 +432,7 @@ func (h *Headscale) handleAuthKeyCommon(
return
}
machineToRegister := Machine{
nodeToRegister := Node{
Hostname: registerRequest.Hostinfo.Hostname,
GivenName: givenName,
UserID: pak.User.ID,
@ -445,16 +445,16 @@ func (h *Headscale) handleAuthKeyCommon(
ForcedTags: pak.toProto().AclTags,
}
machine, err = h.RegisterMachine(
machineToRegister,
node, err = h.RegisterNode(
nodeToRegister,
)
if err != nil {
log.Error().
Caller().
Bool("noise", isNoise).
Err(err).
Msg("could not register machine")
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.User.Name).
Msg("could not register node")
nodeRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.User.Name).
Inc()
http.Error(writer, "Internal server error", http.StatusInternalServerError)
@ -469,7 +469,7 @@ func (h *Headscale) handleAuthKeyCommon(
Bool("noise", isNoise).
Err(err).
Msg("Failed to use pre-auth key")
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.User.Name).
nodeRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.User.Name).
Inc()
http.Error(writer, "Internal server error", http.StatusInternalServerError)
@ -488,16 +488,16 @@ func (h *Headscale) handleAuthKeyCommon(
Caller().
Bool("noise", isNoise).
Str("func", "handleAuthKeyCommon").
Str("machine", registerRequest.Hostinfo.Hostname).
Str("node", registerRequest.Hostinfo.Hostname).
Err(err).
Msg("Cannot encode message")
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.User.Name).
nodeRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.User.Name).
Inc()
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "success", pak.User.Name).
nodeRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "success", pak.User.Name).
Inc()
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
@ -513,14 +513,14 @@ func (h *Headscale) handleAuthKeyCommon(
log.Info().
Str("func", "handleAuthKeyCommon").
Bool("noise", isNoise).
Str("machine", registerRequest.Hostinfo.Hostname).
Str("ips", strings.Join(machine.IPAddresses.ToStringSlice(), ", ")).
Str("node", registerRequest.Hostinfo.Hostname).
Str("ips", strings.Join(node.IPAddresses.ToStringSlice(), ", ")).
Msg("Successfully authenticated via AuthKey")
}
// handleNewMachineCommon exposes for both legacy and Noise the functionality to get a URL
// for authorizing the machine. This url is then showed to the user by the local Tailscale client.
func (h *Headscale) handleNewMachineCommon(
// handleNewNodeCommon exposes for both legacy and Noise the functionality to get a URL
// for authorizing the node. This url is then showed to the user by the local Tailscale client.
func (h *Headscale) handleNewNodeCommon(
writer http.ResponseWriter,
registerRequest tailcfg.RegisterRequest,
machineKey key.MachinePublic,
@ -528,11 +528,11 @@ func (h *Headscale) handleNewMachineCommon(
) {
resp := tailcfg.RegisterResponse{}
// The machine registration is new, redirect the client to the registration URL
// The node registration is new, redirect the client to the registration URL
log.Debug().
Caller().
Bool("noise", isNoise).
Str("machine", registerRequest.Hostinfo.Hostname).
Str("node", registerRequest.Hostinfo.Hostname).
Msg("The node seems to be new, sending auth url")
if h.oauth2Config != nil {
@ -574,13 +574,13 @@ func (h *Headscale) handleNewMachineCommon(
Caller().
Bool("noise", isNoise).
Str("AuthURL", resp.AuthURL).
Str("machine", registerRequest.Hostinfo.Hostname).
Str("node", registerRequest.Hostinfo.Hostname).
Msg("Successfully sent auth url")
}
func (h *Headscale) handleMachineLogOutCommon(
func (h *Headscale) handleNodeLogOutCommon(
writer http.ResponseWriter,
machine Machine,
node Node,
machineKey key.MachinePublic,
isNoise bool,
) {
@ -588,17 +588,17 @@ func (h *Headscale) handleMachineLogOutCommon(
log.Info().
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Msg("Client requested logout")
err := h.ExpireMachine(&machine)
err := h.ExpireNode(&node)
if err != nil {
log.Error().
Caller().
Bool("noise", isNoise).
Str("func", "handleMachineLogOutCommon").
Str("func", "handleNodeLogOutCommon").
Err(err).
Msg("Failed to expire machine")
Msg("Failed to expire node")
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
@ -607,7 +607,7 @@ func (h *Headscale) handleMachineLogOutCommon(
resp.AuthURL = ""
resp.MachineAuthorized = false
resp.NodeKeyExpired = true
resp.User = *machine.User.toTailscaleUser()
resp.User = *node.User.toTailscaleUser()
respBody, err := h.marshalResponse(resp, machineKey, isNoise)
if err != nil {
log.Error().
@ -633,13 +633,13 @@ func (h *Headscale) handleMachineLogOutCommon(
return
}
if machine.isEphemeral() {
err = h.HardDeleteMachine(&machine)
if node.isEphemeral() {
err = h.HardDeleteNode(&node)
if err != nil {
log.Error().
Err(err).
Str("machine", machine.Hostname).
Msg("Cannot delete ephemeral machine from the database")
Str("node", node.Hostname).
Msg("Cannot delete ephemeral node from the database")
}
return
@ -648,29 +648,29 @@ func (h *Headscale) handleMachineLogOutCommon(
log.Info().
Caller().
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Msg("Successfully logged out")
}
func (h *Headscale) handleMachineValidRegistrationCommon(
func (h *Headscale) handleNodeValidRegistrationCommon(
writer http.ResponseWriter,
machine Machine,
node Node,
machineKey key.MachinePublic,
isNoise bool,
) {
resp := tailcfg.RegisterResponse{}
// The machine registration is valid, respond with redirect to /map
// The node registration is valid, respond with redirect to /map
log.Debug().
Caller().
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Msg("Client is registered and we have the current NodeKey. All clear to /map")
resp.AuthURL = ""
resp.MachineAuthorized = true
resp.User = *machine.User.toTailscaleUser()
resp.Login = *machine.User.toTailscaleLogin()
resp.User = *node.User.toTailscaleUser()
resp.Login = *node.User.toTailscaleLogin()
respBody, err := h.marshalResponse(resp, machineKey, isNoise)
if err != nil {
@ -679,13 +679,13 @@ func (h *Headscale) handleMachineValidRegistrationCommon(
Bool("noise", isNoise).
Err(err).
Msg("Cannot encode message")
machineRegistrations.WithLabelValues("update", "web", "error", machine.User.Name).
nodeRegistrations.WithLabelValues("update", "web", "error", node.User.Name).
Inc()
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
machineRegistrations.WithLabelValues("update", "web", "success", machine.User.Name).
nodeRegistrations.WithLabelValues("update", "web", "success", node.User.Name).
Inc()
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
@ -702,14 +702,14 @@ func (h *Headscale) handleMachineValidRegistrationCommon(
log.Info().
Caller().
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Msg("Machine successfully authorized")
Str("node", node.Hostname).
Msg("Node successfully authorized")
}
func (h *Headscale) handleMachineRefreshKeyCommon(
func (h *Headscale) handleNodeRefreshKeyCommon(
writer http.ResponseWriter,
registerRequest tailcfg.RegisterRequest,
machine Machine,
node Node,
machineKey key.MachinePublic,
isNoise bool,
) {
@ -718,22 +718,22 @@ func (h *Headscale) handleMachineRefreshKeyCommon(
log.Info().
Caller().
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Msg("We have the OldNodeKey in the database. This is a key refresh")
machine.NodeKey = NodePublicKeyStripPrefix(registerRequest.NodeKey)
node.NodeKey = NodePublicKeyStripPrefix(registerRequest.NodeKey)
if err := h.db.Save(&machine).Error; err != nil {
if err := h.db.Save(&node).Error; err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to update machine key in the database")
Msg("Failed to update node key in the database")
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
resp.AuthURL = ""
resp.User = *machine.User.toTailscaleUser()
resp.User = *node.User.toTailscaleUser()
respBody, err := h.marshalResponse(resp, machineKey, isNoise)
if err != nil {
log.Error().
@ -762,14 +762,14 @@ func (h *Headscale) handleMachineRefreshKeyCommon(
Bool("noise", isNoise).
Str("node_key", registerRequest.NodeKey.ShortString()).
Str("old_node_key", registerRequest.OldNodeKey.ShortString()).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Msg("Node key successfully refreshed")
}
func (h *Headscale) handleMachineExpiredOrLoggedOutCommon(
func (h *Headscale) handleNodeExpiredOrLoggedOutCommon(
writer http.ResponseWriter,
registerRequest tailcfg.RegisterRequest,
machine Machine,
node Node,
machineKey key.MachinePublic,
isNoise bool,
) {
@ -785,11 +785,11 @@ func (h *Headscale) handleMachineExpiredOrLoggedOutCommon(
log.Trace().
Caller().
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("machine_key", machineKey.ShortString()).
Str("node_key", registerRequest.NodeKey.ShortString()).
Str("node_key_old", registerRequest.OldNodeKey.ShortString()).
Msg("Machine registration has expired or logged out. Sending a auth url to register")
Msg("Node registration has expired or logged out. Sending a auth url to register")
if h.oauth2Config != nil {
resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s",
@ -808,13 +808,13 @@ func (h *Headscale) handleMachineExpiredOrLoggedOutCommon(
Bool("noise", isNoise).
Err(err).
Msg("Cannot encode message")
machineRegistrations.WithLabelValues("reauth", "web", "error", machine.User.Name).
nodeRegistrations.WithLabelValues("reauth", "web", "error", node.User.Name).
Inc()
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
machineRegistrations.WithLabelValues("reauth", "web", "success", machine.User.Name).
nodeRegistrations.WithLabelValues("reauth", "web", "success", node.User.Name).
Inc()
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
@ -834,6 +834,6 @@ func (h *Headscale) handleMachineExpiredOrLoggedOutCommon(
Str("machine_key", machineKey.ShortString()).
Str("node_key", registerRequest.NodeKey.ShortString()).
Str("node_key_old", registerRequest.OldNodeKey.ShortString()).
Str("machine", machine.Hostname).
Msg("Machine logged out. Sent AuthURL for reauthentication")
Str("node", node.Hostname).
Msg("Node logged out. Sent AuthURL for reauthentication")
}

View file

@ -16,29 +16,29 @@ const (
type contextKey string
const machineNameContextKey = contextKey("machineName")
const nodeNameContextKey = contextKey("machineName")
// handlePollCommon is the common code for the legacy and Noise protocols to
// managed the poll loop.
func (h *Headscale) handlePollCommon(
writer http.ResponseWriter,
ctx context.Context,
machine *Machine,
node *Node,
mapRequest tailcfg.MapRequest,
isNoise bool,
) {
machine.Hostname = mapRequest.Hostinfo.Hostname
machine.HostInfo = HostInfo(*mapRequest.Hostinfo)
machine.DiscoKey = DiscoPublicKeyStripPrefix(mapRequest.DiscoKey)
node.Hostname = mapRequest.Hostinfo.Hostname
node.HostInfo = HostInfo(*mapRequest.Hostinfo)
node.DiscoKey = DiscoPublicKeyStripPrefix(mapRequest.DiscoKey)
now := time.Now().UTC()
err := h.processMachineRoutes(machine)
err := h.processNodeRoutes(node)
if err != nil {
log.Error().
Caller().
Err(err).
Str("machine", machine.Hostname).
Msg("Error processing machine routes")
Str("node", node.Hostname).
Msg("Error processing node routes")
}
// update ACLRules with peer informations (to update server tags if necessary)
@ -48,17 +48,17 @@ func (h *Headscale) handlePollCommon(
log.Error().
Caller().
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Err(err)
}
// update routes with peer information
err = h.EnableAutoApprovedRoutes(machine)
err = h.EnableAutoApprovedRoutes(node)
if err != nil {
log.Error().
Caller().
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Err(err).
Msg("Error running auto approved routes")
}
@ -73,32 +73,32 @@ func (h *Headscale) handlePollCommon(
// The intended use is for clients to discover the DERP map at start-up
// before their first real endpoint update.
if !mapRequest.ReadOnly {
machine.Endpoints = mapRequest.Endpoints
machine.LastSeen = &now
node.Endpoints = mapRequest.Endpoints
node.LastSeen = &now
}
if err := h.db.Updates(machine).Error; err != nil {
if err := h.db.Updates(node).Error; err != nil {
if err != nil {
log.Error().
Str("handler", "PollNetMap").
Bool("noise", isNoise).
Str("node_key", machine.NodeKey).
Str("machine", machine.Hostname).
Str("node_key", node.NodeKey).
Str("node", node.Hostname).
Err(err).
Msg("Failed to persist/update machine in the database")
Msg("Failed to persist/update node in the database")
http.Error(writer, "", http.StatusInternalServerError)
return
}
}
mapResp, err := h.getMapResponseData(mapRequest, machine, isNoise)
mapResp, err := h.getMapResponseData(mapRequest, node, isNoise)
if err != nil {
log.Error().
Str("handler", "PollNetMap").
Bool("noise", isNoise).
Str("node_key", machine.NodeKey).
Str("machine", machine.Hostname).
Str("node_key", node.NodeKey).
Str("node", node.Hostname).
Err(err).
Msg("Failed to get Map response")
http.Error(writer, "", http.StatusInternalServerError)
@ -114,7 +114,7 @@ func (h *Headscale) handlePollCommon(
log.Debug().
Str("handler", "PollNetMap").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Bool("readOnly", mapRequest.ReadOnly).
Bool("omitPeers", mapRequest.OmitPeers).
Bool("stream", mapRequest.Stream).
@ -124,7 +124,7 @@ func (h *Headscale) handlePollCommon(
log.Info().
Str("handler", "PollNetMap").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Msg("Client is starting up. Probably interested in a DERP map")
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
@ -155,14 +155,14 @@ func (h *Headscale) handlePollCommon(
log.Trace().
Caller().
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Msg("Loading or creating update channel")
const chanSize = 8
updateChan := make(chan struct{}, chanSize)
pollDataChan := make(chan []byte, chanSize)
defer closeChanWithLog(pollDataChan, machine.Hostname, "pollDataChan")
defer closeChanWithLog(pollDataChan, node.Hostname, "pollDataChan")
keepAliveChan := make(chan []byte)
@ -170,7 +170,7 @@ func (h *Headscale) handlePollCommon(
log.Info().
Str("handler", "PollNetMap").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Msg("Client sent endpoint update and is ok with a response without peer list")
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
@ -183,7 +183,7 @@ func (h *Headscale) handlePollCommon(
}
// It sounds like we should update the nodes when we have received a endpoint update
// even tho the comments in the tailscale code dont explicitly say so.
updateRequestsFromNode.WithLabelValues(machine.User.Name, machine.Hostname, "endpoint-update").
updateRequestsFromNode.WithLabelValues(node.User.Name, node.Hostname, "endpoint-update").
Inc()
updateChan <- struct{}{}
@ -192,7 +192,7 @@ func (h *Headscale) handlePollCommon(
log.Warn().
Str("handler", "PollNetMap").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Msg("Ignoring request, don't know how to handle it")
http.Error(writer, "", http.StatusBadRequest)
@ -202,28 +202,28 @@ func (h *Headscale) handlePollCommon(
log.Info().
Str("handler", "PollNetMap").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Msg("Client is ready to access the tailnet")
log.Info().
Str("handler", "PollNetMap").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Msg("Sending initial map")
pollDataChan <- mapResp
log.Info().
Str("handler", "PollNetMap").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Msg("Notifying peers")
updateRequestsFromNode.WithLabelValues(machine.User.Name, machine.Hostname, "full-update").
updateRequestsFromNode.WithLabelValues(node.User.Name, node.Hostname, "full-update").
Inc()
updateChan <- struct{}{}
h.pollNetMapStream(
writer,
ctx,
machine,
node,
mapRequest,
pollDataChan,
keepAliveChan,
@ -234,7 +234,7 @@ func (h *Headscale) handlePollCommon(
log.Trace().
Str("handler", "PollNetMap").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Msg("Finished stream, closing PollNetMap session")
}
@ -243,7 +243,7 @@ func (h *Headscale) handlePollCommon(
func (h *Headscale) pollNetMapStream(
writer http.ResponseWriter,
ctxReq context.Context,
machine *Machine,
node *Node,
mapRequest tailcfg.MapRequest,
pollDataChan chan []byte,
keepAliveChan chan []byte,
@ -253,7 +253,7 @@ func (h *Headscale) pollNetMapStream(
h.pollNetMapStreamWG.Add(1)
defer h.pollNetMapStreamWG.Done()
ctx := context.WithValue(ctxReq, machineNameContextKey, machine.Hostname)
ctx := context.WithValue(ctxReq, nodeNameContextKey, node.Hostname)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
@ -263,20 +263,20 @@ func (h *Headscale) pollNetMapStream(
updateChan,
keepAliveChan,
mapRequest,
machine,
node,
isNoise,
)
log.Trace().
Str("handler", "pollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Msg("Waiting for data to stream...")
log.Trace().
Str("handler", "pollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Msgf("pollData is %#v, keepAliveChan is %#v, updateChan is %#v", pollDataChan, keepAliveChan, updateChan)
for {
@ -285,7 +285,7 @@ func (h *Headscale) pollNetMapStream(
log.Trace().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "pollData").
Int("bytes", len(data)).
Msg("Sending data received via pollData channel")
@ -294,7 +294,7 @@ func (h *Headscale) pollNetMapStream(
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "pollData").
Err(err).
Msg("Cannot write data")
@ -308,7 +308,7 @@ func (h *Headscale) pollNetMapStream(
Caller().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "pollData").
Msg("Cannot cast writer to http.Flusher")
} else {
@ -318,43 +318,43 @@ func (h *Headscale) pollNetMapStream(
log.Trace().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "pollData").
Int("bytes", len(data)).
Msg("Data from pollData channel written successfully")
// TODO(kradalby): Abstract away all the database calls, this can cause race conditions
// when an outdated machine object is kept alive, e.g. db is update from
// when an outdated node object is kept alive, e.g. db is update from
// command line, but then overwritten.
err = h.UpdateMachineFromDatabase(machine)
err = h.UpdateNodeFromDatabase(node)
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "pollData").
Err(err).
Msg("Cannot update machine from database")
Msg("Cannot update node from database")
// client has been removed from database
// since the stream opened, terminate connection.
return
}
now := time.Now().UTC()
machine.LastSeen = &now
node.LastSeen = &now
lastStateUpdate.WithLabelValues(machine.User.Name, machine.Hostname).
lastStateUpdate.WithLabelValues(node.User.Name, node.Hostname).
Set(float64(now.Unix()))
machine.LastSuccessfulUpdate = &now
node.LastSuccessfulUpdate = &now
err = h.TouchMachine(machine)
err = h.TouchNode(node)
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "pollData").
Err(err).
Msg("Cannot update machine LastSuccessfulUpdate")
Msg("Cannot update node LastSuccessfulUpdate")
return
}
@ -362,15 +362,15 @@ func (h *Headscale) pollNetMapStream(
log.Trace().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "pollData").
Int("bytes", len(data)).
Msg("Machine entry in database updated successfully after sending data")
Msg("Node entry in database updated successfully after sending data")
case data := <-keepAliveChan:
log.Trace().
Str("handler", "PollNetMapStream").
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "keepAlive").
Int("bytes", len(data)).
Msg("Sending keep alive message")
@ -379,7 +379,7 @@ func (h *Headscale) pollNetMapStream(
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "keepAlive").
Err(err).
Msg("Cannot write keep alive message")
@ -392,7 +392,7 @@ func (h *Headscale) pollNetMapStream(
Caller().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "keepAlive").
Msg("Cannot cast writer to http.Flusher")
} else {
@ -402,38 +402,38 @@ func (h *Headscale) pollNetMapStream(
log.Trace().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "keepAlive").
Int("bytes", len(data)).
Msg("Keep alive sent successfully")
// TODO(kradalby): Abstract away all the database calls, this can cause race conditions
// when an outdated machine object is kept alive, e.g. db is update from
// when an outdated node object is kept alive, e.g. db is update from
// command line, but then overwritten.
err = h.UpdateMachineFromDatabase(machine)
err = h.UpdateNodeFromDatabase(node)
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "keepAlive").
Err(err).
Msg("Cannot update machine from database")
Msg("Cannot update node from database")
// client has been removed from database
// since the stream opened, terminate connection.
return
}
now := time.Now().UTC()
machine.LastSeen = &now
err = h.TouchMachine(machine)
node.LastSeen = &now
err = h.TouchNode(node)
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "keepAlive").
Err(err).
Msg("Cannot update machine LastSeen")
Msg("Cannot update node LastSeen")
return
}
@ -441,39 +441,39 @@ func (h *Headscale) pollNetMapStream(
log.Trace().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "keepAlive").
Int("bytes", len(data)).
Msg("Machine updated successfully after sending keep alive")
Msg("Node updated successfully after sending keep alive")
case <-updateChan:
log.Trace().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "update").
Msg("Received a request for update")
updateRequestsReceivedOnChannel.WithLabelValues(machine.User.Name, machine.Hostname).
updateRequestsReceivedOnChannel.WithLabelValues(node.User.Name, node.Hostname).
Inc()
if h.isOutdated(machine) {
if h.isOutdated(node) {
var lastUpdate time.Time
if machine.LastSuccessfulUpdate != nil {
lastUpdate = *machine.LastSuccessfulUpdate
if node.LastSuccessfulUpdate != nil {
lastUpdate = *node.LastSuccessfulUpdate
}
log.Debug().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Time("last_successful_update", lastUpdate).
Time("last_state_change", h.getLastStateChange(machine.User)).
Msgf("There has been updates since the last successful update to %s", machine.Hostname)
data, err := h.getMapResponseData(mapRequest, machine, isNoise)
Time("last_state_change", h.getLastStateChange(node.User)).
Msgf("There has been updates since the last successful update to %s", node.Hostname)
data, err := h.getMapResponseData(mapRequest, node, isNoise)
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "update").
Err(err).
Msg("Could not get the map update")
@ -485,11 +485,11 @@ func (h *Headscale) pollNetMapStream(
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "update").
Err(err).
Msg("Could not write the map response")
updateRequestsSentToNode.WithLabelValues(machine.User.Name, machine.Hostname, "failed").
updateRequestsSentToNode.WithLabelValues(node.User.Name, node.Hostname, "failed").
Inc()
return
@ -501,7 +501,7 @@ func (h *Headscale) pollNetMapStream(
Caller().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "update").
Msg("Cannot cast writer to http.Flusher")
} else {
@ -511,10 +511,10 @@ func (h *Headscale) pollNetMapStream(
log.Trace().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "update").
Msg("Updated Map has been sent")
updateRequestsSentToNode.WithLabelValues(machine.User.Name, machine.Hostname, "success").
updateRequestsSentToNode.WithLabelValues(node.User.Name, node.Hostname, "success").
Inc()
// Keep track of the last successful update,
@ -522,17 +522,17 @@ func (h *Headscale) pollNetMapStream(
// is not picked up by a client and we use this
// to determine if we should "force" an update.
// TODO(kradalby): Abstract away all the database calls, this can cause race conditions
// when an outdated machine object is kept alive, e.g. db is update from
// when an outdated node object is kept alive, e.g. db is update from
// command line, but then overwritten.
err = h.UpdateMachineFromDatabase(machine)
err = h.UpdateNodeFromDatabase(node)
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "update").
Err(err).
Msg("Cannot update machine from database")
Msg("Cannot update node from database")
// client has been removed from database
// since the stream opened, terminate connection.
@ -540,69 +540,69 @@ func (h *Headscale) pollNetMapStream(
}
now := time.Now().UTC()
lastStateUpdate.WithLabelValues(machine.User.Name, machine.Hostname).
lastStateUpdate.WithLabelValues(node.User.Name, node.Hostname).
Set(float64(now.Unix()))
machine.LastSuccessfulUpdate = &now
node.LastSuccessfulUpdate = &now
err = h.TouchMachine(machine)
err = h.TouchNode(node)
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "update").
Err(err).
Msg("Cannot update machine LastSuccessfulUpdate")
Msg("Cannot update node LastSuccessfulUpdate")
return
}
} else {
var lastUpdate time.Time
if machine.LastSuccessfulUpdate != nil {
lastUpdate = *machine.LastSuccessfulUpdate
if node.LastSuccessfulUpdate != nil {
lastUpdate = *node.LastSuccessfulUpdate
}
log.Trace().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Time("last_successful_update", lastUpdate).
Time("last_state_change", h.getLastStateChange(machine.User)).
Msgf("%s is up to date", machine.Hostname)
Time("last_state_change", h.getLastStateChange(node.User)).
Msgf("%s is up to date", node.Hostname)
}
case <-ctx.Done():
log.Info().
Str("handler", "PollNetMapStream").
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Msg("The client has closed the connection")
// TODO: Abstract away all the database calls, this can cause race conditions
// when an outdated machine object is kept alive, e.g. db is update from
// when an outdated node object is kept alive, e.g. db is update from
// command line, but then overwritten.
err := h.UpdateMachineFromDatabase(machine)
err := h.UpdateNodeFromDatabase(node)
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "Done").
Err(err).
Msg("Cannot update machine from database")
Msg("Cannot update node from database")
// client has been removed from database
// since the stream opened, terminate connection.
return
}
now := time.Now().UTC()
machine.LastSeen = &now
err = h.TouchMachine(machine)
node.LastSeen = &now
err = h.TouchNode(node)
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Str("channel", "Done").
Err(err).
Msg("Cannot update machine LastSeen")
Msg("Cannot update node LastSeen")
}
// The connection has been closed, so we can stop polling.
@ -612,7 +612,7 @@ func (h *Headscale) pollNetMapStream(
log.Info().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Msg("The long-poll handler is shutting down")
return
@ -625,7 +625,7 @@ func (h *Headscale) scheduledPollWorker(
updateChan chan struct{},
keepAliveChan chan []byte,
mapRequest tailcfg.MapRequest,
machine *Machine,
node *Node,
isNoise bool,
) {
keepAliveTicker := time.NewTicker(keepAliveInterval)
@ -633,12 +633,12 @@ func (h *Headscale) scheduledPollWorker(
defer closeChanWithLog(
updateChan,
fmt.Sprint(ctx.Value(machineNameContextKey)),
fmt.Sprint(ctx.Value(nodeNameContextKey)),
"updateChan",
)
defer closeChanWithLog(
keepAliveChan,
fmt.Sprint(ctx.Value(machineNameContextKey)),
fmt.Sprint(ctx.Value(nodeNameContextKey)),
"keepAliveChan",
)
@ -648,7 +648,7 @@ func (h *Headscale) scheduledPollWorker(
return
case <-keepAliveTicker.C:
data, err := h.getMapKeepAliveResponseData(mapRequest, machine, isNoise)
data, err := h.getMapKeepAliveResponseData(mapRequest, node, isNoise)
if err != nil {
log.Error().
Str("func", "keepAlive").
@ -661,7 +661,7 @@ func (h *Headscale) scheduledPollWorker(
log.Debug().
Str("func", "keepAlive").
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Bool("noise", isNoise).
Msg("Sending keepalive")
select {
@ -673,10 +673,10 @@ func (h *Headscale) scheduledPollWorker(
case <-updateCheckerTicker.C:
log.Debug().
Str("func", "scheduledPollWorker").
Str("machine", machine.Hostname).
Str("node", node.Hostname).
Bool("noise", isNoise).
Msg("Sending update request")
updateRequestsFromNode.WithLabelValues(machine.User.Name, machine.Hostname, "scheduled-update").
updateRequestsFromNode.WithLabelValues(node.User.Name, node.Hostname, "scheduled-update").
Inc()
select {
case updateChan <- struct{}{}:
@ -687,10 +687,10 @@ func (h *Headscale) scheduledPollWorker(
}
}
func closeChanWithLog[C chan []byte | chan struct{}](channel C, machine, name string) {
func closeChanWithLog[C chan []byte | chan struct{}](channel C, node, name string) {
log.Trace().
Str("handler", "PollNetMap").
Str("machine", machine).
Str("node", node).
Str("channel", "Done").
Msg(fmt.Sprintf("Closing %s channel", name))

View file

@ -14,10 +14,10 @@ import (
func (h *Headscale) getMapResponseData(
mapRequest tailcfg.MapRequest,
machine *Machine,
node *Node,
isNoise bool,
) ([]byte, error) {
mapResponse, err := h.generateMapResponse(mapRequest, machine)
mapResponse, err := h.generateMapResponse(mapRequest, node)
if err != nil {
return nil, err
}
@ -27,7 +27,7 @@ func (h *Headscale) getMapResponseData(
}
var machineKey key.MachinePublic
err = machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machine.MachineKey)))
err = machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(node.MachineKey)))
if err != nil {
log.Error().
Caller().
@ -42,7 +42,7 @@ func (h *Headscale) getMapResponseData(
func (h *Headscale) getMapKeepAliveResponseData(
mapRequest tailcfg.MapRequest,
machine *Machine,
node *Node,
isNoise bool,
) ([]byte, error) {
keepAliveResponse := tailcfg.MapResponse{
@ -54,7 +54,7 @@ func (h *Headscale) getMapKeepAliveResponseData(
}
var machineKey key.MachinePublic
err := machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machine.MachineKey)))
err := machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(node.MachineKey)))
if err != nil {
log.Error().
Caller().

View file

@ -12,7 +12,7 @@ import (
"tailscale.com/types/key"
)
// RegistrationHandler handles the actual registration process of a machine
// RegistrationHandler handles the actual registration process of a node
// Endpoint /machine/:mkey.
func (h *Headscale) RegistrationHandler(
writer http.ResponseWriter,
@ -38,7 +38,7 @@ func (h *Headscale) RegistrationHandler(
Caller().
Err(err).
Msg("Cannot parse machine key")
machineRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc()
nodeRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc()
http.Error(writer, "Cannot parse machine key", http.StatusBadRequest)
return
@ -50,7 +50,7 @@ func (h *Headscale) RegistrationHandler(
Caller().
Err(err).
Msg("Cannot decode message")
machineRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc()
nodeRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc()
http.Error(writer, "Cannot decode message", http.StatusBadRequest)
return

View file

@ -67,12 +67,12 @@ func (h *Headscale) PollNetMapHandler(
return
}
machine, err := h.GetMachineByMachineKey(machineKey)
node, err := h.GetNodeByMachineKey(machineKey)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
log.Warn().
Str("handler", "PollNetMap").
Msgf("Ignoring request, cannot find machine with key %s", machineKey.String())
Msgf("Ignoring request, cannot find node with mkey %s", machineKey.String())
http.Error(writer, "", http.StatusUnauthorized)
@ -80,7 +80,7 @@ func (h *Headscale) PollNetMapHandler(
}
log.Error().
Str("handler", "PollNetMap").
Msgf("Failed to fetch machine from the database with Machine key: %s", machineKey.String())
Msgf("Failed to fetch node from the database with Machine key: %s", machineKey.String())
http.Error(writer, "", http.StatusInternalServerError)
return
@ -89,8 +89,8 @@ func (h *Headscale) PollNetMapHandler(
log.Trace().
Str("handler", "PollNetMap").
Str("id", machineKeyStr).
Str("machine", machine.Hostname).
Msg("A machine is entering polling via the legacy protocol")
Str("machine", node.Hostname).
Msg("A node is entering polling via the legacy protocol")
h.handlePollCommon(writer, req.Context(), machine, mapRequest, false)
h.handlePollCommon(writer, req.Context(), node, mapRequest, false)
}

View file

@ -9,7 +9,7 @@ import (
"tailscale.com/tailcfg"
)
// // NoiseRegistrationHandler handles the actual registration process of a machine.
// NoiseRegistrationHandler handles the actual registration process of a node.
func (t *ts2021App) NoiseRegistrationHandler(
writer http.ResponseWriter,
req *http.Request,
@ -27,7 +27,7 @@ func (t *ts2021App) NoiseRegistrationHandler(
Caller().
Err(err).
Msg("Cannot parse RegisterRequest")
machineRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc()
nodeRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc()
http.Error(writer, "Internal error", http.StatusInternalServerError)
return

View file

@ -41,27 +41,27 @@ func (t *ts2021App) NoisePollNetMapHandler(
return
}
machine, err := t.headscale.GetMachineByAnyKey(t.conn.Peer(), mapRequest.NodeKey, key.NodePublic{})
node, err := t.headscale.GetNodeByAnyKey(t.conn.Peer(), mapRequest.NodeKey, key.NodePublic{})
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
log.Warn().
Str("handler", "NoisePollNetMap").
Msgf("Ignoring request, cannot find machine with key %s", mapRequest.NodeKey.String())
Msgf("Ignoring request, cannot find node with key %s", mapRequest.NodeKey.String())
http.Error(writer, "Internal error", http.StatusNotFound)
return
}
log.Error().
Str("handler", "NoisePollNetMap").
Msgf("Failed to fetch machine from the database with node key: %s", mapRequest.NodeKey.String())
Msgf("Failed to fetch node from the database with node key: %s", mapRequest.NodeKey.String())
http.Error(writer, "Internal error", http.StatusInternalServerError)
return
}
log.Debug().
Str("handler", "NoisePollNetMap").
Str("machine", machine.Hostname).
Msg("A machine is entering polling via the Noise protocol")
Str("node", node.Hostname).
Msg("A node is entering polling via the Noise protocol")
t.headscale.handlePollCommon(writer, req.Context(), machine, mapRequest, true)
t.headscale.handlePollCommon(writer, req.Context(), node, mapRequest, true)
}

View file

@ -32,7 +32,7 @@ var invalidCharsInUserRegex = regexp.MustCompile("[^a-z0-9-.]+")
// User is the way Headscale implements the concept of users in Tailscale
//
// At the end of the day, users in Tailscale are some kind of 'bubbles' or users
// that contain our machines.
// that contain our nodes.
type User struct {
gorm.Model
Name string `gorm:"unique"`
@ -63,18 +63,18 @@ func (h *Headscale) CreateUser(name string) (*User, error) {
}
// DestroyUser destroys a User. Returns error if the User does
// not exist or if there are machines associated with it.
// not exist or if there are nodes associated with it.
func (h *Headscale) DestroyUser(name string) error {
user, err := h.GetUser(name)
if err != nil {
return ErrUserNotFound
}
machines, err := h.ListMachinesByUser(name)
nodes, err := h.ListNodesByUser(name)
if err != nil {
return err
}
if len(machines) > 0 {
if len(nodes) > 0 {
return ErrUserStillHasNodes
}
@ -148,8 +148,8 @@ func (h *Headscale) ListUsers() ([]User, error) {
return users, nil
}
// ListMachinesByUser gets all the nodes in a given user.
func (h *Headscale) ListMachinesByUser(name string) ([]Machine, error) {
// ListNodesByUser gets all the nodes in a given user.
func (h *Headscale) ListNodesByUser(name string) ([]Node, error) {
err := CheckForFQDNRules(name)
if err != nil {
return nil, err
@ -159,16 +159,16 @@ func (h *Headscale) ListMachinesByUser(name string) ([]Machine, error) {
return nil, err
}
machines := []Machine{}
if err := h.db.Preload("AuthKey").Preload("AuthKey.User").Preload("User").Where(&Machine{UserID: user.ID}).Find(&machines).Error; err != nil {
nodes := []Node{}
if err := h.db.Preload("AuthKey").Preload("AuthKey.User").Preload("User").Where(&Node{UserID: user.ID}).Find(&nodes).Error; err != nil {
return nil, err
}
return machines, nil
return nodes, nil
}
// SetMachineUser assigns a Machine to a user.
func (h *Headscale) SetMachineUser(machine *Machine, username string) error {
// SetNodeUser assigns a Node to a user.
func (h *Headscale) SetNodeUser(node *Node, username string) error {
err := CheckForFQDNRules(username)
if err != nil {
return err
@ -177,8 +177,8 @@ func (h *Headscale) SetMachineUser(machine *Machine, username string) error {
if err != nil {
return err
}
machine.User = *user
if result := h.db.Save(&machine); result.Error != nil {
node.User = *user
if result := h.db.Save(&node); result.Error != nil {
return result.Error
}
@ -212,11 +212,11 @@ func (n *User) toTailscaleLogin() *tailcfg.Login {
}
func (h *Headscale) getMapResponseUserProfiles(
machine Machine,
peers Machines,
node Node,
peers Nodes,
) []tailcfg.UserProfile {
userMap := make(map[string]User)
userMap[machine.User.Name] = machine.User
userMap[node.User.Name] = node.User
for _, peer := range peers {
userMap[peer.User.Name] = peer.User // not worth checking if already is there
}

View file

@ -139,8 +139,8 @@ func decode(
return nil
}
func (h *Headscale) getAvailableIPs() (MachineAddresses, error) {
var ips MachineAddresses
func (h *Headscale) getAvailableIPs() (NodeAddresses, error) {
var ips NodeAddresses
var err error
ipPrefixes := h.cfg.IPPrefixes
for _, ipPrefix := range ipPrefixes {
@ -201,12 +201,12 @@ func (h *Headscale) getUsedIPs() (*netipx.IPSet, error) {
// but this was quick to get running and it should be enough
// to begin experimenting with a dual stack tailnet.
var addressesSlices []string
h.db.Model(&Machine{}).Pluck("ip_addresses", &addressesSlices)
h.db.Model(&Node{}).Pluck("ip_addresses", &addressesSlices)
var ips netipx.IPSetBuilder
for _, slice := range addressesSlices {
var machineAddresses MachineAddresses
err := machineAddresses.Scan(slice)
var nodeAddresses NodeAddresses
err := nodeAddresses.Scan(slice)
if err != nil {
return &netipx.IPSet{}, fmt.Errorf(
"failed to read ip from database: %w",
@ -214,7 +214,7 @@ func (h *Headscale) getUsedIPs() (*netipx.IPSet, error) {
)
}
for _, ip := range machineAddresses {
for _, ip := range nodeAddresses {
ips.Add(ip)
}
}