Vendor new syslog library with TLS support.

The syslog package in the stdlib is not maintained anymore.

Signed-off-by: David Calavera <david.calavera@gmail.com>
This commit is contained in:
David Calavera 2016-01-08 12:35:35 -05:00
parent 3a8d694e01
commit 145f020122
12 changed files with 663 additions and 0 deletions

View File

@ -23,6 +23,7 @@ clone git golang.org/x/net 47990a1ba55743e6ef1affd3a14e5bac8553615d https://gith
clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3
clone git github.com/docker/go-connections v0.1.2
clone git github.com/docker/engine-api v0.1.3
clone git github.com/RackSec/srslog 6eb773f331e46fbba8eecb8e794e635e75fc04de
#get libnetwork packages
clone git github.com/docker/libnetwork 9f0563ea8f430d8828553aac97161cbff4056436

View File

@ -0,0 +1 @@
.cover

View File

@ -0,0 +1,13 @@
sudo: required
dist: trusty
group: edge
language: go
go:
- 1.5
script:
- |
go get ./...
go test -v ./...
notifications:
slack:
secure: dtDue9gP6CRR1jYjEf6raXXFak3QKGcCFvCf5mfvv5XScdpmc3udwgqc5TdyjC0goaC9OK/4jTcCD30dYZm/u6ux3E9mo3xwMl2xRLHx76p5r9rSQtloH19BDwA2+A+bpDfFQVz05k2YXuTiGSvNMMdwzx+Dr294Sl/z43RFB4+b9/R/6LlFpRW89IwftvpLAFnBy4K/ZcspQzKM+rQfQTL5Kk+iZ/KBsuR/VziDq6MoJ8t43i4ee8vwS06vFBKDbUiZ4FIZpLgc2RAL5qso5aWRKYXL6waXfoKHZWKPe0w4+9IY1rDJxG1jEb7YGgcbLaF9xzPRRs2b2yO/c87FKpkh6PDgYHfLjpgXotCoojZrL4p1x6MI1ldJr3NhARGPxS9r4liB9n6Y5nD+ErXi1IMf55fuUHcPY27Jc0ySeLFeM6cIWJ8OhFejCgGw6a5DnnmJo0PqopsaBDHhadpLejT1+K6bL2iGkT4SLcVNuRGLs+VyuNf1+5XpkWZvy32vquO7SZOngLLBv+GIem+t3fWm0Z9s/0i1uRCQei1iUutlYjoV/LBd35H2rhob4B5phIuJin9kb0zbHf6HnaoN0CtN8r0d8G5CZiInVlG5Xcid5Byb4dddf5U2EJTDuCMVyyiM7tcnfjqw9UbVYNxtYM9SzcqIq+uVqM8pYL9xSec=

View File

@ -0,0 +1,50 @@
# Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of
fostering an open and welcoming community, we pledge to respect all people who
contribute through reporting issues, posting feature requests, updating
documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free
experience for everyone, regardless of level of experience, gender, gender
identity and expression, sexual orientation, disability, personal appearance,
body size, race, ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic
addresses, without explicit permission
* Other unethical or unprofessional conduct
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to
fairly and consistently applying these principles to every aspect of managing
this project. Project maintainers who do not follow or enforce the Code of
Conduct may be permanently removed from the project team.
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting a project maintainer at [sirsean@gmail.com]. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. Maintainers are
obligated to maintain confidentiality with regard to the reporter of an
incident.
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 1.3.0, available at
[http://contributor-covenant.org/version/1/3/0/][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/3/0/

View File

@ -0,0 +1,27 @@
Copyright (c) 2015 Rackspace. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,131 @@
[![Build Status](https://travis-ci.org/RackSec/srslog.svg?branch=master)](https://travis-ci.org/RackSec/srslog)
# srslog
Go has a `syslog` package in the standard library, but it has the following
shortcomings:
1. It doesn't have TLS support
2. [According to bradfitz on the Go team, it is no longer being maintained.](https://github.com/golang/go/issues/13449#issuecomment-161204716)
I agree that it doesn't need to be in the standard library. So, I've
followed Brad's suggestion and have made a separate project to handle syslog.
This code was taken directly from the Go project as a base to start from.
However, this _does_ have TLS support.
# Usage
Basic usage retains the same interface as the original `syslog` package. We
only added to the interface where required to support new functionality.
Switch from the standard library:
```
import(
//"log/syslog"
syslog "github.com/RackSec/srslog"
)
```
You can still use it for local syslog:
```
w, err := syslog.Dial("", "", syslog.LOG_ERR, "testtag")
```
Or to unencrypted UDP:
```
w, err := syslog.Dial("udp", "192.168.0.50:514", syslog.LOG_ERR, "testtag")
```
Or to unencrypted TCP:
```
w, err := syslog.Dial("tcp", "192.168.0.51:514", syslog.LOG_ERR, "testtag")
```
But now you can also send messages via TLS-encrypted TCP:
```
w, err := syslog.DialWithTLSCertPath("tcp+tls", "192.168.0.52:514", syslog.LOG_ERR, "testtag", "/path/to/servercert.pem")
```
And if you need more control over your TLS configuration :
```
pool := x509.NewCertPool()
serverCert, err := ioutil.ReadFile("/path/to/servercert.pem")
if err != nil {
return nil, err
}
pool.AppendCertsFromPEM(serverCert)
config := tls.Config{
RootCAs: pool,
}
w, err := DialWithTLSConfig(network, raddr, priority, tag, &config)
```
(Note that in both TLS cases, this uses a self-signed certificate, where the
remote syslog server has the keypair and the client has only the public key.)
And then to write log messages, continue like so:
```
if err != nil {
log.Fatal("failed to connect to syslog:", err)
}
defer w.Close()
w.Alert("this is an alert")
w.Crit("this is critical")
w.Err("this is an error")
w.Warning("this is a warning")
w.Notice("this is a notice")
w.Info("this is info")
w.Debug("this is debug")
w.Write([]byte("these are some bytes"))
```
# Generating TLS Certificates
We've provided a script that you can use to generate a self-signed keypair:
```
pip install cryptography
python script/gen-certs.py
```
That outputs the public key and private key to standard out. Put those into
`.pem` files. (And don't put them into any source control. The certificate in
the `test` directory is used by the unit tests, and please do not actually use
it anywhere else.)
# Running Tests
Run the tests as usual:
```
go test
```
But we've also provided a test coverage script that will show you which
lines of code are not covered:
```
script/coverage --html
```
That will open a new browser tab showing coverage information.
# License
This project uses the New BSD License, the same as the Go project itself.
# Code of Conduct
Please note that this project is released with a Contributor Code of Conduct.
By participating in this project you agree to abide by its terms.

View File

@ -0,0 +1,68 @@
package srslog
import (
"errors"
)
// Priority is a combination of the syslog facility and
// severity. For example, LOG_ALERT | LOG_FTP sends an alert severity
// message from the FTP facility. The default severity is LOG_EMERG;
// the default facility is LOG_KERN.
type Priority int
const severityMask = 0x07
const facilityMask = 0xf8
const (
// Severity.
// From /usr/include/sys/syslog.h.
// These are the same on Linux, BSD, and OS X.
LOG_EMERG Priority = iota
LOG_ALERT
LOG_CRIT
LOG_ERR
LOG_WARNING
LOG_NOTICE
LOG_INFO
LOG_DEBUG
)
const (
// Facility.
// From /usr/include/sys/syslog.h.
// These are the same up to LOG_FTP on Linux, BSD, and OS X.
LOG_KERN Priority = iota << 3
LOG_USER
LOG_MAIL
LOG_DAEMON
LOG_AUTH
LOG_SYSLOG
LOG_LPR
LOG_NEWS
LOG_UUCP
LOG_CRON
LOG_AUTHPRIV
LOG_FTP
_ // unused
_ // unused
_ // unused
_ // unused
LOG_LOCAL0
LOG_LOCAL1
LOG_LOCAL2
LOG_LOCAL3
LOG_LOCAL4
LOG_LOCAL5
LOG_LOCAL6
LOG_LOCAL7
)
func validatePriority(p Priority) error {
if p < 0 || p > LOG_LOCAL7|LOG_DEBUG {
return errors.New("log/syslog: invalid priority")
} else {
return nil
}
}

View File

@ -0,0 +1,53 @@
package srslog
import (
"crypto/tls"
"net"
)
func (w Writer) getDialer() func() (serverConn, string, error) {
dialers := map[string]func() (serverConn, string, error){
"": w.unixDialer,
"tcp+tls": w.tlsDialer,
}
dialer, ok := dialers[w.network]
if !ok {
dialer = w.basicDialer
}
return dialer
}
func (w Writer) unixDialer() (serverConn, string, error) {
sc, err := unixSyslog()
hostname := w.hostname
if hostname == "" {
hostname = "localhost"
}
return sc, hostname, err
}
func (w Writer) tlsDialer() (serverConn, string, error) {
c, err := tls.Dial("tcp", w.raddr, w.tlsConfig)
var sc serverConn
hostname := w.hostname
if err == nil {
sc = &netConn{conn: c}
if hostname == "" {
hostname = c.LocalAddr().String()
}
}
return sc, hostname, err
}
func (w Writer) basicDialer() (serverConn, string, error) {
c, err := net.Dial(w.network, w.raddr)
var sc serverConn
hostname := w.hostname
if err == nil {
sc = &netConn{conn: c}
if hostname == "" {
hostname = c.LocalAddr().String()
}
}
return sc, hostname, err
}

View File

@ -0,0 +1,24 @@
package srslog
import (
"fmt"
"net"
"os"
"time"
)
type netConn struct {
conn net.Conn
}
func (n *netConn) writeString(p Priority, hostname, tag, msg string) error {
timestamp := time.Now().Format(time.RFC3339)
_, err := fmt.Fprintf(n.conn, "<%d>%s %s %s[%d]: %s",
p, timestamp, hostname,
tag, os.Getpid(), msg)
return err
}
func (n *netConn) close() error {
return n.conn.Close()
}

View File

@ -0,0 +1,96 @@
package srslog
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"log"
"os"
)
// This interface and the separate syslog_unix.go file exist for
// Solaris support as implemented by gccgo. On Solaris you can not
// simply open a TCP connection to the syslog daemon. The gccgo
// sources have a syslog_solaris.go file that implements unixSyslog to
// return a type that satisfies this interface and simply calls the C
// library syslog function.
type serverConn interface {
writeString(p Priority, hostname, tag, s string) error
close() error
}
// New establishes a new connection to the system log daemon. Each
// write to the returned Writer sends a log message with the given
// priority and prefix.
func New(priority Priority, tag string) (w *Writer, err error) {
return Dial("", "", priority, tag)
}
// Dial establishes a connection to a log daemon by connecting to
// address raddr on the specified network. Each write to the returned
// Writer sends a log message with the given facility, severity and
// tag.
// If network is empty, Dial will connect to the local syslog server.
func Dial(network, raddr string, priority Priority, tag string) (*Writer, error) {
return DialWithTLSConfig(network, raddr, priority, tag, nil)
}
// DialWithTLSCertPath establishes a secure connection to a log daemon by connecting to
// address raddr on the specified network. It uses certPath to load TLS certificates and configure
// the secure connection.
func DialWithTLSCertPath(network, raddr string, priority Priority, tag, certPath string) (*Writer, error) {
pool := x509.NewCertPool()
serverCert, err := ioutil.ReadFile(certPath)
if err != nil {
return nil, err
}
pool.AppendCertsFromPEM(serverCert)
config := tls.Config{
RootCAs: pool,
}
return DialWithTLSConfig(network, raddr, priority, tag, &config)
}
// DialWithTLSConfig establishes a secure connection to a log daemon by connecting to
// address raddr on the specified network. It uses tlsConfig to configure the secure connection.
func DialWithTLSConfig(network, raddr string, priority Priority, tag string, tlsConfig *tls.Config) (*Writer, error) {
if err := validatePriority(priority); err != nil {
return nil, err
}
if tag == "" {
tag = os.Args[0]
}
hostname, _ := os.Hostname()
w := &Writer{
priority: priority,
tag: tag,
hostname: hostname,
network: network,
raddr: raddr,
tlsConfig: tlsConfig,
}
w.Lock()
defer w.Unlock()
err := w.connect()
if err != nil {
return nil, err
}
return w, err
}
// NewLogger creates a log.Logger whose output is written to
// the system log service with the specified priority. The logFlag
// argument is the flag set passed through to log.New to create
// the Logger.
func NewLogger(p Priority, logFlag int) (*log.Logger, error) {
s, err := New(p, "")
if err != nil {
return nil, err
}
return log.New(s, "", logFlag), nil
}

View File

@ -0,0 +1,47 @@
package srslog
import (
"errors"
"fmt"
"net"
"os"
"time"
)
// unixSyslog opens a connection to the syslog daemon running on the
// local machine using a Unix domain socket.
func unixSyslog() (conn serverConn, err error) {
logTypes := []string{"unixgram", "unix"}
logPaths := []string{"/dev/log", "/var/run/syslog", "/var/run/log"}
for _, network := range logTypes {
for _, path := range logPaths {
conn, err := net.Dial(network, path)
if err != nil {
continue
} else {
return &localConn{conn: conn}, nil
}
}
}
return nil, errors.New("Unix syslog delivery error")
}
type localConn struct {
conn net.Conn
}
func (n *localConn) writeString(p Priority, hostname, tag, msg string) error {
// Compared to the network form at srslog.netConn, the changes are:
// 1. Use time.Stamp instead of time.RFC3339.
// 2. Drop the hostname field from the Fprintf.
timestamp := time.Now().Format(time.Stamp)
_, err := fmt.Fprintf(n.conn, "<%d>%s %s[%d]: %s",
p, timestamp,
tag, os.Getpid(), msg)
return err
}
func (n *localConn) close() error {
return n.conn.Close()
}

View File

@ -0,0 +1,152 @@
package srslog
import (
"crypto/tls"
"strings"
"sync"
)
// A Writer is a connection to a syslog server.
type Writer struct {
sync.Mutex // guards conn
priority Priority
tag string
hostname string
network string
raddr string
tlsConfig *tls.Config
conn serverConn
}
// connect makes a connection to the syslog server.
// It must be called with w.mu held.
func (w *Writer) connect() (err error) {
if w.conn != nil {
// ignore err from close, it makes sense to continue anyway
w.conn.close()
w.conn = nil
}
var conn serverConn
var hostname string
dialer := w.getDialer()
conn, hostname, err = dialer()
if err == nil {
w.conn = conn
w.hostname = hostname
}
return
}
// Write sends a log message to the syslog daemon using the default priority
// passed into `srslog.New` or the `srslog.Dial*` functions.
func (w *Writer) Write(b []byte) (int, error) {
return w.writeAndRetry(w.priority, string(b))
}
// Close closes a connection to the syslog daemon.
func (w *Writer) Close() error {
w.Lock()
defer w.Unlock()
if w.conn != nil {
err := w.conn.close()
w.conn = nil
return err
}
return nil
}
// Emerg logs a message with severity LOG_EMERG; this overrides the default
// priority passed to `srslog.New` and the `srslog.Dial*` functions.
func (w *Writer) Emerg(m string) (err error) {
_, err = w.writeAndRetry(LOG_EMERG, m)
return err
}
// Alert logs a message with severity LOG_ALERT; this overrides the default
// priority passed to `srslog.New` and the `srslog.Dial*` functions.
func (w *Writer) Alert(m string) (err error) {
_, err = w.writeAndRetry(LOG_ALERT, m)
return err
}
// Crit logs a message with severity LOG_CRIT; this overrides the default
// priority passed to `srslog.New` and the `srslog.Dial*` functions.
func (w *Writer) Crit(m string) (err error) {
_, err = w.writeAndRetry(LOG_CRIT, m)
return err
}
// Err logs a message with severity LOG_ERR; this overrides the default
// priority passed to `srslog.New` and the `srslog.Dial*` functions.
func (w *Writer) Err(m string) (err error) {
_, err = w.writeAndRetry(LOG_ERR, m)
return err
}
// Warning logs a message with severity LOG_WARNING; this overrides the default
// priority passed to `srslog.New` and the `srslog.Dial*` functions.
func (w *Writer) Warning(m string) (err error) {
_, err = w.writeAndRetry(LOG_WARNING, m)
return err
}
// Notice logs a message with severity LOG_NOTICE; this overrides the default
// priority passed to `srslog.New` and the `srslog.Dial*` functions.
func (w *Writer) Notice(m string) (err error) {
_, err = w.writeAndRetry(LOG_NOTICE, m)
return err
}
// Info logs a message with severity LOG_INFO; this overrides the default
// priority passed to `srslog.New` and the `srslog.Dial*` functions.
func (w *Writer) Info(m string) (err error) {
_, err = w.writeAndRetry(LOG_INFO, m)
return err
}
// Debug logs a message with severity LOG_DEBUG; this overrides the default
// priority passed to `srslog.New` and the `srslog.Dial*` functions.
func (w *Writer) Debug(m string) (err error) {
_, err = w.writeAndRetry(LOG_DEBUG, m)
return err
}
func (w *Writer) writeAndRetry(p Priority, s string) (int, error) {
pr := (w.priority & facilityMask) | (p & severityMask)
w.Lock()
defer w.Unlock()
if w.conn != nil {
if n, err := w.write(pr, s); err == nil {
return n, err
}
}
if err := w.connect(); err != nil {
return 0, err
}
return w.write(pr, s)
}
// write generates and writes a syslog formatted string. The
// format is as follows: <PRI>TIMESTAMP HOSTNAME TAG[PID]: MSG
func (w *Writer) write(p Priority, msg string) (int, error) {
// ensure it ends in a \n
if !strings.HasSuffix(msg, "\n") {
msg += "\n"
}
err := w.conn.writeString(p, w.hostname, w.tag, msg)
if err != nil {
return 0, err
}
// Note: return the length of the input, not the number of
// bytes printed by Fprintf, because this must behave like
// an io.Writer.
return len(msg), nil
}