Skip to content

Commit

Permalink
feat: implement api handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
pallabpain committed Jun 2, 2024
1 parent 1a9e0a8 commit 78737ff
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 28 deletions.
16 changes: 0 additions & 16 deletions cmd/headscale/cli/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (

v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/juanfont/headscale/hscontrol"
"github.com/juanfont/headscale/hscontrol/policy"
"github.com/juanfont/headscale/hscontrol/types"
"github.com/juanfont/headscale/hscontrol/util"
)
Expand All @@ -40,21 +39,6 @@ func getHeadscaleApp() (*hscontrol.Headscale, error) {
return nil, err
}

// We are doing this here, as in the future could be cool to have it also hot-reload

if cfg.Policy.Path != "" {
aclPath := util.AbsolutePathFromConfigPath(cfg.Policy.Path)
pol, err := policy.LoadACLPolicyFromPath(aclPath)
if err != nil {
log.Fatal().
Str("path", aclPath).
Err(err).
Msg("Could not load the ACL policy")
}

app.ACLPolicy = pol
}

return app, nil
}

Expand Down
65 changes: 53 additions & 12 deletions hscontrol/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,10 @@ func (h *Headscale) Serve() error {

var err error

if err = h.loadACLPolicy(); err != nil {
return fmt.Errorf("failed to load ACL policy: %w", err)
}

if dumpConfig {
spew.Dump(h.cfg)
}
Expand Down Expand Up @@ -623,8 +627,8 @@ func (h *Headscale) Serve() error {

// Start the local gRPC server without TLS and without authentication
grpcSocket := grpc.NewServer(
// Uncomment to debug grpc communication.
// zerolog.UnaryInterceptor(),
// Uncomment to debug grpc communication.
// zerolog.UnaryInterceptor(),
)

v1.RegisterHeadscaleServiceServer(grpcSocket, newHeadscaleV1APIServer(h))
Expand Down Expand Up @@ -785,25 +789,19 @@ func (h *Headscale) Serve() error {
Msg("Received SIGHUP, reloading ACL and Config")

// TODO(kradalby): Reload config on SIGHUP
if err := h.loadACLPolicy(); err != nil {
log.Error().Err(err).Msg("failed to reload ACL policy")
}

if h.cfg.Policy.Path != "" {
aclPath := util.AbsolutePathFromConfigPath(h.cfg.Policy.Path)
pol, err := policy.LoadACLPolicyFromPath(aclPath)
if err != nil {
log.Error().Err(err).Msg("Failed to reload ACL policy")
}

h.ACLPolicy = pol
if h.ACLPolicy != nil {
log.Info().
Str("path", aclPath).
Msg("ACL policy successfully reloaded, notifying nodes of change")

ctx := types.NotifyCtx(context.Background(), "acl-sighup", "na")
h.nodeNotifier.NotifyAll(ctx, types.StateUpdate{
Type: types.StateFullUpdate,
})
}

default:
trace := log.Trace().Msgf
log.Info().
Expand Down Expand Up @@ -1013,3 +1011,46 @@ func readOrCreatePrivateKey(path string) (*key.MachinePrivate, error) {

return &machineKey, nil
}

func (h *Headscale) loadACLPolicy() error {
var (
pol *policy.ACLPolicy
err error
)

switch h.cfg.Policy.Mode {
case types.PolicyModeFile:
path := h.cfg.Policy.Path
if len(path) == 0 {
return fmt.Errorf("policy path is empty")
}

absPath := util.AbsolutePathFromConfigPath(path)
pol, err = policy.LoadACLPolicyFromPath(absPath)
if err != nil {
return fmt.Errorf("failed to load ACL policy from file: %w", err)
}
case types.PolicyModeDB:
p, err := h.db.GetPolicy()
if err != nil {
if errors.Is(err, types.ErrPolicyNotFound) {
return nil
}

return fmt.Errorf("failed to get policy from database: %w", err)
}

pol, err = policy.LoadACLPolicyFromBytes([]byte(p.Data), "hujson")
if err != nil {
return fmt.Errorf("failed to parse policy: %w", err)
}
default:
log.Warn().
Str("mode", string(h.cfg.Policy.Mode)).
Msg("Unknown ACL policy mode")
}

h.ACLPolicy = pol

return nil
}
70 changes: 70 additions & 0 deletions hscontrol/grpcv1.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package hscontrol

import (
"context"
"encoding/json"
"errors"
"sort"
"strings"
Expand All @@ -11,12 +12,14 @@ import (
"github.com/rs/zerolog/log"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
"gorm.io/gorm"
"tailscale.com/tailcfg"
"tailscale.com/types/key"

v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/juanfont/headscale/hscontrol/db"
"github.com/juanfont/headscale/hscontrol/policy"
"github.com/juanfont/headscale/hscontrol/types"
"github.com/juanfont/headscale/hscontrol/util"
)
Expand Down Expand Up @@ -671,6 +674,73 @@ func (api headscaleV1APIServer) DeleteApiKey(
return &v1.DeleteApiKeyResponse{}, nil
}

func (api headscaleV1APIServer) GetPolicy(
_ context.Context,
_ *v1.GetPolicyRequest,
) (*v1.GetPolicyResponse, error) {
switch api.h.cfg.Policy.Mode {
case types.PolicyModeDB:
p, err := api.h.db.GetPolicy()
if err != nil {
return nil, err
}

// We retain the HuJSON format of the policy while storing
// it in the database. But should we return the same in the
// get policy response? Or should we consider returning the
// policy in JSON format?

return &v1.GetPolicyResponse{
Policy: p.Data,
UpdatedAt: timestamppb.New(p.UpdatedAt),
}, nil
case types.PolicyModeFile:
b, err := json.Marshal(api.h.ACLPolicy)
if err != nil {
return nil, err
}

return &v1.GetPolicyResponse{Policy: string(b)}, nil
}

return nil, nil
}

func (api headscaleV1APIServer) SetPolicy(
_ context.Context,
request *v1.SetPolicyRequest,
) (*v1.SetPolicyResponse, error) {
if api.h.cfg.Policy.Mode != types.PolicyModeDB {
return nil, types.ErrPolicyUpdateIsDisabled
}

p := request.GetPolicy()

valid, err := policy.LoadACLPolicyFromBytes([]byte(p), "hujson")
if err != nil {
return nil, err
}

updated, err := api.h.db.SetPolicy(p)
if err != nil {
return nil, err
}

api.h.ACLPolicy = valid

ctx := types.NotifyCtx(context.Background(), "acl-update", "na")
api.h.nodeNotifier.NotifyAll(ctx, types.StateUpdate{
Type: types.StateFullUpdate,
})

response := &v1.SetPolicyResponse{
Policy: updated.Data,
UpdatedAt: timestamppb.New(updated.UpdatedAt),
}

return response, nil
}

// The following service calls are for testing and debugging
func (api headscaleV1APIServer) DebugCreateNode(
ctx context.Context,
Expand Down

0 comments on commit 78737ff

Please sign in to comment.