docs: update README with desktop requirements, helper builds, and realistic MVP usage notes
Expand README with desktop platform requirements (Windows x86, macOS ARM), helper build commands, gateway utility scripts, and updated local test flow. Add realistic MVP usage section clarifying current platform build status, gateway configuration needs, and admin debug profile behavior with client private key handling.
This commit is contained in:
@@ -96,11 +96,19 @@ func (s *Service) ListAll(ctx context.Context) ([]Device, error) {
|
||||
}
|
||||
|
||||
func (s *Service) GetLatestEnrollmentByUser(ctx context.Context, userID uuid.UUID) (EnrollmentResponse, error) {
|
||||
return s.repo.GetLatestEnrollmentByUser(ctx, userID)
|
||||
enrollment, err := s.repo.GetLatestEnrollmentByUser(ctx, userID)
|
||||
if err != nil {
|
||||
return EnrollmentResponse{}, err
|
||||
}
|
||||
return withDebugProfile(enrollment), nil
|
||||
}
|
||||
|
||||
func (s *Service) GetEnrollmentByDeviceID(ctx context.Context, deviceID uuid.UUID) (EnrollmentResponse, error) {
|
||||
return s.repo.GetEnrollmentByDeviceID(ctx, deviceID)
|
||||
enrollment, err := s.repo.GetEnrollmentByDeviceID(ctx, deviceID)
|
||||
if err != nil {
|
||||
return EnrollmentResponse{}, err
|
||||
}
|
||||
return withDebugProfile(enrollment), nil
|
||||
}
|
||||
|
||||
func (s *Service) GetConnectionStatus(ctx context.Context, userID uuid.UUID) (ConnectionStatus, error) {
|
||||
@@ -128,3 +136,19 @@ func (s *Service) Revoke(ctx context.Context, deviceID uuid.UUID) error {
|
||||
func (s *Service) Rotate(ctx context.Context, deviceID uuid.UUID) error {
|
||||
return s.repo.Rotate(ctx, deviceID)
|
||||
}
|
||||
|
||||
func withDebugProfile(enrollment EnrollmentResponse) EnrollmentResponse {
|
||||
enrollment.Profile = ProfileView{
|
||||
Format: "wireguard",
|
||||
Content: profile.BuildWireGuardConfig(profile.BuildInput{
|
||||
PrivateKey: "__CLIENT_PRIVATE_KEY_REQUIRED__",
|
||||
Address: enrollment.Peer.AssignedIP,
|
||||
DNSServers: enrollment.Peer.DNSServers,
|
||||
ServerPublicKey: enrollment.Peer.Gateway.PublicKey,
|
||||
ServerEndpoint: enrollment.Peer.Gateway.Endpoint,
|
||||
AllowedIPs: enrollment.Peer.AllowedIPs,
|
||||
PersistentKeepal: 25,
|
||||
}),
|
||||
}
|
||||
return enrollment
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package gateway
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
@@ -35,3 +36,19 @@ func (h *Handler) SyncBundle(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
apiutil.JSON(w, http.StatusOK, bundle)
|
||||
}
|
||||
|
||||
func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
||||
var input UpdateRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
||||
apiutil.Error(w, http.StatusBadRequest, "invalid_json", "invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
item, err := h.service.Update(r.Context(), chi.URLParam(r, "id"), input)
|
||||
if err != nil {
|
||||
apiutil.Error(w, http.StatusBadRequest, "gateway_update_failed", "unable to update gateway")
|
||||
return
|
||||
}
|
||||
|
||||
apiutil.JSON(w, http.StatusOK, item)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package gateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
@@ -13,6 +14,7 @@ type Repository interface {
|
||||
List(ctx context.Context) ([]Gateway, error)
|
||||
FirstActive(ctx context.Context) (Gateway, error)
|
||||
BuildSyncBundle(ctx context.Context, gatewayID uuid.UUID) (wireguard.GatewayBundle, error)
|
||||
Update(ctx context.Context, gatewayID uuid.UUID, input UpdateRequest) (Gateway, error)
|
||||
}
|
||||
|
||||
type PGRepository struct {
|
||||
@@ -66,19 +68,33 @@ func (r *PGRepository) BuildSyncBundle(ctx context.Context, gatewayID uuid.UUID)
|
||||
bundle.Revision = 1
|
||||
|
||||
row := r.db.QueryRow(ctx, `
|
||||
select host(vpn_cidr), listen_port
|
||||
select vpn_cidr::text, listen_port
|
||||
from gateways
|
||||
where id = $1 and deleted_at is null
|
||||
`, gatewayID)
|
||||
if err := row.Scan(&bundle.Interface.Address, &bundle.Interface.ListenPort); err != nil {
|
||||
var vpnCIDR string
|
||||
if err := row.Scan(&vpnCIDR, &bundle.Interface.ListenPort); err != nil {
|
||||
return wireguard.GatewayBundle{}, err
|
||||
}
|
||||
interfaceAddress, err := gatewayInterfaceAddress(vpnCIDR)
|
||||
if err != nil {
|
||||
return wireguard.GatewayBundle{}, err
|
||||
}
|
||||
bundle.Interface.Address = interfaceAddress
|
||||
bundle.Interface.NetworkCIDR = vpnCIDR
|
||||
|
||||
rows, err := r.db.Query(ctx, `
|
||||
select d.id, wp.public_key, host(wp.assigned_ip), coalesce(array_agg(pd.destination::text) filter (where pd.destination is not null), '{}')
|
||||
select
|
||||
d.id,
|
||||
wp.public_key,
|
||||
set_masklen(wp.assigned_ip, 32)::text,
|
||||
coalesce(array_agg(distinct pd.destination::text) filter (where pd.destination is not null), '{}')
|
||||
from devices d
|
||||
join wireguard_peers wp on wp.device_id = d.id and wp.deleted_at is null
|
||||
left join policy_targets pt on pt.target_id = d.id and pt.target_type = 'device'
|
||||
left join policy_targets pt on (
|
||||
(pt.target_type = 'device' and pt.target_id = d.id) or
|
||||
(pt.target_type = 'user' and pt.target_id = d.user_id)
|
||||
)
|
||||
left join policy_destinations pd on pd.policy_id = pt.policy_id
|
||||
where d.gateway_id = $1 and d.deleted_at is null and d.status = 'active'
|
||||
group by d.id, wp.public_key, wp.assigned_ip
|
||||
@@ -100,3 +116,46 @@ func (r *PGRepository) BuildSyncBundle(ctx context.Context, gatewayID uuid.UUID)
|
||||
|
||||
return bundle, rows.Err()
|
||||
}
|
||||
|
||||
func (r *PGRepository) Update(ctx context.Context, gatewayID uuid.UUID, input UpdateRequest) (Gateway, error) {
|
||||
row := r.db.QueryRow(ctx, `
|
||||
update gateways
|
||||
set endpoint = $2,
|
||||
public_key = $3,
|
||||
listen_port = $4,
|
||||
vpn_cidr = $5::cidr,
|
||||
dns_servers = $6::text[],
|
||||
is_active = $7,
|
||||
updated_at = now()
|
||||
where id = $1
|
||||
returning id, name, endpoint, public_key, listen_port, vpn_cidr, dns_servers, is_active
|
||||
`, gatewayID, input.Endpoint, input.PublicKey, input.ListenPort, input.VPNCIDR, input.DNSServers, input.IsActive)
|
||||
|
||||
var item Gateway
|
||||
err := row.Scan(&item.ID, &item.Name, &item.Endpoint, &item.PublicKey, &item.ListenPort, &item.VPNCIDR, &item.DNSServers, &item.IsActive)
|
||||
return item, err
|
||||
}
|
||||
|
||||
func gatewayInterfaceAddress(cidr string) (string, error) {
|
||||
prefix, err := netip.ParsePrefix(cidr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return prefix.Addr().Next().String() + "/" + intToString(prefix.Bits()), nil
|
||||
}
|
||||
|
||||
func intToString(value int) string {
|
||||
if value == 0 {
|
||||
return "0"
|
||||
}
|
||||
|
||||
var digits [20]byte
|
||||
index := len(digits)
|
||||
for value > 0 {
|
||||
index--
|
||||
digits[index] = byte('0' + value%10)
|
||||
value /= 10
|
||||
}
|
||||
return string(digits[index:])
|
||||
}
|
||||
|
||||
@@ -31,3 +31,11 @@ func (s *Service) BuildSyncBundle(ctx context.Context, gatewayID string) (wiregu
|
||||
}
|
||||
return s.repo.BuildSyncBundle(ctx, id)
|
||||
}
|
||||
|
||||
func (s *Service) Update(ctx context.Context, gatewayID string, input UpdateRequest) (Gateway, error) {
|
||||
id, err := uuid.Parse(gatewayID)
|
||||
if err != nil {
|
||||
return Gateway{}, err
|
||||
}
|
||||
return s.repo.Update(ctx, id, input)
|
||||
}
|
||||
|
||||
@@ -12,3 +12,12 @@ type Gateway struct {
|
||||
DNSServers []string `json:"dns_servers"`
|
||||
IsActive bool `json:"is_active"`
|
||||
}
|
||||
|
||||
type UpdateRequest struct {
|
||||
Endpoint string `json:"endpoint"`
|
||||
PublicKey string `json:"public_key"`
|
||||
ListenPort int `json:"listen_port"`
|
||||
VPNCIDR string `json:"vpn_cidr"`
|
||||
DNSServers []string `json:"dns_servers"`
|
||||
IsActive bool `json:"is_active"`
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ func NewRouter(jwtSecret string, handlers Handlers) http.Handler {
|
||||
r.Post("/policies", handlers.Policy.Create)
|
||||
r.Get("/gateways", handlers.Gateway.List)
|
||||
r.Get("/gateways/{id}/sync", handlers.Gateway.SyncBundle)
|
||||
r.Patch("/gateways/{id}", handlers.Gateway.Update)
|
||||
r.Get("/audit-logs", handlers.Audit.List)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -12,6 +12,7 @@ type GatewayBundle struct {
|
||||
Revision int `json:"revision"`
|
||||
Interface struct {
|
||||
Address string `json:"address"`
|
||||
NetworkCIDR string `json:"network_cidr"`
|
||||
ListenPort int `json:"listen_port"`
|
||||
} `json:"interface"`
|
||||
Peers []Peer `json:"peers"`
|
||||
|
||||
Reference in New Issue
Block a user