AppFlowy-Cloud icon indicating copy to clipboard operation
AppFlowy-Cloud copied to clipboard

[FR] Provide a Production docker-compose.yml

Open bhelm opened this issue 1 year ago • 6 comments

1~3 main use cases of the proposed feature

  • making a secure appflowy deployment easier. (it took me 3 hours to setup mine)
  • protect users that are in a hurry from getting hacked and loosing their data.

what types of users can benefit from using your proposed feature Especially users that want to use the cloud on a public server. But also local LAN users should not expose unnecessary ports.

Additional context I feel that the docker compose file is very naive and the setup tutorial does not mention the risks or give any guidance to secure the installation. All services, even if only used locally, are binding ports to any ip, public or not. redis, pgadmin, postgres... everything listens on the public ip and is therefore reachable over the public internet when deployed on a cloud server. I expect there are basically no port binds needed and if, they should bind to 127.0.0.1 by default.

There is also no guide where to change all the default passwords, i hope they are all in the configuration yaml's, but as a new user, i have to figure it out.

I can see many appflowy cloud instances set up in that way that all services are reachable public and all the passwords are still default, because not everyone notices the problem or is able to solve it by him/herself. Its just so easy to follow the tutorial and have your appflowy cloud working... until someone logs into some service, encrypts everything and wants 0.01 BTC for it.

If you have the docker compose deployed on a public ip, you can go to http://domain.com/minio and login with minioadmin:minioadmin and do just everything with it, delete all buckets, you are admin. i have not tried it, but i expect that this works with other services too.

bhelm avatar Jan 10 '24 20:01 bhelm

I have got it working now. It seems to work... but i dont have the full picture of the application. Its possible that i broke some feature by doing it that way. (im new).

Changes i did:

docker-compose.yml:

  • removed ALL port bindings completely, except for these on nginx
  • on minio environment, added:
    • MINIO_ROOT_USER=${AWS_ACCESS_KEY_ID:-minioadmin}
    • MINIO_ROOT_PASSWORD=${AWS_SECRET_ACCESS_KEY:-minioadmin}
  • on appflowy_cloud, changed
    • APPFLOWY_DATABASE_URL=postgres://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-password}@${POSTGRES_HOST:-postgres}:5432/${POSTGRES_DB:-postgres}
  • disabled everything optional except for the admin. Is it really optional?

.env file:

  • changed all secrets and passwords
  • added:
    • POSTGRES_USER=postgres
    • POSTGRES_PASSWORD=password

nginx.conf: commented out /gotrue and /minio.

I also changed the credentials in the configuration directory, but i guess thats not required when the .env are used right?

Suggestions:

  • add POSTGRES_PORT as .env and to docker-compose.yml (port is currently not configurable)
  • keep minimal. comment out all optional services from docker compose and routes from the nginx config to avoid unintended exposure of services. If someone wants pgadmin or the mini console, he knows what it is and can comment it in when he needs it.
  • i have "disabled" the registration by enabling email confirmation without having a mail provider configured. Is there a different way to prevent random bots from registering on my cloud insance? If yes that should be mentioned in the documentation.
  • Add a warning to the documentation that users should change all the passwords, especially when running on a public server.

On my setup specifically, i preferred to also remove the port bindings on nginx and setup traefik to handle the routing. The advantage of this is, that traefik can be easily configured to issue a valid ssl certificate by letsencrypt and takes care of its renewal. Its nice, but its an additional service and possibly out of scope of what the appflowy docker theployment should provide. Let me know if you want to see a config example.

bhelm avatar Jan 10 '24 23:01 bhelm

@bhelm

Hello!

We really appreciate your effort to make self hosting more secure. The philosophy that we have is that the setup is super simple to get started. We expect that more experienced folks who are serious in using self-hosted AppFlowy have their own kind of custom setup, we're trying to be unopinionated as much as possible. It is difficult to reach a balance between ease of deployment, time to deploy (more texts in docs isn't necessary better), security and other factors. That being said, I believe we have rooms for improvement to make the self-hosting Docs better. We are planning to have separated docs and templates for different use cases.

Thank you for the suggestions, we'll keep that in mind. Let me address some of the points

add POSTGRES_PORT as .env and to docker-compose.yml (port is currently not configurable)

  • We will add that in.

i have "disabled" the registration by enabling email confirmation without having a mail provider configured. Is there a different way to prevent random bots from registering on my cloud insance? If yes that should be mentioned in the documentation.

  • I think it's possible, though i haven't look into it deep enough to know how to configure. GoTrue Auth service does provide some kind of "challenge" and ratelimiting for dealing with spamming.

Add a warning to the documentation that users should change all the passwords, especially when running on a public server.

  • We will add that in.

It will definitely be interesting to see another alternative router like traefik to replace nginx. Seems like it's also getting more popular these days. PRs are welcome, but's it's just not the priority at this time. However I do agree with the removal of the port bindings to host machine except for nginx. If you have more feedback or suggestions, will be happy to hear those.

Edit: Currently you can't directly select the listening port for postgres when it start via an environmental variable, you have to edit some config files to achieve that.

the admin_frontend is kinda on the edge as optional component. AppFlowy Cloud still run perfectly without it (if you got OAuth Setup). It's more of a convenience thing and for administrative purpose.

speed2exe avatar Jan 11 '24 03:01 speed2exe

@speed2exe maybe we can add nginx.conf to .gitignore, then put a nginx.conf.example nginx.conf.example: An example configuration file for nginx adhering to the principle of minimalism to prevent inadvertent exposure of services by unaware users, so default is disable admin service like: Minio, PgAdmin and Portainer

before user build Appflowy-Cloud, then need to cp nginx.conf.example to nginx.conf

ThanatosDi avatar Jan 11 '24 08:01 ThanatosDi

@ThanatosDi Thanks for the suggestion, I think nginx resolver will be better solution: https://nginx.org/en/docs/http/ngx_http_core_module.html#resolver This allow us to put an service and it will resolve the ip at run time. This also means that should a service restart, nginx can then resolve it again, without having to restart.

for the more docker-compose.yml, we'll be unexposing all the ports except for the nginx ones

speed2exe avatar Jan 12 '24 06:01 speed2exe

* i have "disabled" the registration by enabling email confirmation without having a mail provider configured. Is there a different way to prevent random bots from registering on my cloud insance? If yes that should be mentioned in the documentation.

I found an undocumented environment variable GOTRUE_DISABLE_SIGNUP which fully disables registration but it causes the appflowy_cloud container to fail to start due to it also blocking the setup_admin_account function in application.rs. There appears to be no method to prevent setup_admin_account from running so I can't see any easy ways to disable registration fully until there's an option to skip admin account creation.

Here's the log

{"timestamp":"2024-06-08T11:26:44.887744Z","level":"INFO","fields":{"message":"Connecting to GoTrue with setting: GoTrueSetting { base_url: \"http://gotrue:9999\", ext_url: \"removed\", jwt_secret: Secret([REDACTED alloc::string::String]), admin_email: \"removed\", admin_password: Secret([REDACTED alloc::string::String]) }"},"target":"appflowy_cloud::application"}
{"timestamp":"2024-06-08T11:26:44.898800Z","level":"ERROR","fields":{"error":"code: 403, msg:Signups not allowed for this instance, error_id: None"},"target":"gotrue::api","span":{"name":"sign_up"},"spans":[{"name":"sign_up"}]}
Error: Failed to initialize application state: code: 403, msg:Signups not allowed for this instance, error_id: None

I'm running nginx in front of AppFlowy for handling TLS offloading so I've put in a couple rules to help keep the situation under control

    location /web-api/signup {
        return 403;
    }

This is only effective at stopping the Sign Up button from working.

    location /gotrue/verify {
        if ($args ~ type=signup) {
            return 403;
        }
        proxy_pass http://appflowyip:80;
    }

It's also possible for a new user to be created by attempting to login with a non existent user. The application creates a user out of thin air then either sends a magic link (if auto confirm is on) or sends a confirmation e-mail. This rule is intended to be used with auto confirm enabled and prevents the e-mail address from being confirmed.

You'll still end up with random users clogging up your database and e-mails going out, but they'll never be able to gain access as any subsequent logon attempt will just generate another confirmation e-mail that they cannot confirm.

So if the developers would please make it so GOTRUE_DISABLE_SIGNUP is usable by either checking for it when running setup_admin_account or making it possible to skip admin account creation on startup, I would really appreciate it.

barelyprofessional avatar Jun 08 '24 12:06 barelyprofessional

I have gotten AppFlowy Cloud to run without dependency on the external third-party service from OpenAI and documented some steps in:

  • #622

It could be used as an inspiration to generate a production-ready compose.yml example for this repository.

almereyda avatar Jun 14 '24 13:06 almereyda