This commit introduces the automatic retrieval of TLS certificates from Let's Encrypt. If the feature is enabled, Vikunja will automagically request a certificate from Let's Encrypt and configure it to server content via TLS.
This commit is contained in:
parent
6a94c39ea8
commit
daa7ad053c
@ -803,6 +803,26 @@
|
||||
"comment": "The proxy password to use when authenticating against the proxy."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "autotls",
|
||||
"children": [
|
||||
{
|
||||
"key": "enabled",
|
||||
"default_value": "false",
|
||||
"comment": "If set to true, Vikunja will automatically request a TLS certificate from Let's Encrypt and use it to serve Vikunja over TLS. By enabling this option, you agree to Let's Encrypt's TOS.\nYou must configure a `service.publicurl` with a valid TLD where Vikunja is reachable to make this work. Furthermore, it is reccomened to set `service.interface` to `:443` if you're using this."
|
||||
},
|
||||
{
|
||||
"key": "email",
|
||||
"default_value": "",
|
||||
"comment": "A valid email address which will be used to register certificates with Let's Encrypt. You must provide this value in order to use autotls."
|
||||
},
|
||||
{
|
||||
"key": "renewbefore",
|
||||
"default_value": "30d",
|
||||
"comment": "A duration when certificates should be renewed before they expire. Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -19,8 +19,11 @@ package cmd
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
@ -33,6 +36,7 @@ import (
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -64,6 +68,52 @@ func setupUnixSocket(e *echo.Echo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupAutoTLS(e *echo.Echo) {
|
||||
if config.ServiceUnixSocket.GetString() != "" {
|
||||
log.Warning("Auto tls is enabled but listening on a unix socket is enabled as well. The latter will be ignored.")
|
||||
}
|
||||
if config.ServicePublicURL.GetString() == "" {
|
||||
log.Fatal("You must configure a publicurl to use autotls.")
|
||||
}
|
||||
parsed, err := url.Parse(config.ServicePublicURL.GetString())
|
||||
if err != nil {
|
||||
log.Fatalf("Could not parse hostname from publicurl: %s", err)
|
||||
}
|
||||
domain := parsed.Hostname()
|
||||
if domain == "" {
|
||||
log.Fatalf("The hostname cannot be empty. Please make sure the configured publicurl contains a hostname.")
|
||||
}
|
||||
if !strings.Contains(domain, ".") {
|
||||
log.Fatalf("The hostname must be a valid TLD. Please make sure the configured publicurl contains a valid TLD.")
|
||||
}
|
||||
renew, err := time.ParseDuration(config.AutoTLSRenewBefore.GetString())
|
||||
if err != nil {
|
||||
log.Fatalf("autotls.renewbefore must be a valid duration: %s", err)
|
||||
}
|
||||
if config.AutoTLSEmail.GetString() == "" {
|
||||
log.Fatalf("You must provide an email address to use autotls.")
|
||||
}
|
||||
e.AutoTLSManager = autocert.Manager{
|
||||
Prompt: autocert.AcceptTOS,
|
||||
Cache: autocert.DirCache(filepath.Join(
|
||||
config.FilesBasePath.GetString(),
|
||||
".certs",
|
||||
)),
|
||||
HostPolicy: autocert.HostWhitelist(domain),
|
||||
RenewBefore: renew,
|
||||
Email: config.AutoTLSEmail.GetString(),
|
||||
}
|
||||
|
||||
if config.ServiceInterface.GetString() != ":443" {
|
||||
log.Warningf("Vikunja's interface is set to %s, with tls it is recommended to set this to :443", config.ServiceInterface.GetString())
|
||||
}
|
||||
|
||||
err = e.StartAutoTLS(config.ServiceInterface.GetString())
|
||||
if err != nil {
|
||||
e.Logger.Info("shutting down...")
|
||||
}
|
||||
}
|
||||
|
||||
var webCmd = &cobra.Command{
|
||||
Use: "web",
|
||||
Short: "Starts the rest api web server",
|
||||
@ -80,18 +130,26 @@ var webCmd = &cobra.Command{
|
||||
routes.RegisterRoutes(e)
|
||||
// Start server
|
||||
go func() {
|
||||
if config.AutoTLSEnabled.GetBool() {
|
||||
setupAutoTLS(e)
|
||||
return
|
||||
}
|
||||
|
||||
// Listen unix socket if needed (ServiceInterface will be ignored)
|
||||
if config.ServiceUnixSocket.GetString() != "" {
|
||||
if err := setupUnixSocket(e); err != nil {
|
||||
e.Logger.Fatal(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err := e.Start(config.ServiceInterface.GetString()); err != nil {
|
||||
|
||||
err := e.Start(config.ServiceInterface.GetString())
|
||||
if err != nil {
|
||||
e.Logger.Info("shutting down...")
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for interrupt signal to gracefully shutdown the server with
|
||||
// Wait for interrupt signal to gracefully shut down the server with
|
||||
// a timeout of 10 seconds.
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, os.Interrupt)
|
||||
|
@ -183,6 +183,10 @@ const (
|
||||
WebhooksTimeoutSeconds Key = `webhooks.timeoutseconds`
|
||||
WebhooksProxyURL Key = `webhooks.proxyurl`
|
||||
WebhooksProxyPassword Key = `webhooks.proxypassword`
|
||||
|
||||
AutoTLSEnabled Key = `autotls.enabled`
|
||||
AutoTLSEmail Key = `autotls.email`
|
||||
AutoTLSRenewBefore Key = `autotls.renewbefore`
|
||||
)
|
||||
|
||||
// GetString returns a string config value
|
||||
@ -407,6 +411,8 @@ func InitDefaultConfig() {
|
||||
// Webhook
|
||||
WebhooksEnabled.setDefault(true)
|
||||
WebhooksTimeoutSeconds.setDefault(30)
|
||||
// AutoTLS
|
||||
AutoTLSRenewBefore.setDefault("720h") // 30days in hours
|
||||
}
|
||||
|
||||
// InitConfig initializes the config, sets defaults etc.
|
||||
@ -481,6 +487,13 @@ func InitConfig() {
|
||||
log.Warning("service.enablemetrics is deprecated and will be removed in a future release. Please use metrics.enable.")
|
||||
MetricsEnabled.Set(true)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(FilesBasePath.GetString(), "/") {
|
||||
FilesBasePath.Set(filepath.Join(
|
||||
ServiceRootpath.GetString(),
|
||||
FilesBasePath.GetString(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
func random(length int) (string, error) {
|
||||
|
Loading…
Reference in New Issue
Block a user