Authentik update resillience: overwrite default blueprints

This commit is contained in:
Moritz 2023-03-22 17:26:25 +01:00
parent d738d95909
commit deb953a94f
10 changed files with 527 additions and 8 deletions

View File

@ -26,6 +26,7 @@
* `abra app secret generate -a <app_name>`
* `abra app deploy <app-name>`
* `abra app cmd <app_name> app set_admin_pass`
* `abra app cmd <app_name> worker apply_blueprints`
## Rotate Secrets
@ -60,4 +61,46 @@ Run this command after every deploy/upgrade:
`abra app command --local <app-name> customize <assets_path>`
## Blueprints
Blueprint Dependency Requirements:
- Recovery with email verification
- Default - Password change flow
- Default - Authentication flow
- Custom Authentication Flow
- Default - Authentication flow
- Recovery with email verification
- Invitation Enrollment Flow
- Default - User settings flow
- Default - Authentication flow
- Default - Source enrollment flow
- Custom Invalidation Flow
- Default - Invalidation flow
- Flow Translations
- Recovery with email verification
- Default - Password change flow
- Default - User settings flow
- Default - Source enrollment flow
- Custom System Tenant
- Default - Tenant
- Recovery with email verification
Blueprint Dependency Graph:
5. Custom System Tenant
- Default - Tenant
4. Invitation Enrollment Flow
3. Flow Translations
- Default - User settings flow
- Default - Source enrollment flow
2. Custom Authentication Flow
1. Recovery with email verification
- Default - Authentication flow
- Default - Password change flow
6. Custom Invalidation Flow
- Default - Invalidation flow
For more, see [`docs.coopcloud.tech`](https://docs.coopcloud.tech).

60
abra.sh
View File

@ -1,5 +1,10 @@
export CUSTOM_CSS_VERSION=v2
export CUSTOM_FLOWS_VERSION=v5
export FLOW_AUTHENTICATION_VERSION=v1
export FLOW_INVITATION_VERSION=v1
export FLOW_INVALIDATION_VERSION=v1
export FLOW_RECOVERY_VERSION=v1
export FLOW_TRANSLATION_VERSION=v1
export SYSTEM_TENANT_VERSION=v1
customize() {
if [ -z "$1" ]
@ -47,3 +52,56 @@ rotate_db_pass() {
db_password=$(cat /run/secrets/db_password)
psql -U authentik -c """ALTER USER authentik WITH PASSWORD '$db_password';"""
}
apply_blueprints() {
enable_blueprint default/flow-default-authentication-flow.yaml
enable_blueprint default/flow-default-user-settings-flow.yaml
enable_blueprint default/flow-password-change.yaml
ak apply_blueprint 6_flow_invalidation.yaml
ak apply_blueprint 5_system_tenant.yaml
disable_blueprint default/flow-default-authentication-flow.yaml
disable_blueprint default/flow-default-user-settings-flow.yaml
disable_blueprint default/flow-password-change.yaml
}
disable_blueprint() {
blueprint_state False $@
}
enable_blueprint() {
blueprint_state True $@
}
blueprint_state() {
TOKEN=$(cat /run/secrets/admin_token)
python -c """
import requests
session = requests.Session()
my_token='$TOKEN'
blueprint_state=$1
blueprint_path='$2'
resp = session.get(f'https://$DOMAIN/api/v3/managed/blueprints/?path={blueprint_path}', headers={'Authorization':f'Bearer {my_token}'})
if not resp.ok:
print(f'Error fetching blueprint: {resp.content}')
exit()
auth_flow_uuid = resp.json()['results'][0]['pk']
blueprint_name = resp.json()['results'][0]['name']
params = {'name': blueprint_name,'path': blueprint_path,'context':{},'enabled': blueprint_state}
resp = session.put(f'https://$DOMAIN/api/v3/managed/blueprints/{auth_flow_uuid}/', json=params, headers={'Authorization':f'Bearer {my_token}'})
if resp.ok:
print(f'{blueprint_name} enabled: {blueprint_state}')
else:
print(f'Error changing blueprint state: {resp.content}')
"""
}
blueprint_cleanup() {
/manage.py shell -c """
delete_flows = ['default-recovery-flow' , 'custom-authentication-flow' , 'invitation-enrollment-flow' , 'initial-setup']
Flow.objects.filter(slug__in=delete_flows).delete()
Stage.objects.filter(flow=None).delete()
Prompt.objects.filter(promptstage=None).delete()
"""
apply_blueprints
}

View File

@ -91,11 +91,20 @@ services:
- media:/media
- /var/run/docker.sock:/var/run/docker.sock
- custom-templates:/templates
- /dev/null:/blueprints/default/flow-default-authentication-flow.yaml
- /dev/null:/blueprints/default/flow-default-invalidation-flow.yaml
- /dev/null:/blueprints/default/flow-oobe.yaml
configs:
- source: custom_flows
target: /blueprints/custom_flows.yaml
- source: flow_recovery
target: /blueprints/1_flow_recovery.yaml
- source: flow_authentication
target: /blueprints/2_flow_authentication.yaml
- source: flow_translation
target: /blueprints/3_flow_translation.yaml
- source: flow_invitation
target: /blueprints/4_flow_invitation.yaml
- source: system_tenant
target: /blueprints/5_system_tenant.yaml
- source: flow_invalidation
target: /blueprints/6_flow_invalidation.yaml
environment: *env
db:
@ -167,7 +176,27 @@ configs:
name: ${STACK_NAME}_custom_css_${CUSTOM_CSS_VERSION}
file: custom.css.tmpl
template_driver: golang
custom_flows:
name: ${STACK_NAME}_custom_flows_${CUSTOM_FLOWS_VERSION}
file: custom_flows.yaml.tmpl
flow_authentication:
name: ${STACK_NAME}_flow_authentication_${FLOW_AUTHENTICATION_VERSION}
file: flow_authentication.yaml.tmpl
template_driver: golang
flow_invitation:
name: ${STACK_NAME}_flow_invitation_${FLOW_INVITATION_VERSION}
file: flow_invitation.yaml.tmpl
template_driver: golang
flow_invalidation:
name: ${STACK_NAME}_flow_invalidation_${FLOW_INVALIDATION_VERSION}
file: flow_invalidation.yaml.tmpl
template_driver: golang
flow_recovery:
name: ${STACK_NAME}_flow_recovery_${FLOW_RECOVERY_VERSION}
file: flow_recovery.yaml.tmpl
template_driver: golang
flow_translation:
name: ${STACK_NAME}_flow_translation_${FLOW_TRANSLATION_VERSION}
file: flow_translation.yaml.tmpl
template_driver: golang
system_tenant:
name: ${STACK_NAME}_system_tenant_${SYSTEM_TENANT_VERSION}
file: system_tenant.yaml.tmpl
template_driver: golang

View File

@ -0,0 +1,45 @@
version: 1
metadata:
labels:
blueprints.goauthentik.io/instantiate: "true"
name: Custom Authentication Flow
context:
welcome_message: {{ if eq (env "WELCOME_MESSAGE") "" }} "Welcome to authentik!" {{ else }} {{ env "WELCOME_MESSAGE" }} {{ end }}
entries:
### DEPENDENCIES
- model: authentik_blueprints.metaapplyblueprint
attrs:
identifiers:
name: Recovery with email verification
required: true
### FLOW
- model: authentik_flows.flow
identifiers:
slug: default-authentication-flow
id: flow
attrs:
name: !Context welcome_message
title: !Context welcome_message
### STAGES
- identifiers:
name: default-authentication-identification
model: authentik_stages_identification.identificationstage
attrs:
password_stage: !Find [authentik_stages_password.passwordstage, [name, default-authentication-password]]
recovery_flow: !Find [authentik_flows.flow, [slug, default-recovery-flow]]
- identifiers:
name: default-authentication-login
model: authentik_stages_user_login.userloginstage
attrs:
session_duration: seconds=0
- identifiers:
order: 20
stage: !Find [authentik_stages_password.passwordstage, [name, default-authentication-password]]
target: !KeyOf flow
model: authentik_flows.flowstagebinding
state: absent

View File

@ -0,0 +1,44 @@
version: 1
metadata:
labels:
blueprints.goauthentik.io/instantiate: "true"
name: Custom Invalidation Flow
entries:
### DEPENDENCIES
- model: authentik_blueprints.metaapplyblueprint
attrs:
identifiers:
name: Default - Invalidation flow
required: true
### STAGE BINDINGS
- identifiers:
order: 0
stage: !Find [authentik_stages_user_logout.userlogoutstage, [name, default-invalidation-logout]]
target: !Find [authentik_flows.flow, [slug, default-invalidation-flow]]
model: authentik_flows.flowstagebinding
attrs:
re_evaluate_policies: true
id: logout-stage-binding
### POLICIES
- attrs:
execution_logging: true
expression: 'context[''flow_plan''].context[''redirect''] = ''{{ env "LOGOUT_REDIRECT" }}''
return True'
identifiers:
name: redirect-policy
id: redirect-policy
model: authentik_policies_expression.expressionpolicy
### POLICY BINDINGS
- identifiers:
policy: !KeyOf redirect-policy
target: !KeyOf logout-stage-binding
order: 0
model: authentik_policies.policybinding
attrs:
enabled: {{ if eq (env "LOGOUT_REDIRECT") "" }} false {{ else }} true {{ end }}
timeout: 30

65
flow_invitation.yaml.tmpl Normal file
View File

@ -0,0 +1,65 @@
version: 1
metadata:
labels:
blueprints.goauthentik.io/instantiate: "true"
name: Invitation Enrollment Flow
context:
welcome_message: {{ if eq (env "WELCOME_MESSAGE") "" }} "Welcome to authentik!" {{ else }} {{ env "WELCOME_MESSAGE" }} {{ end }}
entries:
### DEPENDENCIES
- model: authentik_blueprints.metaapplyblueprint
attrs:
identifiers:
name: Flow Translations
required: true
### FLOW
- attrs:
designation: enrollment
name: invitation-enrollment-flow
title: !Context welcome_message
identifiers:
slug: invitation-enrollment-flow
id: invitation-enrollment-flow
model: authentik_flows.flow
### STAGES
- identifiers:
name: invitation-stage
id: invitation-stage
model: authentik_stages_invitation.invitationstage
- identifiers:
name: enrollment-prompt-userdata
id: enrollment-prompt-userdata
model: authentik_stages_prompt.promptstage
attrs:
fields:
- !Find [authentik_stages_prompt.prompt, [name, default-source-enrollment-field-username]]
- !Find [authentik_stages_prompt.prompt, [name, default-user-settings-field-name]]
- !Find [authentik_stages_prompt.prompt, [name, default-user-settings-field-email]]
- !Find [authentik_stages_prompt.prompt, [name, default-password-change-field-password]]
- !Find [authentik_stages_prompt.prompt, [name, default-password-change-field-password-repeat]]
### STAGE BINDINGS
- identifiers:
order: 1
stage: !KeyOf invitation-stage
target: !KeyOf invitation-enrollment-flow
model: authentik_flows.flowstagebinding
- identifiers:
order: 10
stage: !KeyOf enrollment-prompt-userdata
target: !KeyOf invitation-enrollment-flow
model: authentik_flows.flowstagebinding
- identifiers:
order: 20
stage: !Find [authentik_stages_user_write.userwritestage, [name, default-source-enrollment-write]]
target: !KeyOf invitation-enrollment-flow
model: authentik_flows.flowstagebinding
- identifiers:
order: 100
stage: !Find [authentik_stages_user_login.userloginstage, [name, default-authentication-login]]
target: !KeyOf invitation-enrollment-flow
model: authentik_flows.flowstagebinding

128
flow_recovery.yaml.tmpl Normal file
View File

@ -0,0 +1,128 @@
version: 1
metadata:
labels:
blueprints.goauthentik.io/instantiate: "true"
name: Recovery with email verification
context:
token_expiry: {{ if eq (env "EMAIL_TOKEN_EXPIRY_MINUTES") "" }} 30 {{ else }} {{ env "EMAIL_TOKEN_EXPIRY_MINUTES" }} {{ end }}
subject: {{ if eq (env "EMAIL_SUBJECT") "" }} Account Recovery {{ else }} {{ env "EMAIL_SUBJECT" }} {{ end }}
entries:
### DEPENDENCIES
- model: authentik_blueprints.metaapplyblueprint
attrs:
identifiers:
name: Default - Authentication flow
required: true
### FLOW
- identifiers:
slug: default-recovery-flow
model: authentik_flows.flow
state: created
attrs:
name: Default recovery flow
title: Reset your password
designation: recovery
authentication: require_unauthenticated
### STAGES
- identifiers:
name: default-recovery-email
id: default-recovery-email
model: authentik_stages_email.emailstage
attrs:
use_global_settings: true
token_expiry: !Context token_expiry
subject: !Context subject
template: email/password_reset.html
activate_user_on_success: true
- identifiers:
name: default-recovery-identification
id: default-recovery-identification
model: authentik_stages_identification.identificationstage
attrs:
user_fields:
- email
- username
### STAGE BINDINGS
- identifiers:
target: !Find [authentik_flows.flow, [slug, default-recovery-flow]]
stage: !KeyOf default-recovery-identification
order: 10
model: authentik_flows.flowstagebinding
id: flow-binding-identification
attrs:
evaluate_on_plan: true
re_evaluate_policies: true
policy_engine_mode: any
invalid_response_action: retry
- identifiers:
target: !Find [authentik_flows.flow, [slug, default-recovery-flow]]
stage: !KeyOf default-recovery-email
order: 20
model: authentik_flows.flowstagebinding
id: flow-binding-email
attrs:
evaluate_on_plan: true
re_evaluate_policies: true
policy_engine_mode: any
invalid_response_action: retry
- identifiers:
target: !Find [authentik_flows.flow, [slug, default-recovery-flow]]
stage: !Find [authentik_stages_prompt.promptstage, [name, default-password-change-prompt]]
order: 30
model: authentik_flows.flowstagebinding
attrs:
evaluate_on_plan: true
re_evaluate_policies: false
policy_engine_mode: any
invalid_response_action: retry
- identifiers:
target: !Find [authentik_flows.flow, [slug, default-recovery-flow]]
stage: !Find [authentik_stages_user_write.userwritestage, [name, default-password-change-write]]
order: 40
model: authentik_flows.flowstagebinding
attrs:
evaluate_on_plan: true
re_evaluate_policies: false
policy_engine_mode: any
invalid_response_action: retry
- identifiers:
target: !Find [authentik_flows.flow, [slug, default-recovery-flow]]
stage: !Find [authentik_stages_user_login.userloginstage, [name, default-authentication-login]]
order: 100
model: authentik_flows.flowstagebinding
attrs:
evaluate_on_plan: true
re_evaluate_policies: false
policy_engine_mode: any
invalid_response_action: retry
### POLICIES
- identifiers:
name: default-recovery-skip-if-restored
id: default-recovery-skip-if-restored
model: authentik_policies_expression.expressionpolicy
attrs:
expression: |
return request.context.get('is_restored', False)
- identifiers:
policy: !KeyOf default-recovery-skip-if-restored
target: !KeyOf flow-binding-identification
order: 0
model: authentik_policies.policybinding
attrs:
negate: false
enabled: false # TODO: why does this doesn't work?
timeout: 30
- identifiers:
policy: !KeyOf default-recovery-skip-if-restored
target: !KeyOf flow-binding-email
order: 0
state: absent
model: authentik_policies.policybinding
attrs:
negate: false
enabled: true
timeout: 30

View File

@ -0,0 +1,71 @@
version: 1
metadata:
labels:
blueprints.goauthentik.io/instantiate: "true"
name: Flow Translations
context:
transl_recovery: {{ if eq (env "DEFAULT_LANGUAGE") "de" }} "Passwort Zurücksetzen" {{ else }} "Reset your password" {{ end }}
transl_password: {{ if eq (env "DEFAULT_LANGUAGE") "de" }} "Passwort" {{ else }} "Password" {{ end }}
transl_password_repeat: {{ if eq (env "DEFAULT_LANGUAGE") "de" }} "Passwort (wiederholen)" {{ else }} "Password (repeat)" {{ end }}
transl_username: {{ if eq (env "DEFAULT_LANGUAGE") "de" }} "Benutzername" {{ else }} "Username" {{ end }}
transl_name: {{ if eq (env "DEFAULT_LANGUAGE") "de" }} "Vor- und Nachname" {{ else }} "Full name" {{ end }}
entries:
### DEPENDENCIES
- model: authentik_blueprints.metaapplyblueprint
attrs:
identifiers:
name: Custom Authentication Flow
required: true
- model: authentik_blueprints.metaapplyblueprint
attrs:
identifiers:
name: Default - User settings flow
required: true
- model: authentik_blueprints.metaapplyblueprint
attrs:
identifiers:
name: Default - Source enrollment flow
required: true
### FLOWS
- model: authentik_flows.flow
identifiers:
slug: default-recovery-flow
id: recovery_flow
model: authentik_flows.flow
attrs:
name: Default recovery flow
title: !Context transl_recovery
designation: recovery
### PROMPTS
- model: authentik_stages_prompt.prompt
identifiers:
name: default-password-change-field-password
attrs:
label: !Context transl_password
placeholder: !Context transl_password
- model: authentik_stages_prompt.prompt
identifiers:
name: default-password-change-field-password-repeat
attrs:
label: !Context transl_password_repeat
placeholder: !Context transl_password_repeat
- model: authentik_stages_prompt.prompt
identifiers:
name: default-user-settings-field-username
attrs:
label: !Context transl_username
- model: authentik_stages_prompt.prompt
identifiers:
name: default-user-settings-field-name
attrs:
label: !Context transl_name
- model: authentik_stages_prompt.prompt
identifiers:
name: default-source-enrollment-field-username
attrs:
label: !Context transl_username
placeholder: !Context transl_username

1
release/next Normal file
View File

@ -0,0 +1 @@
Run `abra app cmd <app_name> worker blueprint_cleanup` to apply the new blueprint configuration and delete the old configuration.

35
system_tenant.yaml.tmpl Normal file
View File

@ -0,0 +1,35 @@
version: 1
metadata:
labels:
blueprints.goauthentik.io/instantiate: "true"
name: Custom System Tenant
entries:
### DEPENDENCIES
- model: authentik_blueprints.metaapplyblueprint
attrs:
identifiers:
name: Default - Tenant
required: true
- model: authentik_blueprints.metaapplyblueprint
attrs:
identifiers:
name: Invitation Enrollment Flow
required: true
### SYSTEM TENANT
# remove custom tenant from old recipe
- identifiers:
domain: {{ env "DOMAIN" }}
model: authentik_tenants.tenant
state: absent
- attrs:
attributes:
settings:
locale: {{ if eq (env "DEFAULT_LANGUAGE") "" }} en {{ else }} {{ env "DEFAULT_LANGUAGE" }} {{ end }}
flow_recovery: !Find [authentik_flows.flow, [slug, default-recovery-flow]]
identifiers:
default: true
domain: authentik-default
model: authentik_tenants.tenant