Add policy CRUD, cert management, and web UI updates

- Add PUT /v1/policy/rule endpoint for updating policy rules; expose
  full policy CRUD through the web UI with a dedicated policy page
- Add certificate revoke, delete, and get-cert to CA engine and wire
  REST + gRPC routes; fix missing interceptor registrations
- Update ARCHITECTURE.md to reflect v2 gRPC as the active implementation,
  document ACME endpoints, correct CA permission levels, and add policy/cert
  management route tables
- Add POLICY.md documenting the priority-based ACL engine design
- Add web/templates/policy.html for policy management UI

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-15 19:41:11 -07:00
parent 02ee538213
commit fbd6d1af04
17 changed files with 1055 additions and 58 deletions

View File

@@ -23,6 +23,7 @@ type VaultClient struct {
engine pb.EngineServiceClient
pki pb.PKIServiceClient
ca pb.CAServiceClient
policy pb.PolicyServiceClient
}
// NewVaultClient dials the vault gRPC server and returns a client.
@@ -60,6 +61,7 @@ func NewVaultClient(addr, caCertPath string, logger *slog.Logger) (*VaultClient,
engine: pb.NewEngineServiceClient(conn),
pki: pb.NewPKIServiceClient(conn),
ca: pb.NewCAServiceClient(conn),
policy: pb.NewPolicyServiceClient(conn),
}, nil
}
@@ -379,6 +381,85 @@ func (c *VaultClient) DeleteCert(ctx context.Context, token, mount, serial strin
return err
}
// PolicyRule holds a policy rule for display and management.
type PolicyRule struct {
ID string
Priority int
Effect string
Usernames []string
Roles []string
Resources []string
Actions []string
}
// ListPolicies returns all policy rules from the vault.
func (c *VaultClient) ListPolicies(ctx context.Context, token string) ([]PolicyRule, error) {
resp, err := c.policy.ListPolicies(withToken(ctx, token), &pb.ListPoliciesRequest{})
if err != nil {
return nil, err
}
rules := make([]PolicyRule, 0, len(resp.Rules))
for _, r := range resp.Rules {
rules = append(rules, pbToRule(r))
}
return rules, nil
}
// GetPolicy retrieves a single policy rule by ID.
func (c *VaultClient) GetPolicy(ctx context.Context, token, id string) (*PolicyRule, error) {
resp, err := c.policy.GetPolicy(withToken(ctx, token), &pb.GetPolicyRequest{Id: id})
if err != nil {
return nil, err
}
rule := pbToRule(resp.Rule)
return &rule, nil
}
// CreatePolicy creates a new policy rule.
func (c *VaultClient) CreatePolicy(ctx context.Context, token string, rule PolicyRule) (*PolicyRule, error) {
resp, err := c.policy.CreatePolicy(withToken(ctx, token), &pb.CreatePolicyRequest{
Rule: ruleToPB(rule),
})
if err != nil {
return nil, err
}
created := pbToRule(resp.Rule)
return &created, nil
}
// DeletePolicy removes a policy rule by ID.
func (c *VaultClient) DeletePolicy(ctx context.Context, token, id string) error {
_, err := c.policy.DeletePolicy(withToken(ctx, token), &pb.DeletePolicyRequest{Id: id})
return err
}
func pbToRule(r *pb.PolicyRule) PolicyRule {
if r == nil {
return PolicyRule{}
}
return PolicyRule{
ID: r.Id,
Priority: int(r.Priority),
Effect: r.Effect,
Usernames: r.Usernames,
Roles: r.Roles,
Resources: r.Resources,
Actions: r.Actions,
}
}
func ruleToPB(r PolicyRule) *pb.PolicyRule {
return &pb.PolicyRule{
Id: r.ID,
Priority: int32(r.Priority),
Effect: r.Effect,
Usernames: r.Usernames,
Roles: r.Roles,
Resources: r.Resources,
Actions: r.Actions,
}
}
// CertSummary holds lightweight certificate metadata for list views.
type CertSummary struct {
Serial string