Allow not serving the toplevel domain
This commit is contained in:
parent
a90c68bf5e
commit
5ef2e14ae9
9 changed files with 60 additions and 17 deletions
|
|
@ -39,6 +39,10 @@ http:
|
||||||
# This email address can be configured here.
|
# This email address can be configured here.
|
||||||
certbot_email: null
|
certbot_email: null
|
||||||
|
|
||||||
|
# Serve the panel also on the toplevel domain, and not only on the "_panel" domain.
|
||||||
|
# Enabled by default.
|
||||||
|
panel: null
|
||||||
|
|
||||||
# Enable or Disable the HTTP API.
|
# Enable or Disable the HTTP API.
|
||||||
# In the future, it will be enabled by default, but at this point it is not.
|
# In the future, it will be enabled by default, but at this point it is not.
|
||||||
api: null
|
api: null
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,11 @@ type HTTPConfig struct {
|
||||||
// This email address can be configured here.
|
// This email address can be configured here.
|
||||||
CertbotEmail string `yaml:"certbot_email" validate:"email"`
|
CertbotEmail string `yaml:"certbot_email" validate:"email"`
|
||||||
|
|
||||||
|
// Also serve the panel on the toplevel domain.
|
||||||
|
// Note that the panel is *always* servered under the "_panel" domain.
|
||||||
|
// Disabling this is not recommended.
|
||||||
|
Panel validators.NullableBool `yaml:"panel" validate:"bool" default:"true"`
|
||||||
|
|
||||||
// API determines if the API is enabled.
|
// API determines if the API is enabled.
|
||||||
// In a future version of the distillery, it will be enabled by default.
|
// In a future version of the distillery, it will be enabled by default.
|
||||||
API validators.NullableBool `yaml:"api" validate:"bool" default:"false"`
|
API validators.NullableBool `yaml:"api" validate:"bool" default:"false"`
|
||||||
|
|
@ -36,6 +41,16 @@ type HTTPConfig struct {
|
||||||
PhpMyAdmin validators.NullableBool `yaml:"phpmyadmin" validate:"bool" default:"false"`
|
PhpMyAdmin validators.NullableBool `yaml:"phpmyadmin" validate:"bool" default:"false"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (hcfg HTTPConfig) PublicTopDomain() string {
|
||||||
|
// if we have panel domain enabled, then return it
|
||||||
|
if hcfg.Panel.Set && hcfg.Panel.Value {
|
||||||
|
return hcfg.PrimaryDomain
|
||||||
|
}
|
||||||
|
|
||||||
|
// else use the domain itself
|
||||||
|
return hcfg.Domains(PanelDomain.Domain())[0]
|
||||||
|
}
|
||||||
|
|
||||||
// TSDomain returns the full url to the triplestore, if any
|
// TSDomain returns the full url to the triplestore, if any
|
||||||
func (hcfg HTTPConfig) TSURL() template.URL {
|
func (hcfg HTTPConfig) TSURL() template.URL {
|
||||||
return hcfg.optionalURL(TriplestoreDomain.Domain(), hcfg.TS)
|
return hcfg.optionalURL(TriplestoreDomain.Domain(), hcfg.TS)
|
||||||
|
|
@ -66,7 +81,7 @@ func (hcfg HTTPConfig) optionalURL(domain string, enabled validators.NullableBoo
|
||||||
func (hcfg HTTPConfig) JoinPath(elem ...string) *url.URL {
|
func (hcfg HTTPConfig) JoinPath(elem ...string) *url.URL {
|
||||||
u := url.URL{
|
u := url.URL{
|
||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
Host: hcfg.PrimaryDomain,
|
Host: hcfg.PublicTopDomain(),
|
||||||
Path: "/",
|
Path: "/",
|
||||||
}
|
}
|
||||||
if hcfg.HTTPSEnabled() {
|
if hcfg.HTTPSEnabled() {
|
||||||
|
|
@ -93,6 +108,7 @@ func (hcfg HTTPConfig) HTTPSEnabled() bool {
|
||||||
type SpecialDomain string
|
type SpecialDomain string
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
PanelDomain SpecialDomain = "panel"
|
||||||
TriplestoreDomain SpecialDomain = "ts"
|
TriplestoreDomain SpecialDomain = "ts"
|
||||||
PHPMyAdminDomain SpecialDomain = "phpmyadmin"
|
PHPMyAdminDomain SpecialDomain = "phpmyadmin"
|
||||||
)
|
)
|
||||||
|
|
@ -168,6 +184,27 @@ func (cfg HTTPConfig) SlugFromHost(host string) (slug string, ok bool) {
|
||||||
return "", ok
|
return "", ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NormSlugFromHost is like SlugFromHost, but normalizes the panel host
|
||||||
|
func (cfg HTTPConfig) NormSlugFromHost(host string) (string, bool) {
|
||||||
|
// if we didn't get a domain, don't do anything
|
||||||
|
slug, ok := cfg.SlugFromHost(host)
|
||||||
|
if !ok {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
// always serve the panel domain
|
||||||
|
if slug == PanelDomain.Domain() {
|
||||||
|
return "", true
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we don't serve the toplevel domain then the toplevel domain is an error.
|
||||||
|
if slug == "" && !(cfg.Panel.Set && cfg.Panel.Value) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
return slug, true
|
||||||
|
}
|
||||||
|
|
||||||
func TrimSuffixFold(s string, suffix string) string {
|
func TrimSuffixFold(s string, suffix string) string {
|
||||||
if len(s) >= len(suffix) && strings.EqualFold(s[len(s)-len(suffix):], suffix) {
|
if len(s) >= len(suffix) && strings.EqualFold(s[len(s)-len(suffix):], suffix) {
|
||||||
return s[:len(s)-len(suffix)]
|
return s[:len(s)-len(suffix)]
|
||||||
|
|
@ -175,10 +212,13 @@ func TrimSuffixFold(s string, suffix string) string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultHostRule returns the default traefik hostname rule for this distillery.
|
// DefaultHostRule returns the host rule for the control panel of this distillery.
|
||||||
// This consists of the [DefaultDomain] as well as [ExtraDomains].
|
func (cfg HTTPConfig) PanelHostRule() string {
|
||||||
func (cfg HTTPConfig) DefaultHostRule() string {
|
all := cfg.Domains(PanelDomain.Domain())
|
||||||
return cfg.HostRule("")
|
if cfg.Panel.Set && cfg.Panel.Value {
|
||||||
|
all = append(all, cfg.Domains("")...)
|
||||||
|
}
|
||||||
|
return MakeHostRule(all...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeHostRule builds a new Host() rule string to be used by traefik.
|
// MakeHostRule builds a new Host() rule string to be used by traefik.
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ func (panel *UserPanel) sshRoute(ctx context.Context) http.Handler {
|
||||||
return sc, err
|
return sc, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sc.Domain = panel.Config.HTTP.PrimaryDomain
|
sc.Domain = panel.Config.HTTP.PublicTopDomain()
|
||||||
sc.Port = panel.Config.Listen.SSHPort
|
sc.Port = panel.Config.Listen.SSHPort
|
||||||
|
|
||||||
// pick the first domain that the user has access to as an example
|
// pick the first domain that the user has access to as an example
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ func (home *Home) HandleRoute(ctx context.Context, route string) (http.Handler,
|
||||||
dflt.Fallback = home.publicHandler(ctx)
|
dflt.Fallback = home.publicHandler(ctx)
|
||||||
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
slug, ok := home.Config.HTTP.SlugFromHost(r.Host)
|
slug, ok := home.Config.HTTP.NormSlugFromHost(r.Host)
|
||||||
switch {
|
switch {
|
||||||
case !ok:
|
case !ok:
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ func (server *Server) Server(ctx context.Context, progress io.Writer) (public ht
|
||||||
|
|
||||||
var publicM, internalM mux.Mux[component.RouteContext]
|
var publicM, internalM mux.Mux[component.RouteContext]
|
||||||
publicM.Context = func(r *http.Request) component.RouteContext {
|
publicM.Context = func(r *http.Request) component.RouteContext {
|
||||||
slug, ok := server.Still.Config.HTTP.SlugFromHost(r.Host)
|
slug, ok := server.Config.HTTP.NormSlugFromHost(r.Host)
|
||||||
return component.RouteContext{
|
return component.RouteContext{
|
||||||
DefaultDomain: slug == "" && ok,
|
DefaultDomain: slug == "" && ok,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,14 @@ services:
|
||||||
|
|
||||||
- "traefik.enable=True"
|
- "traefik.enable=True"
|
||||||
- "eu.wiss-ki.barrel.distillery=${DOCKER_NETWORK_NAME}"
|
- "eu.wiss-ki.barrel.distillery=${DOCKER_NETWORK_NAME}"
|
||||||
- "traefik.http.routers.control.rule=${HOST_RULE}"
|
- "traefik.http.routers.core_panel.rule=${HOST_RULE}"
|
||||||
|
|
||||||
- "traefik.http.routers.fallback.rule=HostRegexp(`{catchall:.*}`)"
|
- "traefik.http.routers.core_fallback.rule=HostRegexp(`{catchall:.*}`)"
|
||||||
- "traefik.http.routers.fallback.priority=1"
|
- "traefik.http.routers.core_fallback.priority=1"
|
||||||
|
|
||||||
- "traefik.http.routers.control.tls=${HTTPS_ENABLED}"
|
- "traefik.http.routers.core_panel.tls=${HTTPS_ENABLED}"
|
||||||
- "traefik.http.routers.control.tls.certresolver=distillery"
|
- "traefik.http.routers.core_panel.tls.certresolver=distillery"
|
||||||
- "traefik.http.services.control.loadbalancer.server.port=8888"
|
- "traefik.http.services.core_panel.loadbalancer.server.port=8888"
|
||||||
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ func (server *Server) Stack() component.StackWithResources {
|
||||||
|
|
||||||
EnvContext: map[string]string{
|
EnvContext: map[string]string{
|
||||||
"DOCKER_NETWORK_NAME": server.Config.Docker.Network(),
|
"DOCKER_NETWORK_NAME": server.Config.Docker.Network(),
|
||||||
"HOST_RULE": server.Config.HTTP.DefaultHostRule(),
|
"HOST_RULE": server.Config.HTTP.PanelHostRule(),
|
||||||
"HTTPS_ENABLED": server.Config.HTTP.HTTPSEnabledEnv(),
|
"HTTPS_ENABLED": server.Config.HTTP.HTTPSEnabledEnv(),
|
||||||
|
|
||||||
"CONFIG_PATH": server.Config.ConfigPath,
|
"CONFIG_PATH": server.Config.ConfigPath,
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ func (ssh2 *SSH2) handleConnection(session ssh.Session) {
|
||||||
{"${SLUG}", slug},
|
{"${SLUG}", slug},
|
||||||
{"${HOSTNAME}", slug + "." + ssh2.Config.HTTP.PrimaryDomain},
|
{"${HOSTNAME}", slug + "." + ssh2.Config.HTTP.PrimaryDomain},
|
||||||
|
|
||||||
{"${DOMAIN}", ssh2.Config.HTTP.PrimaryDomain},
|
{"${DOMAIN}", ssh2.Config.HTTP.PublicTopDomain()},
|
||||||
{"${PORT}", strconv.FormatUint(uint64(ssh2.Config.Listen.SSHPort), 10)},
|
{"${PORT}", strconv.FormatUint(uint64(ssh2.Config.Listen.SSHPort), 10)},
|
||||||
|
|
||||||
{"${HELP_URL}", ssh2.Config.HTTP.JoinPath("user", "ssh").String()},
|
{"${HELP_URL}", ssh2.Config.HTTP.JoinPath("user", "ssh").String()},
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ func (ssh *SSH2) Stack() component.StackWithResources {
|
||||||
|
|
||||||
EnvContext: map[string]string{
|
EnvContext: map[string]string{
|
||||||
"DOCKER_NETWORK_NAME": ssh.Config.Docker.Network(),
|
"DOCKER_NETWORK_NAME": ssh.Config.Docker.Network(),
|
||||||
"HOST_RULE": ssh.Config.HTTP.DefaultHostRule(),
|
|
||||||
|
|
||||||
"CONFIG_PATH": ssh.Config.ConfigPath,
|
"CONFIG_PATH": ssh.Config.ConfigPath,
|
||||||
"DEPLOY_ROOT": ssh.Config.Paths.Root,
|
"DEPLOY_ROOT": ssh.Config.Paths.Root,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue