- Rename Go module switchboard-core → armature (155+ files) - Rename Docker image → gobha/armature - Rename K8s resources, secrets, deployments - Rename Prometheus metrics switchboard_* → armature_* - Rename env vars SWITCHBOARD_ADMIN_* → ARMATURE_ADMIN_* - Rename DB names switchboard_core* → armature* - Update all frontend branding, notification templates, docs - Update CI scripts, e2e tests, Keycloak realm, nginx conf - Rename scripts/switchboard-ca.sh → scripts/armature-ca.sh - Rename k8s/switchboard.yaml → k8s/armature.yaml - Rename chart alerting/dashboard files - Fix: DockerHub push uses env: binding for secret injection - Helm chart updated (name, labels, template functions, dashboard, alerting) - Replace favicon/icon assets with Armature brand No functional changes. Pure mechanical rename + CI fix. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
73 lines
2.3 KiB
Go
73 lines
2.3 KiB
Go
package auth
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"armature/store"
|
|
)
|
|
|
|
// ErrNoCert is returned when no client certificate is presented on a
|
|
// connection that requires mTLS authentication.
|
|
var ErrNoCert = errors.New("no client certificate presented")
|
|
|
|
// MTLSNativeConfig holds configuration for the native (non-proxy) mTLS provider.
|
|
type MTLSNativeConfig struct {
|
|
AutoActivate bool // auto-activate new users (default true)
|
|
DefaultTeam string // team ID for auto-provisioned users (optional)
|
|
}
|
|
|
|
// MTLSNativeProvider authenticates by reading the peer certificate
|
|
// directly from the TLS connection state. Unlike MTLSProxyProvider,
|
|
// it does not trust headers — identity is cryptographically verified
|
|
// by the Go TLS stack before the HTTP layer runs.
|
|
//
|
|
// Requires TLS_MODE=mtls so the binary terminates TLS itself.
|
|
type MTLSNativeProvider struct {
|
|
cfg MTLSNativeConfig
|
|
}
|
|
|
|
// NewMTLSNativeProvider creates a native mTLS auth provider.
|
|
func NewMTLSNativeProvider(cfg MTLSNativeConfig) *MTLSNativeProvider {
|
|
return &MTLSNativeProvider{cfg: cfg}
|
|
}
|
|
|
|
func (p *MTLSNativeProvider) Mode() Mode { return ModeMTLS }
|
|
|
|
func (p *MTLSNativeProvider) SupportsRegistration() bool { return false }
|
|
|
|
func (p *MTLSNativeProvider) Register(_ *gin.Context, _ store.Stores) (*Result, error) {
|
|
return nil, ErrNotSupported
|
|
}
|
|
|
|
// Authenticate reads the verified peer certificate from the TLS connection
|
|
// state. The CN becomes the username, and sha256(cert.Raw) is the stable
|
|
// external_id. Returns ErrNoCert when no TLS or no peer certificates.
|
|
func (p *MTLSNativeProvider) Authenticate(c *gin.Context, stores store.Stores) (*Result, error) {
|
|
if c.Request.TLS == nil || len(c.Request.TLS.PeerCertificates) == 0 {
|
|
return nil, fmt.Errorf("%w", ErrNoCert)
|
|
}
|
|
|
|
peer := c.Request.TLS.PeerCertificates[0]
|
|
|
|
cn := peer.Subject.CommonName
|
|
if cn == "" {
|
|
return nil, fmt.Errorf("%w: certificate has no CommonName", ErrInvalidCreds)
|
|
}
|
|
|
|
// Build DN fields from the certificate subject for resolveOrProvision
|
|
dnFields := map[string]string{"CN": cn}
|
|
if len(peer.EmailAddresses) > 0 {
|
|
dnFields["emailAddress"] = peer.EmailAddresses[0]
|
|
}
|
|
|
|
fingerprint := FingerprintCert(peer)
|
|
|
|
return resolveOrProvision(
|
|
c.Request.Context(), stores, cn, dnFields, fingerprint,
|
|
p.cfg.AutoActivate, p.cfg.DefaultTeam,
|
|
)
|
|
}
|