mirror of
https://github.com/go-gost/x.git
synced 2026-10-19 16:16:32 +00:00
Extracts the verified mTLS peer certificate (CN, SANs, SHA-256 fingerprint) from the TLS connection in the HTTP handler after the handshake completes, stores it in the request context, and forwards it to both gRPC and HTTP auth plugins via new AuthenticateRequest fields (client_cn, client_san, client_cert_fingerprint).
113 lines
2.4 KiB
Go
113 lines
2.4 KiB
Go
package auth
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
|
|
"github.com/go-gost/core/auth"
|
|
"github.com/go-gost/core/logger"
|
|
xctx "github.com/go-gost/x/ctx"
|
|
"github.com/go-gost/x/internal/plugin"
|
|
)
|
|
|
|
type httpPluginRequest struct {
|
|
Service string `json:"service"`
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
Client string `json:"client"`
|
|
ClientCn string `json:"clientCn,omitempty"`
|
|
ClientSan []string `json:"clientSan,omitempty"`
|
|
ClientCertFingerprint string `json:"clientCertFingerprint,omitempty"`
|
|
}
|
|
|
|
type httpPluginResponse struct {
|
|
OK bool `json:"ok"`
|
|
ID string `json:"id"`
|
|
}
|
|
|
|
type httpPlugin struct {
|
|
url string
|
|
client *http.Client
|
|
header http.Header
|
|
log logger.Logger
|
|
}
|
|
|
|
// NewHTTPPlugin creates an Authenticator plugin based on HTTP.
|
|
func NewHTTPPlugin(name string, url string, opts ...plugin.Option) auth.Authenticator {
|
|
var options plugin.Options
|
|
for _, opt := range opts {
|
|
opt(&options)
|
|
}
|
|
|
|
return &httpPlugin{
|
|
url: url,
|
|
client: plugin.NewHTTPClient(&options),
|
|
header: options.Header,
|
|
log: logger.Default().WithFields(map[string]any{
|
|
"kind": "auther",
|
|
"auther": name,
|
|
}),
|
|
}
|
|
}
|
|
|
|
func (p *httpPlugin) Authenticate(ctx context.Context, user, password string, opts ...auth.Option) (id string, ok bool) {
|
|
if p.client == nil {
|
|
return
|
|
}
|
|
|
|
var options auth.Options
|
|
for _, opt := range opts {
|
|
opt(&options)
|
|
}
|
|
|
|
var clientAddr string
|
|
if v := xctx.SrcAddrFromContext(ctx); v != nil {
|
|
clientAddr = v.String()
|
|
}
|
|
|
|
rb := httpPluginRequest{
|
|
Service: options.Service,
|
|
Username: user,
|
|
Password: password,
|
|
Client: clientAddr,
|
|
}
|
|
if v := xctx.PeerCertFromContext(ctx); v != nil {
|
|
rb.ClientCn = v.CN
|
|
rb.ClientSan = v.SANs
|
|
rb.ClientCertFingerprint = v.Fingerprint
|
|
}
|
|
v, err := json.Marshal(&rb)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.url, bytes.NewReader(v))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if p.header != nil {
|
|
req.Header = p.header.Clone()
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
resp, err := p.client.Do(req)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
io.Copy(io.Discard, resp.Body)
|
|
return
|
|
}
|
|
|
|
res := httpPluginResponse{}
|
|
if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
|
|
return
|
|
}
|
|
return res.ID, res.OK
|
|
}
|