-
-
Notifications
You must be signed in to change notification settings - Fork 16
/
connect.go
140 lines (129 loc) · 4.48 KB
/
connect.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/*
Copyright 2023 Avi Zimmerman <avi.zimmerman@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package connect contains an implementation of the connect subcommand.
// It is used to connect to the mesh as an ephemeral node. It makes certain
// assumptions about the local environment. For example, it assumes the
// local hostname or a random UUID for the local node ID, an in-memory
// raft store, and to join the cluster as an observer.
package connect
import (
"context"
"fmt"
"os"
"time"
"github.com/webmeshproj/webmesh/pkg/mesh"
"github.com/webmeshproj/webmesh/pkg/services/meshdns"
)
// Options are options for configuring the connect command.
type Options struct {
// InterfaceName is the name of the wireguard interface to use.
InterfaceName string
// ListenPort is the port for wireguard to listen on.
ListenPort uint16
// ForceTUN is whether to force the use of a TUN interface.
ForceTUN bool
// Modprobe is whether to attempt to load the wireguard kernel module.
Modprobe bool
// JoinServer is the address of the join server to use.
JoinServer string
// RaftPort is the port to use for the Raft transport.
RaftPort uint16
// TLSCertFile is the path to a TLS certificate file to use
// for mTLS.
TLSCertFile string
// TLSKeyFile is the path to a TLS key file to use for mTLS.
TLSKeyFile string
// TLSCAFile is the path to a CA file for verifying the join
// server's certificate
TLSCAFile string
// VerifyChainOnly is whether to only verify the join server's
// certificate chain.
VerifyChainOnly bool
// Insecure is whether to not use TLS when joining the cluster.
// This assumes an insecure raft transport as well.
Insecure bool
// NoIPv4 is whether to not use IPv4 when joining the cluster.
NoIPv4 bool
// NoIPv6 is whether to not use IPv6 when joining the cluster.
NoIPv6 bool
// LocalDNS is whether to start a local MeshDNS server.
LocalDNS bool
// LocalDNSPort is the port to use for the local MeshDNS server.
LocalDNSPort uint16
}
// Connect connects to the mesh as an ephemeral node. The context
// is used to cancel the initial join to the cluster. The stopChan
// is used to stop the node.
func Connect(ctx context.Context, opts Options, stopChan chan struct{}) error {
// Configure the raft store
storeOpts := mesh.NewDefaultOptions()
storeOpts.Raft.InMemory = true
storeOpts.Raft.ListenAddress = fmt.Sprintf(":%d", opts.RaftPort)
storeOpts.Raft.LeaveOnShutdown = true
if opts.TLSCertFile != "" && opts.TLSKeyFile != "" {
storeOpts.Auth.MTLS = &mesh.MTLSOptions{
CertFile: opts.TLSCertFile,
KeyFile: opts.TLSKeyFile,
}
}
storeOpts.TLS.CAFile = opts.TLSCAFile
storeOpts.TLS.Insecure = opts.Insecure
storeOpts.TLS.VerifyChainOnly = opts.VerifyChainOnly
storeOpts.Mesh.JoinAddress = opts.JoinServer
storeOpts.Mesh.NoIPv4 = opts.NoIPv4
storeOpts.Mesh.NoIPv6 = opts.NoIPv6
storeOpts.WireGuard.InterfaceName = opts.InterfaceName
storeOpts.WireGuard.ListenPort = int(opts.ListenPort)
storeOpts.WireGuard.ForceTUN = opts.ForceTUN
storeOpts.WireGuard.Modprobe = opts.Modprobe
storeOpts.WireGuard.PersistentKeepAlive = time.Second * 10
// Create the store
st, err := mesh.New(storeOpts)
if err != nil {
return fmt.Errorf("create mesh: %w", err)
}
if err := st.Open(ctx, nil); err != nil {
return fmt.Errorf("open mesh connection: %w", err)
}
if opts.LocalDNS {
// Start a local MeshDNS server
server := meshdns.NewServer(&meshdns.Options{
UDPListenAddr: fmt.Sprintf(":%d", opts.LocalDNSPort),
})
err := server.RegisterDomain(meshdns.DomainOptions{
Mesh: st,
})
if err != nil {
defer func() {
err := st.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: close mesh: %v\n", err)
}
}()
return fmt.Errorf("register domain: %w", err)
}
go func() {
go func() {
if err := server.ListenAndServe(); err != nil {
fmt.Fprintf(os.Stderr, "ERROR: dns serve: %v\n", err)
}
}()
<-stopChan
if err := server.Shutdown(); err != nil {
fmt.Fprintf(os.Stderr, "ERROR: dns shutdown: %v\n", err)
}
}()
}
<-stopChan
return st.Close()
}