Add support for self-signed certificates
This commit is contained in:
parent
79803a8af9
commit
9577036327
26
README.md
26
README.md
|
@ -13,6 +13,32 @@ The goal is to make simple to write simple XMPP clients and components:
|
||||||
|
|
||||||
The library is designed to have minimal dependencies. For now, the library does not depend on any other library.
|
The library is designed to have minimal dependencies. For now, the library does not depend on any other library.
|
||||||
|
|
||||||
|
## Configuration and connection
|
||||||
|
|
||||||
|
### Allowing Insecure TLS connection during development
|
||||||
|
|
||||||
|
It is not recommended to disable the check for domain name and certificate chain. Doing so would open your client
|
||||||
|
to man-in-the-middle attacks.
|
||||||
|
|
||||||
|
However, in development, XMPP servers often use self-signed certificates. In that situation, it is better to add the
|
||||||
|
root CA that signed the certificate to your trusted list of root CA. It avoids changing the code and limit the risk
|
||||||
|
of shipping an insecure client to production.
|
||||||
|
|
||||||
|
That said, if you really want to allow your client to trust any TLS certificate, you can customize Go standard
|
||||||
|
`tls.Config` and set it in Config struct.
|
||||||
|
|
||||||
|
Here is an example code to configure a client to allow connecting to a server with self-signed certificate. Note the
|
||||||
|
`InsecureSkipVerify` option. When using this `tls.Config` option, all the checks on the certificate are skipped.
|
||||||
|
|
||||||
|
```go
|
||||||
|
config := xmpp.Config{
|
||||||
|
Address: "localhost:5222",
|
||||||
|
Jid: "test@localhost",
|
||||||
|
Password: "test",
|
||||||
|
TLSConfig: tls.Config{InsecureSkipVerify: true},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Supported specifications
|
## Supported specifications
|
||||||
|
|
||||||
### Clients
|
### Clients
|
||||||
|
|
|
@ -20,6 +20,7 @@ func main() {
|
||||||
Password: "test",
|
Password: "test",
|
||||||
StreamLogger: os.Stdout,
|
StreamLogger: os.Stdout,
|
||||||
Insecure: true,
|
Insecure: true,
|
||||||
|
// TLSConfig: tls.Config{InsecureSkipVerify: true},
|
||||||
}
|
}
|
||||||
|
|
||||||
router := xmpp.NewRouter()
|
router := xmpp.NewRouter()
|
||||||
|
|
|
@ -86,8 +86,9 @@ func (c *ServerCheck) Check() error {
|
||||||
return fmt.Errorf("expecting starttls proceed: %s", err)
|
return fmt.Errorf("expecting starttls proceed: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
stanza.DefaultTlsConfig.ServerName = c.domain
|
var tlsConfig tls.Config
|
||||||
tlsConn := tls.Client(tcpconn, &stanza.DefaultTlsConfig)
|
tlsConfig.ServerName = c.domain
|
||||||
|
tlsConn := tls.Client(tcpconn, &tlsConfig)
|
||||||
// We convert existing connection to TLS
|
// We convert existing connection to TLS
|
||||||
if err = tlsConn.Handshake(); err != nil {
|
if err = tlsConn.Handshake(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package xmpp
|
package xmpp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
@ -13,6 +14,7 @@ type Config struct {
|
||||||
StreamLogger *os.File // Used for debugging
|
StreamLogger *os.File // Used for debugging
|
||||||
Lang string // TODO: should default to 'en'
|
Lang string // TODO: should default to 'en'
|
||||||
ConnectTimeout int // Client timeout in seconds. Default to 15
|
ConnectTimeout int // Client timeout in seconds. Default to 15
|
||||||
|
TLSConfig tls.Config
|
||||||
// Insecure can be set to true to allow to open a session without TLS. If TLS
|
// Insecure can be set to true to allow to open a session without TLS. If TLS
|
||||||
// is supported on the server, we will still try to use it.
|
// is supported on the server, we will still try to use it.
|
||||||
Insecure bool
|
Insecure bool
|
||||||
|
|
31
session.go
31
session.go
|
@ -35,13 +35,15 @@ func NewSession(conn net.Conn, o Config) (net.Conn, *Session, error) {
|
||||||
|
|
||||||
// starttls
|
// starttls
|
||||||
var tlsConn net.Conn
|
var tlsConn net.Conn
|
||||||
tlsConn = s.startTlsIfSupported(conn, o.parsedJid.Domain)
|
tlsConn = s.startTlsIfSupported(conn, o.parsedJid.Domain, o)
|
||||||
if s.TlsEnabled {
|
|
||||||
s.reset(conn, tlsConn, o)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !s.TlsEnabled && !o.Insecure {
|
if !s.TlsEnabled && !o.Insecure {
|
||||||
return nil, nil, NewConnError(errors.New("failed to negotiate TLS session"), true)
|
err := fmt.Errorf("failed to negotiate TLS session : %s", s.err)
|
||||||
|
return nil, nil, NewConnError(err, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.TlsEnabled {
|
||||||
|
s.reset(conn, tlsConn, o)
|
||||||
}
|
}
|
||||||
|
|
||||||
// auth
|
// auth
|
||||||
|
@ -101,7 +103,7 @@ func (s *Session) open(domain string) (f stanza.StreamFeatures) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) startTlsIfSupported(conn net.Conn, domain string) net.Conn {
|
func (s *Session) startTlsIfSupported(conn net.Conn, domain string, o Config) net.Conn {
|
||||||
if s.err != nil {
|
if s.err != nil {
|
||||||
return conn
|
return conn
|
||||||
}
|
}
|
||||||
|
@ -114,21 +116,30 @@ func (s *Session) startTlsIfSupported(conn net.Conn, domain string) net.Conn {
|
||||||
s.err = errors.New("expecting starttls proceed: " + s.err.Error())
|
s.err = errors.New("expecting starttls proceed: " + s.err.Error())
|
||||||
return conn
|
return conn
|
||||||
}
|
}
|
||||||
s.TlsEnabled = true
|
|
||||||
|
|
||||||
// TODO: add option to accept all TLS certificates: insecureSkipTlsVerify (DefaultTlsConfig.InsecureSkipVerify)
|
o.TLSConfig.ServerName = domain
|
||||||
stanza.DefaultTlsConfig.ServerName = domain
|
tlsConn := tls.Client(conn, &o.TLSConfig)
|
||||||
tlsConn := tls.Client(conn, &stanza.DefaultTlsConfig)
|
|
||||||
// We convert existing connection to TLS
|
// We convert existing connection to TLS
|
||||||
if s.err = tlsConn.Handshake(); s.err != nil {
|
if s.err = tlsConn.Handshake(); s.err != nil {
|
||||||
return tlsConn
|
return tlsConn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !o.TLSConfig.InsecureSkipVerify {
|
||||||
// We check that cert matches hostname
|
// We check that cert matches hostname
|
||||||
s.err = tlsConn.VerifyHostname(domain)
|
s.err = tlsConn.VerifyHostname(domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.err == nil {
|
||||||
|
s.TlsEnabled = true
|
||||||
|
}
|
||||||
return tlsConn
|
return tlsConn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we do not allow cleartext connections, make it explicit that server do not support starttls
|
||||||
|
if !o.Insecure {
|
||||||
|
s.err = errors.New("XMPP server does not advertise support for starttls")
|
||||||
|
}
|
||||||
|
|
||||||
// starttls is not supported => we do not upgrade the connection:
|
// starttls is not supported => we do not upgrade the connection:
|
||||||
return conn
|
return conn
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
package stanza
|
package stanza
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
)
|
)
|
||||||
|
|
||||||
var DefaultTlsConfig tls.Config
|
|
||||||
|
|
||||||
// Used during stream initiation / session establishment
|
// Used during stream initiation / session establishment
|
||||||
type TLSProceed struct {
|
type TLSProceed struct {
|
||||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls proceed"`
|
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls proceed"`
|
||||||
|
|
|
@ -119,7 +119,7 @@ func (sm *StreamManager) connect() error {
|
||||||
var actualErr ConnError
|
var actualErr ConnError
|
||||||
if xerrors.As(err, &actualErr) {
|
if xerrors.As(err, &actualErr) {
|
||||||
if actualErr.Permanent {
|
if actualErr.Permanent {
|
||||||
return xerrors.Errorf("unrecoverable connect error %w", actualErr)
|
return xerrors.Errorf("unrecoverable connect error %#v", actualErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
backoff.wait()
|
backoff.wait()
|
||||||
|
|
Loading…
Reference in a new issue