syslog.go 6.6 KB

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