mirror of
https://github.com/juanfont/headscale.git
synced 2026-10-26 00:06:53 +09:00
hscontrol: remove the proto, gRPC and grpc-gateway stack
With the v1 API served by Huma and the CLI on the HTTP client, nothing uses the gRPC service or its generated code. Delete proto/, the generated gen/go and gen/openapiv2, grpcv1.go, the buf config, the proto .Proto() helpers and gRPC config, and the proto build tooling from the flake and CI.
This commit is contained in:
1 parent
8efa5ad1fe
commit
560b6d81ad
45 files changed
+17
-13097
No files matched your search
@@ -3,10 +3,8 @@ package types
|
||||
import (
|
||||
"time"
|
||||
|
||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
|
||||
"github.com/rs/zerolog"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// NewAPIKeyPrefixLength is the length of the prefix for new API keys.
|
||||
@@ -24,28 +22,6 @@ type APIKey struct {
|
||||
LastSeen *time.Time
|
||||
}
|
||||
|
||||
func (key *APIKey) Proto() *v1.ApiKey {
|
||||
protoKey := v1.ApiKey{
|
||||
Id: key.ID,
|
||||
}
|
||||
|
||||
protoKey.Prefix = key.maskedPrefix()
|
||||
|
||||
if key.Expiration != nil {
|
||||
protoKey.Expiration = timestamppb.New(*key.Expiration)
|
||||
}
|
||||
|
||||
if key.CreatedAt != nil {
|
||||
protoKey.CreatedAt = timestamppb.New(*key.CreatedAt)
|
||||
}
|
||||
|
||||
if key.LastSeen != nil {
|
||||
protoKey.LastSeen = timestamppb.New(*key.LastSeen)
|
||||
}
|
||||
|
||||
return &protoKey
|
||||
}
|
||||
|
||||
// maskedPrefix returns the API key prefix in masked format for safe logging.
|
||||
// SECURITY: Never log the full key or hash, only the masked prefix.
|
||||
func (k *APIKey) maskedPrefix() string {
|
||||
|
||||
@@ -98,8 +98,6 @@ type Config struct {
|
||||
ServerURL string
|
||||
Addr string
|
||||
MetricsAddr string
|
||||
GRPCAddr string
|
||||
GRPCAllowInsecure bool
|
||||
TrustedProxies []netip.Prefix
|
||||
Node NodeConfig
|
||||
PrefixV4 *netip.Prefix
|
||||
@@ -418,9 +416,6 @@ func LoadConfig(path string, isFile bool) error {
|
||||
viper.SetDefault("unix_socket", "/var/run/headscale/headscale.sock")
|
||||
viper.SetDefault("unix_socket_permission", "0o770")
|
||||
|
||||
viper.SetDefault("grpc_listen_addr", ":50443")
|
||||
viper.SetDefault("grpc_allow_insecure", false)
|
||||
|
||||
viper.SetDefault("cli.timeout", "5s")
|
||||
viper.SetDefault("cli.insecure", false)
|
||||
|
||||
@@ -1182,8 +1177,6 @@ func LoadServerConfig() (*Config, error) {
|
||||
ServerURL: serverURL,
|
||||
Addr: viper.GetString("listen_addr"),
|
||||
MetricsAddr: viper.GetString("metrics_listen_addr"),
|
||||
GRPCAddr: viper.GetString("grpc_listen_addr"),
|
||||
GRPCAllowInsecure: viper.GetBool("grpc_allow_insecure"),
|
||||
TrustedProxies: trusted,
|
||||
DisableUpdateCheck: false,
|
||||
|
||||
|
||||
+3
-78
@@ -10,13 +10,11 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||
"github.com/juanfont/headscale/hscontrol/policy/matcher"
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
|
||||
"github.com/rs/zerolog"
|
||||
"go4.org/netipx"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
@@ -483,55 +481,6 @@ func (nodes Nodes) ContainsNodeKey(nodeKey key.NodePublic) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (node *Node) Proto() *v1.Node {
|
||||
nodeProto := &v1.Node{
|
||||
Id: uint64(node.ID),
|
||||
MachineKey: node.MachineKey.String(),
|
||||
|
||||
NodeKey: node.NodeKey.String(),
|
||||
DiscoKey: node.DiscoKey.String(),
|
||||
|
||||
// TODO(kradalby): replace list with v4, v6 field?
|
||||
IpAddresses: node.IPsAsString(),
|
||||
Name: node.Hostname,
|
||||
GivenName: node.GivenName,
|
||||
User: nil, // Will be set below based on node type
|
||||
Tags: node.Tags,
|
||||
Online: node.IsOnline != nil && *node.IsOnline,
|
||||
|
||||
// Only ApprovedRoutes and AvailableRoutes is set here. SubnetRoutes has
|
||||
// to be populated manually with PrimaryRoute, to ensure it includes the
|
||||
// routes that are actively served from the node.
|
||||
ApprovedRoutes: util.PrefixesToString(node.ApprovedRoutes),
|
||||
AvailableRoutes: util.PrefixesToString(node.AnnouncedRoutes()),
|
||||
|
||||
RegisterMethod: node.RegisterMethodToV1Enum(),
|
||||
|
||||
CreatedAt: timestamppb.New(node.CreatedAt),
|
||||
}
|
||||
|
||||
// Set User field based on node ownership
|
||||
// Note: User will be set to [TaggedDevices] in the gRPC layer (grpcv1.go)
|
||||
// for proper [tailcfg.MapResponse] formatting
|
||||
if node.User != nil {
|
||||
nodeProto.User = node.User.Proto()
|
||||
}
|
||||
|
||||
if node.AuthKey != nil {
|
||||
nodeProto.PreAuthKey = node.AuthKey.Proto()
|
||||
}
|
||||
|
||||
if node.LastSeen != nil {
|
||||
nodeProto.LastSeen = timestamppb.New(*node.LastSeen)
|
||||
}
|
||||
|
||||
if node.Expiry != nil {
|
||||
nodeProto.Expiry = timestamppb.New(*node.Expiry)
|
||||
}
|
||||
|
||||
return nodeProto
|
||||
}
|
||||
|
||||
func (node *Node) GetFQDN(baseDomain string) (string, error) {
|
||||
if node.GivenName == "" {
|
||||
return "", fmt.Errorf("creating valid FQDN: %w", ErrNodeHasNoGivenName)
|
||||
@@ -574,11 +523,9 @@ func (node *Node) AnnouncedRoutes() []netip.Prefix {
|
||||
// announces and are approved. Also used by [Node.CanAccess] and [Node.CanAccessRoute] as part
|
||||
// of the subnet-router-as-source identity.
|
||||
//
|
||||
// IMPORTANT: This method is used for internal data structures and should NOT be
|
||||
// used for the gRPC Proto conversion. For Proto, SubnetRoutes must be populated
|
||||
// manually with PrimaryRoutes to ensure it includes only routes actively served
|
||||
// by the node. See the comment in [Node.Proto] method and the implementation in
|
||||
// grpcv1.go/nodesToProto.
|
||||
// IMPORTANT: This method reflects announced-and-approved routes. API
|
||||
// responses that need the routes actively served by the node must
|
||||
// populate SubnetRoutes from the primary-route election instead.
|
||||
func (node *Node) SubnetRoutes() []netip.Prefix {
|
||||
var routes []netip.Prefix
|
||||
|
||||
@@ -684,19 +631,6 @@ func EndpointsChanged(oldEndpoints, newEndpoints []netip.AddrPort) bool {
|
||||
return !equalUnordered(oldEndpoints, newEndpoints, netip.AddrPort.Compare)
|
||||
}
|
||||
|
||||
func (node *Node) RegisterMethodToV1Enum() v1.RegisterMethod {
|
||||
switch node.RegisterMethod {
|
||||
case "authkey":
|
||||
return v1.RegisterMethod_REGISTER_METHOD_AUTH_KEY
|
||||
case "oidc":
|
||||
return v1.RegisterMethod_REGISTER_METHOD_OIDC
|
||||
case "cli":
|
||||
return v1.RegisterMethod_REGISTER_METHOD_CLI
|
||||
default:
|
||||
return v1.RegisterMethod_REGISTER_METHOD_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyPeerChange takes a [tailcfg.PeerChange] struct and updates the node.
|
||||
func (node *Node) ApplyPeerChange(change *tailcfg.PeerChange) {
|
||||
if change.Key != nil {
|
||||
@@ -998,15 +932,6 @@ func (nv NodeView) RequestTags() []string {
|
||||
return nv.Hostinfo().RequestTags().AsSlice()
|
||||
}
|
||||
|
||||
// Proto converts the [NodeView] to a protobuf representation.
|
||||
func (nv NodeView) Proto() *v1.Node {
|
||||
if !nv.Valid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nv.ж.Proto()
|
||||
}
|
||||
|
||||
// HasIP reports if a node has a given IP address.
|
||||
func (nv NodeView) HasIP(i netip.Addr) bool {
|
||||
if !nv.Valid() {
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||
"github.com/juanfont/headscale/hscontrol/policy/matcher"
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"tailscale.com/tailcfg"
|
||||
@@ -657,56 +656,6 @@ func TestApplyPeerChange(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeRegisterMethodToV1Enum(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
node Node
|
||||
want v1.RegisterMethod
|
||||
}{
|
||||
{
|
||||
name: "authkey",
|
||||
node: Node{
|
||||
ID: 1,
|
||||
RegisterMethod: util.RegisterMethodAuthKey,
|
||||
},
|
||||
want: v1.RegisterMethod_REGISTER_METHOD_AUTH_KEY,
|
||||
},
|
||||
{
|
||||
name: "oidc",
|
||||
node: Node{
|
||||
ID: 1,
|
||||
RegisterMethod: util.RegisterMethodOIDC,
|
||||
},
|
||||
want: v1.RegisterMethod_REGISTER_METHOD_OIDC,
|
||||
},
|
||||
{
|
||||
name: "cli",
|
||||
node: Node{
|
||||
ID: 1,
|
||||
RegisterMethod: util.RegisterMethodCLI,
|
||||
},
|
||||
want: v1.RegisterMethod_REGISTER_METHOD_CLI,
|
||||
},
|
||||
{
|
||||
name: "unknown",
|
||||
node: Node{
|
||||
ID: 0,
|
||||
},
|
||||
want: v1.RegisterMethod_REGISTER_METHOD_UNSPECIFIED,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := tt.node.RegisterMethodToV1Enum()
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); diff != "" {
|
||||
t.Errorf("RegisterMethodToV1Enum() unexpected result (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestHasNetworkChanges tests the [NodeView] method for detecting
|
||||
// when a node's network properties have changed.
|
||||
func TestHasNetworkChanges(t *testing.T) {
|
||||
|
||||
@@ -3,11 +3,9 @@ package types
|
||||
import (
|
||||
"time"
|
||||
|
||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
type PAKError string
|
||||
@@ -56,66 +54,6 @@ type PreAuthKeyNew struct {
|
||||
User *User // Can be nil for system-created tagged keys
|
||||
}
|
||||
|
||||
func (key *PreAuthKeyNew) Proto() *v1.PreAuthKey {
|
||||
protoKey := v1.PreAuthKey{
|
||||
Id: key.ID,
|
||||
Key: key.Key,
|
||||
User: nil, // Will be set below if not nil
|
||||
Reusable: key.Reusable,
|
||||
Ephemeral: key.Ephemeral,
|
||||
AclTags: key.Tags,
|
||||
}
|
||||
|
||||
if key.User != nil {
|
||||
protoKey.User = key.User.Proto()
|
||||
}
|
||||
|
||||
if key.Expiration != nil {
|
||||
protoKey.Expiration = timestamppb.New(*key.Expiration)
|
||||
}
|
||||
|
||||
if key.CreatedAt != nil {
|
||||
protoKey.CreatedAt = timestamppb.New(*key.CreatedAt)
|
||||
}
|
||||
|
||||
return &protoKey
|
||||
}
|
||||
|
||||
func (key *PreAuthKey) Proto() *v1.PreAuthKey {
|
||||
protoKey := v1.PreAuthKey{
|
||||
User: nil, // Will be set below if not nil
|
||||
Id: key.ID,
|
||||
Ephemeral: key.Ephemeral,
|
||||
Reusable: key.Reusable,
|
||||
Used: key.Used,
|
||||
AclTags: key.Tags,
|
||||
}
|
||||
|
||||
if key.User != nil {
|
||||
protoKey.User = key.User.Proto()
|
||||
}
|
||||
|
||||
// For new keys (with prefix/hash), show the prefix so users can identify the key
|
||||
// For legacy keys (with plaintext key), show the full key for backwards compatibility
|
||||
if masked := key.maskedPrefix(); masked != "" {
|
||||
protoKey.Key = masked
|
||||
} else if key.Key != "" {
|
||||
// Legacy key - show full key for backwards compatibility
|
||||
// TODO: Consider hiding this in a future major version
|
||||
protoKey.Key = key.Key
|
||||
}
|
||||
|
||||
if key.Expiration != nil {
|
||||
protoKey.Expiration = timestamppb.New(*key.Expiration)
|
||||
}
|
||||
|
||||
if key.CreatedAt != nil {
|
||||
protoKey.CreatedAt = timestamppb.New(*key.CreatedAt)
|
||||
}
|
||||
|
||||
return &protoKey
|
||||
}
|
||||
|
||||
// Validate checks if a pre auth key can be used.
|
||||
func (pak *PreAuthKey) Validate() error {
|
||||
if pak == nil {
|
||||
|
||||
@@ -11,12 +11,10 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"gorm.io/gorm"
|
||||
"tailscale.com/tailcfg"
|
||||
)
|
||||
@@ -182,28 +180,6 @@ func (u UserView) TailscaleUserProfile() tailcfg.UserProfile {
|
||||
return u.ж.TailscaleUserProfile()
|
||||
}
|
||||
|
||||
func (u *User) Proto() *v1.User {
|
||||
// Use Name if set, otherwise fall back to Username() which provides
|
||||
// a display-friendly identifier (Email > ProviderIdentifier > ID).
|
||||
// This ensures OIDC users (who typically have empty Name) display
|
||||
// their email, while CLI users retain their original Name.
|
||||
name := u.Name
|
||||
if name == "" {
|
||||
name = u.Username()
|
||||
}
|
||||
|
||||
return &v1.User{
|
||||
Id: uint64(u.ID),
|
||||
Name: name,
|
||||
CreatedAt: timestamppb.New(u.CreatedAt),
|
||||
DisplayName: u.DisplayName,
|
||||
Email: u.Email,
|
||||
ProviderId: u.ProviderIdentifier.String,
|
||||
Provider: u.Provider,
|
||||
ProfilePicUrl: u.ProfilePicURL,
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalZerologObject implements [zerolog.LogObjectMarshaler] for safe logging.
|
||||
func (u *User) MarshalZerologObject(e *zerolog.Event) {
|
||||
if u == nil {
|
||||
|
||||
Reference in new issue
Block a user