github.com/ruphin/docker@v1.10.1/daemon/logger/syslog/syslog.go (about)

     1  // +build linux
     2  
     3  // Package syslog provides the logdriver for forwarding server logs to syslog endpoints.
     4  package syslog
     5  
     6  import (
     7  	"crypto/tls"
     8  	"errors"
     9  	"fmt"
    10  	"net"
    11  	"net/url"
    12  	"os"
    13  	"path"
    14  	"strconv"
    15  	"strings"
    16  
    17  	syslog "github.com/RackSec/srslog"
    18  
    19  	"github.com/Sirupsen/logrus"
    20  	"github.com/docker/docker/daemon/logger"
    21  	"github.com/docker/docker/daemon/logger/loggerutils"
    22  	"github.com/docker/docker/pkg/urlutil"
    23  	"github.com/docker/go-connections/tlsconfig"
    24  )
    25  
    26  const (
    27  	name        = "syslog"
    28  	secureProto = "tcp+tls"
    29  )
    30  
    31  var facilities = map[string]syslog.Priority{
    32  	"kern":     syslog.LOG_KERN,
    33  	"user":     syslog.LOG_USER,
    34  	"mail":     syslog.LOG_MAIL,
    35  	"daemon":   syslog.LOG_DAEMON,
    36  	"auth":     syslog.LOG_AUTH,
    37  	"syslog":   syslog.LOG_SYSLOG,
    38  	"lpr":      syslog.LOG_LPR,
    39  	"news":     syslog.LOG_NEWS,
    40  	"uucp":     syslog.LOG_UUCP,
    41  	"cron":     syslog.LOG_CRON,
    42  	"authpriv": syslog.LOG_AUTHPRIV,
    43  	"ftp":      syslog.LOG_FTP,
    44  	"local0":   syslog.LOG_LOCAL0,
    45  	"local1":   syslog.LOG_LOCAL1,
    46  	"local2":   syslog.LOG_LOCAL2,
    47  	"local3":   syslog.LOG_LOCAL3,
    48  	"local4":   syslog.LOG_LOCAL4,
    49  	"local5":   syslog.LOG_LOCAL5,
    50  	"local6":   syslog.LOG_LOCAL6,
    51  	"local7":   syslog.LOG_LOCAL7,
    52  }
    53  
    54  type syslogger struct {
    55  	writer *syslog.Writer
    56  }
    57  
    58  func init() {
    59  	if err := logger.RegisterLogDriver(name, New); err != nil {
    60  		logrus.Fatal(err)
    61  	}
    62  	if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil {
    63  		logrus.Fatal(err)
    64  	}
    65  }
    66  
    67  // New creates a syslog logger using the configuration passed in on
    68  // the context. Supported context configuration variables are
    69  // syslog-address, syslog-facility, & syslog-tag.
    70  func New(ctx logger.Context) (logger.Logger, error) {
    71  	tag, err := loggerutils.ParseLogTag(ctx, "{{.ID}}")
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  
    76  	proto, address, err := parseAddress(ctx.Config["syslog-address"])
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  
    81  	facility, err := parseFacility(ctx.Config["syslog-facility"])
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	logTag := path.Base(os.Args[0]) + "/" + tag
    87  
    88  	var log *syslog.Writer
    89  	if proto == secureProto {
    90  		tlsConfig, tlsErr := parseTLSConfig(ctx.Config)
    91  		if tlsErr != nil {
    92  			return nil, tlsErr
    93  		}
    94  		log, err = syslog.DialWithTLSConfig(proto, address, facility, logTag, tlsConfig)
    95  	} else {
    96  		log, err = syslog.Dial(proto, address, facility, logTag)
    97  	}
    98  
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	return &syslogger{
   104  		writer: log,
   105  	}, nil
   106  }
   107  
   108  func (s *syslogger) Log(msg *logger.Message) error {
   109  	if msg.Source == "stderr" {
   110  		return s.writer.Err(string(msg.Line))
   111  	}
   112  	return s.writer.Info(string(msg.Line))
   113  }
   114  
   115  func (s *syslogger) Close() error {
   116  	return s.writer.Close()
   117  }
   118  
   119  func (s *syslogger) Name() string {
   120  	return name
   121  }
   122  
   123  func parseAddress(address string) (string, string, error) {
   124  	if address == "" {
   125  		return "", "", nil
   126  	}
   127  	if !urlutil.IsTransportURL(address) {
   128  		return "", "", fmt.Errorf("syslog-address should be in form proto://address, got %v", address)
   129  	}
   130  	url, err := url.Parse(address)
   131  	if err != nil {
   132  		return "", "", err
   133  	}
   134  
   135  	// unix socket validation
   136  	if url.Scheme == "unix" {
   137  		if _, err := os.Stat(url.Path); err != nil {
   138  			return "", "", err
   139  		}
   140  		return url.Scheme, url.Path, nil
   141  	}
   142  
   143  	// here we process tcp|udp
   144  	host := url.Host
   145  	if _, _, err := net.SplitHostPort(host); err != nil {
   146  		if !strings.Contains(err.Error(), "missing port in address") {
   147  			return "", "", err
   148  		}
   149  		host = host + ":514"
   150  	}
   151  
   152  	return url.Scheme, host, nil
   153  }
   154  
   155  // ValidateLogOpt looks for syslog specific log options
   156  // syslog-address, syslog-facility, & syslog-tag.
   157  func ValidateLogOpt(cfg map[string]string) error {
   158  	for key := range cfg {
   159  		switch key {
   160  		case "syslog-address":
   161  		case "syslog-facility":
   162  		case "syslog-tag":
   163  		case "syslog-tls-ca-cert":
   164  		case "syslog-tls-cert":
   165  		case "syslog-tls-key":
   166  		case "syslog-tls-skip-verify":
   167  		case "tag":
   168  		default:
   169  			return fmt.Errorf("unknown log opt '%s' for syslog log driver", key)
   170  		}
   171  	}
   172  	if _, _, err := parseAddress(cfg["syslog-address"]); err != nil {
   173  		return err
   174  	}
   175  	if _, err := parseFacility(cfg["syslog-facility"]); err != nil {
   176  		return err
   177  	}
   178  	return nil
   179  }
   180  
   181  func parseFacility(facility string) (syslog.Priority, error) {
   182  	if facility == "" {
   183  		return syslog.LOG_DAEMON, nil
   184  	}
   185  
   186  	if syslogFacility, valid := facilities[facility]; valid {
   187  		return syslogFacility, nil
   188  	}
   189  
   190  	fInt, err := strconv.Atoi(facility)
   191  	if err == nil && 0 <= fInt && fInt <= 23 {
   192  		return syslog.Priority(fInt << 3), nil
   193  	}
   194  
   195  	return syslog.Priority(0), errors.New("invalid syslog facility")
   196  }
   197  
   198  func parseTLSConfig(cfg map[string]string) (*tls.Config, error) {
   199  	_, skipVerify := cfg["syslog-tls-skip-verify"]
   200  
   201  	opts := tlsconfig.Options{
   202  		CAFile:             cfg["syslog-tls-ca-cert"],
   203  		CertFile:           cfg["syslog-tls-cert"],
   204  		KeyFile:            cfg["syslog-tls-key"],
   205  		InsecureSkipVerify: skipVerify,
   206  	}
   207  
   208  	return tlsconfig.Client(opts)
   209  }