github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/clients/pkg/promtail/targets/syslog/transport.go (about)

     1  package syslog
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"crypto/x509"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"net"
    11  	"strings"
    12  	"sync"
    13  	"time"
    14  
    15  	"github.com/grafana/dskit/backoff"
    16  	"github.com/mwitkow/go-conntrack"
    17  
    18  	"github.com/go-kit/log"
    19  	"github.com/go-kit/log/level"
    20  	"github.com/grafana/loki/clients/pkg/promtail/scrapeconfig"
    21  	"github.com/grafana/loki/clients/pkg/promtail/targets/syslog/syslogparser"
    22  	"github.com/influxdata/go-syslog/v3"
    23  	"github.com/prometheus/prometheus/model/labels"
    24  )
    25  
    26  var (
    27  	protocolUDP = "udp"
    28  	protocolTCP = "tcp"
    29  )
    30  
    31  type Transport interface {
    32  	Run() error
    33  	Addr() net.Addr
    34  	Ready() bool
    35  	Close() error
    36  	Wait()
    37  }
    38  
    39  type handleMessage func(labels.Labels, syslog.Message)
    40  type handleMessageError func(error)
    41  
    42  type baseTransport struct {
    43  	config *scrapeconfig.SyslogTargetConfig
    44  	logger log.Logger
    45  
    46  	openConnections *sync.WaitGroup
    47  
    48  	handleMessage      handleMessage
    49  	handleMessageError handleMessageError
    50  
    51  	ctx       context.Context
    52  	ctxCancel context.CancelFunc
    53  }
    54  
    55  func (t *baseTransport) close() {
    56  	t.ctxCancel()
    57  }
    58  
    59  // Ready implements SyslogTransport
    60  func (t *baseTransport) Ready() bool {
    61  	return t.ctx.Err() == nil
    62  }
    63  
    64  func (t *baseTransport) idleTimeout() time.Duration {
    65  	if t.config.IdleTimeout != 0 {
    66  		return t.config.IdleTimeout
    67  	}
    68  	return defaultIdleTimeout
    69  }
    70  
    71  func (t *baseTransport) maxMessageLength() int {
    72  	if t.config.MaxMessageLength != 0 {
    73  		return t.config.MaxMessageLength
    74  	}
    75  	return defaultMaxMessageLength
    76  }
    77  
    78  func (t *baseTransport) connectionLabels(ip string) labels.Labels {
    79  	lb := labels.NewBuilder(nil)
    80  	for k, v := range t.config.Labels {
    81  		lb.Set(string(k), string(v))
    82  	}
    83  
    84  	lb.Set("__syslog_connection_ip_address", ip)
    85  	lb.Set("__syslog_connection_hostname", lookupAddr(ip))
    86  
    87  	return lb.Labels()
    88  }
    89  
    90  func ipFromConn(c net.Conn) net.IP {
    91  	switch addr := c.RemoteAddr().(type) {
    92  	case *net.TCPAddr:
    93  		return addr.IP
    94  	}
    95  
    96  	return nil
    97  }
    98  
    99  func lookupAddr(addr string) string {
   100  	names, _ := net.LookupAddr(addr)
   101  	return strings.Join(names, ",")
   102  }
   103  
   104  func newBaseTransport(config *scrapeconfig.SyslogTargetConfig, handleMessage handleMessage, handleError handleMessageError, logger log.Logger) *baseTransport {
   105  	ctx, cancel := context.WithCancel(context.Background())
   106  	return &baseTransport{
   107  		config:             config,
   108  		logger:             logger,
   109  		openConnections:    new(sync.WaitGroup),
   110  		handleMessage:      handleMessage,
   111  		handleMessageError: handleError,
   112  		ctx:                ctx,
   113  		ctxCancel:          cancel,
   114  	}
   115  }
   116  
   117  type idleTimeoutConn struct {
   118  	net.Conn
   119  	idleTimeout time.Duration
   120  }
   121  
   122  func (c *idleTimeoutConn) Write(p []byte) (int, error) {
   123  	c.setDeadline()
   124  	return c.Conn.Write(p)
   125  }
   126  
   127  func (c *idleTimeoutConn) Read(b []byte) (int, error) {
   128  	c.setDeadline()
   129  	return c.Conn.Read(b)
   130  }
   131  
   132  func (c *idleTimeoutConn) setDeadline() {
   133  	_ = c.Conn.SetDeadline(time.Now().Add(c.idleTimeout))
   134  }
   135  
   136  type ConnPipe struct {
   137  	addr net.Addr
   138  	*io.PipeReader
   139  	*io.PipeWriter
   140  }
   141  
   142  func NewConnPipe(addr net.Addr) *ConnPipe {
   143  	pr, pw := io.Pipe()
   144  	return &ConnPipe{
   145  		addr:       addr,
   146  		PipeReader: pr,
   147  		PipeWriter: pw,
   148  	}
   149  }
   150  
   151  func (pipe *ConnPipe) Close() error {
   152  	if err := pipe.PipeWriter.Close(); err != nil {
   153  		return err
   154  	}
   155  	return nil
   156  }
   157  
   158  type TCPTransport struct {
   159  	*baseTransport
   160  	listener net.Listener
   161  }
   162  
   163  func NewSyslogTCPTransport(config *scrapeconfig.SyslogTargetConfig, handleMessage handleMessage, handleError handleMessageError, logger log.Logger) Transport {
   164  	return &TCPTransport{
   165  		baseTransport: newBaseTransport(config, handleMessage, handleError, logger),
   166  	}
   167  }
   168  
   169  // Run implements SyslogTransport
   170  func (t *TCPTransport) Run() error {
   171  	l, err := net.Listen(protocolTCP, t.config.ListenAddress)
   172  	l = conntrack.NewListener(l, conntrack.TrackWithName("syslog_target/"+t.config.ListenAddress))
   173  	if err != nil {
   174  		return fmt.Errorf("error setting up syslog target: %w", err)
   175  	}
   176  
   177  	tlsEnabled := t.config.TLSConfig.CertFile != "" || t.config.TLSConfig.KeyFile != "" || t.config.TLSConfig.CAFile != ""
   178  	if tlsEnabled {
   179  		tlsConfig, err := newTLSConfig(t.config.TLSConfig.CertFile, t.config.TLSConfig.KeyFile, t.config.TLSConfig.CAFile)
   180  		if err != nil {
   181  			return fmt.Errorf("error setting up syslog target: %w", err)
   182  		}
   183  		l = tls.NewListener(l, tlsConfig)
   184  	}
   185  
   186  	t.listener = l
   187  	level.Info(t.logger).Log("msg", "syslog listening on address", "address", t.Addr().String(), "protocol", protocolTCP, "tls", tlsEnabled)
   188  
   189  	t.openConnections.Add(1)
   190  	go t.acceptConnections()
   191  
   192  	return nil
   193  }
   194  
   195  func newTLSConfig(certFile string, keyFile string, caFile string) (*tls.Config, error) {
   196  	if certFile == "" || keyFile == "" {
   197  		return nil, fmt.Errorf("certificate and key files are required")
   198  	}
   199  
   200  	certs, err := tls.LoadX509KeyPair(certFile, keyFile)
   201  	if err != nil {
   202  		return nil, fmt.Errorf("unable to load server certificate or key: %w", err)
   203  	}
   204  
   205  	tlsConfig := &tls.Config{
   206  		Certificates: []tls.Certificate{certs},
   207  	}
   208  
   209  	if caFile != "" {
   210  		caCert, err := ioutil.ReadFile(caFile)
   211  		if err != nil {
   212  			return nil, fmt.Errorf("unable to load client CA certificate: %w", err)
   213  		}
   214  
   215  		caCertPool := x509.NewCertPool()
   216  		if ok := caCertPool.AppendCertsFromPEM(caCert); !ok {
   217  			return nil, fmt.Errorf("unable to parse client CA certificate")
   218  		}
   219  
   220  		tlsConfig.ClientCAs = caCertPool
   221  		tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
   222  	}
   223  
   224  	return tlsConfig, nil
   225  }
   226  
   227  func (t *TCPTransport) acceptConnections() {
   228  	defer t.openConnections.Done()
   229  
   230  	l := log.With(t.logger, "address", t.listener.Addr().String())
   231  
   232  	backoff := backoff.New(t.ctx, backoff.Config{
   233  		MinBackoff: 5 * time.Millisecond,
   234  		MaxBackoff: 1 * time.Second,
   235  	})
   236  
   237  	for {
   238  		c, err := t.listener.Accept()
   239  		if err != nil {
   240  			if !t.Ready() {
   241  				level.Info(l).Log("msg", "syslog server shutting down", "protocol", protocolTCP, "err", t.ctx.Err())
   242  				return
   243  			}
   244  
   245  			if ne, ok := err.(net.Error); ok && ne.Temporary() {
   246  				level.Warn(l).Log("msg", "failed to accept syslog connection", "err", err, "num_retries", backoff.NumRetries())
   247  				backoff.Wait()
   248  				continue
   249  			}
   250  
   251  			level.Error(l).Log("msg", "failed to accept syslog connection. quiting", "err", err)
   252  			return
   253  		}
   254  		backoff.Reset()
   255  
   256  		t.openConnections.Add(1)
   257  		go t.handleConnection(c)
   258  	}
   259  
   260  }
   261  
   262  func (t *TCPTransport) handleConnection(cn net.Conn) {
   263  	defer t.openConnections.Done()
   264  
   265  	c := &idleTimeoutConn{cn, t.idleTimeout()}
   266  
   267  	handlerCtx, cancel := context.WithCancel(t.ctx)
   268  	defer cancel()
   269  	go func() {
   270  		<-handlerCtx.Done()
   271  		_ = c.Close()
   272  	}()
   273  
   274  	lbs := t.connectionLabels(ipFromConn(c).String())
   275  
   276  	err := syslogparser.ParseStream(c, func(result *syslog.Result) {
   277  		if err := result.Error; err != nil {
   278  			t.handleMessageError(err)
   279  			return
   280  		}
   281  		t.handleMessage(lbs.Copy(), result.Message)
   282  	}, t.maxMessageLength())
   283  
   284  	if err != nil {
   285  		level.Warn(t.logger).Log("msg", "error initializing syslog stream", "err", err)
   286  	}
   287  }
   288  
   289  // Close implements SyslogTransport
   290  func (t *TCPTransport) Close() error {
   291  	t.baseTransport.close()
   292  	return t.listener.Close()
   293  }
   294  
   295  // Wait implements SyslogTransport
   296  func (t *TCPTransport) Wait() {
   297  	t.openConnections.Wait()
   298  }
   299  
   300  // Addr implements SyslogTransport
   301  func (t *TCPTransport) Addr() net.Addr {
   302  	return t.listener.Addr()
   303  }
   304  
   305  type UDPTransport struct {
   306  	*baseTransport
   307  	udpConn *net.UDPConn
   308  }
   309  
   310  func NewSyslogUDPTransport(config *scrapeconfig.SyslogTargetConfig, handleMessage handleMessage, handleError handleMessageError, logger log.Logger) Transport {
   311  	return &UDPTransport{
   312  		baseTransport: newBaseTransport(config, handleMessage, handleError, logger),
   313  	}
   314  }
   315  
   316  // Run implements SyslogTransport
   317  func (t *UDPTransport) Run() error {
   318  	var err error
   319  	addr, err := net.ResolveUDPAddr(protocolUDP, t.config.ListenAddress)
   320  	if err != nil {
   321  		return fmt.Errorf("error resolving UDP address: %w", err)
   322  	}
   323  	t.udpConn, err = net.ListenUDP(protocolUDP, addr)
   324  	if err != nil {
   325  		return fmt.Errorf("error setting up syslog target: %w", err)
   326  	}
   327  	_ = t.udpConn.SetReadBuffer(1024 * 1024)
   328  	level.Info(t.logger).Log("msg", "syslog listening on address", "address", t.Addr().String(), "protocol", protocolUDP)
   329  
   330  	t.openConnections.Add(1)
   331  	go t.acceptPackets()
   332  	return nil
   333  }
   334  
   335  // Close implements SyslogTransport
   336  func (t *UDPTransport) Close() error {
   337  	t.baseTransport.close()
   338  	return t.udpConn.Close()
   339  }
   340  
   341  func (t *UDPTransport) acceptPackets() {
   342  	defer t.openConnections.Done()
   343  
   344  	var (
   345  		n    int
   346  		addr net.Addr
   347  		err  error
   348  	)
   349  	streams := make(map[string]*ConnPipe)
   350  	buf := make([]byte, t.maxMessageLength())
   351  
   352  	for {
   353  		if !t.Ready() {
   354  			level.Info(t.logger).Log("msg", "syslog server shutting down", "protocol", protocolUDP, "err", t.ctx.Err())
   355  			for _, stream := range streams {
   356  				if err = stream.Close(); err != nil {
   357  					level.Error(t.logger).Log("msg", "failed to close pipe", "err", err)
   358  				}
   359  			}
   360  			return
   361  		}
   362  		n, addr, err = t.udpConn.ReadFrom(buf)
   363  		if n <= 0 && err != nil {
   364  			level.Warn(t.logger).Log("msg", "failed to read packets", "addr", addr, "err", err)
   365  			continue
   366  		}
   367  
   368  		stream, ok := streams[addr.String()]
   369  		if !ok {
   370  			stream = NewConnPipe(addr)
   371  			streams[addr.String()] = stream
   372  			t.openConnections.Add(1)
   373  			go t.handleRcv(stream)
   374  		}
   375  		if _, err := stream.Write(buf[:n]); err != nil {
   376  			level.Warn(t.logger).Log("msg", "failed to write to stream", "addr", addr, "err", err)
   377  		}
   378  	}
   379  }
   380  
   381  func (t *UDPTransport) handleRcv(c *ConnPipe) {
   382  	defer t.openConnections.Done()
   383  
   384  	lbs := t.connectionLabels(c.addr.String())
   385  	err := syslogparser.ParseStream(c, func(result *syslog.Result) {
   386  		if err := result.Error; err != nil {
   387  			t.handleMessageError(err)
   388  		} else {
   389  			t.handleMessage(lbs.Copy(), result.Message)
   390  		}
   391  	}, t.maxMessageLength())
   392  
   393  	if err != nil {
   394  		level.Warn(t.logger).Log("msg", "error parsing syslog stream", "err", err)
   395  	}
   396  }
   397  
   398  // Wait implements SyslogTransport
   399  func (t *UDPTransport) Wait() {
   400  	t.openConnections.Wait()
   401  }
   402  
   403  // Addr implements SyslogTransport
   404  func (t *UDPTransport) Addr() net.Addr {
   405  	return t.udpConn.LocalAddr()
   406  }