Compare commits
2
Commits
0.1.0+3.2.0
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd8a5d1541 | ||
|
|
5295915dff |
+8
-3
@@ -16,13 +16,18 @@ COMPOSE_FILE="compose.yml"
|
||||
SECRET_DB_PASSWORD_VERSION=v1 # charset=default
|
||||
# SECRET_KEY_BASE, must be at least 32 bytes
|
||||
SECRET_KEY_BASE_VERSION=v1 # length=64
|
||||
# Password for the seeded admin@claper.co account, applied with
|
||||
# `abra app cmd <app-name> app set_admin_password`
|
||||
SECRET_ADMIN_PASS_VERSION=v1
|
||||
# Initial password of the ADMIN_EMAIL account, see ADMIN_EMAIL below
|
||||
SECRET_ADMIN_PASS_VERSION=v1 # length=32
|
||||
|
||||
##############################################################################
|
||||
# CLAPER
|
||||
##############################################################################
|
||||
# Account that is ensured to exist and be an admin, checked on every start.
|
||||
# It is created with the admin_pass secret as password; later password changes
|
||||
# are done in the UI or with `abra app cmd <app-name> app set_admin_password`.
|
||||
# Changing it adds a new admin, the previous one is kept.
|
||||
ADMIN_EMAIL=admin@claper.example.com
|
||||
|
||||
# See https://docs.claper.co/self-hosting/configuration.html
|
||||
#ENABLE_ACCOUNT_CREATION=true
|
||||
#EMAIL_CONFIRMATION=false
|
||||
|
||||
@@ -18,16 +18,38 @@
|
||||
## Quick start
|
||||
|
||||
* `abra app new claper --secrets`
|
||||
* `abra app config <app-name>`
|
||||
* `abra app config <app-name>`, set `ADMIN_EMAIL`
|
||||
* `abra app deploy <app-name>`
|
||||
* Log in as `ADMIN_EMAIL` with the `admin_pass` secret printed by
|
||||
`abra app new --secrets`
|
||||
|
||||
## Admin account
|
||||
|
||||
On every start of the `app` container, the account `ADMIN_EMAIL` is ensured to
|
||||
exist and to be an admin:
|
||||
|
||||
* if it doesn't exist, it is created with the `admin_pass` secret as password
|
||||
* if it exists without admin role, it is promoted to admin
|
||||
* if it already is an admin, nothing changes, the password is never overwritten
|
||||
|
||||
Changing `ADMIN_EMAIL` and redeploying adds a new admin; the previous admin is
|
||||
kept and has to be demoted or deleted in the admin panel if it isn't needed
|
||||
anymore.
|
||||
|
||||
To reset the admin password to the `admin_pass` secret, e.g. after rotating it:
|
||||
|
||||
* `abra app secret rm <app-name> admin_pass` and
|
||||
`abra app secret insert <app-name> admin_pass v1 <password>`, or bump
|
||||
`SECRET_ADMIN_PASS_VERSION` and `abra app secret generate <app-name> admin_pass`
|
||||
* `abra app deploy <app-name> --force`
|
||||
* `abra app cmd <app-name> app set_admin_password`
|
||||
|
||||
On first start Claper creates the admin account `admin@claper.co` with the
|
||||
password `claper`. `set_admin_password` replaces it with the `admin_pass`
|
||||
secret printed by `abra app new --secrets`. Do this right after the first
|
||||
deploy, the default password is public.
|
||||
Without `ADMIN_EMAIL`, Claper's own seeds create `admin@claper.co` with the
|
||||
public password `claper` on an empty database. Change it right away, e.g. by
|
||||
setting `ADMIN_EMAIL=admin@claper.co` and running `set_admin_password`.
|
||||
|
||||
Database migrations and seeds run on every start of the `app` container.
|
||||
Database migrations, the admin bootstrap and seeds run on every start of the
|
||||
`app` container.
|
||||
|
||||
## Email
|
||||
|
||||
@@ -59,9 +81,18 @@ SECRET_OIDC_SECRET_VERSION=v1
|
||||
* `abra app secret insert <app-name> oidc_secret v1 <client-secret>`
|
||||
* `abra app deploy <app-name> --force`
|
||||
|
||||
To allow only OIDC logins (`DISABLE_PASSWORD_LOGIN=true`), first log in once as
|
||||
`admin@claper.co`, log in once via OIDC with the account that should be the
|
||||
admin, promote it to admin in the admin panel, and only then enable the option.
|
||||
To log in as admin via OIDC, set `ADMIN_EMAIL` to the email address of the admin
|
||||
at the provider. The first OIDC login with that address is linked to the
|
||||
existing admin account:
|
||||
|
||||
* directly, if `OIDC_TRUST_EMAIL_VERIFIED=true` and the provider marks the email
|
||||
as verified
|
||||
* otherwise after clicking a confirmation link that Claper mails to that
|
||||
address, which needs [email](#email) to be set up
|
||||
|
||||
With this, `DISABLE_PASSWORD_LOGIN=true` (only OIDC logins) can be enabled from
|
||||
the first deploy on. The `admin_pass` secret is still required, but not used for
|
||||
logging in anymore.
|
||||
|
||||
## S3 storage
|
||||
|
||||
|
||||
@@ -2,18 +2,20 @@
|
||||
# Docs: https://docs.coopcloud.tech/maintainers/handbook/#manage-configs
|
||||
export ABRA_ENTRYPOINT_VERSION=v1
|
||||
export PG_BACKUP_VERSION=v1
|
||||
export BOOTSTRAP_ADMIN_VERSION=v1
|
||||
|
||||
# Replace the password of the seeded admin@claper.co account with the
|
||||
# admin_pass secret.
|
||||
# Reset the password of the ADMIN_EMAIL account to the admin_pass secret.
|
||||
# Usage: abra app cmd <app-name> app set_admin_password
|
||||
set_admin_password() {
|
||||
/app/bin/claper rpc '
|
||||
email = System.get_env("ADMIN_EMAIL", "") |> String.trim()
|
||||
pw = File.read!("/run/secrets/admin_pass") |> String.trim()
|
||||
case Claper.Accounts.get_user_by_email("admin@claper.co") do
|
||||
nil -> IO.puts("admin@claper.co does not exist, nothing to do")
|
||||
case email != "" && Claper.Accounts.get_user_by_email(email) do
|
||||
false -> IO.puts("ADMIN_EMAIL is not set, nothing to do")
|
||||
nil -> IO.puts("#{email} does not exist, nothing to do")
|
||||
user ->
|
||||
{:ok, _} = Claper.Accounts.reset_user_password(user, %{password: pw, password_confirmation: pw})
|
||||
IO.puts("Password of admin@claper.co updated")
|
||||
IO.puts("Password of #{email} updated")
|
||||
end
|
||||
'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# Ensures that the account ADMIN_EMAIL exists and is an admin. Runs on every
|
||||
# start, between the migrations and the seeds:
|
||||
# - missing account: created with the admin_pass secret as password
|
||||
# - account without admin role: promoted to admin
|
||||
# - existing admin: left untouched, the password is never overwritten
|
||||
#
|
||||
# Because the account exists before the seeds run, the seeds never create the
|
||||
# default admin@claper.co/claper account.
|
||||
# Without ADMIN_EMAIL nothing is done and the upstream seeds apply.
|
||||
|
||||
alias Claper.Accounts
|
||||
alias Claper.Accounts.Role
|
||||
alias Claper.Repo
|
||||
|
||||
case System.get_env("ADMIN_EMAIL", "") |> String.trim() do
|
||||
"" ->
|
||||
IO.puts("ADMIN_EMAIL not set, skipping admin bootstrap")
|
||||
|
||||
email ->
|
||||
Application.load(:claper)
|
||||
|
||||
{:ok, _, _} =
|
||||
Ecto.Migrator.with_repo(Repo, fn _repo ->
|
||||
# The roles are normally created by the seeds, which run after this.
|
||||
admin_role =
|
||||
Repo.get_by(Role, name: "admin") ||
|
||||
Repo.insert!(%Role{name: "admin", permissions: %{"all" => true}})
|
||||
|
||||
Repo.get_by(Role, name: "user") || Repo.insert!(%Role{name: "user", permissions: %{}})
|
||||
|
||||
case Accounts.get_user_by_email(email) do
|
||||
nil ->
|
||||
pw = File.read!("/run/secrets/admin_pass") |> String.trim()
|
||||
|
||||
case Accounts.register_user(%{
|
||||
email: email,
|
||||
first_name: "Claper",
|
||||
last_name: "Admin",
|
||||
password: pw,
|
||||
role_id: admin_role.id,
|
||||
confirmed_at: NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
|
||||
}) do
|
||||
{:ok, _} ->
|
||||
IO.puts("Created admin #{email} with the admin_pass secret as password")
|
||||
|
||||
{:error, changeset} ->
|
||||
raise "Could not create admin #{email}: #{inspect(changeset.errors)}"
|
||||
end
|
||||
|
||||
user ->
|
||||
if user.role_id == admin_role.id do
|
||||
IO.puts("Admin #{email} exists, nothing to do")
|
||||
else
|
||||
{:ok, _} = Accounts.assign_role(user, admin_role)
|
||||
IO.puts("Promoted #{email} to admin")
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
+8
-2
@@ -18,11 +18,13 @@ services:
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- "/app/bin/claper eval 'Claper.Release.migrate(); Claper.Release.seeds()' && exec /app/bin/claper start"
|
||||
- "/app/bin/claper eval 'Claper.Release.migrate(); Code.eval_file(\"/bootstrap_admin.exs\"); Claper.Release.seeds()' && exec /app/bin/claper start"
|
||||
configs:
|
||||
- source: abra_entrypoint
|
||||
target: /abra-entrypoint.sh
|
||||
mode: 0555
|
||||
- source: bootstrap_admin
|
||||
target: /bootstrap_admin.exs
|
||||
secrets:
|
||||
- db_password
|
||||
- admin_pass
|
||||
@@ -30,6 +32,7 @@ services:
|
||||
target: SECRET_KEY_BASE
|
||||
environment:
|
||||
- STACK_NAME
|
||||
- ADMIN_EMAIL
|
||||
- BASE_URL=https://${DOMAIN}
|
||||
- SECURE_COOKIE=true
|
||||
- SAME_SITE_COOKIE
|
||||
@@ -66,7 +69,7 @@ services:
|
||||
- "traefik.http.routers.${STACK_NAME}.rule=Host(`${DOMAIN}`${EXTRA_DOMAINS})"
|
||||
- "traefik.http.routers.${STACK_NAME}.entrypoints=web-secure"
|
||||
- "traefik.http.routers.${STACK_NAME}.tls.certresolver=${LETS_ENCRYPT_ENV}"
|
||||
- "coop-cloud.${STACK_NAME}.version=0.1.0+3.2.0"
|
||||
- "coop-cloud.${STACK_NAME}.version=0.1.1+3.2.0"
|
||||
- "coop-cloud.${STACK_NAME}.timeout=${TIMEOUT:-300}"
|
||||
- "backupbot.backup=${ENABLE_BACKUPS:-true}"
|
||||
healthcheck:
|
||||
@@ -124,6 +127,9 @@ configs:
|
||||
abra_entrypoint:
|
||||
name: ${STACK_NAME}_entrypoint_${ABRA_ENTRYPOINT_VERSION}
|
||||
file: abra-entrypoint.sh
|
||||
bootstrap_admin:
|
||||
name: ${STACK_NAME}_bootstrap_admin_${BOOTSTRAP_ADMIN_VERSION}
|
||||
file: bootstrap_admin.exs
|
||||
pg_backup:
|
||||
name: ${STACK_NAME}_pg_backup_${PG_BACKUP_VERSION}
|
||||
file: pg_backup.sh
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
The admin account is now created from the new ADMIN_EMAIL variable, with the
|
||||
admin_pass secret as initial password. The public default admin@claper.co /
|
||||
claper account is no longer created on new installs, and running
|
||||
`abra app cmd <app-name> app set_admin_password` after deploying is not needed
|
||||
anymore. Existing installs without ADMIN_EMAIL are unchanged; set it to make
|
||||
sure that account exists and is an admin. See the README for details.
|
||||
Reference in New Issue
Block a user