-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
MEP-9: Implement VPN keys utilizing headscale (#313)
- Loading branch information
1 parent
f31fbec
commit 1336fd8
Showing
13 changed files
with
1,384 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package headscale | ||
|
||
import ( | ||
"context" | ||
) | ||
|
||
// Implements google.golang.org/grpc/credentials.PerRPCCredentials interface | ||
type tokenAuth struct { | ||
token string | ||
} | ||
|
||
func (t tokenAuth) GetRequestMetadata( | ||
ctx context.Context, | ||
_ ...string, | ||
) (map[string]string, error) { | ||
return map[string]string{ | ||
"authorization": "Bearer " + t.token, | ||
}, nil | ||
} | ||
|
||
func (tokenAuth) RequireTransportSecurity() bool { | ||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
package headscale | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"time" | ||
|
||
"go.uber.org/zap" | ||
|
||
headscalecore "github.com/juanfont/headscale" | ||
headscalev1 "github.com/juanfont/headscale/gen/go/headscale/v1" | ||
"google.golang.org/grpc" | ||
"google.golang.org/grpc/credentials/insecure" | ||
timestamppb "google.golang.org/protobuf/types/known/timestamppb" | ||
) | ||
|
||
type HeadscaleClient struct { | ||
client headscalev1.HeadscaleServiceClient | ||
|
||
address string | ||
controlPlaneAddress string | ||
|
||
ctx context.Context | ||
conn *grpc.ClientConn | ||
cancelFunc context.CancelFunc | ||
logger *zap.SugaredLogger | ||
} | ||
|
||
func NewHeadscaleClient(addr, controlPlaneAddr, apiKey string, logger *zap.SugaredLogger) (client *HeadscaleClient, err error) { | ||
if addr != "" || apiKey != "" { | ||
if addr == "" { | ||
return nil, fmt.Errorf("headscale address should be set with api key") | ||
} | ||
if apiKey == "" { | ||
return nil, fmt.Errorf("headscale api key should be set with address") | ||
} | ||
} else { | ||
return | ||
} | ||
|
||
h := &HeadscaleClient{ | ||
address: addr, | ||
controlPlaneAddress: controlPlaneAddr, | ||
|
||
logger: logger, | ||
} | ||
h.ctx, h.cancelFunc = context.WithCancel(context.Background()) | ||
|
||
if err = h.connect(apiKey); err != nil { | ||
return nil, fmt.Errorf("failed to connect to Headscale server: %w", err) | ||
} | ||
|
||
return h, nil | ||
} | ||
|
||
// Connect or reconnect to Headscale server | ||
func (h *HeadscaleClient) connect(apiKey string) (err error) { | ||
ctx, cancel := context.WithTimeout(h.ctx, 5*time.Second) | ||
defer cancel() | ||
|
||
grpcOptions := []grpc.DialOption{ | ||
grpc.WithBlock(), | ||
grpc.WithTransportCredentials(insecure.NewCredentials()), | ||
grpc.WithPerRPCCredentials(tokenAuth{ | ||
token: apiKey, | ||
}), | ||
} | ||
|
||
h.conn, err = grpc.DialContext(ctx, h.address, grpcOptions...) | ||
if err != nil { | ||
return fmt.Errorf("failed to connect to headscale server %s: %w", h.address, err) | ||
} | ||
|
||
h.client = headscalev1.NewHeadscaleServiceClient(h.conn) | ||
|
||
return | ||
} | ||
|
||
func (h *HeadscaleClient) GetControlPlaneAddress() string { | ||
return h.controlPlaneAddress | ||
} | ||
|
||
func (h *HeadscaleClient) NamespaceExists(name string) bool { | ||
getNSRequest := &headscalev1.GetNamespaceRequest{ | ||
Name: name, | ||
} | ||
if _, err := h.client.GetNamespace(h.ctx, getNSRequest); err != nil { | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
func (h *HeadscaleClient) CreateNamespace(name string) error { | ||
req := &headscalev1.CreateNamespaceRequest{ | ||
Name: name, | ||
} | ||
_, err := h.client.CreateNamespace(h.ctx, req) | ||
if err != nil && !errors.Is(headscalecore.ErrNamespaceExists, err) { | ||
return fmt.Errorf("failed to create new VPN namespace: %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (h *HeadscaleClient) CreatePreAuthKey(namespace string, expiration time.Time) (key string, err error) { | ||
req := &headscalev1.CreatePreAuthKeyRequest{ | ||
Namespace: namespace, | ||
Expiration: timestamppb.New(expiration), | ||
} | ||
resp, err := h.client.CreatePreAuthKey(h.ctx, req) | ||
if err != nil || resp == nil || resp.PreAuthKey == nil { | ||
return "", fmt.Errorf("failed to create new Auth Key: %w", err) | ||
} | ||
|
||
return resp.PreAuthKey.Key, nil | ||
} | ||
|
||
// Close client | ||
func (h *HeadscaleClient) Close() error { | ||
h.cancelFunc() | ||
return h.conn.Close() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package v1 | ||
|
||
import "time" | ||
|
||
type VPNResponse struct { | ||
Address string `json:"address" description:"address of VPN's control plane"` | ||
AuthKey string `json:"auth_key" description:"auth key to connect to the VPN"` | ||
} | ||
|
||
type VPNRequest struct { | ||
Pid string `json:"pid" description:"project ID"` | ||
Expiration *time.Duration `json:"expiration" description:"expiration time" optional:"true"` | ||
} |
Oops, something went wrong.