Merge remote-tracking branch 'origin/master' into problame/replication_refactor

This commit is contained in:
Christian Schwarz 2019-03-16 14:48:01 +01:00
commit da3ba50a2c
7 changed files with 95 additions and 9 deletions

View File

@ -5,6 +5,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/zrepl/yaml-config" "github.com/zrepl/yaml-config"
"io/ioutil" "io/ioutil"
"log/syslog"
"os" "os"
"reflect" "reflect"
"regexp" "regexp"
@ -38,7 +39,7 @@ func (j JobEnum) Name() string {
case *PullJob: name = v.Name case *PullJob: name = v.Name
case *SourceJob: name = v.Name case *SourceJob: name = v.Name
default: default:
panic(fmt.Sprintf("unknownn job type %T", v)) panic(fmt.Sprintf("unknown job type %T", v))
} }
return name return name
} }
@ -289,6 +290,7 @@ type StdoutLoggingOutlet struct {
type SyslogLoggingOutlet struct { type SyslogLoggingOutlet struct {
LoggingOutletCommon `yaml:",inline"` LoggingOutletCommon `yaml:",inline"`
Facility *SyslogFacility `yaml:"facility,optional,fromdefaults"`
RetryInterval time.Duration `yaml:"retry_interval,positive,default=10s"` RetryInterval time.Duration `yaml:"retry_interval,positive,default=10s"`
} }
@ -315,6 +317,14 @@ type PrometheusMonitoring struct {
Listen string `yaml:"listen"` Listen string `yaml:"listen"`
} }
type SyslogFacility syslog.Priority
func (f *SyslogFacility) SetDefault() {
*f = SyslogFacility(syslog.LOG_LOCAL0)
}
var _ yaml.Defaulter = (*SyslogFacility)(nil)
type GlobalControl struct { type GlobalControl struct {
SockPath string `yaml:"sockpath,default=/var/run/zrepl/control"` SockPath string `yaml:"sockpath,default=/var/run/zrepl/control"`
} }
@ -420,6 +430,40 @@ func (t *MonitoringEnum) UnmarshalYAML(u func(interface{}, bool) error) (err err
return return
} }
func (t *SyslogFacility) UnmarshalYAML(u func(interface{}, bool) error) (err error) {
var s string
if err := u(&s, true); err != nil {
return err
}
var level syslog.Priority
switch s {
case "kern": level = syslog.LOG_KERN
case "user": level = syslog.LOG_USER
case "mail": level = syslog.LOG_MAIL
case "daemon": level = syslog.LOG_DAEMON
case "auth": level = syslog.LOG_AUTH
case "syslog": level = syslog.LOG_SYSLOG
case "lpr": level = syslog.LOG_LPR
case "news": level = syslog.LOG_NEWS
case "uucp": level = syslog.LOG_UUCP
case "cron": level = syslog.LOG_CRON
case "authpriv": level = syslog.LOG_AUTHPRIV
case "ftp": level = syslog.LOG_FTP
case "local0": level = syslog.LOG_LOCAL0
case "local1": level = syslog.LOG_LOCAL1
case "local2": level = syslog.LOG_LOCAL2
case "local3": level = syslog.LOG_LOCAL3
case "local4": level = syslog.LOG_LOCAL4
case "local5": level = syslog.LOG_LOCAL5
case "local6": level = syslog.LOG_LOCAL6
case "local7": level = syslog.LOG_LOCAL7
default:
return fmt.Errorf("invalid syslog level: %q", s)
}
*t = SyslogFacility(level)
return nil
}
var ConfigFileDefaultLocations = []string{ var ConfigFileDefaultLocations = []string{
"/etc/zrepl/zrepl.yml", "/etc/zrepl/zrepl.yml",
"/usr/local/etc/zrepl/zrepl.yml", "/usr/local/etc/zrepl/zrepl.yml",

View File

@ -1,9 +1,11 @@
package config package config
import ( import (
"fmt"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/zrepl/yaml-config" "github.com/zrepl/yaml-config"
"log/syslog"
"testing" "testing"
) )
@ -72,6 +74,36 @@ global:
assert.Equal(t, ":9091", conf.Global.Monitoring[0].Ret.(*PrometheusMonitoring).Listen) assert.Equal(t, ":9091", conf.Global.Monitoring[0].Ret.(*PrometheusMonitoring).Listen)
} }
func TestSyslogLoggingOutletFacility(t *testing.T) {
type SyslogFacilityPriority struct {
Facility string
Priority syslog.Priority
}
syslogFacilitiesPriorities := []SyslogFacilityPriority{
{"", syslog.LOG_LOCAL0}, // default
{"kern", syslog.LOG_KERN}, {"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},
}
for _, sFP := range syslogFacilitiesPriorities {
logcfg := fmt.Sprintf(`
global:
logging:
- type: syslog
level: info
format: human
facility: %s
`, sFP.Facility)
conf := testValidGlobalSection(t, logcfg)
assert.Equal(t, 1, len(*conf.Global.Logging))
assert.True(t, SyslogFacility(sFP.Priority) == *(*conf.Global.Logging)[0].Ret.(*SyslogLoggingOutlet).Facility)
}
}
func TestLoggingOutletEnumList_SetDefaults(t *testing.T) { func TestLoggingOutletEnumList_SetDefaults(t *testing.T) {
e := &LoggingOutletEnumList{} e := &LoggingOutletEnumList{}
var i yaml.Defaulter = e var i yaml.Defaulter = e

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"log/syslog"
"os" "os"
"github.com/mattn/go-isatty" "github.com/mattn/go-isatty"
@ -222,6 +223,7 @@ func parseSyslogOutlet(in *config.SyslogLoggingOutlet, formatter EntryFormatter)
out = &SyslogOutlet{} out = &SyslogOutlet{}
out.Formatter = formatter out.Formatter = formatter
out.Formatter.SetMetadataFlags(MetadataNone) out.Formatter.SetMetadataFlags(MetadataNone)
out.Facility = syslog.Priority(*in.Facility)
out.RetryInterval = in.RetryInterval out.RetryInterval = in.RetryInterval
return out, nil return out, nil
} }

View File

@ -124,6 +124,7 @@ func (h *TCPOutlet) WriteEntry(e logger.Entry) error {
type SyslogOutlet struct { type SyslogOutlet struct {
Formatter EntryFormatter Formatter EntryFormatter
RetryInterval time.Duration RetryInterval time.Duration
Facility syslog.Priority
writer *syslog.Writer writer *syslog.Writer
lastConnectAttempt time.Time lastConnectAttempt time.Time
} }
@ -142,7 +143,7 @@ func (o *SyslogOutlet) WriteEntry(entry logger.Entry) error {
if now.Sub(o.lastConnectAttempt) < o.RetryInterval { if now.Sub(o.lastConnectAttempt) < o.RetryInterval {
return nil // not an error toward logger return nil // not an error toward logger
} }
o.writer, err = syslog.New(syslog.LOG_LOCAL0, "zrepl") o.writer, err = syslog.New(o.Facility, "zrepl")
o.lastConnectAttempt = time.Now() o.lastConnectAttempt = time.Now()
if err != nil { if err != nil {
o.writer = nil o.writer = nil

View File

@ -147,6 +147,8 @@ Can only be specified once.
- minimum :ref:`log level <logging-levels>` - minimum :ref:`log level <logging-levels>`
* - ``format`` * - ``format``
- output :ref:`format <logging-formats>` - output :ref:`format <logging-formats>`
* - ``facility``
- Which syslog facility to use (default = ``local0``)
* - ``retry_interval`` * - ``retry_interval``
- Interval between reconnection attempts to syslog (default = 0) - Interval between reconnection attempts to syslog (default = 0)

View File

@ -76,6 +76,7 @@ Connect
The ``tls`` transport uses TCP + TLS with client authentication using client certificates. The ``tls`` transport uses TCP + TLS with client authentication using client certificates.
The client identity is the common name (CN) presented in the client certificate. The client identity is the common name (CN) presented in the client certificate.
It is recommended to set up a dedicated CA infrastructure for this transport, e.g. using OpenVPN's `EasyRSA <https://github.com/OpenVPN/easy-rsa>`_. It is recommended to set up a dedicated CA infrastructure for this transport, e.g. using OpenVPN's `EasyRSA <https://github.com/OpenVPN/easy-rsa>`_.
For a simple 2-machine setup, see the :ref:`instructions below<transport-tcp+tlsclientauth-2machineopenssl>`. For a simple 2-machine setup, see the :ref:`instructions below<transport-tcp+tlsclientauth-2machineopenssl>`.
@ -85,6 +86,10 @@ Since Go binaries are statically linked, you or your distribution need to recomp
All file paths are resolved relative to the zrepl daemon's working directory. All file paths are resolved relative to the zrepl daemon's working directory.
Specify absolute paths if you are unsure what directory that is (or find out from your init system). Specify absolute paths if you are unsure what directory that is (or find out from your init system).
If intermediate CAs are used, the **full chain** must be present in either in the ``ca`` file or the individual ``cert`` files.
Regardless, the client's certificate must be first in the ``cert`` file, with each following certificate directly certifying the one preceding it (see `TLS's specification <https://tools.ietf.org/html/rfc5246#section-7.4.2>`_).
This is the common default when using a CA management tool.
Serve Serve
~~~~~ ~~~~~
@ -96,9 +101,9 @@ Serve
serve: serve:
type: tls type: tls
listen: ":8888" listen: ":8888"
ca: /etc/zrepl/ca.crt ca: /etc/zrepl/ca.crt
cert: /etc/zrepl/prod.crt cert: /etc/zrepl/prod.fullchain
key: /etc/zrepl/prod.key key: /etc/zrepl/prod.key
client_cns: client_cns:
- "laptop1" - "laptop1"
- "homeserver" - "homeserver"
@ -116,8 +121,8 @@ Connect
connect: connect:
type: tls type: tls
address: "server1.foo.bar:8888" address: "server1.foo.bar:8888"
ca: /etc/zrepl/ca.crt ca: /etc/zrepl/ca.crt
cert: /etc/zrepl/backupserver.crt cert: /etc/zrepl/backupserver.fullchain
key: /etc/zrepl/backupserver.key key: /etc/zrepl/backupserver.key
server_cn: "server1" server_cn: "server1"
dial_timeout: # optional, default 10s dial_timeout: # optional, default 10s

View File

@ -83,8 +83,8 @@ func (l *ClientAuthListener) Accept() (tcpConn *net.TCPConn, tlsConn *tls.Conn,
tlsConn.SetDeadline(time.Time{}) tlsConn.SetDeadline(time.Time{})
peerCerts = tlsConn.ConnectionState().PeerCertificates peerCerts = tlsConn.ConnectionState().PeerCertificates
if len(peerCerts) != 1 { if len(peerCerts) < 1 {
err = errors.New("unexpected number of certificates presented by TLS client") err = errors.New("client must present full RFC5246:7.4.2 TLS client certificate chain")
goto CloseAndErr goto CloseAndErr
} }
cn = peerCerts[0].Subject.CommonName cn = peerCerts[0].Subject.CommonName