mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-01 09:52:23 -06:00
* refactor middleware options Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * use ocmemstore micro store implementaiton for token cache Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * refactor ocis store options, support redis sentinel Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * align cache configuration Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * database and tabe are used to build prefixes for inmemory stores Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * add global persistent store options to userlog config Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * log cache errors but continue Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * drup unnecessary type conversion Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * Better description for the default userinfo ttl Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * use global cache options for even more caches Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * don't log userinfo cache misses Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * default to stock memory store Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * use correct mem store typo string Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * split cache options, doc cleanup Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * mint and write userinfo to cache async Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * use hashed token as key Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * go mod tidy Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * update docs Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * update cache store naming Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * bring back depreceted ocis-pkg/store package for backwards compatability Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * update changelog Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * Apply suggestions from code review Co-authored-by: kobergj <jkoberg@owncloud.com> * revert ocis-pkg/cache to store rename Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * add waiting for each step 50 milliseconds * starlack check --------- Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> Co-authored-by: kobergj <jkoberg@owncloud.com> Co-authored-by: Viktor Scharf <scharf.vi@gmail.com>
295 lines
9.0 KiB
Go
295 lines
9.0 KiB
Go
package command
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/coreos/go-oidc/v3/oidc"
|
|
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
|
|
"github.com/cs3org/reva/v2/pkg/token/manager/jwt"
|
|
chimiddleware "github.com/go-chi/chi/v5/middleware"
|
|
"github.com/justinas/alice"
|
|
"github.com/oklog/run"
|
|
"github.com/owncloud/ocis/v2/ocis-pkg/config/configlog"
|
|
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
|
pkgmiddleware "github.com/owncloud/ocis/v2/ocis-pkg/middleware"
|
|
"github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
|
|
"github.com/owncloud/ocis/v2/ocis-pkg/store"
|
|
"github.com/owncloud/ocis/v2/ocis-pkg/version"
|
|
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
|
|
storesvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/store/v0"
|
|
"github.com/owncloud/ocis/v2/services/proxy/pkg/autoprovision"
|
|
"github.com/owncloud/ocis/v2/services/proxy/pkg/config"
|
|
"github.com/owncloud/ocis/v2/services/proxy/pkg/config/parser"
|
|
"github.com/owncloud/ocis/v2/services/proxy/pkg/logging"
|
|
"github.com/owncloud/ocis/v2/services/proxy/pkg/metrics"
|
|
"github.com/owncloud/ocis/v2/services/proxy/pkg/middleware"
|
|
"github.com/owncloud/ocis/v2/services/proxy/pkg/proxy"
|
|
"github.com/owncloud/ocis/v2/services/proxy/pkg/router"
|
|
"github.com/owncloud/ocis/v2/services/proxy/pkg/server/debug"
|
|
proxyHTTP "github.com/owncloud/ocis/v2/services/proxy/pkg/server/http"
|
|
"github.com/owncloud/ocis/v2/services/proxy/pkg/tracing"
|
|
"github.com/owncloud/ocis/v2/services/proxy/pkg/user/backend"
|
|
"github.com/owncloud/ocis/v2/services/proxy/pkg/userroles"
|
|
"github.com/urfave/cli/v2"
|
|
microstore "go-micro.dev/v4/store"
|
|
"golang.org/x/oauth2"
|
|
)
|
|
|
|
// Server is the entrypoint for the server command.
|
|
func Server(cfg *config.Config) *cli.Command {
|
|
return &cli.Command{
|
|
Name: "server",
|
|
Usage: fmt.Sprintf("start the %s service without runtime (unsupervised mode)", cfg.Service.Name),
|
|
Category: "server",
|
|
Before: func(c *cli.Context) error {
|
|
return configlog.ReturnFatal(parser.ParseConfig(cfg))
|
|
},
|
|
Action: func(c *cli.Context) error {
|
|
logger := logging.Configure(cfg.Service.Name, cfg.Log)
|
|
err := tracing.Configure(cfg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = grpc.Configure(grpc.GetClientOptions(cfg.GRPCClientTLS)...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var (
|
|
m = metrics.New()
|
|
)
|
|
|
|
gr := run.Group{}
|
|
ctx, cancel := func() (context.Context, context.CancelFunc) {
|
|
if cfg.Context == nil {
|
|
return context.WithCancel(context.Background())
|
|
}
|
|
return context.WithCancel(cfg.Context)
|
|
}()
|
|
|
|
defer cancel()
|
|
|
|
m.BuildInfo.WithLabelValues(version.GetString()).Set(1)
|
|
|
|
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(
|
|
proxyHTTP.Handler(rp),
|
|
proxyHTTP.Logger(logger),
|
|
proxyHTTP.Context(ctx),
|
|
proxyHTTP.Config(cfg),
|
|
proxyHTTP.Metrics(metrics.New()),
|
|
proxyHTTP.Middlewares(loadMiddlewares(ctx, logger, cfg)),
|
|
)
|
|
|
|
if err != nil {
|
|
logger.Error().
|
|
Err(err).
|
|
Str("server", "http").
|
|
Msg("Failed to initialize server")
|
|
|
|
return err
|
|
}
|
|
|
|
gr.Add(func() error {
|
|
return server.Run()
|
|
}, func(err error) {
|
|
logger.Error().
|
|
Err(err).
|
|
Str("server", "http").
|
|
Msg("Shutting down server")
|
|
|
|
cancel()
|
|
})
|
|
}
|
|
|
|
{
|
|
server, err := debug.Server(
|
|
debug.Logger(logger),
|
|
debug.Context(ctx),
|
|
debug.Config(cfg),
|
|
)
|
|
|
|
if err != nil {
|
|
logger.Error().Err(err).Str("server", "debug").Msg("Failed to initialize server")
|
|
return err
|
|
}
|
|
|
|
gr.Add(server.ListenAndServe, func(_ error) {
|
|
_ = server.Shutdown(ctx)
|
|
cancel()
|
|
})
|
|
}
|
|
|
|
return gr.Run()
|
|
},
|
|
}
|
|
}
|
|
|
|
func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config) alice.Chain {
|
|
rolesClient := settingssvc.NewRoleService("com.owncloud.api.settings", grpc.DefaultClient())
|
|
revaClient, err := pool.GetGatewayServiceClient(cfg.Reva.Address, cfg.Reva.GetRevaOptions()...)
|
|
if err != nil {
|
|
logger.Fatal().Err(err).Msg("Failed to get gateway client")
|
|
}
|
|
tokenManager, err := jwt.New(map[string]interface{}{
|
|
"secret": cfg.TokenManager.JWTSecret,
|
|
})
|
|
if err != nil {
|
|
logger.Fatal().Err(err).
|
|
Msg("Failed to create token manager")
|
|
}
|
|
autoProvsionCreator := autoprovision.NewCreator(autoprovision.WithTokenManager(tokenManager))
|
|
var userProvider backend.UserBackend
|
|
switch cfg.AccountBackend {
|
|
case "cs3":
|
|
|
|
userProvider = backend.NewCS3UserBackend(
|
|
backend.WithLogger(logger),
|
|
backend.WithRevaAuthenticator(revaClient),
|
|
backend.WithMachineAuthAPIKey(cfg.MachineAuthAPIKey),
|
|
backend.WithOIDCissuer(cfg.OIDC.Issuer),
|
|
backend.WithAutoProvisonCreator(autoProvsionCreator),
|
|
)
|
|
default:
|
|
logger.Fatal().Msgf("Invalid accounts backend type '%s'", cfg.AccountBackend)
|
|
}
|
|
|
|
var roleAssigner userroles.UserRoleAssigner
|
|
switch cfg.RoleAssignment.Driver {
|
|
case "default":
|
|
roleAssigner = userroles.NewDefaultRoleAssigner(
|
|
userroles.WithRoleService(rolesClient),
|
|
userroles.WithLogger(logger),
|
|
)
|
|
case "oidc":
|
|
roleAssigner = userroles.NewOIDCRoleAssigner(
|
|
userroles.WithRoleService(rolesClient),
|
|
userroles.WithLogger(logger),
|
|
userroles.WithRolesClaim(cfg.RoleAssignment.OIDCRoleMapper.RoleClaim),
|
|
userroles.WithRoleMapping(cfg.RoleAssignment.OIDCRoleMapper.RoleMapping),
|
|
userroles.WithAutoProvisonCreator(autoProvsionCreator),
|
|
)
|
|
default:
|
|
logger.Fatal().Msgf("Invalid role assignment driver '%s'", cfg.RoleAssignment.Driver)
|
|
}
|
|
|
|
storeClient := storesvc.NewStoreService("com.owncloud.api.store", grpc.DefaultClient())
|
|
if err != nil {
|
|
logger.Error().Err(err).
|
|
Str("gateway", cfg.Reva.Address).
|
|
Msg("Failed to create reva gateway service client")
|
|
}
|
|
|
|
var oidcHTTPClient = &http.Client{
|
|
Transport: &http.Transport{
|
|
TLSClientConfig: &tls.Config{
|
|
MinVersion: tls.VersionTLS12,
|
|
InsecureSkipVerify: cfg.OIDC.Insecure, //nolint:gosec
|
|
},
|
|
DisableKeepAlives: true,
|
|
},
|
|
Timeout: time.Second * 10,
|
|
}
|
|
|
|
var authenticators []middleware.Authenticator
|
|
if cfg.EnableBasicAuth {
|
|
logger.Warn().Msg("basic auth enabled, use only for testing or development")
|
|
authenticators = append(authenticators, middleware.BasicAuthenticator{
|
|
Logger: logger,
|
|
UserProvider: userProvider,
|
|
})
|
|
}
|
|
|
|
cache := store.Create(
|
|
store.Store(cfg.OIDC.UserinfoCache.Store),
|
|
store.TTL(cfg.OIDC.UserinfoCache.TTL),
|
|
store.Size(cfg.OIDC.UserinfoCache.Size),
|
|
microstore.Nodes(cfg.OIDC.UserinfoCache.Nodes...),
|
|
microstore.Database(cfg.OIDC.UserinfoCache.Database),
|
|
microstore.Table(cfg.OIDC.UserinfoCache.Table),
|
|
)
|
|
|
|
authenticators = append(authenticators, middleware.NewOIDCAuthenticator(
|
|
middleware.Logger(logger),
|
|
middleware.Cache(cache),
|
|
middleware.DefaultAccessTokenTTL(cfg.OIDC.UserinfoCache.TTL),
|
|
middleware.HTTPClient(oidcHTTPClient),
|
|
middleware.OIDCIss(cfg.OIDC.Issuer),
|
|
middleware.JWKSOptions(cfg.OIDC.JWKS),
|
|
middleware.AccessTokenVerifyMethod(cfg.OIDC.AccessTokenVerifyMethod),
|
|
middleware.OIDCProviderFunc(func() (middleware.OIDCProvider, error) {
|
|
// Initialize a provider by specifying the issuer URL.
|
|
// it will fetch the keys from the issuer using the .well-known
|
|
// endpoint
|
|
return oidc.NewProvider(
|
|
context.WithValue(ctx, oauth2.HTTPClient, oidcHTTPClient),
|
|
cfg.OIDC.Issuer,
|
|
)
|
|
}),
|
|
))
|
|
authenticators = append(authenticators, middleware.PublicShareAuthenticator{
|
|
Logger: logger,
|
|
RevaGatewayClient: revaClient,
|
|
})
|
|
authenticators = append(authenticators, middleware.SignedURLAuthenticator{
|
|
Logger: logger,
|
|
PreSignedURLConfig: cfg.PreSignedURL,
|
|
UserProvider: userProvider,
|
|
UserRoleAssigner: roleAssigner,
|
|
Store: storeClient,
|
|
})
|
|
|
|
return alice.New(
|
|
// first make sure we log all requests and redirect to https if necessary
|
|
pkgmiddleware.TraceContext,
|
|
chimiddleware.RealIP,
|
|
chimiddleware.RequestID,
|
|
middleware.AccessLog(logger),
|
|
middleware.HTTPSRedirect,
|
|
middleware.OIDCWellKnownRewrite(
|
|
logger, cfg.OIDC.Issuer,
|
|
cfg.OIDC.RewriteWellKnown,
|
|
oidcHTTPClient,
|
|
),
|
|
router.Middleware(cfg.PolicySelector, cfg.Policies, logger),
|
|
middleware.Authentication(
|
|
authenticators,
|
|
middleware.CredentialsByUserAgent(cfg.AuthMiddleware.CredentialsByUserAgent),
|
|
middleware.Logger(logger),
|
|
middleware.OIDCIss(cfg.OIDC.Issuer),
|
|
middleware.EnableBasicAuth(cfg.EnableBasicAuth),
|
|
),
|
|
middleware.AccountResolver(
|
|
middleware.Logger(logger),
|
|
middleware.UserProvider(userProvider),
|
|
middleware.UserRoleAssigner(roleAssigner),
|
|
middleware.UserOIDCClaim(cfg.UserOIDCClaim),
|
|
middleware.UserCS3Claim(cfg.UserCS3Claim),
|
|
middleware.AutoprovisionAccounts(cfg.AutoprovisionAccounts),
|
|
),
|
|
middleware.SelectorCookie(
|
|
middleware.Logger(logger),
|
|
middleware.PolicySelectorConfig(*cfg.PolicySelector),
|
|
),
|
|
middleware.Policies(logger, cfg.PoliciesMiddleware.Query),
|
|
// finally, trigger home creation when a user logs in
|
|
middleware.CreateHome(
|
|
middleware.Logger(logger),
|
|
middleware.RevaGatewayClient(revaClient),
|
|
middleware.RoleQuotas(cfg.RoleQuotas),
|
|
),
|
|
)
|
|
}
|