types: add MarshalZerologObject to domain types

Implement zerolog.LogObjectMarshaler interface on domain types
for structured logging:

- Node: logs node.id, node.name, machine.key (short), node.key (short),
  node.is_tagged, node.expired, node.online, node.tags, user.name
- User: logs user.id, user.name, user.display, user.provider
- PreAuthKey: logs pak.id, pak.prefix (masked), pak.reusable,
  pak.ephemeral, pak.used, pak.is_tagged, pak.tags
- APIKey: logs api_key.id, api_key.prefix (masked), api_key.expiration

Security: PreAuthKey and APIKey only log masked prefixes, never full
keys or hashes. Uses zf.* constants for consistent field naming.
This commit is contained in:
Kristoffer Dalby
2026-01-28 13:37:48 +00:00
parent 58020696fe
commit cf3d30b6f6
4 changed files with 146 additions and 11 deletions

View File

@@ -12,6 +12,8 @@ import (
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"
@@ -194,6 +196,30 @@ func (u *User) Proto() *v1.User {
}
}
// MarshalZerologObject implements zerolog.LogObjectMarshaler for safe logging.
func (u *User) MarshalZerologObject(e *zerolog.Event) {
if u == nil {
return
}
e.Uint(zf.UserID, u.ID)
e.Str(zf.UserName, u.Username())
e.Str(zf.UserDisplay, u.Display())
if u.Provider != "" {
e.Str(zf.UserProvider, u.Provider)
}
}
// MarshalZerologObject implements zerolog.LogObjectMarshaler for UserView.
func (u UserView) MarshalZerologObject(e *zerolog.Event) {
if !u.Valid() {
return
}
u.ж.MarshalZerologObject(e)
}
// JumpCloud returns a JSON where email_verified is returned as a
// string "true" or "false" instead of a boolean.
// This maps bool to a specific type with a custom unmarshaler to