Cosmos-Server/src/httpServer.go

346 lines
12 KiB
Go
Raw Normal View History

2023-02-26 22:26:09 +00:00
package main
import (
"net/http"
2023-03-25 20:15:00 +00:00
"github.com/azukaar/cosmos-server/src/utils"
"github.com/azukaar/cosmos-server/src/user"
"github.com/azukaar/cosmos-server/src/configapi"
"github.com/azukaar/cosmos-server/src/proxy"
"github.com/azukaar/cosmos-server/src/docker"
2023-06-04 14:41:26 +00:00
"github.com/azukaar/cosmos-server/src/authorizationserver"
2023-06-09 18:48:31 +00:00
"github.com/azukaar/cosmos-server/src/market"
2023-02-26 22:26:09 +00:00
"github.com/gorilla/mux"
2023-03-10 20:59:56 +00:00
"strconv"
"time"
2023-03-12 18:17:28 +00:00
"os"
"strings"
2023-03-10 20:59:56 +00:00
"github.com/go-chi/chi/middleware"
"github.com/go-chi/httprate"
"crypto/tls"
2023-03-12 18:17:28 +00:00
spa "github.com/roberthodgen/spa-server"
"github.com/foomo/simplecert"
"github.com/foomo/tlsconfig"
2023-02-26 22:26:09 +00:00
)
2023-03-10 20:59:56 +00:00
var serverPortHTTP = ""
var serverPortHTTPS = ""
2023-02-26 22:26:09 +00:00
func startHTTPServer(router *mux.Router) {
2023-05-06 18:25:10 +00:00
utils.Log("Listening to HTTP on : 0.0.0.0:" + serverPortHTTP)
2023-02-26 22:26:09 +00:00
err := http.ListenAndServe("0.0.0.0:" + serverPortHTTP, router)
if err != nil {
2023-03-10 20:59:56 +00:00
utils.Fatal("Listening to HTTP", err)
2023-02-26 22:26:09 +00:00
}
}
func startHTTPSServer(router *mux.Router, tlsCert string, tlsKey string) {
config := utils.GetMainConfig()
cfg := simplecert.Default
2023-05-18 12:42:47 +00:00
cfg.Domains = utils.GetAllHostnames(false, false)
cfg.CacheDir = "/config/certificates"
cfg.SSLEmail = config.HTTPConfig.SSLEmail
2023-05-06 18:25:10 +00:00
cfg.HTTPAddress = "0.0.0.0:"+serverPortHTTP
cfg.TLSAddress = "0.0.0.0:"+serverPortHTTPS
if config.HTTPConfig.DNSChallengeProvider != "" {
cfg.DNSProvider = config.HTTPConfig.DNSChallengeProvider
}
2023-03-29 20:38:50 +00:00
cfg.FailedToRenewCertificate = func(err error) {
utils.Error("Failed to renew certificate", err)
}
var certReloader *simplecert.CertReloader
var errSimCert error
if(config.HTTPConfig.HTTPSCertificateMode == utils.HTTPSCertModeList["LETSENCRYPT"]) {
2023-06-13 21:30:11 +00:00
if(config.HTTPConfig.DNSChallengeProvider != "") {
newEnv := config.HTTPConfig.DNSChallengeConfig
for key, value := range newEnv {
os.Setenv(key, value)
}
}
certReloader, errSimCert = simplecert.Init(cfg, nil)
if errSimCert != nil {
// Temporary before we have a better way to handle this
2023-05-06 18:25:10 +00:00
utils.Error("Failed to Init Let's Encrypt. HTTPS wont renew", errSimCert)
startHTTPServer(router)
return
}
}
2023-03-25 20:15:00 +00:00
// redirect http to https
go (func () {
2023-06-04 14:41:26 +00:00
httpRouter := mux.NewRouter()
// add support for internal OpenID requests
// if os.Getenv("HOSTNAME") != "" {
// authorizationserver.RegisterHandlers(httpRouter.Host(os.Getenv("HOSTNAME")).Subrouter())
// }
httpRouter.PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// change port in host
if strings.HasSuffix(r.Host, ":" + serverPortHTTP) {
if serverPortHTTPS != "443" {
r.Host = r.Host[:len(r.Host)-len(":" + serverPortHTTP)] + ":" + serverPortHTTPS
} else {
r.Host = r.Host[:len(r.Host)-len(":" + serverPortHTTP)]
2023-02-26 22:26:09 +00:00
}
}
http.Redirect(w, r, "https://"+r.Host+r.URL.String(), http.StatusMovedPermanently)
2023-06-04 14:41:26 +00:00
})
// err := http.ListenAndServe("0.0.0.0:" + serverPortHTTP, http.HandlerFunc(simplecert.Redirect))
err := http.ListenAndServe("0.0.0.0:" + serverPortHTTP, httpRouter)
if err != nil {
utils.Fatal("Listening to HTTP (Redirecting to HTTPS)", err)
}
})()
2023-02-26 22:26:09 +00:00
utils.Log("Listening to HTTP on :" + serverPortHTTP)
utils.Log("Listening to HTTPS on :" + serverPortHTTPS)
utils.IsHTTPS = true
2023-03-10 20:59:56 +00:00
tlsConf := tlsconfig.NewServerTLSConfig(tlsconfig.TLSModeServerStrict)
2023-03-10 20:59:56 +00:00
if(config.HTTPConfig.HTTPSCertificateMode == utils.HTTPSCertModeList["LETSENCRYPT"]) {
tlsConf.GetCertificate = certReloader.GetCertificateFunc()
} else {
2023-03-10 20:59:56 +00:00
cert, errCert := tls.X509KeyPair(([]byte)(tlsCert), ([]byte)(tlsKey))
if errCert != nil {
utils.Fatal("Getting Certificate pair", errCert)
}
tlsConf.Certificates = []tls.Certificate{cert}
}
server := http.Server{
TLSConfig: tlsConf,
2023-05-06 18:25:10 +00:00
Addr: "0.0.0.0:" + serverPortHTTPS,
ReadTimeout: 0,
ReadHeaderTimeout: 10 * time.Second,
WriteTimeout: 0,
IdleTimeout: 30 * time.Second,
Handler: router,
DisableGeneralOptionsHandler: true,
}
2023-02-26 22:26:09 +00:00
// start https server
errServ := server.ListenAndServeTLS("", "")
2023-02-26 22:26:09 +00:00
if errServ != nil {
utils.Fatal("Listening to HTTPS", errServ)
}
2023-02-26 22:26:09 +00:00
}
2023-03-10 20:59:56 +00:00
func tokenMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
//Header.Del
r.Header.Del("x-cosmos-user")
r.Header.Del("x-cosmos-role")
r.Header.Del("x-cosmos-mfa")
2023-03-10 20:59:56 +00:00
u, err := user.RefreshUserToken(w, r)
if err != nil {
return
}
r.Header.Set("x-cosmos-user", u.Nickname)
r.Header.Set("x-cosmos-role", strconv.Itoa((int)(u.Role)))
r.Header.Set("x-cosmos-mfa", strconv.Itoa((int)(u.MFAState)))
2023-03-10 20:59:56 +00:00
next.ServeHTTP(w, r)
})
}
2023-06-04 14:41:26 +00:00
func SecureAPI(userRouter *mux.Router, public bool) {
if(!public) {
userRouter.Use(tokenMiddleware)
}
userRouter.Use(proxy.SmartShieldMiddleware(
utils.SmartShieldPolicy{
Enabled: true,
PolicyStrictness: 1,
PerUserRequestLimit: 5000,
},
))
userRouter.Use(utils.MiddlewareTimeout(45 * time.Second))
userRouter.Use(utils.BlockPostWithoutReferer)
userRouter.Use(proxy.BotDetectionMiddleware)
userRouter.Use(httprate.Limit(60, 1*time.Minute,
httprate.WithKeyFuncs(httprate.KeyByIP),
httprate.WithLimitHandler(func(w http.ResponseWriter, r *http.Request) {
utils.Error("Too many requests. Throttling", nil)
utils.HTTPError(w, "Too many requests",
http.StatusTooManyRequests, "HTTP003")
return
}),
))
}
2023-03-10 20:59:56 +00:00
func StartServer() {
baseMainConfig := utils.GetBaseMainConfig()
config := utils.GetMainConfig()
HTTPConfig := config.HTTPConfig
serverPortHTTP = HTTPConfig.HTTPPort
serverPortHTTPS = HTTPConfig.HTTPSPort
var tlsCert = HTTPConfig.TLSCert
var tlsKey= HTTPConfig.TLSKey
2023-02-26 22:26:09 +00:00
2023-05-18 12:42:47 +00:00
domains := utils.GetAllHostnames(true, true)
oldDomains := baseMainConfig.HTTPConfig.TLSKeyHostsCached
NeedsRefresh := (tlsCert == "" || tlsKey == "") || !utils.StringArrayEquals(domains, oldDomains)
if(NeedsRefresh && HTTPConfig.HTTPSCertificateMode == utils.HTTPSCertModeList["SELFSIGNED"]) {
2023-03-10 20:59:56 +00:00
utils.Log("Generating new TLS certificate")
pub, priv := utils.GenerateRSAWebCertificates(domains)
2023-03-10 20:59:56 +00:00
baseMainConfig.HTTPConfig.TLSCert = pub
baseMainConfig.HTTPConfig.TLSKey = priv
baseMainConfig.HTTPConfig.TLSKeyHostsCached = domains
2023-03-10 20:59:56 +00:00
utils.SetBaseMainConfig(baseMainConfig)
utils.Log("Saved new TLS certificate")
tlsCert = pub
tlsKey = priv
2023-02-26 22:26:09 +00:00
}
if ((HTTPConfig.AuthPublicKey == "" || HTTPConfig.AuthPrivateKey == "") && HTTPConfig.GenerateMissingAuthCert) {
2023-03-10 20:59:56 +00:00
utils.Log("Generating new Auth ED25519 certificate")
pub, priv := utils.GenerateEd25519Certificates()
baseMainConfig.HTTPConfig.AuthPublicKey = pub
baseMainConfig.HTTPConfig.AuthPrivateKey = priv
utils.SetBaseMainConfig(baseMainConfig)
utils.Log("Saved new Auth ED25519 certificate")
2023-02-26 22:26:09 +00:00
}
2023-03-12 18:17:28 +00:00
router := mux.NewRouter().StrictSlash(true)
2023-05-01 10:00:45 +00:00
router.HandleFunc("/logo", SendLogo)
2023-02-26 22:26:09 +00:00
// need rewrite bc it catches too many things and prevent
// client to be notified of the error
2023-05-19 15:23:05 +00:00
2023-03-10 20:59:56 +00:00
router.Use(middleware.Logger)
router.Use(utils.SetSecurityHeaders)
if config.BlockedCountries != nil && len(config.BlockedCountries) > 0 {
router.Use(utils.BlockByCountryMiddleware(config.BlockedCountries))
}
2023-03-12 18:17:28 +00:00
srapi := router.PathPrefix("/cosmos").Subrouter()
2023-03-10 20:59:56 +00:00
2023-06-04 14:41:26 +00:00
srapi.HandleFunc("/api/dns", GetDNSRoute)
srapi.HandleFunc("/api/dns-check", CheckDNSRoute)
2023-03-29 20:38:50 +00:00
srapi.HandleFunc("/api/status", StatusRoute)
2023-05-01 10:00:45 +00:00
srapi.HandleFunc("/api/can-send-email", CanSendEmail)
2023-04-27 18:29:26 +00:00
srapi.HandleFunc("/api/favicon", GetFavicon)
srapi.HandleFunc("/api/ping", PingURL)
2023-03-29 20:38:50 +00:00
srapi.HandleFunc("/api/newInstall", NewInstallRoute)
2023-03-12 18:17:28 +00:00
srapi.HandleFunc("/api/login", user.UserLogin)
srapi.HandleFunc("/api/logout", user.UserLogout)
srapi.HandleFunc("/api/register", user.UserRegister)
srapi.HandleFunc("/api/invite", user.UserResendInviteLink)
srapi.HandleFunc("/api/me", user.Me)
srapi.HandleFunc("/api/mfa", user.API2FA)
srapi.HandleFunc("/api/password-reset", user.ResetPassword)
2023-03-16 18:56:36 +00:00
srapi.HandleFunc("/api/config", configapi.ConfigRoute)
srapi.HandleFunc("/api/restart", configapi.ConfigApiRestart)
2023-03-10 20:59:56 +00:00
2023-03-12 18:17:28 +00:00
srapi.HandleFunc("/api/users/{nickname}", user.UsersIdRoute)
srapi.HandleFunc("/api/users", user.UsersRoute)
2023-05-06 18:25:10 +00:00
2023-05-14 14:48:15 +00:00
srapi.HandleFunc("/api/images/pull-if-missing", docker.PullImageIfMissing)
srapi.HandleFunc("/api/images/pull", docker.PullImage)
srapi.HandleFunc("/api/images", docker.InspectImageRoute)
2023-05-13 17:38:39 +00:00
2023-05-06 18:25:10 +00:00
srapi.HandleFunc("/api/volume/{volumeName}", docker.DeleteVolumeRoute)
2023-05-07 16:47:20 +00:00
srapi.HandleFunc("/api/volumes", docker.VolumesRoute)
2023-05-06 18:25:10 +00:00
srapi.HandleFunc("/api/network/{networkID}", docker.DeleteNetworkRoute)
2023-05-07 16:47:20 +00:00
srapi.HandleFunc("/api/networks", docker.NetworkRoutes)
2023-03-25 20:15:00 +00:00
2023-05-05 18:05:33 +00:00
srapi.HandleFunc("/api/servapps/{containerId}/manage/{action}", docker.ManageContainerRoute)
2023-03-31 19:19:38 +00:00
srapi.HandleFunc("/api/servapps/{containerId}/secure/{status}", docker.SecureContainerRoute)
2023-05-16 17:08:01 +00:00
srapi.HandleFunc("/api/servapps/{containerId}/auto-update/{status}", docker.AutoUpdateContainerRoute)
2023-05-06 18:25:10 +00:00
srapi.HandleFunc("/api/servapps/{containerId}/logs", docker.GetContainerLogsRoute)
2023-05-11 18:15:05 +00:00
srapi.HandleFunc("/api/servapps/{containerId}/terminal/{action}", docker.TerminalRoute)
2023-05-07 16:47:20 +00:00
srapi.HandleFunc("/api/servapps/{containerId}/update", docker.UpdateContainerRoute)
2023-05-06 18:25:10 +00:00
srapi.HandleFunc("/api/servapps/{containerId}/", docker.GetContainerRoute)
2023-05-07 16:47:20 +00:00
srapi.HandleFunc("/api/servapps/{containerId}/network/{networkId}", docker.NetworkContainerRoutes)
srapi.HandleFunc("/api/servapps/{containerId}/networks", docker.NetworkContainerRoutes)
2023-05-14 14:48:15 +00:00
srapi.HandleFunc("/api/servapps/{containerId}/check-update", docker.CanUpdateImageRoute)
2023-03-25 20:15:00 +00:00
srapi.HandleFunc("/api/servapps", docker.ContainersRoute)
2023-05-13 17:38:39 +00:00
srapi.HandleFunc("/api/docker-service", docker.CreateServiceRoute)
2023-06-04 14:41:26 +00:00
2023-06-09 18:48:31 +00:00
srapi.HandleFunc("/api/markets", market.MarketGet)
2023-06-04 14:41:26 +00:00
2023-03-10 20:59:56 +00:00
2023-05-06 18:25:10 +00:00
if(!config.HTTPConfig.AcceptAllInsecureHostname) {
srapi.Use(utils.EnsureHostname)
}
2023-05-05 18:05:33 +00:00
2023-06-04 14:41:26 +00:00
SecureAPI(srapi, false)
2023-03-12 18:17:28 +00:00
pwd,_ := os.Getwd()
utils.Log("Starting in " + pwd)
if _, err := os.Stat(pwd + "/static"); os.IsNotExist(err) {
utils.Fatal("Static folder not found at " + pwd + "/static", err)
}
2023-03-16 18:56:36 +00:00
2023-05-05 18:05:33 +00:00
2023-03-12 18:17:28 +00:00
fs := spa.SpaHandler(pwd + "/static", "index.html")
2023-05-05 18:05:33 +00:00
2023-05-06 18:25:10 +00:00
if(!config.HTTPConfig.AcceptAllInsecureHostname) {
fs = utils.EnsureHostname(fs)
}
2023-05-05 18:05:33 +00:00
2023-06-13 01:03:18 +00:00
router.PathPrefix("/cosmos-ui").Handler(http.StripPrefix("/cosmos-ui", fs))
2023-03-12 18:17:28 +00:00
2023-06-13 11:07:30 +00:00
// temporary message to help people migrate version. DELETE IN NEXT VERSION
router.HandleFunc("/ui", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Please empty your browser's cache and reload the page. You are seeing this message because the UI was moved from /ui to /cosmos-ui, in order to fix compatibility with apps like OpenSense who also use /ui. The issue is that your browser still has the old UI URL cached. This message will disappear in the next version of Cosmos, to solve the compatibility issue. Sorry for the inconvenience."))
})
router = proxy.BuildFromConfig(router, HTTPConfig.ProxyConfig)
2023-04-01 16:49:54 +00:00
router.HandleFunc("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2023-06-13 01:03:18 +00:00
http.Redirect(w, r, "/cosmos-ui", http.StatusMovedPermanently)
2023-04-01 16:49:54 +00:00
}))
2023-03-10 20:59:56 +00:00
2023-06-04 14:41:26 +00:00
userRouter := router.PathPrefix("/oauth2").Subrouter()
SecureAPI(userRouter, false)
serverRouter := router.PathPrefix("/oauth2").Subrouter()
SecureAPI(userRouter, true)
wellKnownRouter := router.PathPrefix("/.well-known").Subrouter()
SecureAPI(userRouter, true)
authorizationserver.RegisterHandlers(wellKnownRouter, userRouter, serverRouter)
if ((HTTPConfig.HTTPSCertificateMode == utils.HTTPSCertModeList["SELFSIGNED"] || HTTPConfig.HTTPSCertificateMode == utils.HTTPSCertModeList["PROVIDED"]) &&
tlsCert != "" && tlsKey != "") || (HTTPConfig.HTTPSCertificateMode == utils.HTTPSCertModeList["LETSENCRYPT"]) {
2023-03-10 20:59:56 +00:00
utils.Log("TLS certificate exist, starting HTTPS servers and redirecting HTTP to HTTPS")
2023-02-26 22:26:09 +00:00
startHTTPSServer(router, tlsCert, tlsKey)
} else {
utils.Log("TLS certificates do not exists or are disabled, starting HTTP server only")
2023-02-26 22:26:09 +00:00
startHTTPServer(router)
}
}