5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-12-22 23:25:39 +00:00

Remove TLS root validation

This is just too complicated compared to the per-peer/per-listener/per-interface password
approach.
This commit is contained in:
Neil Alexander 2023-10-11 18:25:35 +01:00
parent 6dc847de31
commit 45b773eade
No known key found for this signature in database
GPG Key ID: A02A2019A2BB0944
7 changed files with 29 additions and 196 deletions

View File

@ -3,7 +3,6 @@ package main
import ( import (
"context" "context"
"crypto/ed25519" "crypto/ed25519"
"crypto/sha1"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"flag" "flag"
@ -45,8 +44,6 @@ func main() {
useconffile := flag.String("useconffile", "", "read HJSON/JSON config from specified file path") useconffile := flag.String("useconffile", "", "read HJSON/JSON config from specified file path")
normaliseconf := flag.Bool("normaliseconf", false, "use in combination with either -useconf or -useconffile, outputs your configuration normalised") normaliseconf := flag.Bool("normaliseconf", false, "use in combination with either -useconf or -useconffile, outputs your configuration normalised")
exportkey := flag.Bool("exportkey", false, "use in combination with either -useconf or -useconffile, outputs your private key in PEM format") exportkey := flag.Bool("exportkey", false, "use in combination with either -useconf or -useconffile, outputs your private key in PEM format")
exportcsr := flag.Bool("exportcsr", false, "use in combination with either -useconf or -useconffile, outputs your self-signed certificate request in PEM format")
exportcert := flag.Bool("exportcert", false, "use in combination with either -useconf or -useconffile, outputs your self-signed certificate in PEM format")
confjson := flag.Bool("json", false, "print configuration from -genconf or -normaliseconf as JSON instead of HJSON") confjson := flag.Bool("json", false, "print configuration from -genconf or -normaliseconf as JSON instead of HJSON")
autoconf := flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)") autoconf := flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)")
ver := flag.Bool("version", false, "prints the version of this build") ver := flag.Bool("version", false, "prints the version of this build")
@ -177,30 +174,10 @@ func main() {
} }
fmt.Println(string(pem)) fmt.Println(string(pem))
return return
case *exportcsr:
pem, err := cfg.GenerateCertificateSigningRequest()
if err != nil {
panic(err)
}
fmt.Println(string(pem))
return
case *exportcert:
pem, err := cfg.MarshalPEMCertificate()
if err != nil {
panic(err)
}
fmt.Println(string(pem))
return
} }
n := &node{} n := &node{}
// Track certificate fingerprints for configured roots, so
// that we can match them using the multicast discriminator.
fingerprints := map[[20]byte]struct{}{}
// Setup the Yggdrasil node itself. // Setup the Yggdrasil node itself.
{ {
options := []core.SetupOption{ options := []core.SetupOption{
@ -218,10 +195,6 @@ func main() {
options = append(options, core.Peer{URI: peer, SourceInterface: intf}) options = append(options, core.Peer{URI: peer, SourceInterface: intf})
} }
} }
for _, root := range cfg.RootCertificates {
options = append(options, core.RootCertificate(*root))
fingerprints[sha1.Sum(root.Raw[:])] = struct{}{}
}
for _, allowed := range cfg.AllowedPublicKeys { for _, allowed := range cfg.AllowedPublicKeys {
k, err := hex.DecodeString(allowed) k, err := hex.DecodeString(allowed)
if err != nil { if err != nil {
@ -259,29 +232,6 @@ func main() {
Priority: uint8(intf.Priority), Priority: uint8(intf.Priority),
}) })
} }
if len(fingerprints) > 0 {
var matcher multicast.DiscriminatorMatch = func(b []byte) bool {
// Break apart the discriminator into 20-byte chunks and
// see whether any of them match the configured root CA
// fingerprints. If any of them match, we'll return true.
var f [20]byte
for len(b) >= len(f) {
b = b[copy(f[:], b):]
if _, ok := fingerprints[f]; ok {
return true
}
}
return false
}
// Populate our own discriminator with the fingerprints of our
// configured root CAs.
var discriminator multicast.Discriminator
for f := range fingerprints {
discriminator = append(discriminator, f[:]...)
}
options = append(options, matcher)
options = append(options, discriminator)
}
if n.multicast, err = multicast.New(n.core, logger, options...); err != nil { if n.multicast, err = multicast.New(n.core, logger, options...); err != nil {
panic(err) panic(err)
} }

View File

@ -70,9 +70,6 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error {
} }
options = append(options, core.AllowedPublicKey(k[:])) options = append(options, core.AllowedPublicKey(k[:]))
} }
for _, root := range m.config.RootCertificates {
options = append(options, core.RootCertificate(*root))
}
var err error var err error
m.core, err = core.New(m.config.Certificate, logger, options...) m.core, err = core.New(m.config.Certificate, logger, options...)
if err != nil { if err != nil {

View File

@ -43,9 +43,6 @@ type NodeConfig struct {
PrivateKey KeyBytes `comment:"Your private key. DO NOT share this with anyone!"` PrivateKey KeyBytes `comment:"Your private key. DO NOT share this with anyone!"`
PrivateKeyPath string `json:",omitempty"` PrivateKeyPath string `json:",omitempty"`
Certificate *tls.Certificate `json:"-"` Certificate *tls.Certificate `json:"-"`
CertificatePath string `json:",omitempty"`
RootCertificates []*x509.Certificate `json:"-"`
RootCertificatePaths []string `json:",omitempty"`
Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."` Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."`
InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."` InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."`
Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."` Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."`
@ -138,19 +135,6 @@ func (cfg *NodeConfig) postprocessConfig() error {
return err return err
} }
} }
if cfg.CertificatePath != "" {
if cfg.PrivateKeyPath == "" {
return fmt.Errorf("CertificatePath requires PrivateKeyPath")
}
cfg.Certificate = nil
f, err := os.ReadFile(cfg.CertificatePath)
if err != nil {
return err
}
if err := cfg.UnmarshalPEMCertificate(f); err != nil {
return err
}
}
switch { switch {
case cfg.Certificate == nil: case cfg.Certificate == nil:
// No self-signed certificate has been generated yet. // No self-signed certificate has been generated yet.
@ -163,35 +147,6 @@ func (cfg *NodeConfig) postprocessConfig() error {
return err return err
} }
} }
cfg.RootCertificates = cfg.RootCertificates[:0]
for _, path := range cfg.RootCertificatePaths {
f, err := os.ReadFile(path)
if err != nil {
return err
}
if err := cfg.UnmarshalRootCertificate(f); err != nil {
return err
}
}
return nil
}
func (cfg *NodeConfig) UnmarshalRootCertificate(b []byte) error {
p, _ := pem.Decode(b)
if p == nil {
return fmt.Errorf("failed to parse PEM file")
}
if p.Type != "CERTIFICATE" {
return fmt.Errorf("unexpected PEM type %q", p.Type)
}
cert, err := x509.ParseCertificate(p.Bytes)
if err != nil {
return fmt.Errorf("failed to load X.509 keypair: %w", err)
}
if !cert.IsCA {
return fmt.Errorf("supplied root certificate is not a certificate authority")
}
cfg.RootCertificates = append(cfg.RootCertificates, cert)
return nil return nil
} }
@ -215,26 +170,6 @@ func (cfg *NodeConfig) GenerateSelfSignedCertificate() error {
return nil return nil
} }
func (cfg *NodeConfig) GenerateCertificateSigningRequest() ([]byte, error) {
template := &x509.CertificateRequest{
Subject: pkix.Name{
CommonName: hex.EncodeToString(cfg.PrivateKey),
},
SignatureAlgorithm: x509.PureEd25519,
}
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, template, ed25519.PrivateKey(cfg.PrivateKey))
if err != nil {
return nil, err
}
pemBytes := bytes.NewBuffer(nil)
if err := pem.Encode(pemBytes, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes}); err != nil {
return nil, err
}
return pemBytes.Bytes(), nil
}
func (cfg *NodeConfig) MarshalPEMCertificate() ([]byte, error) { func (cfg *NodeConfig) MarshalPEMCertificate() ([]byte, error) {
privateKey := ed25519.PrivateKey(cfg.PrivateKey) privateKey := ed25519.PrivateKey(cfg.PrivateKey)
publicKey := privateKey.Public().(ed25519.PublicKey) publicKey := privateKey.Public().(ed25519.PublicKey)
@ -263,15 +198,6 @@ func (cfg *NodeConfig) MarshalPEMCertificate() ([]byte, error) {
return pem.EncodeToMemory(block), nil return pem.EncodeToMemory(block), nil
} }
func (cfg *NodeConfig) UnmarshalPEMCertificate(b []byte) error {
tlsCert, err := tls.LoadX509KeyPair(cfg.CertificatePath, cfg.PrivateKeyPath)
if err != nil {
return fmt.Errorf("failed to load X.509 keypair: %w", err)
}
cfg.Certificate = &tlsCert
return nil
}
func (cfg *NodeConfig) NewPrivateKey() { func (cfg *NodeConfig) NewPrivateKey() {
_, spriv, err := ed25519.GenerateKey(nil) _, spriv, err := ed25519.GenerateKey(nil)
if err != nil { if err != nil {

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"crypto/ed25519" "crypto/ed25519"
"crypto/tls" "crypto/tls"
"crypto/x509"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io" "io"
@ -40,7 +39,6 @@ type Core struct {
addPeerTimer *time.Timer addPeerTimer *time.Timer
config struct { config struct {
tls *tls.Config // immutable after startup tls *tls.Config // immutable after startup
roots *x509.CertPool // immutable after startup
//_peers map[Peer]*linkInfo // configurable after startup //_peers map[Peer]*linkInfo // configurable after startup
_listeners map[ListenAddress]struct{} // configurable after startup _listeners map[ListenAddress]struct{} // configurable after startup
nodeinfo NodeInfo // immutable after startup nodeinfo NodeInfo // immutable after startup
@ -110,9 +108,6 @@ func New(cert *tls.Certificate, logger Logger, opts ...SetupOption) (*Core, erro
c.log.Infof("Your public key is %s", hex.EncodeToString(c.public)) c.log.Infof("Your public key is %s", hex.EncodeToString(c.public))
c.log.Infof("Your IPv6 address is %s", address.String()) c.log.Infof("Your IPv6 address is %s", address.String())
c.log.Infof("Your IPv6 subnet is %s", subnet.String()) c.log.Infof("Your IPv6 subnet is %s", subnet.String())
if c.config.roots != nil {
c.log.Println("Yggdrasil is running in TLS-only mode")
}
c.proto.init(c) c.proto.init(c)
if err := c.links.init(c); err != nil { if err := c.links.init(c); err != nil {
return nil, fmt.Errorf("error initialising links: %w", err) return nil, fmt.Errorf("error initialising links: %w", err)
@ -169,10 +164,6 @@ func (c *Core) _close() error {
return err return err
} }
func (c *Core) isTLSOnly() bool {
return c.config.roots != nil
}
func (c *Core) MTU() uint64 { func (c *Core) MTU() uint64 {
const sessionTypeOverhead = 1 const sessionTypeOverhead = 1
MTU := c.PacketConn.MTU() - sessionTypeOverhead MTU := c.PacketConn.MTU() - sessionTypeOverhead

View File

@ -69,9 +69,6 @@ func (l *linkTCP) dialersFor(url *url.URL, info linkInfo) ([]*tcpDialer, error)
} }
func (l *linkTCP) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) { func (l *linkTCP) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
if l.core.isTLSOnly() {
return nil, fmt.Errorf("TCP peer prohibited in TLS-only mode")
}
dialers, err := l.dialersFor(url, info) dialers, err := l.dialersFor(url, info)
if err != nil { if err != nil {
return nil, err return nil, err
@ -92,9 +89,6 @@ func (l *linkTCP) dial(ctx context.Context, url *url.URL, info linkInfo, options
} }
func (l *linkTCP) listen(ctx context.Context, url *url.URL, sintf string) (net.Listener, error) { func (l *linkTCP) listen(ctx context.Context, url *url.URL, sintf string) (net.Listener, error) {
if l.core.isTLSOnly() {
return nil, fmt.Errorf("TCP listener prohibited in TLS-only mode")
}
hostport := url.Host hostport := url.Host
if sintf != "" { if sintf != "" {
if host, port, err := net.SplitHostPort(hostport); err == nil { if host, port, err := net.SplitHostPort(hostport); err == nil {

View File

@ -2,19 +2,12 @@ package core
import ( import (
"crypto/ed25519" "crypto/ed25519"
"crypto/x509"
"fmt" "fmt"
"net/url" "net/url"
) )
func (c *Core) _applyOption(opt SetupOption) (err error) { func (c *Core) _applyOption(opt SetupOption) (err error) {
switch v := opt.(type) { switch v := opt.(type) {
case RootCertificate:
cert := x509.Certificate(v)
if c.config.roots == nil {
c.config.roots = x509.NewCertPool()
}
c.config.roots.AddCert(&cert)
case Peer: case Peer:
u, err := url.Parse(v.URI) u, err := url.Parse(v.URI)
if err != nil { if err != nil {
@ -39,7 +32,6 @@ type SetupOption interface {
isSetupOption() isSetupOption()
} }
type RootCertificate x509.Certificate
type ListenAddress string type ListenAddress string
type Peer struct { type Peer struct {
URI string URI string
@ -49,7 +41,6 @@ type NodeInfo map[string]interface{}
type NodeInfoPrivacy bool type NodeInfoPrivacy bool
type AllowedPublicKey ed25519.PublicKey type AllowedPublicKey ed25519.PublicKey
func (a RootCertificate) isSetupOption() {}
func (a ListenAddress) isSetupOption() {} func (a ListenAddress) isSetupOption() {}
func (a Peer) isSetupOption() {} func (a Peer) isSetupOption() {}
func (a NodeInfo) isSetupOption() {} func (a NodeInfo) isSetupOption() {}

View File

@ -17,39 +17,20 @@ func (c *Core) generateTLSConfig(cert *tls.Certificate) (*tls.Config, error) {
VerifyConnection: c.verifyTLSConnection, VerifyConnection: c.verifyTLSConnection,
InsecureSkipVerify: true, InsecureSkipVerify: true,
MinVersion: tls.VersionTLS13, MinVersion: tls.VersionTLS13,
NextProtos: []string{"yggdrasil/0.5"}, NextProtos: []string{
fmt.Sprintf("yggdrasil/%d.%d", ProtocolVersionMajor, ProtocolVersionMinor),
},
} }
return config, nil return config, nil
} }
func (c *Core) verifyTLSCertificate(rawCerts [][]byte, _ [][]*x509.Certificate) error { func (c *Core) verifyTLSCertificate(rawCerts [][]byte, _ [][]*x509.Certificate) error {
if c.config.roots == nil { if len(rawCerts) != 1 {
// If there's no certificate pool configured then we will return fmt.Errorf("expected one certificate")
// accept all TLS certificates.
return nil
}
if len(rawCerts) == 0 {
return fmt.Errorf("expected at least one certificate")
}
opts := x509.VerifyOptions{
Roots: c.config.roots,
}
for i, rawCert := range rawCerts {
if i == 0 {
// The first certificate is the leaf certificate. All other
// certificates in the list are intermediates, so add them
// into the VerifyOptions.
continue
}
cert, err := x509.ParseCertificate(rawCert)
if err != nil {
return fmt.Errorf("failed to parse intermediate certificate: %w", err)
}
opts.Intermediates.AddCert(cert)
} }
/*
opts := x509.VerifyOptions{}
cert, err := x509.ParseCertificate(rawCerts[0]) cert, err := x509.ParseCertificate(rawCerts[0])
if err != nil { if err != nil {
return fmt.Errorf("failed to parse leaf certificate: %w", err) return fmt.Errorf("failed to parse leaf certificate: %w", err)
@ -57,6 +38,9 @@ func (c *Core) verifyTLSCertificate(rawCerts [][]byte, _ [][]*x509.Certificate)
_, err = cert.Verify(opts) _, err = cert.Verify(opts)
return err return err
*/
return nil
} }
func (c *Core) verifyTLSConnection(cs tls.ConnectionState) error { func (c *Core) verifyTLSConnection(cs tls.ConnectionState) error {