-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
227 lines (204 loc) · 6.89 KB
/
main.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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
package main
import (
"fmt"
"math/rand"
"strings"
"time"
"github.com/getsentry/sentry-go"
homedir "github.com/mitchellh/go-homedir"
"github.com/pkg/errors"
"github.com/urfave/cli/v2"
"go.uber.org/automaxprocs/maxprocs"
"github.com/khulnasoft/netscale/cmd/netscale/access"
"github.com/khulnasoft/netscale/cmd/netscale/cliutil"
"github.com/khulnasoft/netscale/cmd/netscale/proxydns"
"github.com/khulnasoft/netscale/cmd/netscale/tail"
"github.com/khulnasoft/netscale/cmd/netscale/tunnel"
"github.com/khulnasoft/netscale/cmd/netscale/updater"
"github.com/khulnasoft/netscale/config"
"github.com/khulnasoft/netscale/logger"
"github.com/khulnasoft/netscale/metrics"
"github.com/khulnasoft/netscale/overwatch"
"github.com/khulnasoft/netscale/token"
"github.com/khulnasoft/netscale/tracing"
"github.com/khulnasoft/netscale/watcher"
)
const (
versionText = "Print the version"
)
var (
Version = "DEV"
BuildTime = "unknown"
BuildType = ""
// Mostly network errors that we don't want reported back to Sentry, this is done by substring match.
ignoredErrors = []string{
"connection reset by peer",
"An existing connection was forcibly closed by the remote host.",
"use of closed connection",
"You need to enable Argo Smart Routing",
"3001 connection closed",
"3002 connection dropped",
"rpc exception: dial tcp",
"rpc exception: EOF",
}
)
func main() {
rand.Seed(time.Now().UnixNano())
metrics.RegisterBuildInfo(BuildType, BuildTime, Version)
maxprocs.Set()
bInfo := cliutil.GetBuildInfo(BuildType, Version)
// Graceful shutdown channel used by the app. When closed, app must terminate gracefully.
// Windows service manager closes this channel when it receives stop command.
graceShutdownC := make(chan struct{})
cli.VersionFlag = &cli.BoolFlag{
Name: "version",
Aliases: []string{"v", "V"},
Usage: versionText,
}
app := &cli.App{}
app.Name = "netscale"
app.Usage = "Khulnasoft's command-line tool and agent"
app.UsageText = "netscale [global options] [command] [command options]"
app.Copyright = fmt.Sprintf(
`(c) %d Khulnasoft Inc.
Your installation of netscale software constitutes a symbol of your signature indicating that you accept
the terms of the Apache License Version 2.0 (https://developers.khulnasoft.com/khulnasoft-one/connections/connect-apps/license),
Terms (https://www.khulnasoft.com/terms/) and Privacy Policy (https://www.khulnasoft.com/privacypolicy/).`,
time.Now().Year(),
)
app.Version = fmt.Sprintf("%s (built %s%s)", Version, BuildTime, bInfo.GetBuildTypeMsg())
app.Description = `netscale connects your machine or user identity to Khulnasoft's global network.
You can use it to authenticate a session to reach an API behind Access, route web traffic to this machine,
and configure access control.
See https://developers.khulnasoft.com/khulnasoft-one/connections/connect-apps for more in-depth documentation.`
app.Flags = flags()
app.Action = action(graceShutdownC)
app.Commands = commands(cli.ShowVersion)
tunnel.Init(bInfo, graceShutdownC) // we need this to support the tunnel sub command...
access.Init(graceShutdownC, Version)
updater.Init(Version)
tracing.Init(Version)
token.Init(Version)
tail.Init(bInfo)
runApp(app, graceShutdownC)
}
func commands(version func(c *cli.Context)) []*cli.Command {
cmds := []*cli.Command{
{
Name: "update",
Action: cliutil.ConfiguredAction(updater.Update),
Usage: "Update the agent if a new version exists",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "beta",
Usage: "specify if you wish to update to the latest beta version",
},
&cli.BoolFlag{
Name: "force",
Usage: "specify if you wish to force an upgrade to the latest version regardless of the current version",
Hidden: true,
},
&cli.BoolFlag{
Name: "staging",
Usage: "specify if you wish to use the staging url for updating",
Hidden: true,
},
&cli.StringFlag{
Name: "version",
Usage: "specify a version you wish to upgrade or downgrade to",
Hidden: false,
},
},
Description: `Looks for a new version on the official download server.
If a new version exists, updates the agent binary and quits.
Otherwise, does nothing.
To determine if an update happened in a script, check for error code 11.`,
},
{
Name: "version",
Action: func(c *cli.Context) (err error) {
version(c)
return nil
},
Usage: versionText,
Description: versionText,
},
}
cmds = append(cmds, tunnel.Commands()...)
cmds = append(cmds, proxydns.Command(false))
cmds = append(cmds, access.Commands()...)
cmds = append(cmds, tail.Command())
return cmds
}
func flags() []cli.Flag {
flags := tunnel.Flags()
return append(flags, access.Flags()...)
}
func isEmptyInvocation(c *cli.Context) bool {
return c.NArg() == 0 && c.NumFlags() == 0
}
func action(graceShutdownC chan struct{}) cli.ActionFunc {
return cliutil.ConfiguredAction(func(c *cli.Context) (err error) {
if isEmptyInvocation(c) {
return handleServiceMode(c, graceShutdownC)
}
func() {
defer sentry.Recover()
err = tunnel.TunnelCommand(c)
}()
if err != nil {
captureError(err)
}
return err
})
}
func userHomeDir() (string, error) {
// This returns the home dir of the executing user using OS-specific method
// for discovering the home dir. It's not recommended to call this function
// when the user has root permission as $HOME depends on what options the user
// use with sudo.
homeDir, err := homedir.Dir()
if err != nil {
return "", errors.Wrap(err, "Cannot determine home directory for the user")
}
return homeDir, nil
}
// In order to keep the amount of noise sent to Sentry low, typical network errors can be filtered out here by a substring match.
func captureError(err error) {
errorMessage := err.Error()
for _, ignoredErrorMessage := range ignoredErrors {
if strings.Contains(errorMessage, ignoredErrorMessage) {
return
}
}
sentry.CaptureException(err)
}
// netscale was started without any flags
func handleServiceMode(c *cli.Context, shutdownC chan struct{}) error {
log := logger.CreateLoggerFromContext(c, logger.DisableTerminalLog)
// start the main run loop that reads from the config file
f, err := watcher.NewFile()
if err != nil {
log.Err(err).Msg("Cannot load config file")
return err
}
configPath := config.FindOrCreateConfigPath()
configManager, err := config.NewFileManager(f, configPath, log)
if err != nil {
log.Err(err).Msg("Cannot setup config file for monitoring")
return err
}
log.Info().Msgf("monitoring config file at: %s", configPath)
serviceCallback := func(t string, name string, err error) {
if err != nil {
log.Err(err).Msgf("%s service: %s encountered an error", t, name)
}
}
serviceManager := overwatch.NewAppManager(serviceCallback)
appService := NewAppService(configManager, serviceManager, shutdownC, log)
if err := appService.Run(); err != nil {
log.Err(err).Msg("Failed to start app service")
return err
}
return nil
}