feat: user roles - admins and users limited to the machines given to them, user editor with machines, password sign-in and its lock; direct address without Cloudflare as the agents' fallback and in the installer; version 0.1.2
This commit is contained in:
1 parent
36dc12b69d
commit
021da93229
29 files changed
+844
-97
No files matched your search
+53
-14
@@ -71,6 +71,7 @@ func New(opts Options) (*Server, error) {
|
||||
if err := opts.DB.ensureDefaults(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mainHost = func() string { return directHost(opts.DB.HostAddress()) }
|
||||
staticFS, _ := fs.Sub(webFS, "web/static")
|
||||
assets := assetHashes(staticFS)
|
||||
funcs := template.FuncMap{
|
||||
@@ -139,13 +140,19 @@ func (s *Server) tunnelListenAddr(port int) string {
|
||||
}
|
||||
|
||||
// tunnelAdvert is the tunnel address handed to agents as "host:port". The host
|
||||
// is empty unless one is set in Settings; agents then use the panel's host.
|
||||
// is the one set in Settings, else the direct address's (the main one may be
|
||||
// behind a proxy that carries no raw TCP), else empty: agents then use the
|
||||
// panel's host.
|
||||
func (s *Server) tunnelAdvert() string {
|
||||
port := s.TunnelPort()
|
||||
if port == 0 {
|
||||
return ""
|
||||
}
|
||||
return net.JoinHostPort(s.db.TunnelHost(), strconv.Itoa(port))
|
||||
host := s.db.TunnelHost()
|
||||
if host == "" {
|
||||
host = directHost(s.db.DirectAddress())
|
||||
}
|
||||
return net.JoinHostPort(host, strconv.Itoa(port))
|
||||
}
|
||||
|
||||
// setTunnelPort moves the direct tunnel to another port. Connected agents are
|
||||
@@ -229,15 +236,17 @@ func (s *Server) Handler() http.Handler {
|
||||
mux.Handle("POST /agents/{id}/user", s.requireAdmin(s.handleAgentSetUser))
|
||||
mux.Handle("POST /agents/{id}/transport", s.requireAdmin(s.handleAgentTransport))
|
||||
mux.Handle("POST /agents/{id}/users/add", s.requireAdmin(s.handleAgentAddUser))
|
||||
mux.Handle("POST /agents/{id}/delete", s.requireAdmin(s.handleAgentDelete))
|
||||
mux.Handle("GET /settings", s.requireAdmin(s.handleSettings))
|
||||
mux.Handle("POST /settings/host", s.requireAdmin(s.handleSetHost))
|
||||
mux.Handle("POST /settings/tunnel", s.requireAdmin(s.handleSetTunnel))
|
||||
mux.Handle("POST /settings/secret/rotate", s.requireAdmin(s.handleRotateSecret))
|
||||
mux.Handle("POST /settings/install/code", s.requireAdmin(s.handleInstallCode))
|
||||
mux.Handle("POST /settings/admins/add", s.requireAdmin(s.handleAddAdmin))
|
||||
mux.Handle("POST /settings/admins/delete", s.requireAdmin(s.handleDeleteAdmin))
|
||||
mux.Handle("POST /settings/admins/keys/reset", s.requireAdmin(s.handleResetAdminKeys))
|
||||
mux.Handle("POST /agents/{id}/delete", s.adminsOnly(s.handleAgentDelete))
|
||||
mux.Handle("GET /settings", s.adminsOnly(s.handleSettings))
|
||||
mux.Handle("POST /settings/host", s.adminsOnly(s.handleSetHost))
|
||||
mux.Handle("POST /settings/tunnel", s.adminsOnly(s.handleSetTunnel))
|
||||
mux.Handle("POST /settings/secret/rotate", s.adminsOnly(s.handleRotateSecret))
|
||||
mux.Handle("POST /settings/install/code", s.adminsOnly(s.handleInstallCode))
|
||||
mux.Handle("POST /settings/admins/add", s.adminsOnly(s.handleAddAdmin))
|
||||
mux.Handle("POST /settings/admins/delete", s.adminsOnly(s.handleDeleteAdmin))
|
||||
mux.Handle("GET /api/panel/users/{id}/agents", s.adminsOnly(s.handleUserAgents))
|
||||
mux.Handle("POST /api/panel/users/{id}", s.adminsOnly(s.handleUserEdit))
|
||||
mux.Handle("POST /settings/admins/keys/reset", s.adminsOnly(s.handleResetAdminKeys))
|
||||
mux.Handle("GET /api/panel/account", s.requireAdmin(s.handleAccount))
|
||||
mux.Handle("POST /api/panel/account/keys/begin", s.requireAdmin(s.handleKeyRegisterBegin))
|
||||
mux.Handle("POST /api/panel/account/keys/finish", s.requireAdmin(s.handleKeyRegisterFinish))
|
||||
@@ -247,9 +256,9 @@ func (s *Server) Handler() http.Handler {
|
||||
mux.Handle("GET /api/panel/pin", s.requireAdmin(s.handlePinState))
|
||||
mux.Handle("POST /api/panel/pin/check", s.requireAdmin(s.handlePinCheck))
|
||||
mux.Handle("POST /api/panel/account/password/change", s.requireAdmin(s.handlePasswordChange))
|
||||
mux.Handle("POST /settings/ufw/toggle", s.requireAdmin(s.handleUfwToggle))
|
||||
mux.Handle("POST /settings/ufw/add", s.requireAdmin(s.handleUfwAdd))
|
||||
mux.Handle("POST /settings/ufw/revoke", s.requireAdmin(s.handleUfwRevoke))
|
||||
mux.Handle("POST /settings/ufw/toggle", s.adminsOnly(s.handleUfwToggle))
|
||||
mux.Handle("POST /settings/ufw/add", s.adminsOnly(s.handleUfwAdd))
|
||||
mux.Handle("POST /settings/ufw/revoke", s.adminsOnly(s.handleUfwRevoke))
|
||||
|
||||
// Panel terminal over HTTP long-polling (admin-only).
|
||||
mux.Handle("POST /api/panel/term/{agentID}/open", s.requireAdmin(s.handlePanelOpen))
|
||||
@@ -317,6 +326,36 @@ func (s *Server) requireAdmin(h http.HandlerFunc) http.Handler {
|
||||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
// A user who is not an admin uses only the machines given to them:
|
||||
// any other is as if it were not there.
|
||||
if !admin.IsAdmin() {
|
||||
p := r.URL.Path
|
||||
if strings.HasPrefix(p, "/agents/") || strings.HasPrefix(p, "/api/panel/agents/") || strings.HasPrefix(p, "/api/panel/term/") {
|
||||
id := r.PathValue("id")
|
||||
if id == "" {
|
||||
id = r.PathValue("agentID")
|
||||
}
|
||||
if id != "" && id != "presence" && !s.db.CanUseAgent(admin, id) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
h(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// adminsOnly lets only admins through: the settings, users, the firewall.
|
||||
func (s *Server) adminsOnly(h http.HandlerFunc) http.Handler {
|
||||
return s.requireAdmin(func(w http.ResponseWriter, r *http.Request) {
|
||||
if !s.currentAdmin(r).IsAdmin() {
|
||||
if r.Method == http.MethodGet && !strings.HasPrefix(r.URL.Path, "/api/") {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
http.Error(w, "только для администраторов", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
h(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user