Repository: owncloud/ocis
Pull request #4949 (2022-11-01 14:52:11)

[full-ci] Introduce TLS Settings for go-micro based http services
TLS for the services can be configure by setting the "OCIS_HTTP_TLS_ENABLED",
"OCIS_HTTP_TLS_CERTIFICATE" and "OCIS_HTTP_TLS_KEY" environment variables.
Currently the ocis proxy is this only service that directly accesses backend
services. It determines whether to use TLS or not by looking a the new registry
metadata "use_tls". As specific CA Cert for certificate verification
can be set with the "PROXY_HTTPS_CACERT" environment variable.


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=4949)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4949&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4949&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4949&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4949&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4949&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4949&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4949&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4949&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4949&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4949&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4949&resolved=false&types=CODE_SMELL) [19 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4949&resolved=false&types=CODE_SMELL)

[![1.8%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '1.8%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4949&metric=new_coverage&view=list) [1.8% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4949&metric=new_coverage&view=list)  
[![37.2%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/20plus-16px.png '37.2%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4949&metric=new_duplicated_lines_density&view=list) [37.2% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4949&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=4949)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4949&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4949&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4949&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4949&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4949&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4949&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4949&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4949&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4949&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4949&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4949&resolved=false&types=CODE_SMELL) [19 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4949&resolved=false&types=CODE_SMELL)

[![1.8%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '1.8%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4949&metric=new_coverage&view=list) [1.8% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4949&metric=new_coverage&view=list)  
[![37.2%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/20plus-16px.png '37.2%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4949&metric=new_duplicated_lines_density&view=list) [37.2% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4949&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b5e5967ddce..d4af401f406 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -44,6 +44,7 @@ The following sections list the changes for unreleased.
 * Enhancement - Add thumbnails support for tiff and bmp files: [#4634](https://github.com/owncloud/ocis/pull/4634)
 * Enhancement - Update reva: [#4588](https://github.com/owncloud/ocis/pull/4588)
 * Enhancement - Update ownCloud Web to v6.0.0-rc.2: [#4786](https://github.com/owncloud/ocis/pull/4786)
+* Enhancement - Validate space names: [#4955](https://github.com/owncloud/ocis/pull/4955)
 * Enhancement - Make it possible to configure a WOPI folderurl: [#4716](https://github.com/owncloud/ocis/pull/4716)
 
 ## Details
@@ -416,6 +417,15 @@ The following sections list the changes for unreleased.
    https://github.com/owncloud/ocis/pull/4786
    https://github.com/owncloud/web/releases/tag/v6.0.0-rc.2
 
+* Enhancement - Validate space names: [#4955](https://github.com/owncloud/ocis/pull/4955)
+
+   We now return `BAD REQUEST` when space names are - too long (max 255 characters) - containing
+   evil characters (`/`, `\`, `.`, `\\`, `:`, `?`, `*`, `"`, `>`, `<`, `|`)
+
+   Additionally leading and trailing spaces will be removed silently.
+
+   https://github.com/owncloud/ocis/pull/4955
+
 * Enhancement - Make it possible to configure a WOPI folderurl: [#4716](https://github.com/owncloud/ocis/pull/4716)
 
    The wopi folder URL is used to jump back from an application to the containing folder in the files
diff --git a/changelog/unreleased/validate-space-names.md b/changelog/unreleased/validate-space-names.md
new file mode 100644
index 00000000000..d96e255dd82
--- /dev/null
+++ b/changelog/unreleased/validate-space-names.md
@@ -0,0 +1,9 @@
+Enhancement: Validate space names
+
+We now return `BAD REQUEST` when space names are
+-  too long (max 255 characters)
+-  containing evil characters (`/`, `\`, `.`, `\\`, `:`, `?`, `*`, `"`, `>`, `<`, `|`)
+
+Additionally leading and trailing spaces will be removed silently.
+
+https://github.com/owncloud/ocis/pull/4955
diff --git a/ocis-pkg/config/config.go b/ocis-pkg/config/config.go
index b13c4c3a574..968019372a7 100644
--- a/ocis-pkg/config/config.go
+++ b/ocis-pkg/config/config.go
@@ -61,6 +61,7 @@ type Config struct {
 	CacheStore     *shared.CacheStore     `yaml:"cache_store"`
 	GRPCClientTLS  *shared.GRPCClientTLS  `yaml:"grpc_client_tls"`
 	GRPCServiceTLS *shared.GRPCServiceTLS `yaml:"grpc_service_tls"`
+	HTTPServiceTLS shared.HTTPServiceTLS  `yaml:"http_service_tls"`
 
 	Mode    Mode // DEPRECATED
 	File    string
diff --git a/ocis-pkg/config/parser/parse.go b/ocis-pkg/config/parser/parse.go
index 3063de201b2..aa25038dd87 100644
--- a/ocis-pkg/config/parser/parse.go
+++ b/ocis-pkg/config/parser/parse.go
@@ -109,6 +109,8 @@ func EnsureCommons(cfg *config.Config) {
 		cfg.Commons.GRPCServiceTLS = cfg.GRPCServiceTLS
 	}
 
+	cfg.Commons.HTTPServiceTLS = cfg.HTTPServiceTLS
+
 	// copy token manager to the commons part if set
 	if cfg.TokenManager != nil {
 		cfg.Commons.TokenManager = cfg.TokenManager
diff --git a/ocis-pkg/crypto/gencert.go b/ocis-pkg/crypto/gencert.go
index 581f14e68e0..6787f9890bf 100644
--- a/ocis-pkg/crypto/gencert.go
+++ b/ocis-pkg/crypto/gencert.go
@@ -4,6 +4,7 @@ import (
 	"crypto/ecdsa"
 	"crypto/rand"
 	"crypto/rsa"
+	"crypto/tls"
 	"crypto/x509"
 	"encoding/pem"
 	"fmt"
@@ -12,6 +13,7 @@ import (
 	"path/filepath"
 
 	"github.com/owncloud/ocis/v2/ocis-pkg/log"
+	mtls "go-micro.dev/v4/util/tls"
 )
 
 var (
@@ -50,6 +52,16 @@ func GenCert(certName string, keyName string, l log.Logger) error {
 	return nil
 }
 
+// GenTempCertForAddr generates temporary TLS-Certificates in memory.
+func GenTempCertForAddr(addr string) (tls.Certificate, error) {
+	subjects := defaultHosts
+
+	if host, _, err := net.SplitHostPort(addr); err == nil && host != "" {
+		subjects = []string{host}
+	}
+	return mtls.Certificate(subjects...)
+}
+
 // persistCertificate generates a certificate using pk as private key and proceeds to store it into a file named certName.
 func persistCertificate(certName string, l log.Logger, pk interface{}) error {
 	if err := ensureExistsDir(certName); err != nil {
diff --git a/ocis-pkg/service/grpc/service.go b/ocis-pkg/service/grpc/service.go
index 3ea16be0481..823ad5430ac 100644
--- a/ocis-pkg/service/grpc/service.go
+++ b/ocis-pkg/service/grpc/service.go
@@ -3,17 +3,16 @@ package grpc
 import (
 	"crypto/tls"
 	"fmt"
-	"net"
 	"strings"
 	"time"
 
 	mgrpcs "github.com/go-micro/plugins/v4/server/grpc"
 	"github.com/go-micro/plugins/v4/wrapper/monitoring/prometheus"
 	"github.com/go-micro/plugins/v4/wrapper/trace/opencensus"
+	ociscrypto "github.com/owncloud/ocis/v2/ocis-pkg/crypto"
 	"github.com/owncloud/ocis/v2/ocis-pkg/registry"
 	"go-micro.dev/v4"
 	"go-micro.dev/v4/server"
-	mtls "go-micro.dev/v4/util/tls"
 )
 
 // Service simply wraps the go-micro grpc service.
@@ -38,15 +37,7 @@ func NewService(opts ...Option) (Service, error) {
 		} else {
 			// Generate a self-signed server certificate on the fly. This requires the clients
 			// to connect with InsecureSkipVerify.
-			subj := []string{sopts.Address}
-			if host, _, err := net.SplitHostPort(sopts.Address); err == nil && host != "" {
-				subj = []string{host}
-			}
-
-			sopts.Logger.Warn().Str("address", sopts.Address).
-				Msg("GRPC: No server certificate configured. Generating a temporary self-signed certificate")
-
-			cert, err = mtls.Certificate(subj...)
+			cert, err = ociscrypto.GenTempCertForAddr(sopts.Address)
 			if err != nil {
 				return Service{}, fmt.Errorf("grpc service error creating temporary self-signed certificate: %w", err)
 			}
diff --git a/ocis-pkg/service/http/option.go b/ocis-pkg/service/http/option.go
index 8176187205e..6f533c00dcb 100644
--- a/ocis-pkg/service/http/option.go
+++ b/ocis-pkg/service/http/option.go
@@ -2,10 +2,10 @@ package http
 
 import (
 	"context"
-	"crypto/tls"
 	"net/http"
 
 	"github.com/owncloud/ocis/v2/ocis-pkg/log"
+	"github.com/owncloud/ocis/v2/ocis-pkg/shared"
 	"github.com/urfave/cli/v2"
 )
 
@@ -15,7 +15,7 @@ type Option func(o *Options)
 // Options defines the available options for this package.
 type Options struct {
 	Logger    log.Logger
-	TLSConfig *tls.Config
+	TLSConfig shared.HTTPServiceTLS
 	Namespace string
 	Name      string
 	Version   string
@@ -88,7 +88,7 @@ func Flags(flags ...cli.Flag) Option {
 }
 
 // TLSConfig provides a function to set the TLSConfig option.
-func TLSConfig(config *tls.Config) Option {
+func TLSConfig(config shared.HTTPServiceTLS) Option {
 	return func(o *Options) {
 		o.TLSConfig = config
 	}
diff --git a/ocis-pkg/service/http/service.go b/ocis-pkg/service/http/service.go
index dfeeaee5542..cc7516ed176 100644
--- a/ocis-pkg/service/http/service.go
+++ b/ocis-pkg/service/http/service.go
@@ -1,6 +1,8 @@
 package http
 
 import (
+	"crypto/tls"
+	"fmt"
 	"strings"
 	"time"
 
@@ -8,6 +10,7 @@ import (
 	"github.com/owncloud/ocis/v2/ocis-pkg/registry"
 
 	mhttps "github.com/go-micro/plugins/v4/server/http"
+	ociscrypto "github.com/owncloud/ocis/v2/ocis-pkg/crypto"
 	"go-micro.dev/v4"
 	"go-micro.dev/v4/server"
 )
@@ -18,11 +21,42 @@ type Service struct {
 }
 
 // NewService initializes a new http service.
-func NewService(opts ...Option) Service {
+func NewService(opts ...Option) (Service, error) {
 	noopBroker := broker.NoOp{}
 	sopts := newOptions(opts...)
+	var mServer server.Server
+	if sopts.TLSConfig.Enabled {
+		var cert tls.Certificate
+		var err error
+		if sopts.TLSConfig.Cert != "" {
+			cert, err = tls.LoadX509KeyPair(sopts.TLSConfig.Cert, sopts.TLSConfig.Key)
+			if err != nil {
+				sopts.Logger.Error().Err(err).
+					Str("cert", sopts.TLSConfig.Cert).
+					Str("key", sopts.TLSConfig.Key).
+					Msg("error loading server certifcate and key")
+				return Service{}, fmt.Errorf("error loading server certificate and key: %w", err)
+			}
+		} else {
+			// Generate a self-signed server certificate on the fly. This requires the clients
+			// to connect with InsecureSkipVerify.
+			sopts.Logger.Warn().Str("address", sopts.Address).
+				Msg("No server certificate configured. Generating a temporary self-signed certificate")
+			cert, err = ociscrypto.GenTempCertForAddr(sopts.Address)
+			if err != nil {
+				return Service{}, fmt.Errorf("error creating temporary self-signed certificate: %w", err)
+			}
+		}
+		tlsConfig := &tls.Config{
+			Certificates: []tls.Certificate{cert},
+		}
+		mServer = mhttps.NewServer(server.TLSConfig(tlsConfig))
+	} else {
+		mServer = mhttps.NewServer()
+	}
+
 	wopts := []micro.Option{
-		micro.Server(mhttps.NewServer(server.TLSConfig(sopts.TLSConfig))),
+		micro.Server(mServer),
 		micro.Broker(noopBroker),
 		micro.Address(sopts.Address),
 		micro.Name(strings.Join([]string{sopts.Namespace, sopts.Name}, ".")),
@@ -33,6 +67,9 @@ func NewService(opts ...Option) Service {
 		micro.RegisterTTL(time.Second * 30),
 		micro.RegisterInterval(time.Second * 10),
 	}
+	if sopts.TLSConfig.Enabled {
+		wopts = append(wopts, micro.Metadata(map[string]string{"use_tls": "true"}))
+	}
 
-	return Service{micro.NewService(wopts...)}
+	return Service{micro.NewService(wopts...)}, nil
 }
diff --git a/ocis-pkg/shared/shared_types.go b/ocis-pkg/shared/shared_types.go
index ea93eb3b690..9796caca573 100644
--- a/ocis-pkg/shared/shared_types.go
+++ b/ocis-pkg/shared/shared_types.go
@@ -46,6 +46,12 @@ type GRPCServiceTLS struct {
 	Key     string `yaml:"key" env:"OCIS_GRPC_TLS_KEY" desc:"Path/File name for the TLS certificate key (in PEM format) for the server certificate to use for the grpc services."`
 }
 
+type HTTPServiceTLS struct {
+	Enabled bool   `yaml:"enabled" env:"OCIS_HTTP_TLS_ENABLED"`
+	Cert    string `yaml:"cert" env:"OCIS_HTTP_TLS_CERTIFICATE" desc:"Path/File name of the TLS server certificate (in PEM format) for the http services."`
+	Key     string `yaml:"key" env:"OCIS_HTTP_TLS_KEY" desc:"Path/File name for the TLS certificate key (in PEM format) for the server certificate to use for the http services."`
+}
+
 type CacheStore struct {
 	Type    string `yaml:"type" env:"OCIS_CACHE_STORE_TYPE" desc:"The type of the cache store. Valid options are \"noop\", \"ocmem\", \"etcd\" and \"memory\""`
 	Address string `yaml:"address" env:"OCIS_CACHE_STORE_ADDRESS" desc:"A comma-separated list of addresses to connect to. Only valid if the above setting is set to \"etcd\""`
@@ -60,6 +66,7 @@ type Commons struct {
 	CacheStore        *CacheStore     `yaml:"cache_store"`
 	GRPCClientTLS     *GRPCClientTLS  `yaml:"grpc_client_tls"`
 	GRPCServiceTLS    *GRPCServiceTLS `yaml:"grpc_service_tls"`
+	HTTPServiceTLS    HTTPServiceTLS  `yaml:"http_service_tls"`
 	OcisURL           string          `yaml:"ocis_url" env:"OCIS_URL" desc:"URL, where oCIS is reachable for users."`
 	TokenManager      *TokenManager   `mask:"struct" yaml:"token_manager"`
 	Reva              *Reva           `yaml:"reva"`
diff --git a/ocis/README.md b/ocis/README.md
new file mode 100644
index 00000000000..6d5a12fbbdc
--- /dev/null
+++ b/ocis/README.md
@@ -0,0 +1,22 @@
+# ocis
+
+The ocis package contains the Infinite Scale runtime and the commands for the Infinite Scale cli.
+
+## Service registry
+
+This package also configures the service registry which will be used to look up the service addresses. It defaults to mDNS. Keep that in mind when using systems with mDNS disabled by default (i.e. SUSE).
+
+Available registries are:
+
+-   nats
+-   kubernetes
+-   etcd
+-   consul
+-   memory
+-   mdns (default)
+
+To configure which registry to use, you have to set the environment variable `MICRO_REGISTRY`, and for all except `memory` and `mdns` you also have to set the registry address via `MICRO_REGISTRY_ADDRESS`.
+
+### etcd
+
+To authenticate the connection to the etcd registry, you have to set `ETCD_USERNAME` and `ETCD_PASSWORD`.
diff --git a/services/graph/pkg/config/defaults/defaultconfig.go b/services/graph/pkg/config/defaults/defaultconfig.go
index eb3ad18fd6e..f238f21b212 100644
--- a/services/graph/pkg/config/defaults/defaultconfig.go
+++ b/services/graph/pkg/config/defaults/defaultconfig.go
@@ -121,6 +121,10 @@ func EnsureDefaults(cfg *config.Config) {
 			cfg.GRPCClientTLS.CACert = cfg.Commons.GRPCClientTLS.CACert
 		}
 	}
+
+	if cfg.Commons != nil {
+		cfg.HTTP.TLS = cfg.Commons.HTTPServiceTLS
+	}
 }
 
 func Sanitize(cfg *config.Config) {
diff --git a/services/graph/pkg/config/http.go b/services/graph/pkg/config/http.go
index 86784b67f99..ec521094c89 100644
--- a/services/graph/pkg/config/http.go
+++ b/services/graph/pkg/config/http.go
@@ -1,8 +1,11 @@
 package config
 
+import "github.com/owncloud/ocis/v2/ocis-pkg/shared"
+
 // HTTP defines the available http configuration.
 type HTTP struct {
-	Addr      string `yaml:"addr" env:"GRAPH_HTTP_ADDR" desc:"The bind address of the HTTP service."`
-	Namespace string `yaml:"-"`
-	Root      string `yaml:"root" env:"GRAPH_HTTP_ROOT" desc:"Subdirectory that serves as the root for this HTTP service."`
+	Addr      string                `yaml:"addr" env:"GRAPH_HTTP_ADDR" desc:"The bind address of the HTTP service."`
+	Namespace string                `yaml:"-"`
+	Root      string                `yaml:"root" env:"GRAPH_HTTP_ROOT" desc:"Subdirectory that serves as the root for this HTTP service."`
+	TLS       shared.HTTPServiceTLS `yaml:"tls"`
 }
diff --git a/services/graph/pkg/server/http/server.go b/services/graph/pkg/server/http/server.go
index 489e6ed8660..15851c2fe3e 100644
--- a/services/graph/pkg/server/http/server.go
+++ b/services/graph/pkg/server/http/server.go
@@ -3,6 +3,7 @@ package http
 import (
 	"crypto/tls"
 	"crypto/x509"
+	"fmt"
 	"os"
 
 	"github.com/cs3org/reva/v2/pkg/events/server"
@@ -24,7 +25,8 @@ import (
 func Server(opts ...Option) (http.Service, error) {
 	options := newOptions(opts...)
 
-	service := http.NewService(
+	service, err := http.NewService(
+		http.TLSConfig(options.Config.HTTP.TLS),
 		http.Logger(options.Logger),
 		http.Namespace(options.Config.HTTP.Namespace),
 		http.Name("graph"),
@@ -33,6 +35,12 @@ func Server(opts ...Option) (http.Service, error) {
 		http.Context(options.Context),
 		http.Flags(options.Flags...),
 	)
+	if err != nil {
+		options.Logger.Error().
+			Err(err).
+			Msg("Error initializing http service")
+		return http.Service{}, fmt.Errorf("could not initialize http service: %w", err)
+	}
 
 	var publisher events.Stream
 
diff --git a/services/graph/pkg/service/v0/drives.go b/services/graph/pkg/service/v0/drives.go
index 05dd0aeb341..e75b09a86bf 100644
--- a/services/graph/pkg/service/v0/drives.go
+++ b/services/graph/pkg/service/v0/drives.go
@@ -33,6 +33,20 @@ import (
 	merrors "go-micro.dev/v4/errors"
 )
 
+var (
+	_invalidSpaceNameCharacters = []string{`/`, `\`, `.`, `:`, `?`, `*`, `"`, `>`, `<`, `|`}
+	_maxSpaceNameLength         = 255
+
+	// ErrNameTooLong is thrown when the spacename is too long
+	ErrNameTooLong = fmt.Errorf("spacename must be smaller than %d", _maxSpaceNameLength)
+
+	// ErrNameEmpty is thrown when the spacename is empty
+	ErrNameEmpty = errors.New("spacename must not be empty")
+
+	// ErrForbiddenCharacter is thrown when the spacename contains an invalid character
+	ErrForbiddenCharacter = fmt.Errorf("spacenames must not contain %v", _invalidSpaceNameCharacters)
+)
+
 // GetDrives lists all drives the current user has access to
 func (g Graph) GetDrives(w http.ResponseWriter, r *http.Request) {
 	g.getDrives(w, r, false)
@@ -229,10 +243,10 @@ func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) {
 		errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid body schema definition")
 		return
 	}
-	spaceName := *drive.Name
-	if spaceName == "" {
-		logger.Debug().Str("name", spaceName).Msg("could not create drive: invalid name")
-		errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid name")
+	spaceName := strings.TrimSpace(*drive.Name)
+	if err := validateSpaceName(spaceName); err != nil {
+		logger.Debug().Str("name", spaceName).Err(err).Msg("could not create drive: name validation failed")
+		errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("invalid spacename: %s", err.Error()))
 		return
 	}
 
@@ -376,7 +390,14 @@ func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if drive.Name != nil {
-		updateSpaceRequest.StorageSpace.Name = *drive.Name
+		spacename := strings.TrimSpace(*drive.Name)
+		if err := validateSpaceName(spacename); err != nil {
+			logger.Info().Err(err).Msg("could not update drive: spacename invalid")
+			errorcode.GeneralException.Render(w, r, http.StatusBadRequest, err.Error())
+			return
+		}
+
+		updateSpaceRequest.StorageSpace.Name = spacename
 	}
 
 	if drive.Quota.HasTotal() {
@@ -901,3 +922,21 @@ func sortSpaces(req *godata.GoDataRequest, spaces []*libregraph.Drive) ([]*libre
 	sort.Sort(sorter)
 	return spaces, nil
 }
+
+func validateSpaceName(name string) error {
+	if name == "" {
+		return ErrNameEmpty
+	}
+
+	if len(name) > _maxSpaceNameLength {
+		return ErrNameTooLong
+	}
+
+	for _, c := range _invalidSpaceNameCharacters {
+		if strings.Contains(name, c) {
+			return ErrForbiddenCharacter
+		}
+	}
+
+	return nil
+}
diff --git a/services/idp/pkg/server/http/server.go b/services/idp/pkg/server/http/server.go
index 6e03aa2ab2d..68c424f4e5f 100644
--- a/services/idp/pkg/server/http/server.go
+++ b/services/idp/pkg/server/http/server.go
@@ -1,13 +1,14 @@
 package http
 
 import (
-	"crypto/tls"
+	"fmt"
 	"os"
 
 	chimiddleware "github.com/go-chi/chi/v5/middleware"
 	pkgcrypto "github.com/owncloud/ocis/v2/ocis-pkg/crypto"
 	"github.com/owncloud/ocis/v2/ocis-pkg/middleware"
 	"github.com/owncloud/ocis/v2/ocis-pkg/service/http"
+	"github.com/owncloud/ocis/v2/ocis-pkg/shared"
 	"github.com/owncloud/ocis/v2/ocis-pkg/version"
 	svc "github.com/owncloud/ocis/v2/services/idp/pkg/service/v0"
 	"go-micro.dev/v4"
@@ -17,7 +18,6 @@ import (
 func Server(opts ...Option) (http.Service, error) {
 	options := newOptions(opts...)
 
-	var tlsConfig *tls.Config
 	if options.Config.HTTP.TLS {
 		_, certErr := os.Stat(options.Config.HTTP.TLSCert)
 		_, keyErr := os.Stat(options.Config.HTTP.TLSKey)
@@ -29,17 +29,9 @@ func Server(opts ...Option) (http.Service, error) {
 				os.Exit(1)
 			}
 		}
-
-		cer, err := tls.LoadX509KeyPair(options.Config.HTTP.TLSCert, options.Config.HTTP.TLSKey)
-		if err != nil {
-			options.Logger.Fatal().Err(err).Msg("Could not setup TLS")
-			os.Exit(1)
-		}
-
-		tlsConfig = &tls.Config{MinVersion: tls.VersionTLS12, Certificates: []tls.Certificate{cer}}
 	}
 
-	service := http.NewService(
+	service, err := http.NewService(
 		http.Logger(options.Logger),
 		http.Namespace(options.Config.HTTP.Namespace),
 		http.Name(options.Config.Service.Name),
@@ -47,8 +39,18 @@ func Server(opts ...Option) (http.Service, error) {
 		http.Address(options.Config.HTTP.Addr),
 		http.Context(options.Context),
 		http.Flags(options.Flags...),
-		http.TLSConfig(tlsConfig),
+		http.TLSConfig(shared.HTTPServiceTLS{
+			Enabled: options.Config.HTTP.TLS,
+			Cert:    options.Config.HTTP.TLSCert,
+			Key:     options.Config.HTTP.TLSKey,
+		}),
 	)
+	if err != nil {
+		options.Logger.Error().
+			Err(err).
+			Msg("Error initializing http service")
+		return http.Service{}, fmt.Errorf("could not initialize http service: %w", err)
+	}
 
 	handle := svc.NewService(
 		svc.Logger(options.Logger),
diff --git a/services/ocs/pkg/config/defaults/defaultconfig.go b/services/ocs/pkg/config/defaults/defaultconfig.go
index 7bbc0cafb8c..30dc68871b0 100644
--- a/services/ocs/pkg/config/defaults/defaultconfig.go
+++ b/services/ocs/pkg/config/defaults/defaultconfig.go
@@ -106,6 +106,9 @@ func EnsureDefaults(cfg *config.Config) {
 			cfg.GRPCClientTLS.CACert = cfg.Commons.GRPCClientTLS.CACert
 		}
 	}
+	if cfg.Commons != nil {
+		cfg.HTTP.TLS = cfg.Commons.HTTPServiceTLS
+	}
 }
 
 func Sanitize(cfg *config.Config) {
diff --git a/services/ocs/pkg/config/http.go b/services/ocs/pkg/config/http.go
index a89e6180e58..4062e219523 100644
--- a/services/ocs/pkg/config/http.go
+++ b/services/ocs/pkg/config/http.go
@@ -1,11 +1,14 @@
 package config
 
+import "github.com/owncloud/ocis/v2/ocis-pkg/shared"
+
 // HTTP defines the available http configuration.
 type HTTP struct {
-	Addr      string `yaml:"addr" env:"OCS_HTTP_ADDR" desc:"The bind address of the HTTP service."`
-	Root      string `yaml:"root" env:"OCS_HTTP_ROOT" desc:"Subdirectory that serves as the root for this HTTP service."`
-	Namespace string `yaml:"-"`
-	CORS      CORS   `yaml:"cors"`
+	Addr      string                `yaml:"addr" env:"OCS_HTTP_ADDR" desc:"The bind address of the HTTP service."`
+	Root      string                `yaml:"root" env:"OCS_HTTP_ROOT" desc:"Subdirectory that serves as the root for this HTTP service."`
+	Namespace string                `yaml:"-"`
+	CORS      CORS                  `yaml:"cors"`
+	TLS       shared.HTTPServiceTLS `yaml:"tls"`
 }
 
 // CORS defines the available cors configuration.
diff --git a/services/ocs/pkg/server/http/server.go b/services/ocs/pkg/server/http/server.go
index 1f3a4b608d5..58d9525a04c 100644
--- a/services/ocs/pkg/server/http/server.go
+++ b/services/ocs/pkg/server/http/server.go
@@ -1,6 +1,8 @@
 package http
 
 import (
+	"fmt"
+
 	chimiddleware "github.com/go-chi/chi/v5/middleware"
 	"github.com/owncloud/ocis/v2/ocis-pkg/cors"
 	"github.com/owncloud/ocis/v2/ocis-pkg/middleware"
@@ -15,7 +17,8 @@ import (
 func Server(opts ...Option) (http.Service, error) {
 	options := newOptions(opts...)
 
-	service := http.NewService(
+	service, err := http.NewService(
+		http.TLSConfig(options.Config.HTTP.TLS),
 		http.Logger(options.Logger),
 		http.Name(options.Config.Service.Name),
 		http.Version(version.GetString()),
@@ -24,6 +27,12 @@ func Server(opts ...Option) (http.Service, error) {
 		http.Context(options.Context),
 		http.Flags(options.Flags...),
 	)
+	if err != nil {
+		options.Logger.Error().
+			Err(err).
+			Msg("Error initializing http service")
+		return http.Service{}, fmt.Errorf("could not initialize http service: %w", err)
+	}
 
 	handle := svc.NewService(
 		svc.Logger(options.Logger),
diff --git a/services/proxy/pkg/command/server.go b/services/proxy/pkg/command/server.go
index cca24fd2b39..5c8823d37f9 100644
--- a/services/proxy/pkg/command/server.go
+++ b/services/proxy/pkg/command/server.go
@@ -72,10 +72,13 @@ func Server(cfg *config.Config) *cli.Command {
 
 			m.BuildInfo.WithLabelValues(version.GetString()).Set(1)
 
-			rp := proxy.NewMultiHostReverseProxy(
+			rp, err := proxy.NewMultiHostReverseProxy(
 				proxy.Logger(logger),
 				proxy.Config(cfg),
 			)
+			if err != nil {
+				return fmt.Errorf("Failed to initialize reverse proxy: %w", err)
+			}
 
 			{
 				server, err := proxyHTTP.Server(
diff --git a/services/proxy/pkg/config/config.go b/services/proxy/pkg/config/config.go
index 0e3ac4461d5..26d3618e2d9 100644
--- a/services/proxy/pkg/config/config.go
+++ b/services/proxy/pkg/config/config.go
@@ -33,6 +33,7 @@ type Config struct {
 	AutoprovisionAccounts bool            `yaml:"auto_provision_accounts" env:"PROXY_AUTOPROVISION_ACCOUNTS" desc:"Set this to 'true' to automatically provision users that do not yet exist in the users service on-demand upon first sign-in. To use this a write-enabled libregraph user backend needs to be setup an running."`
 	EnableBasicAuth       bool            `yaml:"enable_basic_auth" env:"PROXY_ENABLE_BASIC_AUTH" desc:"Set this to true to enable 'basic authentication' (username/password)."`
 	InsecureBackends      bool            `yaml:"insecure_backends" env:"PROXY_INSECURE_BACKENDS" desc:"Disable TLS certificate validation for all HTTP backend connections."`
+	BackendHTTPSCACert    string          `yaml:"backend_https_cacert" env:"PROXY_HTTPS_CACERT" desc:"The root CA certificate used to validate TLS server certificates of https enabled backend services."`
 	AuthMiddleware        AuthMiddleware  `yaml:"auth_middleware"`
 
 	Context context.Context `yaml:"-" json:"-"`
diff --git a/services/proxy/pkg/config/defaults/defaultconfig.go b/services/proxy/pkg/config/defaults/defaultconfig.go
index 3f605bcb7b7..19585cb9d05 100644
--- a/services/proxy/pkg/config/defaults/defaultconfig.go
+++ b/services/proxy/pkg/config/defaults/defaultconfig.go
@@ -70,47 +70,47 @@ func DefaultPolicies() []config.Policy {
 			Routes: []config.Route{
 				{
 					Endpoint:    "/",
-					Backend:     "http://localhost:9100",
+					Service:     "com.owncloud.web.web",
 					Unprotected: true,
 				},
 				{
 					Endpoint:    "/.well-known/",
-					Backend:     "http://localhost:9130",
+					Service:     "com.owncloud.web.idp",
 					Unprotected: true,
 				},
 				{
 					Endpoint:    "/konnect/",
-					Backend:     "http://localhost:9130",
+					Service:     "com.owncloud.web.idp",
 					Unprotected: true,
 				},
 				{
 					Endpoint:    "/signin/",
-					Backend:     "http://localhost:9130",
+					Service:     "com.owncloud.web.idp",
 					Unprotected: true,
 				},
 				{
 					Endpoint: "/archiver",
-					Backend:  "http://localhost:9140",
+					Service:  "com.owncloud.web.frontend",
 				},
 				{
 					Type:     config.RegexRoute,
 					Endpoint: "/ocs/v[12].php/cloud/user/signing-key", // only `user/signing-key` is left in ocis-ocs
-					Backend:  "http://localhost:9110",
+					Service:  "com.owncloud.web.ocs",
 				},
 				{
 					Type:        config.RegexRoute,
 					Endpoint:    "/ocs/v[12].php/config",
-					Backend:     "http://localhost:9140",
+					Service:     "com.owncloud.web.frontend",
 					Unprotected: true,
 				},
 				{
 					Endpoint: "/ocs/",
-					Backend:  "http://localhost:9140",
+					Service:  "com.owncloud.web.frontend",
 				},
 				{
 					Type:     config.QueryRoute,
 					Endpoint: "/remote.php/?preview=1",
-					Backend:  "http://localhost:9115",
+					Service:  "com.owncloud.web.webdav",
 				},
 				// TODO the actual REPORT goes to /dav/files/{username}, which is user specific ... how would this work in a spaces world?
 				// TODO what paths are returned? the href contains the full path so it should be possible to return urls from other spaces?
@@ -119,27 +119,27 @@ func DefaultPolicies() []config.Policy {
 				{
 					Method:   "REPORT",
 					Endpoint: "/remote.php/dav/",
-					Backend:  "http://localhost:9115", // TODO use registry?
+					Service:  "com.owncloud.web.webdav",
 				},
 				{
 					Method:   "REPORT",
 					Endpoint: "/remote.php/webdav",
-					Backend:  "http://localhost:9115", // TODO use registry?
+					Service:  "com.owncloud.web.webdav",
 				},
 				{
 					Method:   "REPORT",
 					Endpoint: "/dav/spaces",
-					Backend:  "http://localhost:9115",
+					Service:  "com.owncloud.web.webdav",
 				},
 				{
 					Type:     config.QueryRoute,
 					Endpoint: "/dav/?preview=1",
-					Backend:  "http://localhost:9115",
+					Service:  "com.owncloud.web.webdav",
 				},
 				{
 					Type:     config.QueryRoute,
 					Endpoint: "/webdav/?preview=1",
-					Backend:  "http://localhost:9115",
+					Service:  "com.owncloud.web.webdav",
 				},
 				{
 					Endpoint: "/remote.php/",
@@ -173,29 +173,29 @@ func DefaultPolicies() []config.Policy {
 				},
 				{
 					Endpoint:    "/data",
-					Backend:     "http://localhost:9140",
+					Service:     "com.owncloud.web.frontend",
 					Unprotected: true,
 				},
 				{
 					Endpoint:    "/app/list",
-					Backend:     "http://localhost:9140",
+					Service:     "com.owncloud.web.frontend",
 					Unprotected: true,
 				},
 				{
 					Endpoint: "/app/", // /app or /apps? ocdav only handles /apps
-					Backend:  "http://localhost:9140",
+					Service:  "com.owncloud.web.frontend",
 				},
 				{
 					Endpoint: "/graph/",
-					Backend:  "http://localhost:9120",
+					Service:  "com.owncloud.graph.graph",
 				},
 				{
 					Endpoint: "/api/v0/settings",
-					Backend:  "http://localhost:9190",
+					Service:  "com.owncloud.web.settings",
 				},
 				{
 					Endpoint:    "/settings.js",
-					Backend:     "http://localhost:9190",
+					Service:     "com.owncloud.web.settings",
 					Unprotected: true,
 				},
 			},
diff --git a/services/proxy/pkg/proxy/proxy.go b/services/proxy/pkg/proxy/proxy.go
index 12b49cdb664..1642484aeb9 100644
--- a/services/proxy/pkg/proxy/proxy.go
+++ b/services/proxy/pkg/proxy/proxy.go
@@ -2,7 +2,10 @@ package proxy
 
 import (
 	"crypto/tls"
+	"crypto/x509"
+	"errors"
 	"fmt"
+	"io/ioutil"
 	"net"
 	"net/http"
 	"net/http/httputil"
@@ -33,7 +36,7 @@ type MultiHostReverseProxy struct {
 }
 
 // NewMultiHostReverseProxy creates a new MultiHostReverseProxy
-func NewMultiHostReverseProxy(opts ...Option) *MultiHostReverseProxy {
+func NewMultiHostReverseProxy(opts ...Option) (*MultiHostReverseProxy, error) {
 	options := newOptions(opts...)
 
 	rp := &MultiHostReverseProxy{
@@ -47,6 +50,20 @@ func NewMultiHostReverseProxy(opts ...Option) *MultiHostReverseProxy {
 		ri.Director()(r)
 	}
 
+	tlsConf := &tls.Config{
+		InsecureSkipVerify: options.Config.InsecureBackends, //nolint:gosec
+	}
+	if options.Config.BackendHTTPSCACert != "" {
+		certs := x509.NewCertPool()
+		pemData, err := ioutil.ReadFile(options.Config.BackendHTTPSCACert)
+		if err != nil {
+			return nil, err
+		}
+		if !certs.AppendCertsFromPEM(pemData) {
+			return nil, errors.New("Error initializing LDAP Backend. Adding CA cert failed")
+		}
+		tlsConf.RootCAs = certs
+	}
 	// equals http.DefaultTransport except TLSClientConfig
 	rp.Transport = &http.Transport{
 		Proxy: http.ProxyFromEnvironment,
@@ -60,11 +77,9 @@ func NewMultiHostReverseProxy(opts ...Option) *MultiHostReverseProxy {
 		IdleConnTimeout:       90 * time.Second,
 		TLSHandshakeTimeout:   10 * time.Second,
 		ExpectContinueTimeout: 1 * time.Second,
-		TLSClientConfig: &tls.Config{
-			InsecureSkipVerify: options.Config.InsecureBackends, //nolint:gosec
-		},
+		TLSClientConfig:       tlsConf,
 	}
-	return rp
+	return rp, nil
 }
 
 func (p *MultiHostReverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
diff --git a/services/proxy/pkg/router/router.go b/services/proxy/pkg/router/router.go
index d0a5959cadb..d61e99a720b 100644
--- a/services/proxy/pkg/router/router.go
+++ b/services/proxy/pkg/router/router.go
@@ -148,7 +148,9 @@ func (rt Router) addHost(policy string, target *url.URL, route config.Route) {
 				}
 				req.URL.Host = node.Address
 				req.URL.Scheme = node.Metadata["protocol"] // TODO check property exists?
-
+				if node.Metadata["use_tls"] == "true" {
+					req.URL.Scheme = "https"
+				}
 			} else {
 				req.URL.Host = target.Host
 				req.URL.Scheme = target.Scheme
diff --git a/services/proxy/pkg/server/http/server.go b/services/proxy/pkg/server/http/server.go
index 8eb8f1ba050..ce41fa33892 100644
--- a/services/proxy/pkg/server/http/server.go
+++ b/services/proxy/pkg/server/http/server.go
@@ -1,11 +1,13 @@
 package http
 
 import (
-	"crypto/tls"
+	"fmt"
 	"os"
 
 	pkgcrypto "github.com/owncloud/ocis/v2/ocis-pkg/crypto"
+	"github.com/owncloud/ocis/v2/ocis-pkg/service/http"
 	svc "github.com/owncloud/ocis/v2/ocis-pkg/service/http"
+	"github.com/owncloud/ocis/v2/ocis-pkg/shared"
 	"github.com/owncloud/ocis/v2/ocis-pkg/version"
 	"go-micro.dev/v4"
 )
@@ -16,9 +18,6 @@ func Server(opts ...Option) (svc.Service, error) {
 	l := options.Logger
 	httpCfg := options.Config.HTTP
 
-	var cer tls.Certificate
-
-	var tlsConfig *tls.Config
 	if options.Config.HTTP.TLS {
 		l.Warn().Msgf("No tls certificate provided, using a generated one")
 		_, certErr := os.Stat(httpCfg.TLSCert)
@@ -31,27 +30,29 @@ func Server(opts ...Option) (svc.Service, error) {
 				os.Exit(1)
 			}
 		}
-
-		cer, certErr = tls.LoadX509KeyPair(httpCfg.TLSCert, httpCfg.TLSKey)
-		if certErr != nil {
-			options.Logger.Fatal().Err(certErr).Msg("Could not setup TLS")
-			os.Exit(1)
-		}
-
-		tlsConfig = &tls.Config{MinVersion: tls.VersionTLS12, Certificates: []tls.Certificate{cer}}
 	}
 	chain := options.Middlewares.Then(options.Handler)
 
-	service := svc.NewService(
+	service, err := svc.NewService(
 		svc.Name(options.Config.Service.Name),
 		svc.Version(version.GetString()),
-		svc.TLSConfig(tlsConfig),
+		http.TLSConfig(shared.HTTPServiceTLS{
+			Enabled: options.Config.HTTP.TLS,
+			Cert:    options.Config.HTTP.TLSCert,
+			Key:     options.Config.HTTP.TLSKey,
+		}),
 		svc.Logger(options.Logger),
 		svc.Address(options.Config.HTTP.Addr),
 		svc.Namespace(options.Config.HTTP.Namespace),
 		svc.Context(options.Context),
 		svc.Flags(options.Flags...),
 	)
+	if err != nil {
+		options.Logger.Error().
+			Err(err).
+			Msg("Error initializing http service")
+		return http.Service{}, fmt.Errorf("could not initialize http service: %w", err)
+	}
 
 	if err := micro.RegisterHandler(service.Server(), chain); err != nil {
 		return svc.Service{}, err
diff --git a/services/settings/pkg/command/server.go b/services/settings/pkg/command/server.go
index 0d989314eba..98f55f3f6bc 100644
--- a/services/settings/pkg/command/server.go
+++ b/services/settings/pkg/command/server.go
@@ -52,13 +52,19 @@ func Server(cfg *config.Config) *cli.Command {
 			mtrcs.BuildInfo.WithLabelValues(version.GetString()).Set(1)
 
 			// prepare an HTTP server and add it to the group run.
-			httpServer := http.Server(
+			httpServer, err := http.Server(
 				http.Name(cfg.Service.Name),
 				http.Logger(logger),
 				http.Context(ctx),
 				http.Config(cfg),
 				http.Metrics(mtrcs),
 			)
+			if err != nil {
+				logger.Error().
+					Err(err).
+					Msg("Error initializing http service")
+				return fmt.Errorf("could not initialize http service: %w", err)
+			}
 			servers.Add(httpServer.Run, func(_ error) {
 				logger.Info().Str("server", "http").Msg("Shutting down server")
 				cancel()
diff --git a/services/settings/pkg/config/defaults/defaultconfig.go b/services/settings/pkg/config/defaults/defaultconfig.go
index 18a6ba4ad47..02c7b0bf7ff 100644
--- a/services/settings/pkg/config/defaults/defaultconfig.go
+++ b/services/settings/pkg/config/defaults/defaultconfig.go
@@ -117,6 +117,10 @@ func EnsureDefaults(cfg *config.Config) {
 			cfg.GRPC.TLS.Key = cfg.Commons.GRPCServiceTLS.Key
 		}
 	}
+
+	if cfg.Commons != nil {
+		cfg.HTTP.TLS = cfg.Commons.HTTPServiceTLS
+	}
 }
 
 func Sanitize(cfg *config.Config) {
diff --git a/services/settings/pkg/config/http.go b/services/settings/pkg/config/http.go
index 5749e8550f6..e43daa0cad9 100644
--- a/services/settings/pkg/config/http.go
+++ b/services/settings/pkg/config/http.go
@@ -1,12 +1,15 @@
 package config
 
+import "github.com/owncloud/ocis/v2/ocis-pkg/shared"
+
 // HTTP defines the available http configuration.
 type HTTP struct {
-	Addr      string `yaml:"addr" env:"SETTINGS_HTTP_ADDR" desc:"The bind address of the HTTP service."`
-	Namespace string `yaml:"-"`
-	Root      string `yaml:"root" env:"SETTINGS_HTTP_ROOT" desc:"Subdirectory that serves as the root for this HTTP service."`
-	CacheTTL  int    `yaml:"cache_ttl" env:"SETTINGS_CACHE_TTL" desc:"Browser cache control max-age value in seconds for settings Web UI assets."`
-	CORS      CORS   `yaml:"cors"`
+	Addr      string                `yaml:"addr" env:"SETTINGS_HTTP_ADDR" desc:"The bind address of the HTTP service."`
+	TLS       shared.HTTPServiceTLS `yaml:"tls"`
+	Namespace string                `yaml:"-"`
+	Root      string                `yaml:"root" env:"SETTINGS_HTTP_ROOT" desc:"Subdirectory that serves as the root for this HTTP service."`
+	CacheTTL  int                   `yaml:"cache_ttl" env:"SETTINGS_CACHE_TTL" desc:"Browser cache control max-age value in seconds for settings Web UI assets."`
+	CORS      CORS                  `yaml:"cors"`
 }
 
 // CORS defines the available cors configuration.
diff --git a/services/settings/pkg/server/http/server.go b/services/settings/pkg/server/http/server.go
index ec96b0f4d4b..fd92344ad17 100644
--- a/services/settings/pkg/server/http/server.go
+++ b/services/settings/pkg/server/http/server.go
@@ -1,6 +1,8 @@
 package http
 
 import (
+	"fmt"
+
 	"github.com/go-chi/chi/v5"
 	chimiddleware "github.com/go-chi/chi/v5/middleware"
 	"github.com/owncloud/ocis/v2/ocis-pkg/account"
@@ -15,10 +17,11 @@ import (
 )
 
 // Server initializes the http service and server.
-func Server(opts ...Option) http.Service {
+func Server(opts ...Option) (http.Service, error) {
 	options := newOptions(opts...)
 
-	service := http.NewService(
+	service, err := http.NewService(
+		http.TLSConfig(options.Config.HTTP.TLS),
 		http.Logger(options.Logger),
 		http.Name(options.Name),
 		http.Version(version.GetString()),
@@ -27,6 +30,12 @@ func Server(opts ...Option) http.Service {
 		http.Context(options.Context),
 		http.Flags(options.Flags...),
 	)
+	if err != nil {
+		options.Logger.Error().
+			Err(err).
+			Msg("Error initializing http service")
+		return http.Service{}, fmt.Errorf("could not initialize http service: %w", err)
+	}
 
 	handle := svc.NewService(options.Config, options.Logger)
 
@@ -81,5 +90,5 @@ func Server(opts ...Option) http.Service {
 
 	micro.RegisterHandler(service.Server(), mux)
 
-	return service
+	return service, nil
 }
diff --git a/services/thumbnails/pkg/config/defaults/defaultconfig.go b/services/thumbnails/pkg/config/defaults/defaultconfig.go
index 5ec4ed848fe..cc59f6affaa 100644
--- a/services/thumbnails/pkg/config/defaults/defaultconfig.go
+++ b/services/thumbnails/pkg/config/defaults/defaultconfig.go
@@ -88,6 +88,10 @@ func EnsureDefaults(cfg *config.Config) {
 			cfg.GRPC.TLS.Key = cfg.Commons.GRPCServiceTLS.Key
 		}
 	}
+
+	if cfg.Commons != nil {
+		cfg.HTTP.TLS = cfg.Commons.HTTPServiceTLS
+	}
 }
 
 func Sanitize(cfg *config.Config) {
diff --git a/services/thumbnails/pkg/config/http.go b/services/thumbnails/pkg/config/http.go
index d01215550d4..4133b1df441 100644
--- a/services/thumbnails/pkg/config/http.go
+++ b/services/thumbnails/pkg/config/http.go
@@ -1,8 +1,11 @@
 package config
 
+import "github.com/owncloud/ocis/v2/ocis-pkg/shared"
+
 // HTTP defines the available http configuration.
 type HTTP struct {
-	Addr      string `yaml:"addr" env:"THUMBNAILS_HTTP_ADDR" desc:"The bind address of the HTTP service."`
-	Root      string `yaml:"root" env:"THUMBNAILS_HTTP_ROOT" desc:"Subdirectory that serves as the root for this HTTP service."`
-	Namespace string `yaml:"-"`
+	Addr      string                `yaml:"addr" env:"THUMBNAILS_HTTP_ADDR" desc:"The bind address of the HTTP service."`
+	TLS       shared.HTTPServiceTLS `yaml:"tls"`
+	Root      string                `yaml:"root" env:"THUMBNAILS_HTTP_ROOT" desc:"Subdirectory that serves as the root for this HTTP service."`
+	Namespace string                `yaml:"-"`
 }
diff --git a/services/thumbnails/pkg/server/http/server.go b/services/thumbnails/pkg/server/http/server.go
index 7f03f4080e5..4401ac681c8 100644
--- a/services/thumbnails/pkg/server/http/server.go
+++ b/services/thumbnails/pkg/server/http/server.go
@@ -1,6 +1,8 @@
 package http
 
 import (
+	"fmt"
+
 	"github.com/go-chi/chi/v5/middleware"
 	ocismiddleware "github.com/owncloud/ocis/v2/ocis-pkg/middleware"
 	"github.com/owncloud/ocis/v2/ocis-pkg/service/http"
@@ -14,7 +16,8 @@ import (
 func Server(opts ...Option) (http.Service, error) {
 	options := newOptions(opts...)
 
-	service := http.NewService(
+	service, err := http.NewService(
+		http.TLSConfig(options.Config.HTTP.TLS),
 		http.Logger(options.Logger),
 		http.Name(options.Config.Service.Name),
 		http.Version(version.GetString()),
@@ -22,6 +25,12 @@ func Server(opts ...Option) (http.Service, error) {
 		http.Address(options.Config.HTTP.Addr),
 		http.Context(options.Context),
 	)
+	if err != nil {
+		options.Logger.Error().
+			Err(err).
+			Msg("Error initializing http service")
+		return http.Service{}, fmt.Errorf("could not initialize http service: %w", err)
+	}
 
 	handle := svc.NewService(
 		svc.Logger(options.Logger),
diff --git a/services/web/pkg/config/defaults/defaultconfig.go b/services/web/pkg/config/defaults/defaultconfig.go
index 2ed1d41bf42..42bc08af123 100644
--- a/services/web/pkg/config/defaults/defaultconfig.go
+++ b/services/web/pkg/config/defaults/defaultconfig.go
@@ -96,6 +96,10 @@ func EnsureDefaults(cfg *config.Config) {
 	} else if cfg.Tracing == nil {
 		cfg.Tracing = &config.Tracing{}
 	}
+
+	if cfg.Commons != nil {
+		cfg.HTTP.TLS = cfg.Commons.HTTPServiceTLS
+	}
 }
 
 func Sanitize(cfg *config.Config) {
diff --git a/services/web/pkg/config/http.go b/services/web/pkg/config/http.go
index addb0fce0c1..a827ba5be27 100644
--- a/services/web/pkg/config/http.go
+++ b/services/web/pkg/config/http.go
@@ -1,9 +1,12 @@
 package config
 
+import "github.com/owncloud/ocis/v2/ocis-pkg/shared"
+
 // HTTP defines the available http configuration.
 type HTTP struct {
-	Addr      string `yaml:"addr" env:"WEB_HTTP_ADDR" desc:"The bind address of the HTTP service."`
-	Namespace string `yaml:"-"`
-	Root      string `yaml:"root" env:"WEB_HTTP_ROOT" desc:"Subdirectory that serves as the root for this HTTP service."`
-	CacheTTL  int    `yaml:"cache_ttl" env:"WEB_CACHE_TTL" desc:"Cache policy in seconds for ownCloud Web assets."`
+	Addr      string                `yaml:"addr" env:"WEB_HTTP_ADDR" desc:"The bind address of the HTTP service."`
+	TLS       shared.HTTPServiceTLS `yaml:"tls"`
+	Namespace string                `yaml:"-"`
+	Root      string                `yaml:"root" env:"WEB_HTTP_ROOT" desc:"Subdirectory that serves as the root for this HTTP service."`
+	CacheTTL  int                   `yaml:"cache_ttl" env:"WEB_CACHE_TTL" desc:"Cache policy in seconds for ownCloud Web assets."`
 }
diff --git a/services/web/pkg/server/http/server.go b/services/web/pkg/server/http/server.go
index 6984c65b44a..c36406407b6 100644
--- a/services/web/pkg/server/http/server.go
+++ b/services/web/pkg/server/http/server.go
@@ -1,6 +1,8 @@
 package http
 
 import (
+	"fmt"
+
 	chimiddleware "github.com/go-chi/chi/v5/middleware"
 	"github.com/owncloud/ocis/v2/ocis-pkg/middleware"
 	"github.com/owncloud/ocis/v2/ocis-pkg/service/http"
@@ -14,7 +16,8 @@ import (
 func Server(opts ...Option) (http.Service, error) {
 	options := newOptions(opts...)
 
-	service := http.NewService(
+	service, err := http.NewService(
+		http.TLSConfig(options.Config.HTTP.TLS),
 		http.Logger(options.Logger),
 		http.Namespace(options.Namespace),
 		http.Name("web"),
@@ -23,6 +26,12 @@ func Server(opts ...Option) (http.Service, error) {
 		http.Context(options.Context),
 		http.Flags(options.Flags...),
 	)
+	if err != nil {
+		options.Logger.Error().
+			Err(err).
+			Msg("Error initializing http service")
+		return http.Service{}, fmt.Errorf("could not initialize http service: %w", err)
+	}
 
 	handle := svc.NewService(
 		svc.Logger(options.Logger),
diff --git a/services/webdav/pkg/config/defaults/defaultconfig.go b/services/webdav/pkg/config/defaults/defaultconfig.go
index 5c6c46742a8..083d0a1a325 100644
--- a/services/webdav/pkg/config/defaults/defaultconfig.go
+++ b/services/webdav/pkg/config/defaults/defaultconfig.go
@@ -73,6 +73,10 @@ func EnsureDefaults(cfg *config.Config) {
 			cfg.GRPCClientTLS.CACert = cfg.Commons.GRPCClientTLS.CACert
 		}
 	}
+
+	if cfg.Commons != nil {
+		cfg.HTTP.TLS = cfg.Commons.HTTPServiceTLS
+	}
 }
 
 func Sanitize(cfg *config.Config) {
diff --git a/services/webdav/pkg/config/http.go b/services/webdav/pkg/config/http.go
index 26ba1ed1cfd..7b44da9e0de 100644
--- a/services/webdav/pkg/config/http.go
+++ b/services/webdav/pkg/config/http.go
@@ -1,5 +1,7 @@
 package config
 
+import "github.com/owncloud/ocis/v2/ocis-pkg/shared"
+
 // CORS defines the available cors configuration.
 type CORS struct {
 	AllowedOrigins   []string `yaml:"allow_origins" env:"OCIS_CORS_ALLOW_ORIGINS;WEBDAV_CORS_ALLOW_ORIGINS" desc:"A comma-separated list of allowed CORS origins. See following chapter for more details: *Access-Control-Allow-Origin* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin"`
@@ -10,8 +12,9 @@ type CORS struct {
 
 // HTTP defines the available http configuration.
 type HTTP struct {
-	Addr      string `yaml:"addr" env:"WEBDAV_HTTP_ADDR" desc:"The bind address of the HTTP service."`
-	Namespace string `yaml:"-"`
-	Root      string `yaml:"root" env:"WEBDAV_HTTP_ROOT" desc:"Subdirectory that serves as the root for this HTTP service."`
-	CORS      CORS   `yaml:"cors"`
+	Addr      string                `yaml:"addr" env:"WEBDAV_HTTP_ADDR" desc:"The bind address of the HTTP service."`
+	Namespace string                `yaml:"-"`
+	Root      string                `yaml:"root" env:"WEBDAV_HTTP_ROOT" desc:"Subdirectory that serves as the root for this HTTP service."`
+	CORS      CORS                  `yaml:"cors"`
+	TLS       shared.HTTPServiceTLS `yaml:"tls"`
 }
diff --git a/services/webdav/pkg/server/http/server.go b/services/webdav/pkg/server/http/server.go
index f5b782507fc..02e87486679 100644
--- a/services/webdav/pkg/server/http/server.go
+++ b/services/webdav/pkg/server/http/server.go
@@ -1,6 +1,8 @@
 package http
 
 import (
+	"fmt"
+
 	chimiddleware "github.com/go-chi/chi/v5/middleware"
 	"github.com/owncloud/ocis/v2/ocis-pkg/cors"
 	"github.com/owncloud/ocis/v2/ocis-pkg/middleware"
@@ -14,7 +16,8 @@ import (
 func Server(opts ...Option) (http.Service, error) {
 	options := newOptions(opts...)
 
-	service := http.NewService(
+	service, err := http.NewService(
+		http.TLSConfig(options.Config.HTTP.TLS),
 		http.Logger(options.Logger),
 		http.Namespace(options.Config.HTTP.Namespace),
 		http.Name(options.Config.Service.Name),
@@ -23,6 +26,12 @@ func Server(opts ...Option) (http.Service, error) {
 		http.Context(options.Context),
 		http.Flags(options.Flags...),
 	)
+	if err != nil {
+		options.Logger.Error().
+			Err(err).
+			Msg("Error initializing http service")
+		return http.Service{}, fmt.Errorf("could not initialize http service: %w", err)
+	}
 
 	handle, err := svc.NewService(
 		svc.Logger(options.Logger),


Test patch:
diff --git a/services/graph/pkg/service/v0/drives_test.go b/services/graph/pkg/service/v0/drives_test.go
index 954b24c4ccc..ac18991b473 100644
--- a/services/graph/pkg/service/v0/drives_test.go
+++ b/services/graph/pkg/service/v0/drives_test.go
@@ -7,6 +7,7 @@ import (
 	"github.com/CiscoM31/godata"
 	libregraph "github.com/owncloud/libre-graph-api-go"
 	"github.com/stretchr/testify/assert"
+	"github.com/test-go/testify/require"
 )
 
 type sortTest struct {
@@ -121,3 +122,35 @@ func TestSort(t *testing.T) {
 		assert.Equal(t, test.DrivesSorted, sorted)
 	}
 }
+
+func TestSpaceNameValidation(t *testing.T) {
+	// set max length
+	_maxSpaceNameLength = 10
+
+	testCases := []struct {
+		Alias         string
+		SpaceName     string
+		ExpectedError error
+	}{
+		{"Happy Path", "Space", nil},
+		{"Just not too Long", "abcdefghij", nil},
+		{"Too Long", "abcdefghijk", ErrNameTooLong},
+		{"Empty", "", ErrNameEmpty},
+		{"Contains /", "Space/", ErrForbiddenCharacter},
+		{`Contains \`, `Space\`, ErrForbiddenCharacter},
+		{`Contains \\`, `Space\\`, ErrForbiddenCharacter},
+		{"Contains .", "Space.", ErrForbiddenCharacter},
+		{"Contains :", "Space:", ErrForbiddenCharacter},
+		{"Contains ?", "Sp?ace", ErrForbiddenCharacter},
+		{"Contains *", "Spa*ce", ErrForbiddenCharacter},
+		{`Contains "`, `"Space"`, ErrForbiddenCharacter},
+		{`Contains >`, `Sp>ce`, ErrForbiddenCharacter},
+		{`Contains <`, `S<pce`, ErrForbiddenCharacter},
+		{`Contains |`, `S|p|e`, ErrForbiddenCharacter},
+	}
+
+	for _, tc := range testCases {
+		err := validateSpaceName(tc.SpaceName)
+		require.Equal(t, tc.ExpectedError, err, tc.Alias)
+	}
+}
diff --git a/services/proxy/pkg/proxy/proxy_integration_test.go b/services/proxy/pkg/proxy/proxy_integration_test.go
index 65a8c037b8c..0f19c3a4a94 100644
--- a/services/proxy/pkg/proxy/proxy_integration_test.go
+++ b/services/proxy/pkg/proxy/proxy_integration_test.go
@@ -164,7 +164,7 @@ func TestProxyIntegration(t *testing.T) {
 }
 
 func newTestProxy(cfg *config.Config, fn RoundTripFunc) *MultiHostReverseProxy {
-	rp := NewMultiHostReverseProxy(Config(cfg))
+	rp, _ := NewMultiHostReverseProxy(Config(cfg))
 	rp.Transport = fn
 	return rp
 }