Add auth, security headers & a gateway¶
The transport servers are composed from small building blocks: authentication and
security-header middleware from transport/http (and AuthInterceptor from
transport/grpc), stitched together with a go/transit
Chain, plus an optional grpc-gateway.
Authenticate HTTP requests¶
AuthMiddleware verifies each request against a go/authn
verifier and stores the identity in the request context. It is an ordinary transit
Middleware, so it composes into a Chain:
import (
"gitlab.com/phpboyscout/go/authn"
transithttp "gitlab.com/phpboyscout/go/transit/http"
transporthttp "gitlab.com/phpboyscout/go/transport/http"
)
auth, err := transporthttp.AuthMiddleware(
transporthttp.WithBearerVerifier(jwtVerifier), // authn.Verifier
transporthttp.WithAuthLogger(log),
)
if err != nil {
return err
}
chain := transithttp.NewChain(auth, transporthttp.SecurityHeadersMiddleware())
srv, _ := transporthttp.Register(ctx, "api", controller, log, handler, settings,
transporthttp.WithTLSPair(pair), transporthttp.WithMiddleware(chain))
On failure it writes a generic 401/403 with the credential redacted from logs.
Read the verified identity downstream with transporthttp.IdentityFromContext(ctx).
Add security headers¶
SecurityHeadersMiddleware sets a secure baseline (X-Content-Type-Options,
X-Frame-Options, Referrer-Policy, and optionally HSTS/CSP). Tune it with options:
sec := transporthttp.SecurityHeadersMiddleware(
transporthttp.WithHSTS(365*24*time.Hour, true, true),
transporthttp.WithContentSecurityPolicy("default-src 'self'"),
transporthttp.WithFrameOptions("DENY"),
)
Authenticate gRPC calls¶
transport/grpc mirrors the HTTP auth surface as an interceptor:
import transportgrpc "gitlab.com/phpboyscout/go/transport/grpc"
ic, err := transportgrpc.AuthInterceptor(
transportgrpc.WithGRPCBearerVerifier(jwtVerifier),
transportgrpc.WithGRPCMethodSkipper(func(m string) bool { return m == "/grpc.health.v1.Health/Check" }),
)
srv, _ := transportgrpc.NewServer(settings, transportgrpc.WithInterceptors(
transitgrpc.NewInterceptorChain(ic),
))
Require client certificates (mTLS)¶
Server-side mTLS is config-driven end to end through the
go/tls Pair: set ClientCAs (PEM CA files
client certificates must chain to) and ClientAuth (request,
verify-if-given, or require-verify) on the pair you pass to Register via
WithTLSPair, and pair it with the mTLS verifier on the auth
middleware/interceptor:
pair := gtls.Pair{
Enabled: true,
Cert: "/etc/certs/server.pem",
Key: "/etc/certs/server-key.pem",
ClientCAs: []string{"/etc/certs/client-ca.pem"},
ClientAuth: gtls.ClientAuthRequireVerify,
}
// HTTP: the handler sees non-empty r.TLS.VerifiedChains, and WithMTLSVerifier
// authenticates the request from them.
auth, _ := transporthttp.AuthMiddleware(transporthttp.WithMTLSVerifier(authn.NewMTLSVerifier()))
// gRPC: WithGRPCMTLSVerifier sees the verified chains via peer.FromContext.
ic, _ := transportgrpc.AuthInterceptor(transportgrpc.WithGRPCMTLSVerifier(authn.NewMTLSVerifier()))
Both managed paths honour a caller-supplied server TLS config
(transporthttp.WithServerTLSConfig / transportgrpc.WithServerTLSConfig) by
merging the pair into it rather than replacing it: the pair contributes its
certificate (and, on the gRPC listener, ALPN h2), while everything the caller
set — MinVersion, verification hooks, a client-certificate policy — survives.
Precedence: a config that already carries ClientCAs or a non-zero
ClientAuth wins over the pair's ClientCAs/ClientAuth fields.
Serve TLS from an in-memory certificate¶
A gtls.Pair describes a certificate by file path. When the certificate is
minted in memory and never written to disk — a self-signed fallback used when the
local CA cannot be installed into the system trust store, an in-memory
ACME/autocert certificate, a short-lived issuer, or a test — pass it with
WithServedCertificate and leave the pair disabled:
cert, _ := someIssuer.Mint() // *tls.Certificate, held in RAM
_, err := transporthttp.Register(
ctx, "studio", controller, logger, handler,
transporthttp.ServerSettings{Port: 8443},
transporthttp.WithServedCertificate(cert), // serve TLS from RAM, no pair
)
The server serves over TLS even though no pair is enabled — there is no need to
persist a throwaway certificate to disk to satisfy the pair shape. The
certificate is appended to the server's TLS config, so a config supplied via
WithServerTLSConfig keeps its MinVersion, verification hooks, and any
client-certificate policy; the passed *tls.Certificate is not mutated. A
certificate placed directly on a WithServerTLSConfig is likewise served —
populated certificate material is treated as TLS intent.
Expose a gRPC service over REST with the gateway¶
transport/gateway wraps a gRPC connection in a grpc-gateway http.Handler. Give it a
connection (dial the gRPC server with go/grpcclient
or transport/grpc.DialLocal) and your generated registration function:
import transportgateway "gitlab.com/phpboyscout/go/transport/gateway"
conn, _ := transportgrpc.DialLocal(grpcSettings, grpcPair)
handler, err := transportgateway.New(ctx, conn,
func(ctx context.Context, mux *runtime.ServeMux, c *grpc.ClientConn) error {
return widgetv1.RegisterWidgetServiceHandler(ctx, mux, c)
},
transportgateway.WithMiddleware(chain), // optional transit Chain over the REST handler
)
The gateway handler is a plain http.Handler, so serve it through transport/http like
any other — the whole stack composes.
When the gRPC server requires client certificates, WithDialOptions is the
escape hatch for the gateway's outbound dial: pass
grpc.WithTransportCredentials(credentials.NewTLS(cfg)) with a client
*tls.Config carrying the gateway's client certificate and the CA pool that
signed the server's certificate — an explicit transport-credentials option
overrides the automatically selected transport security.
Where the full option lists live¶
This page composes the pieces; the exhaustive tables — every option, its default, and what happens when a value is wrong — are in the reference:
- HTTP server reference —
AuthMiddlewareoptions, credential precedence, the exact 401/403 responses, and the security-header defaults. - gRPC server reference —
AuthInterceptoroptions, and which methods skip authentication whatever you configure. - TLS settings reference — the
gtls.Pairfields, theClientAuthmodes, and which TLS setting wins when two disagree. - Gateway reference — what
NewandRegistereach own, including who closes the gRPC connection.
Two constraints that bite here and are stated in full under
What transport does not do: gRPC reflection
cannot be authenticated, and an enabled TLS pair must not be combined with your
own grpc.Creds.