created handlers for certificate manipulation in vault. Inserted device mTLS guards for public faced endpoints

This commit is contained in:
dtv
2025-10-04 23:12:03 +03:00
parent 35e59c4879
commit 6a5ddd66ba
8 changed files with 555 additions and 5 deletions

View File

@@ -0,0 +1,86 @@
package vault
import (
"context"
"encoding/json"
"fmt"
"time"
vault "github.com/hashicorp/vault-client-go"
)
type PKIClient struct {
client *vault.Client
mount string // e.g. "pki_iot"
role string // e.g. "device"
}
type SignResponse struct {
Certificate string `json:"certificate"`
IssuingCA string `json:"issuing_ca"`
CAChain []string `json:"ca_chain"`
PrivateKey string `json:"private_key,omitempty"`
PrivateKeyType string `json:"private_key_type,omitempty"`
SerialNumber string `json:"serial_number"`
}
func NewPKI(addr, token, mount, role string, timeout time.Duration) (*PKIClient, error) {
client, err := vault.New(
vault.WithAddress(addr),
vault.WithRequestTimeout(timeout),
)
if err != nil {
return nil, fmt.Errorf("vault new: %w", err)
}
if err := client.SetToken(token); err != nil {
return nil, fmt.Errorf("set token: %w", err)
}
return &PKIClient{client: client, mount: mount, role: role}, nil
}
// SignCSR calls: /v1/<mount>/sign/<role>
func (p *PKIClient) SignCSR(ctx context.Context, csrPEM, uriSAN string, ttl string) (*SignResponse, error) {
if p.client == nil {
return nil, fmt.Errorf("vault client is nil")
}
path := fmt.Sprintf("/%s/sign/%s", p.mount, p.role)
req := map[string]any{
"csr": csrPEM,
"uri_sans": uriSAN, // e.g. "urn:device:<GUID>"
}
if ttl != "" {
req["ttl"] = ttl // e.g. "720h"
}
resp, err := p.client.Write(ctx, path, req)
if err != nil {
return nil, err
}
if resp == nil || resp.Data == nil {
return nil, fmt.Errorf("vault sign: empty response")
}
// resp.Data contains the fields we need
var out SignResponse
if err := mapToStruct(resp.Data, &out); err != nil {
return nil, err
}
return &out, nil
}
// RebuildCRL triggers CRL regeneration (rotate).
// HCP: POST /v1/<mount>/crl/rotate or read /crl to force render; well call rotate when available,
// else read to ensure CRL is (re)generated.
func (p *PKIClient) RebuildCRL(ctx context.Context) error {
_, _ = p.client.Write(ctx, fmt.Sprintf("/%s/crl/rotate", p.mount), nil) // best effort
_, err := p.client.Read(ctx, fmt.Sprintf("/%s/crl", p.mount))
return err
}
func mapToStruct(m map[string]any, out any) error {
b, err := json.Marshal(m)
if err != nil {
return err
}
return json.Unmarshal(b, out)
}