Skip to content

Commit

Permalink
providers/radius: simple radius outpost (#1796)
Browse files Browse the repository at this point in the history
* initial implementation

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* cleanup

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add migrations

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix web

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* minor fixes

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* use search-select

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* update locale

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fixup

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix ip with port being sent to delegated ip

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add radius tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* update docs

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
  • Loading branch information
BeryJu committed Mar 20, 2023
1 parent 84c2da8 commit 3f5effb
Show file tree
Hide file tree
Showing 54 changed files with 5,433 additions and 677 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ jobs:
glob: tests/e2e/test_provider_saml* tests/e2e/test_source_saml*
- name: ldap
glob: tests/e2e/test_provider_ldap* tests/e2e/test_source_ldap*
- name: radius
glob: tests/e2e/test_provider_radius*
- name: flows
glob: tests/e2e/test_flows*
steps:
Expand Down
8 changes: 5 additions & 3 deletions .github/workflows/ci-outpost.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@ jobs:
type:
- proxy
- ldap
- radius
arch:
- 'linux/amd64'
- "linux/amd64"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand Down Expand Up @@ -106,6 +107,7 @@ jobs:
type:
- proxy
- ldap
- radius
goos: [linux]
goarch: [amd64, arm64]
steps:
Expand All @@ -115,8 +117,8 @@ jobs:
go-version: "^1.17"
- uses: actions/setup-node@v3.6.0
with:
node-version: '18'
cache: 'npm'
node-version: "18"
cache: "npm"
cache-dependency-path: web/package-lock.json
- name: Generate API
run: make gen-client-go
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/release-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ jobs:
type:
- proxy
- ldap
- radius
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
Expand Down Expand Up @@ -99,6 +100,7 @@ jobs:
type:
- proxy
- ldap
- radius
goos: [linux, darwin]
goarch: [amd64, arm64]
steps:
Expand Down
3 changes: 3 additions & 0 deletions authentik/api/v3/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
RefreshTokenViewSet,
)
from authentik.providers.proxy.api import ProxyOutpostConfigViewSet, ProxyProviderViewSet
from authentik.providers.radius.api import RadiusOutpostConfigViewSet, RadiusProviderViewSet
from authentik.providers.saml.api.property_mapping import SAMLPropertyMappingViewSet
from authentik.providers.saml.api.providers import SAMLProviderViewSet
from authentik.providers.scim.api.property_mapping import SCIMMappingViewSet
Expand Down Expand Up @@ -128,6 +129,7 @@
router.register("outposts/service_connections/kubernetes", KubernetesServiceConnectionViewSet)
router.register("outposts/proxy", ProxyOutpostConfigViewSet)
router.register("outposts/ldap", LDAPOutpostConfigViewSet)
router.register("outposts/radius", RadiusOutpostConfigViewSet)

router.register("flows/instances", FlowViewSet)
router.register("flows/bindings", FlowStageBindingViewSet)
Expand Down Expand Up @@ -166,6 +168,7 @@
router.register("providers/oauth2", OAuth2ProviderViewSet)
router.register("providers/saml", SAMLProviderViewSet)
router.register("providers/scim", SCIMProviderViewSet)
router.register("providers/radius", RadiusProviderViewSet)

router.register("oauth2/authorization_codes", AuthorizationCodeViewSet)
router.register("oauth2/refresh_tokens", RefreshTokenViewSet)
Expand Down
2 changes: 2 additions & 0 deletions authentik/outposts/api/outposts.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
)
from authentik.providers.ldap.models import LDAPProvider
from authentik.providers.proxy.models import ProxyProvider
from authentik.providers.radius.models import RadiusProvider


class OutpostSerializer(ModelSerializer):
Expand All @@ -51,6 +52,7 @@ def validate_providers(self, providers: list[Provider]) -> list[Provider]:
type_map = {
OutpostType.LDAP: LDAPProvider,
OutpostType.PROXY: ProxyProvider,
OutpostType.RADIUS: RadiusProvider,
None: Provider,
}
for provider in providers:
Expand Down
20 changes: 20 additions & 0 deletions authentik/outposts/migrations/0020_alter_outpost_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 4.1.7 on 2023-03-20 10:58

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("authentik_outposts", "0019_alter_outpost_name_and_more"),
]

operations = [
migrations.AlterField(
model_name="outpost",
name="type",
field=models.TextField(
choices=[("proxy", "Proxy"), ("ldap", "Ldap"), ("radius", "Radius")],
default="proxy",
),
),
]
1 change: 1 addition & 0 deletions authentik/outposts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class OutpostType(models.TextChoices):

PROXY = "proxy"
LDAP = "ldap"
RADIUS = "radius"


def default_outpost_config(host: Optional[str] = None):
Expand Down
Empty file.
65 changes: 65 additions & 0 deletions authentik/providers/radius/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""RadiusProvider API Views"""
from rest_framework.fields import CharField
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet

from authentik.core.api.providers import ProviderSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.providers.radius.models import RadiusProvider


class RadiusProviderSerializer(ProviderSerializer):
"""RadiusProvider Serializer"""

class Meta:
model = RadiusProvider
fields = ProviderSerializer.Meta.fields + [
"client_networks",
# Shared secret is not a write-only field, as
# an admin might have to view it
"shared_secret",
]
extra_kwargs = ProviderSerializer.Meta.extra_kwargs


class RadiusProviderViewSet(UsedByMixin, ModelViewSet):
"""RadiusProvider Viewset"""

queryset = RadiusProvider.objects.all()
serializer_class = RadiusProviderSerializer
ordering = ["name"]
search_fields = ["name", "client_networks"]
filterset_fields = {
"application": ["isnull"],
"name": ["iexact"],
"authorization_flow__slug": ["iexact"],
"client_networks": ["iexact"],
}


class RadiusOutpostConfigSerializer(ModelSerializer):
"""RadiusProvider Serializer"""

application_slug = CharField(source="application.slug")
auth_flow_slug = CharField(source="authorization_flow.slug")

class Meta:
model = RadiusProvider
fields = [
"pk",
"name",
"application_slug",
"auth_flow_slug",
"client_networks",
"shared_secret",
]


class RadiusOutpostConfigViewSet(ReadOnlyModelViewSet):
"""RadiusProvider Viewset"""

queryset = RadiusProvider.objects.filter(application__isnull=False)
serializer_class = RadiusOutpostConfigSerializer
ordering = ["name"]
search_fields = ["name"]
filterset_fields = ["name"]
10 changes: 10 additions & 0 deletions authentik/providers/radius/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""authentik radius provider app config"""
from django.apps import AppConfig


class AuthentikProviderRadiusConfig(AppConfig):
"""authentik radius provider app config"""

name = "authentik.providers.radius"
label = "authentik_providers_radius"
verbose_name = "authentik Providers.Radius"
Empty file.
14 changes: 14 additions & 0 deletions authentik/providers/radius/controllers/docker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""Radius Provider Docker Controller"""
from authentik.outposts.controllers.base import DeploymentPort
from authentik.outposts.controllers.docker import DockerController
from authentik.outposts.models import DockerServiceConnection, Outpost


class RadiusDockerController(DockerController):
"""Radius Provider Docker Controller"""

def __init__(self, outpost: Outpost, connection: DockerServiceConnection):
super().__init__(outpost, connection)
self.deployment_ports = [
DeploymentPort(1812, "radius", "udp", 1812),
]
14 changes: 14 additions & 0 deletions authentik/providers/radius/controllers/kubernetes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""Radius Provider Kubernetes Controller"""
from authentik.outposts.controllers.base import DeploymentPort
from authentik.outposts.controllers.kubernetes import KubernetesController
from authentik.outposts.models import KubernetesServiceConnection, Outpost


class RadiusKubernetesController(KubernetesController):
"""Radius Provider Kubernetes Controller"""

def __init__(self, outpost: Outpost, connection: KubernetesServiceConnection):
super().__init__(outpost, connection)
self.deployment_ports = [
DeploymentPort(1812, "radius", "udp", 1812),
]
52 changes: 52 additions & 0 deletions authentik/providers/radius/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Generated by Django 4.1.7 on 2023-03-20 10:58

import django.db.models.deletion
from django.db import migrations, models

import authentik.lib.generators


class Migration(migrations.Migration):
initial = True

dependencies = [
("authentik_core", "0027_alter_user_uuid"),
]

operations = [
migrations.CreateModel(
name="RadiusProvider",
fields=[
(
"provider_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="authentik_core.provider",
),
),
(
"shared_secret",
models.TextField(
default=authentik.lib.generators.generate_key,
help_text="Shared secret between clients and server to hash packets.",
),
),
(
"client_networks",
models.TextField(
default="0.0.0.0/0, ::/0",
help_text="List of CIDRs (comma-separated) that clients can connect from. A more specific CIDR will match before a looser one. Clients connecting from a non-specified CIDR will be dropped.",
),
),
],
options={
"verbose_name": "Radius Provider",
"verbose_name_plural": "Radius Providers",
},
bases=("authentik_core.provider", models.Model),
),
]
Empty file.
50 changes: 50 additions & 0 deletions authentik/providers/radius/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""Radius Provider"""
from typing import Optional, Type

from django.db import models
from django.utils.translation import gettext_lazy as _
from rest_framework.serializers import Serializer

from authentik.core.models import Provider
from authentik.lib.generators import generate_key
from authentik.outposts.models import OutpostModel


class RadiusProvider(OutpostModel, Provider):
"""Allow applications to authenticate against authentik's users using Radius."""

shared_secret = models.TextField(
default=generate_key,
help_text=_("Shared secret between clients and server to hash packets."),
)

client_networks = models.TextField(
default="0.0.0.0/0, ::/0",
help_text=_(
"List of CIDRs (comma-separated) that clients can connect from. A more specific "
"CIDR will match before a looser one. Clients connecting from a non-specified CIDR "
"will be dropped."
),
)

@property
def launch_url(self) -> Optional[str]:
"""Radius never has a launch URL"""
return None

@property
def component(self) -> str:
return "ak-provider-radius-form"

@property
def serializer(self) -> Type[Serializer]:
from authentik.providers.radius.api import RadiusProviderSerializer

return RadiusProviderSerializer

def __str__(self):
return f"Radius Provider {self.name}"

class Meta:
verbose_name = _("Radius Provider")
verbose_name_plural = _("Radius Providers")
1 change: 1 addition & 0 deletions authentik/root/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"authentik.providers.ldap",
"authentik.providers.oauth2",
"authentik.providers.proxy",
"authentik.providers.radius",
"authentik.providers.saml",
"authentik.providers.scim",
"authentik.recovery",
Expand Down
1 change: 1 addition & 0 deletions blueprints/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"authentik_providers_oauth2.refreshtoken",
"authentik_providers_oauth2.scopemapping",
"authentik_providers_proxy.proxyprovider",
"authentik_providers_radius.radiusprovider",
"authentik_providers_saml.samlpropertymapping",
"authentik_providers_saml.samlprovider",
"authentik_providers_scim.scimmapping",
Expand Down

0 comments on commit 3f5effb

Please sign in to comment.