Compare commits
4 Commits
openapi-ge
...
version/20
Author | SHA1 | Date | |
---|---|---|---|
72d67f65e5 | |||
ea75741ec2 | |||
aaa9b398f4 | |||
d54d01b118 |
@ -1,5 +1,5 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 2023.8.3
|
current_version = 2023.8.4
|
||||||
tag = True
|
tag = True
|
||||||
commit = True
|
commit = True
|
||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
|
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
from os import environ
|
from os import environ
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
__version__ = "2023.8.3"
|
__version__ = "2023.8.4"
|
||||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
||||||
|
|
||||||
|
|
||||||
|
25
authentik/flows/migrations/0026_alter_flow_options.py
Normal file
25
authentik/flows/migrations/0026_alter_flow_options.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Generated by Django 4.2.6 on 2023-10-10 17:18
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("authentik_flows", "0025_alter_flowstagebinding_evaluate_on_plan_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name="flow",
|
||||||
|
options={
|
||||||
|
"permissions": [
|
||||||
|
("export_flow", "Can export a Flow"),
|
||||||
|
("inspect_flow", "Can inspect a Flow's execution"),
|
||||||
|
("view_flow_cache", "View Flow's cache metrics"),
|
||||||
|
("clear_flow_cache", "Clear Flow's cache metrics"),
|
||||||
|
],
|
||||||
|
"verbose_name": "Flow",
|
||||||
|
"verbose_name_plural": "Flows",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
34
authentik/flows/migrations/0027_auto_20231028_1424.py
Normal file
34
authentik/flows/migrations/0027_auto_20231028_1424.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# Generated by Django 4.2.6 on 2023-10-28 14:24
|
||||||
|
|
||||||
|
from django.apps.registry import Apps
|
||||||
|
from django.db import migrations
|
||||||
|
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||||
|
|
||||||
|
|
||||||
|
def set_oobe_flow_authentication(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
|
||||||
|
from guardian.shortcuts import get_anonymous_user
|
||||||
|
|
||||||
|
Flow = apps.get_model("authentik_flows", "Flow")
|
||||||
|
User = apps.get_model("authentik_core", "User")
|
||||||
|
|
||||||
|
db_alias = schema_editor.connection.alias
|
||||||
|
|
||||||
|
users = User.objects.using(db_alias).exclude(username="akadmin")
|
||||||
|
try:
|
||||||
|
users = users.exclude(pk=get_anonymous_user().pk)
|
||||||
|
# pylint: disable=broad-except
|
||||||
|
except Exception: # nosec
|
||||||
|
pass
|
||||||
|
|
||||||
|
if users.exists():
|
||||||
|
Flow.objects.filter(slug="initial-setup").update(authentication="require_superuser")
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("authentik_flows", "0026_alter_flow_options"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(set_oobe_flow_authentication),
|
||||||
|
]
|
@ -171,6 +171,8 @@ class MetadataProcessor:
|
|||||||
entity_descriptor, f"{{{NS_SAML_METADATA}}}IDPSSODescriptor"
|
entity_descriptor, f"{{{NS_SAML_METADATA}}}IDPSSODescriptor"
|
||||||
)
|
)
|
||||||
idp_sso_descriptor.attrib["protocolSupportEnumeration"] = NS_SAML_PROTOCOL
|
idp_sso_descriptor.attrib["protocolSupportEnumeration"] = NS_SAML_PROTOCOL
|
||||||
|
if self.provider.verification_kp:
|
||||||
|
idp_sso_descriptor.attrib["WantAuthnRequestsSigned"] = "true"
|
||||||
|
|
||||||
signing_descriptor = self.get_signing_key_descriptor()
|
signing_descriptor = self.get_signing_key_descriptor()
|
||||||
if signing_descriptor is not None:
|
if signing_descriptor is not None:
|
||||||
|
@ -12,7 +12,7 @@ from authentik.lib.xml import lxml_from_string
|
|||||||
from authentik.providers.saml.models import SAMLBindings, SAMLPropertyMapping, SAMLProvider
|
from authentik.providers.saml.models import SAMLBindings, SAMLPropertyMapping, SAMLProvider
|
||||||
from authentik.providers.saml.processors.metadata import MetadataProcessor
|
from authentik.providers.saml.processors.metadata import MetadataProcessor
|
||||||
from authentik.providers.saml.processors.metadata_parser import ServiceProviderMetadataParser
|
from authentik.providers.saml.processors.metadata_parser import ServiceProviderMetadataParser
|
||||||
from authentik.sources.saml.processors.constants import NS_MAP
|
from authentik.sources.saml.processors.constants import NS_MAP, NS_SAML_METADATA
|
||||||
|
|
||||||
|
|
||||||
class TestServiceProviderMetadataParser(TestCase):
|
class TestServiceProviderMetadataParser(TestCase):
|
||||||
@ -55,6 +55,24 @@ class TestServiceProviderMetadataParser(TestCase):
|
|||||||
schema = etree.XMLSchema(etree.parse("schemas/saml-schema-metadata-2.0.xsd")) # nosec
|
schema = etree.XMLSchema(etree.parse("schemas/saml-schema-metadata-2.0.xsd")) # nosec
|
||||||
self.assertTrue(schema.validate(metadata))
|
self.assertTrue(schema.validate(metadata))
|
||||||
|
|
||||||
|
def test_schema_want_authn_requests_signed(self):
|
||||||
|
"""Test metadata generation with WantAuthnRequestsSigned"""
|
||||||
|
cert = create_test_cert()
|
||||||
|
provider = SAMLProvider.objects.create(
|
||||||
|
name=generate_id(),
|
||||||
|
authorization_flow=self.flow,
|
||||||
|
verification_kp=cert,
|
||||||
|
)
|
||||||
|
Application.objects.create(
|
||||||
|
name=generate_id(),
|
||||||
|
slug=generate_id(),
|
||||||
|
provider=provider,
|
||||||
|
)
|
||||||
|
request = self.factory.get("/")
|
||||||
|
metadata = lxml_from_string(MetadataProcessor(provider, request).build_entity_descriptor())
|
||||||
|
idp_sso_descriptor = metadata.findall(f"{{{NS_SAML_METADATA}}}IDPSSODescriptor")[0]
|
||||||
|
self.assertEqual(idp_sso_descriptor.attrib["WantAuthnRequestsSigned"], "true")
|
||||||
|
|
||||||
def test_simple(self):
|
def test_simple(self):
|
||||||
"""Test simple metadata without Signing"""
|
"""Test simple metadata without Signing"""
|
||||||
metadata = ServiceProviderMetadataParser().parse(load_fixture("fixtures/simple.xml"))
|
metadata = ServiceProviderMetadataParser().parse(load_fixture("fixtures/simple.xml"))
|
||||||
|
@ -47,9 +47,11 @@ class FreeIPA(BaseLDAPSynchronizer):
|
|||||||
return
|
return
|
||||||
# For some reason, nsaccountlock is not defined properly in the schema as bool
|
# For some reason, nsaccountlock is not defined properly in the schema as bool
|
||||||
# hence we get it as a list of strings
|
# hence we get it as a list of strings
|
||||||
_is_active = str(self._flatten(attributes.get("nsaccountlock", ["FALSE"])))
|
_is_locked = str(self._flatten(attributes.get("nsaccountlock", ["FALSE"])))
|
||||||
# So we have to attempt to convert it to a bool
|
# So we have to attempt to convert it to a bool
|
||||||
is_active = _is_active.lower() == "true"
|
is_locked = _is_locked.lower() == "true"
|
||||||
|
# And then invert it since freeipa saves locked and we save active
|
||||||
|
is_active = not is_locked
|
||||||
if is_active != user.is_active:
|
if is_active != user.is_active:
|
||||||
user.is_active = is_active
|
user.is_active = is_active
|
||||||
user.save()
|
user.save()
|
||||||
|
@ -137,6 +137,7 @@ class LDAPSyncTests(TestCase):
|
|||||||
user_sync.sync_full()
|
user_sync.sync_full()
|
||||||
self.assertTrue(User.objects.filter(username="user0_sn").exists())
|
self.assertTrue(User.objects.filter(username="user0_sn").exists())
|
||||||
self.assertFalse(User.objects.filter(username="user1_sn").exists())
|
self.assertFalse(User.objects.filter(username="user1_sn").exists())
|
||||||
|
self.assertFalse(User.objects.get(username="user-nsaccountlock").is_active)
|
||||||
|
|
||||||
def test_sync_groups_ad(self):
|
def test_sync_groups_ad(self):
|
||||||
"""Test group sync"""
|
"""Test group sync"""
|
||||||
|
@ -85,6 +85,19 @@ entries:
|
|||||||
identifiers:
|
identifiers:
|
||||||
name: default-oobe-password-usable
|
name: default-oobe-password-usable
|
||||||
model: authentik_policies_expression.expressionpolicy
|
model: authentik_policies_expression.expressionpolicy
|
||||||
|
- attrs:
|
||||||
|
expression: |
|
||||||
|
# This policy ensures that the setup flow can only be
|
||||||
|
# used one time
|
||||||
|
from authentik.flows.models import Flow, FlowAuthenticationRequirement
|
||||||
|
Flow.objects.filter(slug="initial-setup").update(
|
||||||
|
authentication=FlowAuthenticationRequirement.REQUIRE_SUPERUSER,
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
id: policy-default-oobe-flow-set-authentication
|
||||||
|
identifiers:
|
||||||
|
name: default-oobe-flow-set-authentication
|
||||||
|
model: authentik_policies_expression.expressionpolicy
|
||||||
- attrs:
|
- attrs:
|
||||||
fields:
|
fields:
|
||||||
- !KeyOf prompt-field-header
|
- !KeyOf prompt-field-header
|
||||||
@ -129,6 +142,7 @@ entries:
|
|||||||
evaluate_on_plan: true
|
evaluate_on_plan: true
|
||||||
invalid_response_action: retry
|
invalid_response_action: retry
|
||||||
re_evaluate_policies: false
|
re_evaluate_policies: false
|
||||||
|
id: binding-login
|
||||||
identifiers:
|
identifiers:
|
||||||
order: 100
|
order: 100
|
||||||
stage: !KeyOf stage-default-authentication-login
|
stage: !KeyOf stage-default-authentication-login
|
||||||
@ -144,3 +158,8 @@ entries:
|
|||||||
policy: !KeyOf policy-default-oobe-prefill-user
|
policy: !KeyOf policy-default-oobe-prefill-user
|
||||||
target: !KeyOf binding-password-write
|
target: !KeyOf binding-password-write
|
||||||
model: authentik_policies.policybinding
|
model: authentik_policies.policybinding
|
||||||
|
- identifiers:
|
||||||
|
order: 0
|
||||||
|
policy: !KeyOf policy-default-oobe-flow-set-authentication
|
||||||
|
target: !KeyOf binding-login
|
||||||
|
model: authentik_policies.policybinding
|
||||||
|
@ -42,9 +42,3 @@ entries:
|
|||||||
user: !KeyOf admin-user
|
user: !KeyOf admin-user
|
||||||
attrs:
|
attrs:
|
||||||
key: !Context token
|
key: !Context token
|
||||||
- model: authentik_blueprints.blueprintinstance
|
|
||||||
identifiers:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
blueprints.goauthentik.io/system-bootstrap: "true"
|
|
||||||
state: absent
|
|
||||||
|
@ -32,7 +32,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- redis:/data
|
- redis:/data
|
||||||
server:
|
server:
|
||||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.8.3}
|
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.8.4}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: server
|
command: server
|
||||||
environment:
|
environment:
|
||||||
@ -53,7 +53,7 @@ services:
|
|||||||
- postgresql
|
- postgresql
|
||||||
- redis
|
- redis
|
||||||
worker:
|
worker:
|
||||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.8.3}
|
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.8.4}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: worker
|
command: worker
|
||||||
environment:
|
environment:
|
||||||
|
@ -29,4 +29,4 @@ func UserAgent() string {
|
|||||||
return fmt.Sprintf("authentik@%s", FullVersion())
|
return fmt.Sprintf("authentik@%s", FullVersion())
|
||||||
}
|
}
|
||||||
|
|
||||||
const VERSION = "2023.8.3"
|
const VERSION = "2023.8.4"
|
||||||
|
@ -113,7 +113,7 @@ filterwarnings = [
|
|||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "authentik"
|
name = "authentik"
|
||||||
version = "2023.8.3"
|
version = "2023.8.4"
|
||||||
description = ""
|
description = ""
|
||||||
authors = ["authentik Team <hello@goauthentik.io>"]
|
authors = ["authentik Team <hello@goauthentik.io>"]
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
openapi: 3.0.3
|
openapi: 3.0.3
|
||||||
info:
|
info:
|
||||||
title: authentik
|
title: authentik
|
||||||
version: 2023.8.3
|
version: 2023.8.4
|
||||||
description: Making authentication simple.
|
description: Making authentication simple.
|
||||||
contact:
|
contact:
|
||||||
email: hello@goauthentik.io
|
email: hello@goauthentik.io
|
||||||
|
@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
|
|||||||
export const ERROR_CLASS = "pf-m-danger";
|
export const ERROR_CLASS = "pf-m-danger";
|
||||||
export const PROGRESS_CLASS = "pf-m-in-progress";
|
export const PROGRESS_CLASS = "pf-m-in-progress";
|
||||||
export const CURRENT_CLASS = "pf-m-current";
|
export const CURRENT_CLASS = "pf-m-current";
|
||||||
export const VERSION = "2023.8.3";
|
export const VERSION = "2023.8.4";
|
||||||
export const TITLE_DEFAULT = "authentik";
|
export const TITLE_DEFAULT = "authentik";
|
||||||
export const ROUTE_SEPARATOR = ";";
|
export const ROUTE_SEPARATOR = ";";
|
||||||
|
|
||||||
|
@ -155,6 +155,10 @@ image:
|
|||||||
- web: don't import entire SourceViewPage in flow and user interface (#6761)
|
- web: don't import entire SourceViewPage in flow and user interface (#6761)
|
||||||
- web: replace ampersand (#6737)
|
- web: replace ampersand (#6737)
|
||||||
|
|
||||||
|
## Fixed in 2023.8.4
|
||||||
|
|
||||||
|
- \*: fix [GHSA-rjvp-29xq-f62w](../security/GHSA-rjvp-29xq-f62w), Reported by [@devSparkle](https://github.com/devSparkle)
|
||||||
|
|
||||||
## API Changes
|
## API Changes
|
||||||
|
|
||||||
#### What's New
|
#### What's New
|
||||||
|
27
website/docs/security/GHSA-rjvp-29xq-f62w.md
Normal file
27
website/docs/security/GHSA-rjvp-29xq-f62w.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# GHSA-rjvp-29xq-f62w
|
||||||
|
|
||||||
|
_Reported by [@devSparkle](https://github.com/devSparkle)_
|
||||||
|
|
||||||
|
## Potential Installation takeover when default admin user is deleted
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
In the affected versions, when the default admin user has been deleted, it is potentially possible for an attacker to set the password of the default admin user without any authentication.
|
||||||
|
|
||||||
|
### Patches
|
||||||
|
|
||||||
|
authentik 2023.8.4 and 2023.10.2 fix this issue, for other versions the workaround can be used.
|
||||||
|
|
||||||
|
### Impact
|
||||||
|
|
||||||
|
authentik uses a blueprint to create the default admin user, which can also optionally set the default admin users' password from an environment variable. When the user is deleted, the `initial-setup` flow used to configure authentik after the first installation becomes available again.
|
||||||
|
|
||||||
|
### Workarounds
|
||||||
|
|
||||||
|
Ensure the default admin user (Username `akadmin`) exists and has a password set. It is recommended to use a very strong password for this user, and store it in a secure location like a password manager. It is also possible to deactivate the user to prevent any logins as akadmin.
|
||||||
|
|
||||||
|
### For more information
|
||||||
|
|
||||||
|
If you have any questions or comments about this advisory:
|
||||||
|
|
||||||
|
- Email us at [security@goauthentik.io](mailto:security@goauthentik.io)
|
@ -362,6 +362,7 @@ const docsSidebar = {
|
|||||||
},
|
},
|
||||||
items: [
|
items: [
|
||||||
"security/policy",
|
"security/policy",
|
||||||
|
"security/GHSA-rjvp-29xq-f62w",
|
||||||
"security/CVE-2023-39522",
|
"security/CVE-2023-39522",
|
||||||
"security/CVE-2023-36456",
|
"security/CVE-2023-36456",
|
||||||
"security/2023-06-cure53",
|
"security/2023-06-cure53",
|
||||||
|
Reference in New Issue
Block a user