123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- // Package syslog provides the logdriver for forwarding server logs to syslog endpoints.
- package syslog // import "github.com/docker/docker/daemon/logger/syslog"
- import (
- "crypto/tls"
- "errors"
- "fmt"
- "net"
- "net/url"
- "os"
- "strconv"
- "strings"
- "time"
- syslog "github.com/RackSec/srslog"
- "github.com/docker/docker/daemon/logger"
- "github.com/docker/docker/daemon/logger/loggerutils"
- "github.com/docker/go-connections/tlsconfig"
- )
- const (
- name = "syslog"
- secureProto = "tcp+tls"
- defaultPort = "514"
- )
- var facilities = map[string]syslog.Priority{
- "kern": syslog.LOG_KERN,
- "user": syslog.LOG_USER,
- "mail": syslog.LOG_MAIL,
- "daemon": syslog.LOG_DAEMON,
- "auth": syslog.LOG_AUTH,
- "syslog": syslog.LOG_SYSLOG,
- "lpr": syslog.LOG_LPR,
- "news": syslog.LOG_NEWS,
- "uucp": syslog.LOG_UUCP,
- "cron": syslog.LOG_CRON,
- "authpriv": syslog.LOG_AUTHPRIV,
- "ftp": syslog.LOG_FTP,
- "local0": syslog.LOG_LOCAL0,
- "local1": syslog.LOG_LOCAL1,
- "local2": syslog.LOG_LOCAL2,
- "local3": syslog.LOG_LOCAL3,
- "local4": syslog.LOG_LOCAL4,
- "local5": syslog.LOG_LOCAL5,
- "local6": syslog.LOG_LOCAL6,
- "local7": syslog.LOG_LOCAL7,
- }
- type syslogger struct {
- writer *syslog.Writer
- }
- func init() {
- if err := logger.RegisterLogDriver(name, New); err != nil {
- panic(err)
- }
- if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil {
- panic(err)
- }
- }
- // rsyslog uses appname part of syslog message to fill in an %syslogtag% template
- // attribute in rsyslog.conf. In order to be backward compatible to rfc3164
- // tag will be also used as an appname
- func rfc5424formatterWithAppNameAsTag(p syslog.Priority, hostname, tag, content string) string {
- timestamp := time.Now().Format(time.RFC3339)
- pid := os.Getpid()
- msg := fmt.Sprintf("<%d>%d %s %s %s %d %s - %s",
- p, 1, timestamp, hostname, tag, pid, tag, content)
- return msg
- }
- // The timestamp field in rfc5424 is derived from rfc3339. Whereas rfc3339 makes allowances
- // for multiple syntaxes, there are further restrictions in rfc5424, i.e., the maximum
- // resolution is limited to "TIME-SECFRAC" which is 6 (microsecond resolution)
- func rfc5424microformatterWithAppNameAsTag(p syslog.Priority, hostname, tag, content string) string {
- timestamp := time.Now().Format("2006-01-02T15:04:05.000000Z07:00")
- pid := os.Getpid()
- msg := fmt.Sprintf("<%d>%d %s %s %s %d %s - %s",
- p, 1, timestamp, hostname, tag, pid, tag, content)
- return msg
- }
- // New creates a syslog logger using the configuration passed in on
- // the context. Supported context configuration variables are
- // syslog-address, syslog-facility, syslog-format.
- func New(info logger.Info) (logger.Logger, error) {
- tag, err := loggerutils.ParseLogTag(info, loggerutils.DefaultTemplate)
- if err != nil {
- return nil, err
- }
- proto, address, err := parseAddress(info.Config["syslog-address"])
- if err != nil {
- return nil, err
- }
- facility, err := parseFacility(info.Config["syslog-facility"])
- if err != nil {
- return nil, err
- }
- syslogFormatter, syslogFramer, err := parseLogFormat(info.Config["syslog-format"], proto)
- if err != nil {
- return nil, err
- }
- var log *syslog.Writer
- if proto == secureProto {
- tlsConfig, tlsErr := parseTLSConfig(info.Config)
- if tlsErr != nil {
- return nil, tlsErr
- }
- log, err = syslog.DialWithTLSConfig(proto, address, facility, tag, tlsConfig)
- } else {
- log, err = syslog.Dial(proto, address, facility, tag)
- }
- if err != nil {
- return nil, err
- }
- log.SetFormatter(syslogFormatter)
- log.SetFramer(syslogFramer)
- return &syslogger{
- writer: log,
- }, nil
- }
- func (s *syslogger) Log(msg *logger.Message) error {
- if len(msg.Line) == 0 {
- return nil
- }
- line := string(msg.Line)
- source := msg.Source
- logger.PutMessage(msg)
- if source == "stderr" {
- return s.writer.Err(line)
- }
- return s.writer.Info(line)
- }
- func (s *syslogger) Close() error {
- return s.writer.Close()
- }
- func (s *syslogger) Name() string {
- return name
- }
- func parseAddress(address string) (string, string, error) {
- if address == "" {
- return "", "", nil
- }
- addr, err := url.Parse(address)
- if err != nil {
- return "", "", err
- }
- // unix and unixgram socket validation
- if addr.Scheme == "unix" || addr.Scheme == "unixgram" {
- if _, err := os.Stat(addr.Path); err != nil {
- return "", "", err
- }
- return addr.Scheme, addr.Path, nil
- }
- if addr.Scheme != "udp" && addr.Scheme != "tcp" && addr.Scheme != secureProto {
- return "", "", fmt.Errorf("unsupported scheme: '%s'", addr.Scheme)
- }
- // here we process tcp|udp
- host := addr.Host
- if _, _, err := net.SplitHostPort(host); err != nil {
- if !strings.Contains(err.Error(), "missing port in address") {
- return "", "", err
- }
- host = net.JoinHostPort(host, defaultPort)
- }
- return addr.Scheme, host, nil
- }
- // ValidateLogOpt looks for syslog specific log options
- // syslog-address, syslog-facility.
- func ValidateLogOpt(cfg map[string]string) error {
- for key := range cfg {
- switch key {
- case "env":
- case "env-regex":
- case "labels":
- case "labels-regex":
- case "syslog-address":
- case "syslog-facility":
- case "syslog-tls-ca-cert":
- case "syslog-tls-cert":
- case "syslog-tls-key":
- case "syslog-tls-skip-verify":
- case "tag":
- case "syslog-format":
- default:
- return fmt.Errorf("unknown log opt '%s' for syslog log driver", key)
- }
- }
- if _, _, err := parseAddress(cfg["syslog-address"]); err != nil {
- return err
- }
- if _, err := parseFacility(cfg["syslog-facility"]); err != nil {
- return err
- }
- if _, _, err := parseLogFormat(cfg["syslog-format"], ""); err != nil {
- return err
- }
- return nil
- }
- func parseFacility(facility string) (syslog.Priority, error) {
- if facility == "" {
- return syslog.LOG_DAEMON, nil
- }
- if syslogFacility, valid := facilities[facility]; valid {
- return syslogFacility, nil
- }
- fInt, err := strconv.Atoi(facility)
- if err == nil && 0 <= fInt && fInt <= 23 {
- return syslog.Priority(fInt << 3), nil
- }
- return syslog.Priority(0), errors.New("invalid syslog facility")
- }
- func parseTLSConfig(cfg map[string]string) (*tls.Config, error) {
- _, skipVerify := cfg["syslog-tls-skip-verify"]
- opts := tlsconfig.Options{
- CAFile: cfg["syslog-tls-ca-cert"],
- CertFile: cfg["syslog-tls-cert"],
- KeyFile: cfg["syslog-tls-key"],
- InsecureSkipVerify: skipVerify,
- }
- return tlsconfig.Client(opts)
- }
- func parseLogFormat(logFormat, proto string) (syslog.Formatter, syslog.Framer, error) {
- switch logFormat {
- case "":
- return syslog.UnixFormatter, syslog.DefaultFramer, nil
- case "rfc3164":
- return syslog.RFC3164Formatter, syslog.DefaultFramer, nil
- case "rfc5424":
- if proto == secureProto {
- return rfc5424formatterWithAppNameAsTag, syslog.RFC5425MessageLengthFramer, nil
- }
- return rfc5424formatterWithAppNameAsTag, syslog.DefaultFramer, nil
- case "rfc5424micro":
- if proto == secureProto {
- return rfc5424microformatterWithAppNameAsTag, syslog.RFC5425MessageLengthFramer, nil
- }
- return rfc5424microformatterWithAppNameAsTag, syslog.DefaultFramer, nil
- default:
- return nil, nil, errors.New("Invalid syslog format")
- }
- }
|