-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathconfig.go
More file actions
192 lines (183 loc) · 5.69 KB
/
Copy pathconfig.go
File metadata and controls
192 lines (183 loc) · 5.69 KB
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
package main
import (
"bytes"
"compress/gzip"
"crypto/sha256"
"errors"
"log"
"os"
"path/filepath"
"strings"
"github.com/BurntSushi/toml"
)
type DomainInfo struct {
Domain string
Target string
WidgetBotName string
WidgetBotToken string
UserSuffix string
Whitelist []string
LoginFrom string
NoAuth bool
TelegramUsers map[string]string
AuthorizedKeys string
}
type Config struct {
SSH struct {
Enabled bool
Port string
ServerKey string
AuthorizedKeys string
}
Certificate struct {
Type string
Email string
Cert string
Key string
}
FilterSpam bool
Gzip bool
DropPrivileges bool
Listen string
HttpsPort string
DefaultTarget string
RedirectHTTP bool
MaxNonActiveTime int
CustomPage string
LogoutURL string
SSO string
// TelegramBotName string
// TelegramBotToken string
TelegramUsers map[string]string
Domains []DomainInfo
}
type SSH_Info struct {
keyType string
keyData []byte
username string
}
func loadConfig() {
// Config filename can be provided via command line
config_file := "jauth.toml"
if len(os.Args) > 1 {
config_file = os.Args[1]
// Change current dir to dir of config file
err := os.Chdir(filepath.Dir(config_file))
if err != nil {
log.Fatal(err)
}
}
// Some default settings
cfg.SSH.Enabled = true
cfg.SSH.Port = "2222"
cfg.SSH.ServerKey = "~/.ssh/id_rsa"
cfg.SSH.AuthorizedKeys = "~/.ssh/authorized_keys"
cfg.Certificate.Type = "self-signed"
cfg.DefaultTarget = "8080"
cfg.FilterSpam = true // Less spam like `http: TLS handshake error...`
cfg.Gzip = true // Enable gzip'ing the responses
cfg.DropPrivileges = false // Drop privileges if started from root
cfg.Listen = "0.0.0.0" // Interface to listen
cfg.HttpsPort = "443"
cfg.RedirectHTTP = true // Start server on 80 port that will redirect all to 443 port
cfg.MaxNonActiveTime = 30 // TOKEN_COUNTDOWN_TIMER
cfg.LogoutURL = "/jauth-logout"
// Load user's config file
// Toml module will automatically parse file into struct
_, err := toml.DecodeFile(config_file, &cfg)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
log.Print(red("Configuration file not found"))
} else {
log.Fatal(err)
}
}
// Support paths with tilde ~
cfg.SSH.ServerKey = expandTilde(cfg.SSH.ServerKey)
cfg.SSH.AuthorizedKeys = expandTilde(cfg.SSH.AuthorizedKeys)
cfg.Certificate.Cert = expandTilde(cfg.Certificate.Cert)
cfg.Certificate.Key = expandTilde(cfg.Certificate.Key)
// Load default authorized_keys
if cfg.SSH.Enabled && cfg.SSH.AuthorizedKeys != "" {
defaultWhitelist = loadAuthorizedKeys(cfg.SSH.AuthorizedKeys)
}
defaultWhitelist = append(defaultWhitelist, handleTelegramUsers(cfg.TelegramUsers)...)
defaultWhitelist = removeDuplicates(defaultWhitelist)
log.Printf("Default WhiteList: %s", strings.Join(defaultWhitelist, ", "))
// Load login page. There is a built-in and the user can provide his own
raw_index_page := []byte(embed_index_html)
if cfg.CustomPage != "" {
raw_index_page, err = os.ReadFile(cfg.CustomPage)
if err != nil {
log.Fatal(err)
}
log.Print("Using custom login page: ", cfg.CustomPage)
}
// Add Domain_Info for non specified domains, ip address
cfg.Domains = append(cfg.Domains, DomainInfo{})
// Domain processing
for i, domain := range cfg.Domains {
// In case the user has not specified a target
// Also for Domain_Info declared just above
if domain.Target == "" {
domain.Target = cfg.DefaultTarget
}
// "Register" per domain TelegramUsers and SshKeys
newUsers := handleTelegramUsers(domain.TelegramUsers)
if domain.AuthorizedKeys != "" {
newUsers = append(newUsers, loadAuthorizedKeys(expandTilde(domain.AuthorizedKeys))...)
}
// Fill empty whitelist with default and per domain values
if len(domain.Whitelist) == 0 {
domain.Whitelist = newUsers
domain.Whitelist = append(domain.Whitelist, defaultWhitelist...)
}
// Each domain can be configured to sign in through a different domain
// Otherwise use Single Sign-On url
if (domain.LoginFrom == "") && (domain.Domain != cfg.SSO) {
domain.LoginFrom = cfg.SSO
}
// Calc key for HMAC. Need for verification telegram widget auth
if domain.WidgetBotToken != "" {
tmp_sha256 := sha256.New()
tmp_sha256.Write([]byte(domain.WidgetBotToken))
domainToTokenSHA256[domain.Domain] = tmp_sha256.Sum(nil)
}
// We need to know if there is at least one widget, otherwise we turn off its support
current_widget_enabled := domain.WidgetBotName != ""
if current_widget_enabled {
telegramWidgetEnabled = true
}
// CSS for hiding blocks
widget_block_css := "display: none;"
if current_widget_enabled {
widget_block_css = ""
}
ssh_block_css := "display: none;"
if cfg.SSH.Enabled {
ssh_block_css = ""
}
// Fill template for each domain
login_page := string(raw_index_page)
login_page = strings.Replace(login_page, "{WIDGET_DISABLING_CSS}", widget_block_css, -1)
login_page = strings.Replace(login_page, "{TELEGRAM_BOT_NAME}", domain.WidgetBotName, -1)
login_page = strings.Replace(login_page, "{SSH_DISABLING_CSS}", ssh_block_css, -1)
login_page = strings.Replace(login_page, "{SSH_PORT}", cfg.SSH.Port, -1)
login_page = strings.Replace(login_page, "{DOMAIN}", domain.Domain, -1)
// GZip page
var buf bytes.Buffer
zw, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression)
_, err = zw.Write([]byte(login_page))
if err != nil {
log.Fatal(err)
}
if err := zw.Close(); err != nil {
log.Fatal(err)
}
domainToLoginPage[domain.Domain] = buf.Bytes()
domainNoAuth[domain.Domain] = domain.NoAuth
// For easy lookup without loops
domains[domain.Domain] = domain
cfg.Domains[i] = domain
}
}