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"
2023-03-27 17:52:54 +00:00
"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"
2023-03-27 17:52:54 +00:00
"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 ) {
2023-03-27 17:52:54 +00:00
config := utils . GetMainConfig ( )
cfg := simplecert . Default
2023-05-18 12:42:47 +00:00
cfg . Domains = utils . GetAllHostnames ( false , false )
2023-03-27 17:52:54 +00:00
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
2023-04-30 12:03:14 +00:00
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 )
}
2023-03-27 17:52:54 +00:00
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 )
}
}
2023-03-27 17:52:54 +00:00
certReloader , errSimCert = simplecert . Init ( cfg , nil )
if errSimCert != nil {
2023-04-01 17:21:47 +00:00
// 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 )
2023-05-04 17:15:28 +00:00
startHTTPServer ( router )
return
2023-03-27 17:52:54 +00:00
}
}
2023-03-25 20:15:00 +00:00
2023-03-27 17:52:54 +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 ) {
2023-03-27 17:52:54 +00:00
// 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
}
}
2023-03-27 17:52:54 +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 )
2023-03-27 17:52:54 +00:00
if err != nil {
utils . Fatal ( "Listening to HTTP (Redirecting to HTTPS)" , err )
}
} ) ( )
2023-02-26 22:26:09 +00:00
2023-03-27 17:52:54 +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
2023-03-27 17:52:54 +00:00
tlsConf := tlsconfig . NewServerTLSConfig ( tlsconfig . TLSModeServerStrict )
2023-03-10 20:59:56 +00:00
2023-03-27 17:52:54 +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 )
}
2023-03-27 17:52:54 +00:00
tlsConf . Certificates = [ ] tls . Certificate { cert }
}
server := http . Server {
TLSConfig : tlsConf ,
2023-05-06 18:25:10 +00:00
Addr : "0.0.0.0:" + serverPortHTTPS ,
2023-03-27 17:52:54 +00:00
ReadTimeout : 0 ,
ReadHeaderTimeout : 10 * time . Second ,
WriteTimeout : 0 ,
IdleTimeout : 30 * time . Second ,
Handler : router ,
DisableGeneralOptionsHandler : true ,
}
2023-02-26 22:26:09 +00:00
2023-03-27 17:52:54 +00:00
// start https server
errServ := server . ListenAndServeTLS ( "" , "" )
2023-02-26 22:26:09 +00:00
2023-03-27 17:52:54 +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 ) {
2023-04-18 15:50:12 +00:00
//Header.Del
2023-04-30 12:03:14 +00:00
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 ) ) )
2023-04-30 12:03:14 +00:00
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 ( )
2023-04-30 12:03:14 +00:00
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 )
2023-04-30 12:03:14 +00:00
oldDomains := baseMainConfig . HTTPConfig . TLSKeyHostsCached
2023-03-27 17:52:54 +00:00
2023-04-30 12:03:14 +00:00
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" )
2023-04-30 12:03:14 +00:00
pub , priv := utils . GenerateRSAWebCertificates ( domains )
2023-03-10 20:59:56 +00:00
baseMainConfig . HTTPConfig . TLSCert = pub
baseMainConfig . HTTPConfig . TLSKey = priv
2023-04-30 12:03:14 +00:00
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
}
2023-04-30 12:03:14 +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
2023-04-18 15:50:12 +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 )
2023-04-30 12:03:14 +00:00
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 )
2023-04-30 12:03:14 +00:00
srapi . HandleFunc ( "/api/mfa" , user . API2FA )
2023-04-30 16:23:48 +00:00
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 ) {
2023-06-13 22:51:43 +00:00
w . Write ( [ ] byte ( "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. Please empty your browser's cache and reload the page. Also, make sure you don't have a bookmark with the /ui in the URL. This message will disappear in the next version of Cosmos, to solve the compatibility issue. Sorry for the inconvenience." ) )
2023-06-13 11:07:30 +00:00
} )
2023-04-30 12:03:14 +00:00
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 )
2023-04-30 12:03:14 +00:00
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 {
2023-03-27 17:52:54 +00:00
utils . Log ( "TLS certificates do not exists or are disabled, starting HTTP server only" )
2023-02-26 22:26:09 +00:00
startHTTPServer ( router )
}
}