Add Vault certificate rotation operator

This commit is contained in:
ms2k 2026-10-19 01:03:13 +09:00
commit 8009b5fd9c

96
internal/rotator/http.go Normal file
View file

@ -0,0 +1,96 @@
package rotator
import (
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
)
func tlsClient(certificatePaths []string, serverName string) (*http.Client, error) {
pool, err := certificatePool(certificatePaths)
if err != nil {
return nil, err
}
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.TLSClientConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
RootCAs: pool,
ServerName: serverName,
}
return &http.Client{Transport: transport, Timeout: 20 * time.Second}, nil
}
func certificatePool(certificatePaths []string) (*x509.CertPool, error) {
pool := x509.NewCertPool()
for _, path := range certificatePaths {
certificate, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read CA certificate %s: %w", path, err)
}
if !pool.AppendCertsFromPEM(certificate) {
return nil, fmt.Errorf("parse CA certificate %s", path)
}
}
return pool, nil
}
func jsonRequest(ctx context.Context, client *http.Client, method, endpoint string, body any, headers map[string]string, output any) (*http.Response, error) {
var payload io.Reader
if body != nil {
encoded, err := json.Marshal(body)
if err != nil {
return nil, fmt.Errorf("encode request: %w", err)
}
payload = bytes.NewReader(encoded)
}
request, err := http.NewRequestWithContext(ctx, method, endpoint, payload)
if err != nil {
return nil, fmt.Errorf("create request: %w", err)
}
if body != nil {
request.Header.Set("Content-Type", "application/json")
}
for name, value := range headers {
request.Header.Set(name, value)
}
response, err := client.Do(request)
if err != nil {
return nil, err
}
defer func() { _ = response.Body.Close() }()
limited := io.LimitReader(response.Body, 1<<20)
if response.StatusCode < 200 || response.StatusCode >= 300 {
_, _ = io.Copy(io.Discard, limited)
return response, fmt.Errorf("%s returned HTTP %d", request.URL.Path, response.StatusCode)
}
if output == nil {
_, err = io.Copy(io.Discard, limited)
return response, err
}
decoder := json.NewDecoder(limited)
if err := decoder.Decode(output); err != nil {
return response, fmt.Errorf("decode %s response: %w", request.URL.Path, err)
}
return response, nil
}
func readTrimmed(path string) (string, error) {
value, err := os.ReadFile(path)
if err != nil {
return "", err
}
trimmed := strings.TrimSpace(string(value))
if trimmed == "" {
return "", errors.New("file is empty")
}
return trimmed, nil
}