Compare commits
	
		
			4 Commits
		
	
	
		
			version/20
			...
			version/20
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 72d67f65e5 | |||
| ea75741ec2 | |||
| aaa9b398f4 | |||
| d54d01b118 | 
@ -1,5 +1,5 @@
 | 
			
		||||
[bumpversion]
 | 
			
		||||
current_version = 2023.8.3
 | 
			
		||||
current_version = 2023.8.4
 | 
			
		||||
tag = True
 | 
			
		||||
commit = True
 | 
			
		||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@
 | 
			
		||||
from os import environ
 | 
			
		||||
from typing import Optional
 | 
			
		||||
 | 
			
		||||
__version__ = "2023.8.3"
 | 
			
		||||
__version__ = "2023.8.4"
 | 
			
		||||
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"
 | 
			
		||||
        )
 | 
			
		||||
        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()
 | 
			
		||||
        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.processors.metadata import MetadataProcessor
 | 
			
		||||
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):
 | 
			
		||||
@ -55,6 +55,24 @@ class TestServiceProviderMetadataParser(TestCase):
 | 
			
		||||
        schema = etree.XMLSchema(etree.parse("schemas/saml-schema-metadata-2.0.xsd"))  # nosec
 | 
			
		||||
        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):
 | 
			
		||||
        """Test simple metadata without Signing"""
 | 
			
		||||
        metadata = ServiceProviderMetadataParser().parse(load_fixture("fixtures/simple.xml"))
 | 
			
		||||
 | 
			
		||||
@ -47,9 +47,11 @@ class FreeIPA(BaseLDAPSynchronizer):
 | 
			
		||||
            return
 | 
			
		||||
        # For some reason, nsaccountlock is not defined properly in the schema as bool
 | 
			
		||||
        # 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
 | 
			
		||||
        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:
 | 
			
		||||
            user.is_active = is_active
 | 
			
		||||
            user.save()
 | 
			
		||||
 | 
			
		||||
@ -137,6 +137,7 @@ class LDAPSyncTests(TestCase):
 | 
			
		||||
            user_sync.sync_full()
 | 
			
		||||
            self.assertTrue(User.objects.filter(username="user0_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):
 | 
			
		||||
        """Test group sync"""
 | 
			
		||||
 | 
			
		||||
@ -85,6 +85,19 @@ entries:
 | 
			
		||||
  identifiers:
 | 
			
		||||
    name: default-oobe-password-usable
 | 
			
		||||
  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:
 | 
			
		||||
    fields:
 | 
			
		||||
    - !KeyOf prompt-field-header
 | 
			
		||||
@ -129,6 +142,7 @@ entries:
 | 
			
		||||
    evaluate_on_plan: true
 | 
			
		||||
    invalid_response_action: retry
 | 
			
		||||
    re_evaluate_policies: false
 | 
			
		||||
  id: binding-login
 | 
			
		||||
  identifiers:
 | 
			
		||||
    order: 100
 | 
			
		||||
    stage: !KeyOf stage-default-authentication-login
 | 
			
		||||
@ -144,3 +158,8 @@ entries:
 | 
			
		||||
    policy: !KeyOf policy-default-oobe-prefill-user
 | 
			
		||||
    target: !KeyOf binding-password-write
 | 
			
		||||
  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
 | 
			
		||||
    attrs:
 | 
			
		||||
      key: !Context token
 | 
			
		||||
  - model: authentik_blueprints.blueprintinstance
 | 
			
		||||
    identifiers:
 | 
			
		||||
      metadata:
 | 
			
		||||
        labels:
 | 
			
		||||
          blueprints.goauthentik.io/system-bootstrap: "true"
 | 
			
		||||
    state: absent
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@ services:
 | 
			
		||||
    volumes:
 | 
			
		||||
      - redis:/data
 | 
			
		||||
  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
 | 
			
		||||
    command: server
 | 
			
		||||
    environment:
 | 
			
		||||
@ -53,7 +53,7 @@ services:
 | 
			
		||||
      - postgresql
 | 
			
		||||
      - redis
 | 
			
		||||
  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
 | 
			
		||||
    command: worker
 | 
			
		||||
    environment:
 | 
			
		||||
 | 
			
		||||
@ -29,4 +29,4 @@ func UserAgent() string {
 | 
			
		||||
	return fmt.Sprintf("authentik@%s", FullVersion())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const VERSION = "2023.8.3"
 | 
			
		||||
const VERSION = "2023.8.4"
 | 
			
		||||
 | 
			
		||||
@ -113,7 +113,7 @@ filterwarnings = [
 | 
			
		||||
 | 
			
		||||
[tool.poetry]
 | 
			
		||||
name = "authentik"
 | 
			
		||||
version = "2023.8.3"
 | 
			
		||||
version = "2023.8.4"
 | 
			
		||||
description = ""
 | 
			
		||||
authors = ["authentik Team <hello@goauthentik.io>"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
openapi: 3.0.3
 | 
			
		||||
info:
 | 
			
		||||
  title: authentik
 | 
			
		||||
  version: 2023.8.3
 | 
			
		||||
  version: 2023.8.4
 | 
			
		||||
  description: Making authentication simple.
 | 
			
		||||
  contact:
 | 
			
		||||
    email: hello@goauthentik.io
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
 | 
			
		||||
export const ERROR_CLASS = "pf-m-danger";
 | 
			
		||||
export const PROGRESS_CLASS = "pf-m-in-progress";
 | 
			
		||||
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 ROUTE_SEPARATOR = ";";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -155,6 +155,10 @@ image:
 | 
			
		||||
-   web: don't import entire SourceViewPage in flow and user interface (#6761)
 | 
			
		||||
-   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
 | 
			
		||||
 | 
			
		||||
#### 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: [
 | 
			
		||||
                "security/policy",
 | 
			
		||||
                "security/GHSA-rjvp-29xq-f62w",
 | 
			
		||||
                "security/CVE-2023-39522",
 | 
			
		||||
                "security/CVE-2023-36456",
 | 
			
		||||
                "security/2023-06-cure53",
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user