diff --git a/Dockerfile b/Dockerfile index a1fb283..fc5a327 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,8 +33,8 @@ COPY . . # Build the application RUN make -# Expose the port for the webserver -EXPOSE 6157 +# Expose the ports +EXPOSE 6157 2222 # Mount the .opengist volume VOLUME /root/.opengist diff --git a/config.yml b/config.yml index 06d8321..36e0740 100644 --- a/config.yml +++ b/config.yml @@ -1,58 +1,58 @@ +# Set the log level to one of the following: trace, debug, info, warn, error, fatal, panic. Default: warn +log-level: warn + +# Public URL for the Git HTTP/SSH connection. +# If not set, uses the URL from the request +external-url: + +# Prevents the creation of new accounts (either `true` or `false`). Default: false +disable-signup: false + # Directory where Opengist will store its data. Default: ~/.opengist/ opengist-home: # Name of the SQLite database file. Default: opengist.db db-filename: opengist.db -# Prevents the creation of new accounts (either `true` or `false`). Default: false -disable-signup: false - -# Set the log level to one of the following: trace, debug, info, warn, error, fatal, panic. Default: warn -log-level: warn # HTTP server configuration -http: +# Host to bind to. Default: 0.0.0.0 +http.host: 0.0.0.0 - # Host to bind to. Default: 0.0.0.0 - host: 0.0.0.0 +# Port to bind to. Default: 6157 +http.port: 6157 - # Port to bind to. Default: 6157 - port: 6157 +# Enable or disable git operations (clone, pull, push) via HTTP (either `true` or `false`). Default: true +http.git-enabled: true - # Domain to use in links. Default: localhost - domain: localhost +# Enable or disable TLS (either `true` or `false`). Default: false +http.tls-enabled: false - # Enable or disable git operations (clone, pull, push) via HTTP (either `true` or `false`). Default: true - git-enabled: true +# Path to the TLS certificate file if TLS is enabled +http.cert-file: - # Enable or disable TLS (either `true` or `false`). Default: false - tls-enabled: false - - # Path to the TLS certificate file if TLS is enabled - cert-file: - - # Path to the TLS key file if TLS is enabled - key-file: +# Path to the TLS key file if TLS is enabled +http.key-file: # SSH built-in server configuration # Note: it is not using the SSH daemon from your machine (yet) -ssh: - # Enable or disable SSH built-in server - # for git operations (clone, pull, push) via SSH (either `true` or `false`). Default: true - enabled: true +# Enable or disable SSH built-in server +# for git operations (clone, pull, push) via SSH (either `true` or `false`). Default: true +ssh.git-enabled: true - # Host to bind to. Default: 0.0.0.0 - host: 0.0.0.0 +# Host to bind to. Default: 0.0.0.0 +ssh.host: 0.0.0.0 - # Port to bind to. Default: 2222 - # Note: it cannot be the same port as the SSH daemon if it's currently running - # If you want to use the port 22 for the built-in SSH server, - # you can either change the port of the SSH daemon or stop it - port: 2222 +# Port to bind to. Default: 2222 +# Note: it cannot be the same port as the SSH daemon if it's currently running +# If you want to use the port 22 for the built-in SSH server, +# you can either change the port of the SSH daemon or stop it +ssh.port: 2222 - # Domain to use in links. Default: localhost - domain: localhost +# Public domain for the Git SSH connection, if it has to be different from the HTTP one. +# If not set, uses the URL from the request +ssh.external-domain: - # Path or alias to ssh-keygen executable. Default: ssh-keygen - keygen-executable: ssh-keygen +# Path or alias to ssh-keygen executable. Default: ssh-keygen +ssh.keygen-executable: ssh-keygen diff --git a/internal/config/config.go b/internal/config/config.go index d3f129d..69bb9a5 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -15,29 +15,27 @@ var OpengistVersion = "0.0.1" var C *config +// Not using nested structs because the library +// doesn't support dot notation in this case sadly type config struct { + LogLevel string `yaml:"log-level"` + ExternalUrl string `yaml:"external-url"` + DisableSignup bool `yaml:"disable-signup"` OpengistHome string `yaml:"opengist-home"` DBFilename string `yaml:"db-filename"` - DisableSignup bool `yaml:"disable-signup"` - LogLevel string `yaml:"log-level"` - HTTP struct { - Host string `yaml:"host"` - Port string `yaml:"port"` - Domain string `yaml:"domain"` - Git bool `yaml:"git-enabled"` - TLSEnabled bool `yaml:"tls-enabled"` - CertFile string `yaml:"cert-file"` - KeyFile string `yaml:"key-file"` - } `yaml:"http"` + HttpHost string `yaml:"http.host"` + HttpPort string `yaml:"http.port"` + HttpGit bool `yaml:"http.git-enabled"` + HttpTLSEnabled bool `yaml:"http.tls-enabled"` + HttpCertFile string `yaml:"http.cert-file"` + HttpKeyFile string `yaml:"http.key-file"` - SSH struct { - Enabled bool `yaml:"enabled"` - Host string `yaml:"host"` - Port string `yaml:"port"` - Domain string `yaml:"domain"` - Keygen string `yaml:"keygen-executable"` - } `yaml:"ssh"` + SshGit bool `yaml:"ssh.git-enabled"` + SshHost string `yaml:"ssh.host"` + SshPort string `yaml:"ssh.port"` + SshExternalDomain string `yaml:"ssh.external-domain"` + SshKeygen string `yaml:"ssh.keygen-executable"` } func configWithDefaults() (*config, error) { @@ -47,43 +45,53 @@ func configWithDefaults() (*config, error) { return c, err } + c.LogLevel = "warn" + c.DisableSignup = false c.OpengistHome = filepath.Join(homeDir, ".opengist") c.DBFilename = "opengist.db" - c.DisableSignup = false - c.LogLevel = "warn" - c.HTTP.Host = "0.0.0.0" - c.HTTP.Port = "6157" - c.HTTP.Domain = "localhost" - c.HTTP.Git = true + c.HttpHost = "0.0.0.0" + c.HttpPort = "6157" + c.HttpGit = true + c.HttpTLSEnabled = false - c.HTTP.TLSEnabled = false - - c.SSH.Enabled = true - c.SSH.Host = "0.0.0.0" - c.SSH.Port = "2222" - c.SSH.Domain = "localhost" - c.SSH.Keygen = "ssh-keygen" + c.SshGit = true + c.SshHost = "0.0.0.0" + c.SshPort = "2222" + c.SshKeygen = "ssh-keygen" return c, nil } func InitConfig(configPath string) error { + // Default values c, err := configWithDefaults() if err != nil { return err } file, err := os.Open(configPath) - if err != nil { - return err - } - defer file.Close() + if err == nil { + fmt.Println("Using config file: " + configPath) - d := yaml.NewDecoder(file) - if err = d.Decode(&c); err != nil { - return err + // Override default values with values from config.yml + d := yaml.NewDecoder(file) + if err = d.Decode(&c); err != nil { + return err + } + defer file.Close() } + + // Override default values with environment variables (as yaml) + configEnv := os.Getenv("CONFIG") + if configEnv != "" { + fmt.Println("Using config from environment variable: CONFIG") + d := yaml.NewDecoder(strings.NewReader(configEnv)) + if err = d.Decode(&c); err != nil { + return err + } + } + C = c return nil diff --git a/internal/ssh/run.go b/internal/ssh/run.go index 580bfe1..15c7f7b 100644 --- a/internal/ssh/run.go +++ b/internal/ssh/run.go @@ -18,7 +18,7 @@ import ( ) func Start() { - if !config.C.SSH.Enabled { + if !config.C.SshGit { return } @@ -47,8 +47,8 @@ func Start() { } func listen(serverConfig *ssh.ServerConfig) { - log.Info().Msg("Starting SSH server on ssh://" + config.C.SSH.Host + ":" + config.C.SSH.Port) - listener, err := net.Listen("tcp", config.C.SSH.Host+":"+config.C.SSH.Port) + log.Info().Msg("Starting SSH server on ssh://" + config.C.SshHost + ":" + config.C.SshPort) + listener, err := net.Listen("tcp", config.C.SshHost+":"+config.C.SshPort) if err != nil { log.Fatal().Err(err).Msg("SSH: Failed to start SSH server") } @@ -129,7 +129,7 @@ func setupHostKey() (ssh.Signer, error) { keyPath := filepath.Join(dir, "opengist-ed25519") if _, err := os.Stat(keyPath); err != nil && !os.IsExist(err) { - cmd := exec.Command(config.C.SSH.Keygen, + cmd := exec.Command(config.C.SshKeygen, "-t", "ssh-ed25519", "-f", keyPath, "-m", "PEM", diff --git a/internal/web/gist.go b/internal/web/gist.go index 5ea8804..844a21a 100644 --- a/internal/web/gist.go +++ b/internal/web/gist.go @@ -30,11 +30,19 @@ func gistInit(next echo.HandlerFunc) echo.HandlerFunc { } setData(ctx, "gist", gist) - if config.C.SSH.Enabled { - if config.C.SSH.Port == "22" { - setData(ctx, "sshCloneUrl", config.C.SSH.Domain+":"+userName+"/"+gistName+".git") + if config.C.SshGit { + var sshDomain string + + if config.C.SshExternalDomain != "" { + sshDomain = config.C.SshExternalDomain } else { - setData(ctx, "sshCloneUrl", "ssh://"+config.C.SSH.Domain+":"+config.C.SSH.Port+"/"+userName+"/"+gistName+".git") + sshDomain = strings.Split(ctx.Request().Host, ":")[0] + } + + if config.C.SshPort == "22" { + setData(ctx, "sshCloneUrl", sshDomain+":"+userName+"/"+gistName+".git") + } else { + setData(ctx, "sshCloneUrl", "ssh://"+sshDomain+":"+config.C.SshPort+"/"+userName+"/"+gistName+".git") } } @@ -44,20 +52,19 @@ func gistInit(next echo.HandlerFunc) echo.HandlerFunc { } setData(ctx, "httpProtocol", strings.ToUpper(httpProtocol)) - if config.C.HTTP.Git { - if config.C.HTTP.Port == "80" || config.C.HTTP.Port == "443" { - setData(ctx, "httpCloneUrl", httpProtocol+"://"+config.C.HTTP.Domain+"/"+userName+"/"+gistName+".git") - } else { - setData(ctx, "httpCloneUrl", httpProtocol+"://"+config.C.HTTP.Domain+":"+config.C.HTTP.Port+"/"+userName+"/"+gistName+".git") - } - } - - if config.C.HTTP.Port == "80" || config.C.HTTP.Port == "443" { - setData(ctx, "httpCopyUrl", httpProtocol+"://"+config.C.HTTP.Domain+"/"+userName+"/"+gistName) + var baseHttpUrl string + // if a custom external url is set, use it + if config.C.ExternalUrl != "" { + baseHttpUrl = config.C.ExternalUrl } else { - setData(ctx, "httpCopyUrl", httpProtocol+"://"+config.C.HTTP.Domain+":"+config.C.HTTP.Port+"/"+userName+"/"+gistName) + baseHttpUrl = httpProtocol + "://" + ctx.Request().Host } + if config.C.HttpGit { + setData(ctx, "httpCloneUrl", baseHttpUrl+"/"+userName+"/"+gistName+".git") + } + + setData(ctx, "httpCopyUrl", baseHttpUrl+"/"+userName+"/"+gistName) setData(ctx, "currentUrl", template.URL(ctx.Request().URL.Path)) nbCommits, err := gist.NbCommits() diff --git a/internal/web/run.go b/internal/web/run.go index dbf24e2..978e43d 100644 --- a/internal/web/run.go +++ b/internal/web/run.go @@ -209,18 +209,18 @@ func Start() { debugStr := "" // Git HTTP routes - if config.C.HTTP.Git { + if config.C.HttpGit { e.Any("/:user/:gistname/*", gitHttp, gistInit) debugStr = " (with Git over HTTP)" } e.Any("/*", noRouteFound) - addr := config.C.HTTP.Host + ":" + config.C.HTTP.Port + addr := config.C.HttpHost + ":" + config.C.HttpPort - if config.C.HTTP.TLSEnabled { + if config.C.HttpTLSEnabled { log.Info().Msg("Starting HTTPS server on https://" + addr + debugStr) - if err := e.StartTLS(addr, config.C.HTTP.CertFile, config.C.HTTP.KeyFile); err != nil { + if err := e.StartTLS(addr, config.C.HttpCertFile, config.C.HttpKeyFile); err != nil { log.Fatal().Err(err).Msg("Failed to start HTTPS server") } } else { diff --git a/opengist.go b/opengist.go index 74e43c0..bd7b130 100644 --- a/opengist.go +++ b/opengist.go @@ -14,6 +14,8 @@ import ( ) func initialize() { + fmt.Println("Opengist v" + config.OpengistVersion) + configPath := flag.String("config", "config.yml", "Path to a config file in YML format") flag.Parse() absolutePath, _ := filepath.Abs(*configPath) @@ -27,9 +29,6 @@ func initialize() { config.InitLog() - fmt.Println("Opengist v" + config.OpengistVersion) - fmt.Println("Using config file: " + absolutePath) - gitVersion, err := git.GetGitVersion() if err != nil { log.Fatal().Err(err).Send()