cs3org / reva

WebDAV/gRPC/HTTP high performance server to link high level clients to storage backends
https://reva.link
Apache License 2.0
167 stars 113 forks source link

Improve configuration #65

Closed butonic closed 4 years ago

butonic commented 5 years ago

I had a lengthy discussion with @felixboehm about the toml config file ... while I like the declarative nature of it Felix would like to see some form of dynamic configuration, so an admin can do

wget https://.../reva
reva
reva service enable ocdavsvc
reva service enable authsvc
reva service enable storageprovider --driver local

to get a working instance.

To me that is sugar on top. AFAICT the cli still needs to change the config and kick the reva process to reload the changes (or we add monitoring, but the less magic the better IMO). @labkode IIRC you mentioned reva or the os being able to queue any requests while reva is restarting / rereading the config. Could you elaborate and give an example? Maybe as a PR in the docs?

Anyway, I do agree that the single config file mixes two things that should be separate:

  1. the services that should be started
  2. the configuration for each service

I think we should use a config folder that contains a config file per service. Each service should write its default config if it is not present. That way the services are responsible for defining their configuration. Reva itself should maybe write individual config files for core.toml, log.toml http.toml and grpc.toml Maybe the services should have a subfolder in the reva config dir. That would allow them to freely choose config file names, without being able to overwrite another services config.

When using docker containers we can then mount a config dir (passing in env vars should still be possible in order to overwrite individual settings)

labkode commented 5 years ago

@butonic @felixboehm the configuration of REVA focuses on simplicity, like NGINX does, I took inspiration from it. As Jorn mentioned reva service enable authsvc is just sugar to avoid having to do:

vim /etc/revad/revad.toml
systemctl restart revad

Changes to REVA need to be driven by the configuration file, what you put in the config is what you get when you run the service. The oc model of having the DB to store important configuration items is not sysadmin-friendly at all and complicates debugging.

I think we need to strive for simplicity, it's very easy to explain that if you want X you put X in the config file. Having multiple config files like NGINX/Apache do can be considered.

NGINX provides well documented defaults when you don't set any configuration for a module, the same should apply to REVA, hence no need to have default configuration files per service.

labkode commented 5 years ago

For reloading the configuration:

./cmd/revad/revad -p revad.pid -s reload

is a TODO :)

You can just kill -s SIGHUP pid

If you are in a Docker container it will not work as the new process will be 2, and 1 will be killed which will kill the container.

butonic commented 5 years ago

ok, what are the defaults we should pick for reva and its services? by default it does not run anything. I wanted to document how to implement a storage provider and ended up explaining the config file in https://central.owncloud.org/t/digging-into-the-reva-hello-world-example-md/19896 ... only to stumble upon https://github.com/cernbox/reva/blob/master/docs/beginner-guide.md which basically does the same thing.

If we want reva a generic framework it should only render the hello world thing. However, I think it makes more sense to have it start a sensible set of services because it is inteded to be the referece implmentation of the CS3 apis. As such it should:

that is kind of the minimal config I see. @labkode @felixboehm opinions?

labkode commented 5 years ago

@butonic I agree that those basic services should be the default services so you can play with REVA out of the box.

We need to come up with a basic config file to use like prometheus tells you to do so in: https://prometheus.io/docs/prometheus/latest/getting_started/#getting-started

The bootstrap services needs to be configured in the config file while the configuration for those can use defaults. If we lunch reva without a config file, it will assume that runs those services by defaults and you will need a negative config directive to disable default services ... and that is ugly.

butonic commented 5 years ago

@tboerger could you give us your strongest opinion on this?

butonic commented 5 years ago

I am currently using three configs, but they require a small patch:

  1. a backend service that only starts the grpc services and relies completely on token based authentication:
    
    # This b-o.toml config file will start a reva grpc service backend (b) that:
    # - does no authentication (-) but trusts jwt tokens
    # - stores files with the owncloud (o) storage driver

[core] log_file = "stderr" log_mode = "dev" max_cpus = "2"

tracing_enabled = true

disable_http = true

[log] level = "debug" mode = "console"

What grpc services should be started?

[grpc] network = "tcp" address = "0.0.0.0:9999" enabled_services = ["storageprovidersvc", "storageregistrysvc", "usershareprovidersvc"] enabled_interceptors = ["auth"]

Order and configuration of grpc interceptors

GRPC interceptors

[grpc.interceptors.auth]

keys for grpc metadata are always lowercase, so interceptors headers need to use lowercase.

token_manager = "jwt"

[grpc.interceptors.auth.token_managers.jwt] secret = "Uv38ByGCZU8WP18PmmIdcpVmx00QA3xN"

GRPC services

The storage registry service

[grpc.services.storageregistrysvc] driver = "static"

[grpc.services.storageregistrysvc.drivers.static.rules] "/" = "localhost:9999" "123e4567-e89b-12d3-a456-426655440000" = "localhost:9999"

The storage provider service

[grpc.services.storageprovidersvc] driver = "owncloud" mount_path = "/" mount_id = "123e4567-e89b-12d3-a456-426655440000" data_server_url = "http://127.0.0.1:9998/data"

[grpc.services.storageprovidersvc.available_checksums] md5 = 100 unset = 1000

[grpc.services.storageprovidersvc.drivers.owncloud] datadirectory = "/data"

The user share provider service

[grpc.services.usershareprovidersvc] driver = "memory"

2.  a frontend gateway on port 9998 that contains a minimal openid connect provider:
```toml
# This goo.toml config file will start a reva frontent gateway (g) that:
# - authenticates user using openid connect (o) auth
# - stores files with the owncloud (o) storage driver

[core]
log_file = "stderr"
log_mode = "dev"
max_cpus = "100%"
#tracing_enabled = true

[log]
level = "debug"
mode = "console"

# What http services should be started?

[http]
network = "tcp"
address = "0.0.0.0:9998"
enabled_services = [
    "appregistrysvc",
    "ocdavsvc",
    "ocssvc",
    "datasvc",
    "preferencessvc",
    "prometheussvc",
    "oidcprovider",
    "wellknown"
]
enabled_middlewares = ["cors", "auth"]

# HTTP middlewares

[http.middlewares.auth]
priority = 100
# directly talk to the backend for authentication to save a grpc request
gatewaysvc = "0.0.0.0:10008"
credential_strategy = "oidc"
token_strategy = "header"
token_writer = "header"
token_manager = "jwt"
skip_methods = [
    "/status.php",
    "/oauth2",
    "/oauth2/auth", 
    "/oauth2/token", 
    "/oauth2/introspect",
    "/oauth2/userinfo", 
    "/oauth2/sessions", 
    "/.well-known/openid-configuration",
    "/metrics"
]

[http.middlewares.auth.token_managers.jwt]
secret = "Uv38ByGCZU8WP18PmmIdcpVmx00QA3xN"

[http.middlewares.auth.token_strategies.header]
header = "X-Access-Token"
[http.middlewares.auth.token_writers.header]
header = "X-Access-Token"

[http.middlewares.cors]
priority = 200
allowed_origins = ["*"]
allow_credentials = true
allowed_methods = ["OPTIONS", "GET", "PUT", "POST", "DELETE", "MKCOL", "PROPFIND", "PROPPATCH", "MOVE", "COPY", "REPORT", "SEARCH"]
allowed_headers = ["Origin", "Accept", "Content-Type", "X-Requested-With", "Authorization", "Ocs-Apirequest", "If-None-Match"]
options_passthrough = true

# HTTP services

[http.services.appregistrysvc]
prefix = "appregistry"
gatewaysvc = "localhost:9999"

[http.services.preferencessvc]
prefix = "preferences"
gatewaysvc = "localhost:9999"

[http.services.iframeuisvc]
prefix = "iframe"

[http.services.webuisvc]
prefix = "ui"

[http.services.ocdavsvc]
prefix = ""
gatewaysvc = "localhost:9999"
chunk_folder = "/var/tmp/owncloud/chunks"

[http.services.ocssvc]
prefix = "ocs"
gatewaysvc = "localhost:9999"
# the list of share recipients is taken fro the user.json file
user_manager = "json"

[http.services.ocssvc.user_managers.json]
users = "/data/users.json"

[http.services.ocssvc.config]
version = "1.8"
website = "nexus"
host = "https://localhost:9998"
contact = "admin@localhost"
ssl = "true"
[http.services.ocssvc.capabilities.capabilities.core]
poll_interval = 60
webdav_root = "remote.php/webdav"
[http.services.ocssvc.capabilities.capabilities.core.status]
installed = true
maintenance = false
needsDbUpgrade = false
version = "10.0.9.5"
versionstring = "10.0.9"
edition = "community"
productname = "reva"
hostname = ""
[http.services.ocssvc.capabilities.capabilities.checksums]
supported_types = ["SHA256"]
preferred_upload_type = "SHA256"
[http.services.ocssvc.capabilities.capabilities.files]
private_links = true
bigfilechunking = true
blacklisted_files = ["foo"]
undelete = true
versioning = true
[http.services.ocssvc.capabilities.capabilities.dav]
chunking = "1.0"
trashbin = "1.0"
reports = ["custom"]

[http.services.ocssvc.capabilities.capabilities.files_sharing]
api_enabled = "1"
resharing = true
group_sharing = true
auto_accept_share = true
share_with_group_members_only = true
share_with_membership_groups_only = true
default_permissions = 22
search_min_length = 3
[http.services.ocssvc.capabilities.capabilities.files_sharing.public]
enabled = "1"
send_mail = true
social_share = true
upload = true
multiple = true
supports_upload_only = true
[http.services.ocssvc.capabilities.capabilities.files_sharing.public.password]
enforced = true
[http.services.ocssvc.capabilities.capabilities.files_sharing.public.password.enforced_for]
read_only = true
read_write = true
upload_only = true
[http.services.ocssvc.capabilities.capabilities.files_sharing.public.expire_date]
enabled = true
[http.services.ocssvc.capabilities.capabilities.files_sharing.user]
send_mail = true
[http.services.ocssvc.capabilities.capabilities.files_sharing.user_enumeration]
enabled = true
group_members_only = true
[http.services.ocssvc.capabilities.capabilities.files_sharing.federation]
outgoing = true
incoming = true
[http.services.ocssvc.capabilities.capabilities.notifications]
endpoints = ["list", "get", "delete"]
[http.services.ocssvc.capabilities.version]
edition = "nexus"
major = 10
minor = 0
micro = 11
string = "10.0.11"

[http.services.datasvc]
driver = "owncloud"
prefix = "data"
temp_folder = "/var/tmp/"

[http.services.datasvc.drivers.owncloud]
datadirectory = "/data"

[http.services.oidcprovider]
prefix = "oauth2"
auth_manager = "json"
user_manager = "json"

[http.services.oidcprovider.auth_managers.json]
users = "/data/users.json"

[http.services.oidcprovider.user_managers.json]
users = "/data/users.json"

[http.services.wellknown]
prefix = ".well-known"

# also start the grpc gateway for other cs3 services so we have one frontend process

[grpc]
network = "tcp"
address = "0.0.0.0:10008"
enabled_services = ["authsvc", "gatewaysvc"]

[grpc.services.gatewaysvc]
storageregistrysvc = "localhost:9999"
authsvc = ""
usershareprovidersvc = "localhost:9999"
appregistrysvc = "localhost:9999"
preferencessvc = "localhost:9999"
commit_share_to_storage_grant = true
commit_share_to_storage_ref = true

# Order and configuration of grpc interceptors 

# GRPC interceptors

[grpc.interceptors.auth]
# keys for grpc metadata are always lowercase, so interceptors headers need to use lowercase.
token_manager = "jwt"
# GenerateAccessToken contains the credentials in the payload. Skip auth, otherwise services cannot obtain a token.
skip_methods = ["/cs3.authv0alpha.AuthService/GenerateAccessToken", "/cs3.authv0alpha.AuthService/WhoAmI"]

[grpc.interceptors.auth.token_managers.jwt]
secret = "Uv38ByGCZU8WP18PmmIdcpVmx00QA3xN"

# GRPC services

## The authentication service

[grpc.services.authsvc]
token_manager = "jwt"
# users are authorized by checking their password matches the one in the users.json file
auth_manager = "oidc"
# user info is read from the user.json file
user_manager = "oidc"

[grpc.services.authsvc.auth_managers.oidc]
provider = "http://localhost:9998"
insecure = true
# the client credentials for the token introspection beckchannel
client_id = "phoenix"
client_secret = "foobar"

[grpc.services.authsvc.token_managers.jwt]
secret = "Uv38ByGCZU8WP18PmmIdcpVmx00QA3xN"
  1. a frontend gateway on port 9997 I can use with clients that only support basic auth:
    
    # This gbo.toml config file will start a reva frontent gateway (g) that:
    # - authenticates user using basic (b) auth
    # - stores files with the owncloud (o) storage driver

[core] log_file = "stderr" log_mode = "dev" max_cpus = "100%"

tracing_enabled = true

[log] level = "debug" mode = "console"

What http services should be started?

[http] network = "tcp" address = "0.0.0.0:9997" enabled_services = [ "ocdavsvc", "ocssvc", "datasvc", "prometheussvc", ] enabled_middlewares = ["auth"]

HTTP middlewares

[http.middlewares.auth] priority = 100

directly talk to the backend for authentication to save a grpc request

gatewaysvc = "0.0.0.0:10007" credential_strategy = "basic" token_strategy = "header" token_writer = "header" token_manager = "jwt" skip_methods = [ "/status.php", "/metrics" ]

[http.middlewares.auth.token_managers.jwt] secret = "Uv38ByGCZU8WP18PmmIdcpVmx00QA3xN"

[http.middlewares.auth.token_strategies.header] header = "X-Access-Token" [http.middlewares.auth.token_writers.header] header = "X-Access-Token"

HTTP services

[http.services.ocdavsvc] prefix = "" gatewaysvc = "localhost:9999" chunk_folder = "/var/tmp/owncloud/chunks"

[http.services.ocssvc] prefix = "ocs" gatewaysvc = "localhost:9999"

the list of share recipients is taken fro the user.json file

user_manager = "json"

[http.services.ocssvc.user_managers.json] users = "/data/users.json"

[http.services.ocssvc.config] version = "1.8" website = "nexus" host = "https://localhost:9997" contact = "admin@localhost" ssl = "true" [http.services.ocssvc.capabilities.capabilities.core] poll_interval = 60 webdav_root = "remote.php/webdav" [http.services.ocssvc.capabilities.capabilities.core.status] installed = true maintenance = false needsDbUpgrade = false version = "10.0.9.5" versionstring = "10.0.9" edition = "community" productname = "reva" hostname = "" [http.services.ocssvc.capabilities.capabilities.checksums] supported_types = ["SHA256"] preferred_upload_type = "SHA256" [http.services.ocssvc.capabilities.capabilities.files] private_links = true bigfilechunking = true blacklisted_files = ["foo"] undelete = true versioning = true [http.services.ocssvc.capabilities.capabilities.dav] chunking = "1.0" trashbin = "1.0" reports = ["custom"]

[http.services.ocssvc.capabilities.capabilities.files_sharing] api_enabled = "1" resharing = true group_sharing = true auto_accept_share = true share_with_group_members_only = true share_with_membership_groups_only = true default_permissions = 22 search_min_length = 3 [http.services.ocssvc.capabilities.capabilities.files_sharing.public] enabled = "1" send_mail = true social_share = true upload = true multiple = true supports_upload_only = true [http.services.ocssvc.capabilities.capabilities.files_sharing.public.password] enforced = true [http.services.ocssvc.capabilities.capabilities.files_sharing.public.password.enforced_for] read_only = true read_write = true upload_only = true [http.services.ocssvc.capabilities.capabilities.files_sharing.public.expire_date] enabled = true [http.services.ocssvc.capabilities.capabilities.files_sharing.user] send_mail = true [http.services.ocssvc.capabilities.capabilities.files_sharing.user_enumeration] enabled = true group_members_only = true [http.services.ocssvc.capabilities.capabilities.files_sharing.federation] outgoing = true incoming = true [http.services.ocssvc.capabilities.capabilities.notifications] endpoints = ["list", "get", "delete"] [http.services.ocssvc.capabilities.version] edition = "nexus" major = 10 minor = 0 micro = 11 string = "10.0.11"

[http.services.datasvc] driver = "owncloud" prefix = "data" temp_folder = "/var/tmp/"

[http.services.datasvc.drivers.owncloud] datadirectory = "/data"

also start the grpc gateway for other cs3 services so we have one frontend process

[grpc] network = "tcp" address = "0.0.0.0:10007" enabled_services = ["authsvc", "gatewaysvc"]

[grpc.services.gatewaysvc] storageregistrysvc = "localhost:9999" authsvc = "" usershareprovidersvc = "localhost:9999" appregistrysvc = "localhost:9999" preferencessvc = "localhost:9999" commit_share_to_storage_grant = true commit_share_to_storage_ref = true

The authentication service

[grpc.services.authsvc] token_manager = "jwt"

users are authorized by checking their password matches the one in the users.json file

auth_manager = "json"

user info is read from the user.json file

user_manager = "json"

[grpc.services.authsvc.auth_managers.json] users = "/data/users.json"

[grpc.services.authsvc.user_managers.json] users = "/data/users.json"

[grpc.services.authsvc.token_managers.jwt] secret = "Uv38ByGCZU8WP18PmmIdcpVmx00QA3xN"

Order and configuration of grpc interceptors

GRPC interceptors

[grpc.interceptors.auth]

keys for grpc metadata are always lowercase, so interceptors headers need to use lowercase.

token_manager = "jwt"

GenerateAccessToken contains the credentials in the payload. Skip auth, otherwise services cannot obtain a token.

skip_methods = ["/cs3.authv0alpha.AuthService/GenerateAccessToken", "/cs3.authv0alpha.AuthService/WhoAmI"]

[grpc.interceptors.auth.token_managers.jwt] secret = "Uv38ByGCZU8WP18PmmIdcpVmx00QA3xN"


the gbo.toml can be used to do a `reva login`on the cli on port 10007

```json
[
        {
                "id": {
                        "opaque_id": "c6e5995d6c7fa1986b830b78b478e6c2",
                        "idp": "http://localhost:9998"
                },
                "username": "aaliyah_abernathy",
                "secret": "secret",
                "mail": "aaliyah_abernathy@owncloudqa.com",
                "display_name": "Aaliyah Abernathy"
        },
        {
                "id": {
                        "opaque_id": "9fb5f8d212cbf3fc55f1bf67d97ed05d",
                        "idp": "http://localhost:9998"
                },
                "username": "aaliyah_adams",
                "secret": "secret",
                "mail": "aaliyah_adams@owncloudqa.com",
                "display_name": "Aaliyah Adams"
        },
        {
                "id": {
                        "opaque_id": "a84075b398fe6a0aee1155f8ead13331",
                        "idp": "http://localhost:9998"
                },
                "username": "aaliyah_anderson",
                "secret": "secret",
                "mail": "aaliyah_anderson@owncloudqa.com",
                "display_name": "Aaliyah Anderson"
        }
]
butonic commented 4 years ago

we added an opinionated config in ocis and reva has reduced some config overhead as well! :rocket: