Skip to content

Commit

Permalink
Features: Allow the TCP Input to receive events over a TLS connection
Browse files Browse the repository at this point in the history
We can now receive events on the TCP input with a TLS connection, the
input uses existing type to make sure we have the same naming convention
and code used by outputs that support TLS communication (Elasticsearch
and Logstash).

The configuration will look like this:

```
  host: "localhost:9000"
  ssl.enabled: true
  ssl.verification_mode: full # default
  ssl.supported_protocols: [TLSv1.1]
  ssl.cipher_suites: []
  ssl.certificate_authorities: ["/etc/cacert"]
  ssl.certificate: /etc/mycert.crt
  ssl.key: /etc/mycert.key
  ssl.client_authentication: required

```

One added configuration is `client_authentication`, this option is
only used in the context of server and define how we will force the
authentification, it support the three following options:

- `required`: Assume that the client will provide a certificate and we
will verify it. (default)
- `optional`: If a certificate is given by the client.
- `none`: We don't validate client certificate.

*Note: This commit contains a script to generate certs from a self
signed CA.

Fixes: #6873
  • Loading branch information
ph committed May 17, 2018
1 parent dba6756 commit d686b9b
Show file tree
Hide file tree
Showing 24 changed files with 760 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ https://github.com/elastic/beats/compare/v6.2.3...master[Check the HEAD diff]
- Add Syslog input to ingest RFC3164 Events via TCP and UDP {pull}6842[6842]
- Support MySQL 5.7.19 by mysql/slowlog {pull}6969[6969]
- Correctly join partial log lines when using `docker` input. {pull}6967[6967]
- Add support for TLS with client authentication to the TCP input {pull}7056[7056]

*Heartbeat*

Expand Down
9 changes: 9 additions & 0 deletions filebeat/docs/inputs/input-common-tcp-options.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,12 @@ Specify the characters used to split the incoming events. The default is '\n'.
==== `timeout`

The number of seconds of inactivity before a remote connection is closed. The default is `300s`.

[float]
[id="{beatname_lc}-input-{type}-tcp-ssl"]
===== `ssl`

Configuration options for SSL parameters like the certificate, key and the certificate authorities
to use.

See <<configuration-ssl>> for more information.
9 changes: 9 additions & 0 deletions filebeat/inputsource/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ type Network interface {
type NetworkMetadata struct {
RemoteAddr net.Addr
Truncated bool
TLS *TLSMetadata
}

// TLSMetadata defines information about the current SSL connection.
type TLSMetadata struct {
TLSVersion string
CipherSuite string
ServerName string
PeerCertificates []string
}

// NetworkFunc defines callback executed when a new event is received from a network source.
Expand Down
1 change: 0 additions & 1 deletion filebeat/inputsource/network_metadata.go

This file was deleted.

41 changes: 39 additions & 2 deletions filebeat/inputsource/tcp/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package tcp

import (
"bufio"
"crypto/tls"
"crypto/x509"
"net"
"time"

"github.com/pkg/errors"

"github.com/elastic/beats/filebeat/inputsource"
"github.com/elastic/beats/libbeat/common/transport/tlscommon"
"github.com/elastic/beats/libbeat/logp"
)

Expand All @@ -33,16 +36,18 @@ func newClient(
) *client {
client := &client{
conn: conn,
log: log.With("address", conn.RemoteAddr()),
log: log.With("remote_address", conn.RemoteAddr()),
callback: callback,
done: make(chan struct{}),
splitFunc: splitFunc,
maxMessageSize: maxReadMessage,
timeout: timeout,
metadata: inputsource.NetworkMetadata{
RemoteAddr: conn.RemoteAddr(),
TLS: extractSSLInformation(conn),
},
}
extractSSLInformation(conn)
return client
}

Expand All @@ -63,17 +68,49 @@ func (c *client) handle() error {
}
// This is a user defined limit and we should notify the user.
if IsMaxReadBufferErr(err) {
c.log.Errorw("client errors", "error", err)
c.log.Errorw("client error", "error", err)
}
return errors.Wrap(err, "tcp client error")
}
r.Reset()
c.callback(scanner.Bytes(), c.metadata)
}

// We are out of the scanner, either we reached EOF or another fatal error occured.
// like we failed to complete the TLS handshake or we are missing the client certificate when
// mutual auth is on, which is the default.
if err := scanner.Err(); err != nil {
return err
}

return nil
}

func (c *client) close() {
close(c.done)
c.conn.Close()
}

func extractSSLInformation(c net.Conn) *inputsource.TLSMetadata {
if tls, ok := c.(*tls.Conn); ok {
state := tls.ConnectionState()
return &inputsource.TLSMetadata{
TLSVersion: tlscommon.ResolveTLSVersion(state.Version),
CipherSuite: tlscommon.ResolveCipherSuite(state.CipherSuite),
ServerName: state.ServerName,
PeerCertificates: extractCertificate(state.PeerCertificates),
}
}
return nil
}

func extractCertificate(certificates []*x509.Certificate) []string {
strCertificate := make([]string, len(certificates))
for idx, c := range certificates {
// Ignore errors here, problematics cert have failed
//the handshake at this point.
b, _ := x509.MarshalPKIXPublicKey(c.PublicKey)
strCertificate[idx] = string(b)
}
return strCertificate
}
10 changes: 6 additions & 4 deletions filebeat/inputsource/tcp/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"time"

"github.com/elastic/beats/libbeat/common/cfgtype"
"github.com/elastic/beats/libbeat/common/transport/tlscommon"
)

// Name is the human readable name and identifier.
Expand All @@ -14,10 +15,11 @@ type size uint64

// Config exposes the tcp configuration.
type Config struct {
Host string `config:"host"`
LineDelimiter string `config:"line_delimiter" validate:"nonzero"`
Timeout time.Duration `config:"timeout" validate:"nonzero,positive"`
MaxMessageSize cfgtype.ByteSize `config:"max_message_size" validate:"nonzero,positive"`
Host string `config:"host"`
LineDelimiter string `config:"line_delimiter" validate:"nonzero"`
Timeout time.Duration `config:"timeout" validate:"nonzero,positive"`
MaxMessageSize cfgtype.ByteSize `config:"max_message_size" validate:"nonzero,positive"`
TLS *tlscommon.ServerConfig `config:"ssl"`
}

// Validate validates the Config option for the tcp input.
Expand Down
31 changes: 28 additions & 3 deletions filebeat/inputsource/tcp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ package tcp
import (
"bufio"
"bytes"
"crypto/tls"
"fmt"
"net"
"sync"

"github.com/elastic/beats/filebeat/inputsource"
"github.com/elastic/beats/libbeat/common/transport/tlscommon"
"github.com/elastic/beats/libbeat/logp"
"github.com/elastic/beats/libbeat/outputs/transport"
)

// Server represent a TCP server
Expand All @@ -22,6 +25,7 @@ type Server struct {
done chan struct{}
splitFunc bufio.SplitFunc
log *logp.Logger
tlsConfig *transport.TLSConfig
}

// New creates a new tcp server
Expand All @@ -34,6 +38,11 @@ func New(
return nil, fmt.Errorf("empty line delimiter")
}

tlsConfig, err := tlscommon.LoadTLSServerConfig(config.TLS)
if err != nil {
return nil, err
}

sf := splitFunc([]byte(config.LineDelimiter))
return &Server{
config: config,
Expand All @@ -42,13 +51,14 @@ func New(
done: make(chan struct{}),
splitFunc: sf,
log: logp.NewLogger("tcp").With("address", config.Host),
tlsConfig: tlsConfig,
}, nil
}

// Start listen to the TCP socket.
func (s *Server) Start() error {
var err error
s.Listener, err = net.Listen("tcp", s.config.Host)
s.Listener, err = s.createServer()
if err != nil {
return err
}
Expand Down Expand Up @@ -86,7 +96,7 @@ func (s *Server) run() {
s.config.Timeout,
)

s.log.Debugw("New client", "address", conn.RemoteAddr(), "total", s.clientsCount())
s.log.Debugw("New client", "remote_address", conn.RemoteAddr(), "total", s.clientsCount())
s.wg.Add(1)
go func() {
defer logp.Recover("recovering from a tcp client crash")
Expand All @@ -101,7 +111,13 @@ func (s *Server) run() {
s.log.Debugw("Client error", "error", err)
}

s.log.Debugw("Client disconnected", "address", conn.RemoteAddr(), "total", s.clientsCount())
s.log.Debugw(
"Client disconnected",
"remote_address",
conn.RemoteAddr(),
"total",
s.clientsCount(),
)
}()
}
}
Expand Down Expand Up @@ -142,6 +158,15 @@ func (s *Server) allClients() []*client {
return currentClients
}

func (s *Server) createServer() (net.Listener, error) {
if s.tlsConfig != nil {
t := s.tlsConfig.BuildModuleConfig(s.config.Host)
s.log.Info("Listening over TLS")
return tls.Listen("tcp", s.config.Host, t)
}
return net.Listen("tcp", s.config.Host)
}

func (s *Server) clientsCount() int {
s.RLock()
defer s.RUnlock()
Expand Down
17 changes: 17 additions & 0 deletions filebeat/tests/system/config/certificates/beats1.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICpDCCAYwCCQDgN4JA8Sp4TzANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
b2NhbGhvc3QwHhcNMTgwNTAzMjA0NDQxWhcNMTkwNTAzMjA0NDQxWjAUMRIwEAYD
VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCt
cIgOk6on3kfqqUMaw9l4UUH1kX4d7ocNVbz0UcqazCoo0dXoBcUlL+6wE8RLwKgY
eTJQslTPb8XC3mcoU2pFUUXZ3xOk8Wmq9fT9KHtKQRuiAzsw3xQnA+ofhC9vCU7f
pdDbzp1e42Ixni4ajqvOOqWIAdbsP+janLfGR4n62NDmLDYDbpEzKLzcHaOd5u6B
kMSGhDe4Oq8H4WGrj1C8SZzMH2kPYMr9uMXVAHj3m7q1A6qRcJoS4AD5Rkr+cXgn
AsRLxgWU6l6+2g4MJhjx7GphNueLoZiXV3pKMWOqeBrDWbYg+c07aZWnF9O8YBGx
c8vtp4awm+A0OWfMqpXjAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAHh1wBn3vKXH
Fqc85GCfko2aFy7UYFaXjNTati3RW77yhsHn2PIJzTpPu4BaTms5JTup4hmDfPr1
IJu2ZZ6b+BwdQGngIg6Mx6zA3+4h+JEDiZiJh/v/63EX+4MdasmlBomcrBOo3OWJ
jDIFUJIxsdGtWdR1nsQml77pQf2ECo3u3yV1qvKl2QP8UrJmyU5Vr23Gz9WOCt46
3kejega+bnsOLNcfHBvrv31zwPBzpA6lfCaCc0SURrBmWPM37/8zZZQIFBMYN30M
id2iUSr+0zZ2O0vrzsET/wsWcLBxRDuxEK3iOjE70/ljBMc/RvhfiUpU7pAmyMOc
8DHX1eqqPM4=
-----END CERTIFICATE-----
28 changes: 28 additions & 0 deletions filebeat/tests/system/config/certificates/beats1.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCtcIgOk6on3kfq
qUMaw9l4UUH1kX4d7ocNVbz0UcqazCoo0dXoBcUlL+6wE8RLwKgYeTJQslTPb8XC
3mcoU2pFUUXZ3xOk8Wmq9fT9KHtKQRuiAzsw3xQnA+ofhC9vCU7fpdDbzp1e42Ix
ni4ajqvOOqWIAdbsP+janLfGR4n62NDmLDYDbpEzKLzcHaOd5u6BkMSGhDe4Oq8H
4WGrj1C8SZzMH2kPYMr9uMXVAHj3m7q1A6qRcJoS4AD5Rkr+cXgnAsRLxgWU6l6+
2g4MJhjx7GphNueLoZiXV3pKMWOqeBrDWbYg+c07aZWnF9O8YBGxc8vtp4awm+A0
OWfMqpXjAgMBAAECggEAU0EYXn7th/PAa9lSN+/ZXVMMKXfspSuRson8iP8WFVZ5
ylxnpNfYObCXj/f9GyvgxNawm2HvKiAKOy+NLyNTePC5agsWH/Aue/1S247qF8vH
Gu8YI66BuvS7FuPZ7R/3t42eA1Vb32ehBBZdJSxlNjqaqP3REFjhSsc9xDUk+777
O6wHVSest5cfwmhqhIlbyr5nYcrw6q1WEGc/lbwC5lDJNXrw3JbkMAatadQYNiCx
7BsH8JJBQeNKrKGVXC37ny1wrR3aYnQJFAsPGlRykMhwDHy/wpmFyKgPbDyMyjNp
hDK3L7lKDlwXDexFRTI18alVARI7SIyOaVlT0XvU+QKBgQDikNv09jx5uK/NBg6C
+nIGOKkUfv101aWKfgNtkSqPX0djj6EpKpICq5RCqKcc5GL5ZcZ81/6hnvoY2xX6
QDG7DfFOOsaMzNm/K9pUjtbF1eABUgLaadO+o+dU2K3TsVBekAUx4O2IrJHrWFK6
Tcq+ruohkKcgqLwTnDDtdLS9TQKBgQDD+MuWUMlMC4SSHINGlZuGj09LUTF5RH3p
fHx9aJdAgIxMNgGcl70gMwBQGQKSdOn/zseS8VtdoESO9IOcmG5NsXXITNqIASFd
u4G0cvyn5rb7xbkAQWynLdvnRRloUiVm59YBuEsCo2m9USS48ODU6TxceasW5xsC
LjsAtGPH7wKBgBxXTox3+NypE6Sr60jLF1Rb5hqgQAhLaWfHl5ovHInOu1li8B/8
KUOYPvWPr0fX8eGMr6WSR1HkVxig30DsosvpVJQamhem0F4vmCIXtBoPRPQlVCIK
NuiUDC365NOkTI9nqJ6yqkP8gkxUQhT4AjKUIsmMFLvrH4u8cApkHO/JAoGAaOgn
vAj1KWNFDZ572+48unS/IveM/3jd2n7MeanixiHKeQW0KSrFkJYcxcQNr28s0MbV
6WCQ43bnHIviZJLpAWhNP/N8TLAmN3IoBfxEKnGEZRU8atmbG4eeH5jK+CB5azQS
SQtqBDiMY08r1GEGSpOsv2hWYUVIHQu4hDEM9TsCgYEAgxvDd/gSHkanGPR3mqXt
u8IDUWBw4ORkQ0ncYqmDV6jed3v7uCAM2DHaA4ckK91nGdy05EkpTyKin3b0iRxT
TSjwO+wUlwuqQMXYEESJ5XDxy5WSDlYuvKGUcyxLDYo7OMg0UWM6JpH3c2sRy4pU
XztmTa6m0GXerVSyFgx81M8=
-----END PRIVATE KEY-----
17 changes: 17 additions & 0 deletions filebeat/tests/system/config/certificates/beats2.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICpDCCAYwCCQDGVhMwcBRF9TANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
b2NhbGhvc3QwHhcNMTgwNTAzMjA0NDQzWhcNMTkwNTAzMjA0NDQzWjAUMRIwEAYD
VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDG
8KkEAd+2Oi6QlQOQPy9DREP92iVJcRNESYnTloy3mP/iK1vPeNCvjnPWSAeiuqzm
3iOXIWiV98hk3cAXAg0Gdi/FoNzCJCJqumVu8REBTNbdHlGspMxIyAL+WNdOtTER
21G1BExjjtGki71FR2Z3wdV898zf7yEfJgGhhxCSwQ9NT1C1R++7kWb+HP/TlXWN
4XEM4TF6Czv65+x3HRA0lRVSK6I0Wy0Ct/+Du4RUCAjtOBLj6WkeMw239Wr7jqaw
3vufORWO/qqEXcpdM7LWaSV14Dh+IpQm+Px0q45gZpT93Y3jYEfwBp+QyU4ICHRw
hkBzH0kBEKsxoi3epL1tAgMBAAEwDQYJKoZIhvcNAQELBQADggEBADNfS5ESaEwg
rQ6DBjujxnVwydWLjvkNlByyJ1WTCAsGDRUltzhLK4jIDrvV/xonAnLsVpDIEiFI
O7O1UZ0Z1sLp1Mh9CHma01qmKtLSOC2w7hOKZXdG6wJxrN9s3pmu/K37BhdHZewL
gdaSc3mogsAL852cIVIgjrQjIkbo7Fs9wiD2u1nnpgthUzCE9PvlbRkTu3GJZguy
T50rHoDNJbwsm5MADjH3yCISm+94pJIlxzl/vHvYIa0YLVEG6Hm5t2+6VRr3b7FH
kNbgX7a3Vvn+NDCWnJYXmq57FdgMGHzpmMR7hgBTxQyxu1eEQ0aCkckguyF90vFi
BlGLYh2BmDA=
-----END CERTIFICATE-----
28 changes: 28 additions & 0 deletions filebeat/tests/system/config/certificates/beats2.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDG8KkEAd+2Oi6Q
lQOQPy9DREP92iVJcRNESYnTloy3mP/iK1vPeNCvjnPWSAeiuqzm3iOXIWiV98hk
3cAXAg0Gdi/FoNzCJCJqumVu8REBTNbdHlGspMxIyAL+WNdOtTER21G1BExjjtGk
i71FR2Z3wdV898zf7yEfJgGhhxCSwQ9NT1C1R++7kWb+HP/TlXWN4XEM4TF6Czv6
5+x3HRA0lRVSK6I0Wy0Ct/+Du4RUCAjtOBLj6WkeMw239Wr7jqaw3vufORWO/qqE
XcpdM7LWaSV14Dh+IpQm+Px0q45gZpT93Y3jYEfwBp+QyU4ICHRwhkBzH0kBEKsx
oi3epL1tAgMBAAECggEBAItBXN4cPa++TGgSlxr6z6ejeoCQmfctutHbNCozQS8n
Xz6/IRY6uMGa0sUVSJsFVxCIQJuC3EXV26WXf+XbHqj+Q/Rv5PfU+W6CKYyeHEwv
sFu55GwYM5Nixo2qPJSwyrd4MVqntp0eFOu8kXGUSEreGQXQI85BAotePFgIRnai
hXPSBDJxccPclOutntIieZdGatrRtKU6+Jq5O95oktJRh0NJ/7h6eVF9+zPhf1Kn
AztRjsn1Y9TrIfop9eLAUt2hG9MTdLp8cxqpfCnL9+zIWhWpQeOMfC4yKn3aHBJh
OJw1Z801XXPkqRXGg8KV9IavfzDFnAfPcSWoIJktmOECgYEA/XQh8HHq70W/9SUo
oNunqBzTyCmq+CQPDGi52wWc8or6r7jPH2VLPGrKLA+Vl/J/aLRy6ent+7s586lh
eLXqc7MIB2hMMsYqGTZlsi4LkgGyVRCEA8t8o7TAo053BNS516Ka+L+noukkgBc8
FClUcdT+/nS3iF0eKyCngE59eiUCgYEAyPBSckeVhMwZnfQPoNt+xtpCn1UmHznC
/OLYY6T108A/EKqrlb2LW06rB9CwdIfB507yoN+p/+GNlF8l1u9zhKCRvliZ84ru
h6IVoeUxkmHEk3VGBVDfS8EQOh9Jq62VyaGEWldZm2elf3AtyZHc5xCVEeaKAvX+
M9rAy/3AP6kCgYANekh3vccNdDsR8Sjo7OVMdkP4x+Z0jY5TTZpcgD7pUuSjxYMW
G0/V5aPclfORge6uhbH6qFrkYP9i6qXpQls2TdXmdvBeXtVMQ+1CfVpWKErwZRFw
FjkJh4oa5QhFNH6xbc8p3R8v9Y/gU9v5An2gFAB/TXuY/8Kj7neZxhK8FQKBgAGw
e5D4ZxppuOFqFuOMVOGJHjxGs/5ZNvl3Ushrr6FrIVybgrvMjypiW+/B4mnoZkny
kPmnR7+d/tm7fw7yjm8UBoWqKwkwJtc/Fp141tTbO5Ldzovm5Sm24tMKRk1KNVMv
p3Q2/crfsTWEFO536nqK2iX/YTOrK8r10N8mMAKZAoGANGgsdIrOwZO1aTxEpzto
33dtZljvONfpvhiQ7Bytj/H1nvGYTvdPmStwEQdXd/1vE4EZri93ihWAeWeSinfr
5ROWKuor0jZvzAbVUAuISV04+agQ48CDZICsk/srnPvPTJhC/YpJ8ihG2QlH2VZ0
AIArT7DcZYLwM5m+oJrlQFY=
-----END PRIVATE KEY-----
22 changes: 22 additions & 0 deletions filebeat/tests/system/config/certificates/cacert.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[req]
distinguished_name = dname
[dname]
[ extensions ]
basicConstraints = critical, CA:TRUE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer:always
keyUsage = critical, cRLSign, digitalSignature, keyCertSign

[ client ]
basicConstraints = critical, CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer:always
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment, keyAgreement
#extendedKeyUsage = critical, serverAuth

[ server ]
basicConstraints = critical, CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer:always
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
#kextendedKeyUsage = critical, clientAuth
23 changes: 23 additions & 0 deletions filebeat/tests/system/config/certificates/cacert.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIID6jCCAtKgAwIBAgIJAOPYgOhthM8ZMA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNV
BAYTAkNBMQ8wDQYDVQQIDAZRdWViZWMxETAPBgNVBAcMCE1vbnRyZWFsMQ4wDAYD
VQQKDAViZWF0czENMAsGA1UECwwEcm9vdDAeFw0xODA1MDMyMDQ0NDdaFw0xOTA1
MDMyMDQ0NDdaMFAxCzAJBgNVBAYTAkNBMQ8wDQYDVQQIDAZRdWViZWMxETAPBgNV
BAcMCE1vbnRyZWFsMQ4wDAYDVQQKDAViZWF0czENMAsGA1UECwwEcm9vdDCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMunCRWeX8FyHmW5rkTItJiAICBq
ru1uMLLg/XUs5OmCJ6goYFX1yuRVrxPoKsblIX7zLfLX3hQSbQWSEzELaufn7jsQ
pW4PUxesEuTqOW7PA3lTfamJ6Hr+IH4Ja3GYX0FZOZOcTBaKB6bd2H1MSg2CzjIi
UYnXlgTULbVmnkpB1UdyBLI9Z2WbqnoiGiB/D1Oko8VXzkA3xT9pgCAXHhxqK5t7
ostCLYt7AzbSVeU6K6c+4i0VyPcxFZMdB1lcRrLkUuYeUwh/tGXXi6j6iHjS54O1
hyJjEc79wChOqQewLSgvMj3ASdA3pqvXvQB2NTa5wzznoEx83N3DkkIfjN8CAwEA
AaOBxjCBwzAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSjmGSd3zYagxM07hjS
e2KIJfYuGzCBgAYDVR0jBHkwd4AUo5hknd82GoMTNO4Y0ntiiCX2LhuhVKRSMFAx
CzAJBgNVBAYTAkNBMQ8wDQYDVQQIDAZRdWViZWMxETAPBgNVBAcMCE1vbnRyZWFs
MQ4wDAYDVQQKDAViZWF0czENMAsGA1UECwwEcm9vdIIJAOPYgOhthM8ZMA4GA1Ud
DwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAQEAQ+CNwnchjhHRkp5mEkWVOysU
H4ajfpsrOOyBsSgIEWW9Wg+uRglm39fgODscUTpTmFId9Y3qy317e8NU0WwhEVBd
JwO1M4s2PHUcQImnf4h8oeun0qhlxZdS8MC8ac6/jfqwqFfuvsdfub8R/o4zvuEL
ZlziVxjrAWqwuj9YtOV4Rnlivy4tEfJlpS0tAXqbEBN2PWwEmJ408Qkv3L9m1Dvs
6FsBEUoO36sPylSzyAfJQcEWlWW+j9YOnDPM40Iefcxgo4dqgL6uepvoYYERKXpR
JDvDF773yo/rZpjyWws8NJsBBt/341TJqejE/NQq5edludYcesummreMLXI16A==
-----END CERTIFICATE-----
Loading

0 comments on commit d686b9b

Please sign in to comment.