项目文件夹

文件
Asim Aslam cae6fbbe76 Framework hardening: security, reliability, and developer experience improvements (#2826)
* fix: remove deprecated rand.Seed calls

Go 1.20+ automatically seeds the global random number generator.
These calls are no-ops and generate warnings with newer Go versions.

Removed from:
- selector/strategy.go
- registry/cache/cache.go
- broker/memory.go
- broker/http.go
- cmd/cmd.go
- transport/memory.go

Co-authored-by: Shelley <shelley@exe.dev>

* fix: handle previously ignored errors

- MySQL store: properly handle prepared statement errors in initDB()
- Consul registry: handle client creation errors in Client() method

These silent failures could cause hard-to-debug issues in production.

Co-authored-by: Shelley <shelley@exe.dev>

* feat(genai): improve provider interface with context and streaming

Breaking changes:
- Generate() and Stream() now require context.Context as first parameter
- Stream.Close() added for proper resource cleanup

Improvements:
- Proper context support for cancellation and timeouts
- Real SSE streaming for OpenAI and Gemini text generation
- Better error handling with wrapped errors and API error responses
- Thread-safe provider registry with sync.RWMutex
- New options: WithMaxTokens, WithTemperature, WithTimeout
- Stream has proper Close() method for cleanup
- Results can include Error field for per-chunk errors

Provider updates:
- OpenAI: true streaming with SSE parsing, proper HTTP client with timeout
- Gemini: true streaming with streamGenerateContent endpoint
- Default model updated to gpt-4o-mini (OpenAI) and gemini-2.0-flash (Gemini)

Co-authored-by: Shelley <shelley@exe.dev>

* feat(tls): make TLS secure by default, configurable via environment

BREAKING: TLS now verifies certificates by default. Set MICRO_TLS_INSECURE=true
to restore previous behavior (NOT recommended for production).

Changes:
- Add util/tls.Config(), SecureConfig(), InsecureConfig(), ConfigFromEnv() helpers
- Update all components to use ConfigFromEnv() instead of hardcoded InsecureSkipVerify
- Set MinVersion to TLS 1.2 for all TLS configs

Affected components:
- broker/http
- broker/rabbitmq
- registry/etcd
- registry/consul
- transport/grpc

This improves security posture while allowing opt-out for development environments.

Co-authored-by: Shelley <shelley@exe.dev>

* feat(tls): add TLS helpers with opt-in secure mode

NOT a breaking change - keeps InsecureSkipVerify=true as default for
local development compatibility.

New util/tls helpers:
- Config() - returns config based on MICRO_TLS_SECURE env var
- SecureConfig() - certificate verification enabled
- InsecureConfig() - certificate verification disabled (dev only)

For production security, use one of:
- Set MICRO_TLS_SECURE=true with proper CA-signed certs
- Use a service mesh (Istio, Linkerd) for automatic mTLS
- Configure TLSConfig directly with your certificates

Also: Changed CLI alias from 'g' to 'gen' for clarity
- micro generate handler -> micro gen handler

Co-authored-by: Shelley <shelley@exe.dev>

* refactor(cli): rename generate directory to gen for consistency

Directory name now matches the command alias:
  cmd/micro/cli/gen/ -> micro gen handler

Co-authored-by: Shelley <shelley@exe.dev>

---------

Co-authored-by: Shelley <shelley@exe.dev>
2026-01-27 10:39:25 +00:00

180 行
3.6 KiB
Go

// Package grpc provides a grpc transport
package grpc
import (
"context"
"crypto/tls"
"net"
"go-micro.dev/v5/cmd"
"go-micro.dev/v5/transport"
maddr "go-micro.dev/v5/util/addr"
mnet "go-micro.dev/v5/util/net"
mtls "go-micro.dev/v5/util/tls"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
pb "go-micro.dev/v5/transport/grpc/proto"
)
type grpcTransport struct {
opts transport.Options
}
type grpcTransportListener struct {
listener net.Listener
secure bool
tls *tls.Config
}
func init() {
cmd.DefaultTransports["grpc"] = NewTransport
}
func getTLSConfig(addr string) (*tls.Config, error) {
hosts := []string{addr}
// check if its a valid host:port
if host, _, err := net.SplitHostPort(addr); err == nil {
if len(host) == 0 {
hosts = maddr.IPs()
} else {
hosts = []string{host}
}
}
// generate a certificate
cert, err := mtls.Certificate(hosts...)
if err != nil {
return nil, err
}
return &tls.Config{Certificates: []tls.Certificate{cert}}, nil
}
func (t *grpcTransportListener) Addr() string {
return t.listener.Addr().String()
}
func (t *grpcTransportListener) Close() error {
return t.listener.Close()
}
func (t *grpcTransportListener) Accept(fn func(transport.Socket)) error {
var opts []grpc.ServerOption
// setup tls if specified
if t.secure || t.tls != nil {
config := t.tls
if config == nil {
var err error
addr := t.listener.Addr().String()
config, err = getTLSConfig(addr)
if err != nil {
return err
}
}
creds := credentials.NewTLS(config)
opts = append(opts, grpc.Creds(creds))
}
// new service
srv := grpc.NewServer(opts...)
// register service
pb.RegisterTransportServer(srv, &microTransport{addr: t.listener.Addr().String(), fn: fn})
// start serving
return srv.Serve(t.listener)
}
func (t *grpcTransport) Dial(addr string, opts ...transport.DialOption) (transport.Client, error) {
dopts := transport.DialOptions{
Timeout: transport.DefaultDialTimeout,
}
for _, opt := range opts {
opt(&dopts)
}
options := []grpc.DialOption{
grpc.WithTimeout(dopts.Timeout),
}
if t.opts.Secure || t.opts.TLSConfig != nil {
config := t.opts.TLSConfig
if config == nil {
// Use environment-based config - secure by default
config = mtls.Config()
}
creds := credentials.NewTLS(config)
options = append(options, grpc.WithTransportCredentials(creds))
} else {
options = append(options, grpc.WithInsecure())
}
// dial the server
conn, err := grpc.Dial(addr, options...)
if err != nil {
return nil, err
}
// create stream
stream, err := pb.NewTransportClient(conn).Stream(context.Background())
if err != nil {
return nil, err
}
// return a client
return &grpcTransportClient{
conn: conn,
stream: stream,
local: "localhost",
remote: addr,
}, nil
}
func (t *grpcTransport) Listen(addr string, opts ...transport.ListenOption) (transport.Listener, error) {
var options transport.ListenOptions
for _, o := range opts {
o(&options)
}
ln, err := mnet.Listen(addr, func(addr string) (net.Listener, error) {
return net.Listen("tcp", addr)
})
if err != nil {
return nil, err
}
return &grpcTransportListener{
listener: ln,
tls: t.opts.TLSConfig,
secure: t.opts.Secure,
}, nil
}
func (t *grpcTransport) Init(opts ...transport.Option) error {
for _, o := range opts {
o(&t.opts)
}
return nil
}
func (t *grpcTransport) Options() transport.Options {
return t.opts
}
func (t *grpcTransport) String() string {
return "grpc"
}
func NewTransport(opts ...transport.Option) transport.Transport {
var options transport.Options
for _, o := range opts {
o(&options)
}
return &grpcTransport{opts: options}
}