-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.go
181 lines (168 loc) · 6.31 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
package main
import (
"dhens/drawbridge/cmd/analytics"
"dhens/drawbridge/cmd/dashboard/ui"
"dhens/drawbridge/cmd/drawbridge"
"dhens/drawbridge/cmd/drawbridge/persistence"
"dhens/drawbridge/cmd/drawbridge/services"
flagger "dhens/drawbridge/cmd/flags"
"dhens/drawbridge/cmd/utils"
"flag"
"log"
"log/slog"
"os"
"path"
"path/filepath"
"time"
)
func main() {
flagger.FLAGS = &flagger.CommandLineArgs{}
flag.UintVar(
&flagger.FLAGS.DrawbridgePort,
"api",
3100,
"listening port for the drawbridge mTLS TCP server - emissary connects directly to this e.g 3100",
)
flag.StringVar(
&flagger.FLAGS.FrontendAPIHostAndPort,
"fapi",
"localhost:3000",
"listening host and port for the drawbridge dashboard page e.g 'localhost:3000'",
)
flag.StringVar(
&flagger.FLAGS.BackendAPIHostAndPort,
"jsonapi",
"localhost:3001",
"(currently unused) listening host and port for emissary json http api e.g 'localhost:3001'",
)
flag.StringVar(
&flagger.FLAGS.SqliteFilename,
"sqlfile",
"drawbridge.db",
"file name for Drawbridge sqlite database",
)
flag.StringVar(
&flagger.FLAGS.Env,
"env",
"production",
"the environment that Drawbridge is running in ('production', 'development'). development mode increases logging verbosity.",
)
flag.StringVar(
&flagger.FLAGS.NoGUI,
"nogui",
"",
"if passed, the Drawbridge Dashboard will not automatically open in the default browser",
)
flag.Parse()
// Show debugger messages in development mode.
if flagger.FLAGS.Env == "development" {
programLevel := new(slog.LevelVar)
programLevel.Set(slog.LevelDebug)
h := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: programLevel})
slog.SetDefault(slog.New(h))
}
// Append Drawbridge binary location to sqlite filepath to avoid writing to home directory.
// Ensure we are only reading files from our executable and not where the terminal is executing from.
execPath, err := os.Executable()
if err != nil {
log.Fatal(err)
}
execDirPath := path.Dir(execPath)
flagger.FLAGS.SqliteFilename = filepath.Join(execDirPath, flagger.FLAGS.SqliteFilename)
// Migrate sqlite tables
db := persistence.NewSQLiteRepository(persistence.OpenDatabaseFile(flagger.FLAGS.SqliteFilename))
err = db.MigrateServices()
if err != nil {
log.Fatalf("Error running services db migration: %s", err)
}
err = db.MigrateEmissaryClient()
if err != nil {
log.Fatalf("Error running emissary_client db migration: %s", err)
}
err = db.MigrateEmissaryClientEvent()
if err != nil {
log.Fatalf("Error running emissary_client_event db migration: %s", err)
}
err = db.MigrateDrawbridgeConfig()
if err != nil {
log.Fatalf("Error running drawbridge_config db migration: %s", err)
}
drawbridgeAPI := &drawbridge.Drawbridge{
ProtectedServices: make(map[int64]services.RunningProtectedService, 0),
DB: db,
ListeningPort: flagger.FLAGS.DrawbridgePort,
OutboundServices: make(map[int64]*services.ProtectedService, 0),
}
// Onboarding configuration has been complete and we can load all existing config files and start servers.
// Otherwise, we set up the certificate authority and dependent servers once the user submits
// their listening address via the onboarding popup modal, which POSTs to /admin/post/config.
services, err := db.GetAllServices()
if err != nil {
log.Fatalf("Could not get all services: %s", err)
}
// Check if a listening address has been saved in either the old config/listening_address.txt file
// or the database.
listeningAddress, err := db.GetDrawbridgeConfigValueByName("listening_address")
if err != nil {
slog.Error("Database", slog.Any("Error: %s", err))
}
if *listeningAddress == "" {
if utils.FileExists("config/listening_address.txt") {
addressBytes := utils.ReadFile("config/listening_address.txt")
// If someone has a historical Drawbridge install, we will insert their listening address
// into sqlite to get them up-to-date.
if addressBytes != nil {
address := string(*addressBytes)
listeningAddress = &address
err = db.CreateNewDrawbridgeConfigSettings("listening_address", *listeningAddress)
if err != nil {
slog.Error("Database Insert", slog.Any("error saving listening_address to drawbridge_config table", err))
} else {
// TODO
// Make sure we are a good citizen and not deleting folders without user confirmation.
// utils.DeleteDirectory("config")
}
}
}
} else {
go drawbridgeAPI.SetUpCAAndDependentServices(services)
}
drawbridgeAPI.ListeningAddress = *listeningAddress
// Initalize DAU ping only if enabled by the Drawbridge admin.
dauPingEnabled, err := db.GetDrawbridgeConfigValueByName("dau_ping_enabled")
if err != nil {
slog.Error("Database", slog.Any("Error getting dau_ping_enabled: %s", err))
} else if *dauPingEnabled == "true" {
lastPingTime, err := db.GetDrawbridgeConfigValueByName("last_ping_timestamp")
if err != nil {
slog.Error("Database", slog.Any("Error getting last_ping_timestamp: %s", err))
}
// Parse timestamp if it exists and we didn't error out earlier.
if *lastPingTime != "" && err == nil {
lastPingTimestamp, err := time.Parse(time.RFC3339, *lastPingTime)
if err != nil {
slog.Error("Time Parse", slog.Any("Error parsing last_ping_timestamp: %s", err))
}
// Drawbridge hasn't been run within the last 24 hours since the last ping, so we
// can run a DAU ping immediately.
if time.Since(lastPingTimestamp) >= time.Hour*24 {
go analytics.DAUPing(db)
// We haven't waited 24 hours since our last DAU ping, so we need to schedule the future time
// to do one.
} else {
nextTimeToPing := time.Until(lastPingTimestamp.AddDate(0, 0, 1))
slog.Debug("DAU Ping", slog.Any("Next Ping Time", nextTimeToPing))
time.AfterFunc(nextTimeToPing, func() { analytics.DAUPing(db) })
}
// kick off DAU pings as it has been enabled but we can't get the latest ping timestamp.
} else {
analytics.DAUPing(db)
}
}
frontendController := ui.Controller{
DrawbridgeAPI: drawbridgeAPI,
ProtectedServices: services,
DB: db}
// Set up templ controller used to return hypermedia to our htmx frontend.
frontendController.SetUp(flagger.FLAGS.FrontendAPIHostAndPort)
}