perf: AKSK添加访问IP控制

This commit is contained in:
wangruidong 2023-10-31 14:15:07 +08:00 committed by Bryan
parent bc54685a31
commit dc841650cf
7 changed files with 100 additions and 32 deletions

View File

@ -8,7 +8,7 @@ from django.utils.translation import gettext as _
from rest_framework import authentication, exceptions from rest_framework import authentication, exceptions
from common.auth import signature from common.auth import signature
from common.utils import get_object_or_none from common.utils import get_object_or_none, get_request_ip_or_data, contains_ip
from ..models import AccessKey, PrivateToken from ..models import AccessKey, PrivateToken
@ -122,3 +122,14 @@ class SignatureAuthentication(signature.SignatureAuthentication):
return user, secret return user, secret
except (AccessKey.DoesNotExist, exceptions.ValidationError): except (AccessKey.DoesNotExist, exceptions.ValidationError):
return None, None return None, None
def is_ip_allow(self, key_id, request):
try:
ak = AccessKey.objects.get(id=key_id)
ip_group = ak.ip_group
ip = get_request_ip_or_data(request)
if not contains_ip(ip, ip_group):
return False
return True
except (AccessKey.DoesNotExist, exceptions.ValidationError):
return False

View File

@ -0,0 +1,19 @@
# Generated by Django 4.1.10 on 2023-10-31 05:37
import authentication.models.access_key
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0023_auto_20231010_1101'),
]
operations = [
migrations.AddField(
model_name='accesskey',
name='ip_group',
field=models.JSONField(default=authentication.models.access_key.defatult_ip_group, verbose_name='IP group'),
),
]

View File

@ -12,9 +12,14 @@ def default_secret():
return random_string(36) return random_string(36)
def defatult_ip_group():
return ["*"]
class AccessKey(models.Model): class AccessKey(models.Model):
id = models.UUIDField(verbose_name='AccessKeyID', primary_key=True, default=uuid.uuid4, editable=False) id = models.UUIDField(verbose_name='AccessKeyID', primary_key=True, default=uuid.uuid4, editable=False)
secret = models.CharField(verbose_name='AccessKeySecret', default=default_secret, max_length=36) secret = models.CharField(verbose_name='AccessKeySecret', default=default_secret, max_length=36)
ip_group = models.JSONField(default=defatult_ip_group, verbose_name=_('IP group'))
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='User', user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='User',
on_delete=common.db.models.CASCADE_SIGNAL_SKIP, related_name='access_keys') on_delete=common.db.models.CASCADE_SIGNAL_SKIP, related_name='access_keys')
is_active = models.BooleanField(default=True, verbose_name=_('Active')) is_active = models.BooleanField(default=True, verbose_name=_('Active'))

View File

@ -4,6 +4,7 @@ from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from acls.serializers.rules import ip_group_child_validator, ip_group_help_text
from common.utils import get_object_or_none, random_string from common.utils import get_object_or_none, random_string
from users.models import User from users.models import User
from users.serializers import UserProfileSerializer from users.serializers import UserProfileSerializer
@ -17,9 +18,14 @@ __all__ = [
class AccessKeySerializer(serializers.ModelSerializer): class AccessKeySerializer(serializers.ModelSerializer):
ip_group = serializers.ListField(
default=['*'], label=_('AccessIP'), help_text=ip_group_help_text,
child=serializers.CharField(max_length=1024, validators=[ip_group_child_validator])
)
class Meta: class Meta:
model = AccessKey model = AccessKey
fields = ['id', 'is_active', 'date_created', 'date_last_used'] fields = ['id', 'is_active', 'date_created', 'date_last_used'] + ['ip_group']
read_only_fields = ['id', 'date_created', 'date_last_used'] read_only_fields = ['id', 'date_created', 'date_last_used']

View File

@ -17,6 +17,7 @@ Reusing failure exceptions serves several purposes:
""" """
FAILED = exceptions.AuthenticationFailed('Invalid signature.') FAILED = exceptions.AuthenticationFailed('Invalid signature.')
IP_NOT_ALLOW = exceptions.AuthenticationFailed('Ip is not in access ip list.')
class SignatureAuthentication(authentication.BaseAuthentication): class SignatureAuthentication(authentication.BaseAuthentication):
@ -43,6 +44,9 @@ class SignatureAuthentication(authentication.BaseAuthentication):
"""Returns a tuple (User, secret) or (None, None).""" """Returns a tuple (User, secret) or (None, None)."""
raise NotImplementedError() raise NotImplementedError()
def is_ip_allow(self, key_id, request):
raise NotImplementedError()
def authenticate_header(self, request): def authenticate_header(self, request):
""" """
DRF sends this for unauthenticated responses if we're the primary DRF sends this for unauthenticated responses if we're the primary
@ -78,15 +82,19 @@ class SignatureAuthentication(authentication.BaseAuthentication):
if len({"keyid", "algorithm", "signature"} - set(fields.keys())) > 0: if len({"keyid", "algorithm", "signature"} - set(fields.keys())) > 0:
raise FAILED raise FAILED
key_id = fields["keyid"]
# Fetch the secret associated with the keyid # Fetch the secret associated with the keyid
user, secret = self.fetch_user_data( user, secret = self.fetch_user_data(
fields["keyid"], key_id,
algorithm=fields["algorithm"] algorithm=fields["algorithm"]
) )
if not (user and secret): if not (user and secret):
raise FAILED raise FAILED
if not self.is_ip_allow(key_id, request):
raise IP_NOT_ALLOW
# Gather all request headers and translate them as stated in the Django docs: # Gather all request headers and translate them as stated in the Django docs:
# https://docs.djangoproject.com/en/1.6/ref/request-response/#django.http.HttpRequest.META # https://docs.djangoproject.com/en/1.6/ref/request-response/#django.http.HttpRequest.META
headers = {} headers = {}

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-10-30 11:28+0800\n" "POT-Creation-Date: 2023-10-31 14:04+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -41,7 +41,7 @@ msgstr "パスワード"
msgid "SSH key" msgid "SSH key"
msgstr "SSH キー" msgstr "SSH キー"
#: accounts/const/account.py:8 authentication/models/access_key.py:37 #: accounts/const/account.py:8 authentication/models/access_key.py:42
msgid "Access key" msgid "Access key"
msgstr "アクセスキー" msgstr "アクセスキー"
@ -843,7 +843,7 @@ msgstr "关联平台,可以配置推送参数,如果不关联,则使用默
#: assets/models/group.py:20 common/db/models.py:36 ops/models/adhoc.py:26 #: assets/models/group.py:20 common/db/models.py:36 ops/models/adhoc.py:26
#: ops/models/job.py:145 ops/models/playbook.py:31 rbac/models/role.py:37 #: ops/models/job.py:145 ops/models/playbook.py:31 rbac/models/role.py:37
#: settings/models.py:37 terminal/models/applet/applet.py:45 #: settings/models.py:37 terminal/models/applet/applet.py:45
#: terminal/models/applet/applet.py:321 terminal/models/applet/host.py:143 #: terminal/models/applet/applet.py:317 terminal/models/applet/host.py:143
#: terminal/models/component/endpoint.py:24 #: terminal/models/component/endpoint.py:24
#: terminal/models/component/endpoint.py:104 #: terminal/models/component/endpoint.py:104
#: terminal/models/session/session.py:46 tickets/models/comment.py:32 #: terminal/models/session/session.py:46 tickets/models/comment.py:32
@ -1014,7 +1014,7 @@ msgstr "1-100、低い値は最初に一致します"
msgid "Reviewers" msgid "Reviewers"
msgstr "レビュー担当者" msgstr "レビュー担当者"
#: acls/models/base.py:43 authentication/models/access_key.py:20 #: acls/models/base.py:43 authentication/models/access_key.py:25
#: authentication/models/connection_token.py:53 #: authentication/models/connection_token.py:53
#: authentication/templates/authentication/_access_key_modal.html:32 #: authentication/templates/authentication/_access_key_modal.html:32
#: perms/models/asset_permission.py:81 terminal/models/session/sharing.py:29 #: perms/models/asset_permission.py:81 terminal/models/session/sharing.py:29
@ -1712,7 +1712,7 @@ msgstr "アセットの自動化タスク"
#: assets/models/automations/base.py:113 audits/models.py:207 #: assets/models/automations/base.py:113 audits/models.py:207
#: audits/serializers.py:51 ops/models/base.py:49 ops/models/job.py:220 #: audits/serializers.py:51 ops/models/base.py:49 ops/models/job.py:220
#: terminal/models/applet/applet.py:320 terminal/models/applet/host.py:140 #: terminal/models/applet/applet.py:316 terminal/models/applet/host.py:140
#: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 #: terminal/models/component/status.py:30 terminal/serializers/applet.py:18
#: terminal/serializers/applet_host.py:124 tickets/models/ticket/general.py:283 #: terminal/serializers/applet_host.py:124 tickets/models/ticket/general.py:283
#: tickets/serializers/super_ticket.py:13 #: tickets/serializers/super_ticket.py:13
@ -2653,7 +2653,7 @@ msgid "Added on"
msgstr "に追加" msgstr "に追加"
#: authentication/backends/passkey/models.py:14 #: authentication/backends/passkey/models.py:14
#: authentication/models/access_key.py:21 #: authentication/models/access_key.py:26
#: authentication/models/private_token.py:8 #: authentication/models/private_token.py:8
msgid "Date last used" msgid "Date last used"
msgstr "最後に使用した日付" msgstr "最後に使用した日付"
@ -2956,6 +2956,11 @@ msgstr "MFAタイプ ({}) が有効になっていない"
msgid "Please change your password" msgid "Please change your password"
msgstr "パスワードを変更してください" msgstr "パスワードを変更してください"
#: authentication/models/access_key.py:22
#: terminal/models/component/endpoint.py:95
msgid "IP group"
msgstr "IP グループ"
#: authentication/models/connection_token.py:38 #: authentication/models/connection_token.py:38
#: terminal/serializers/storage.py:113 #: terminal/serializers/storage.py:113
msgid "Account name" msgid "Account name"
@ -3108,7 +3113,13 @@ msgstr "メール"
msgid "The {} cannot be empty" msgid "The {} cannot be empty"
msgstr "{} 空にしてはならない" msgstr "{} 空にしてはならない"
#: authentication/serializers/token.py:86 perms/serializers/permission.py:37 #: authentication/serializers/token.py:23
#, fuzzy
#| msgid "Access key"
msgid "AccessIP"
msgstr "アクセスキー"
#: authentication/serializers/token.py:93 perms/serializers/permission.py:37
#: perms/serializers/permission.py:59 users/serializers/user.py:98 #: perms/serializers/permission.py:59 users/serializers/user.py:98
#: users/serializers/user.py:168 #: users/serializers/user.py:168
msgid "Is valid" msgid "Is valid"
@ -4615,7 +4626,7 @@ msgid "My assets"
msgstr "私の資産" msgstr "私の資産"
#: rbac/tree.py:56 terminal/models/applet/applet.py:52 #: rbac/tree.py:56 terminal/models/applet/applet.py:52
#: terminal/models/applet/applet.py:317 terminal/models/applet/host.py:30 #: terminal/models/applet/applet.py:313 terminal/models/applet/host.py:30
#: terminal/serializers/applet.py:15 #: terminal/serializers/applet.py:15
msgid "Applet" msgid "Applet"
msgstr "リモートアプリケーション" msgstr "リモートアプリケーション"
@ -6346,7 +6357,7 @@ msgstr "カスタムプラットフォームのみをサポート"
msgid "Missing type in platform.yml" msgid "Missing type in platform.yml"
msgstr "platform.ymlにタイプがありません" msgstr "platform.ymlにタイプがありません"
#: terminal/models/applet/applet.py:319 terminal/models/applet/host.py:36 #: terminal/models/applet/applet.py:315 terminal/models/applet/host.py:36
#: terminal/models/applet/host.py:138 #: terminal/models/applet/host.py:138
msgid "Hosting" msgid "Hosting"
msgstr "ホスト マシン" msgstr "ホスト マシン"
@ -6425,10 +6436,6 @@ msgstr "Redis ポート"
msgid "Endpoint" msgid "Endpoint"
msgstr "エンドポイント" msgstr "エンドポイント"
#: terminal/models/component/endpoint.py:95
msgid "IP group"
msgstr "IP グループ"
#: terminal/models/component/endpoint.py:108 #: terminal/models/component/endpoint.py:108
msgid "Endpoint rule" msgid "Endpoint rule"
msgstr "エンドポイントルール" msgstr "エンドポイントルール"
@ -6732,7 +6739,10 @@ msgid ""
"Connect to the host using the same account first. For security reasons, " "Connect to the host using the same account first. For security reasons, "
"please set the configuration item CACHE_LOGIN_PASSWORD_ENABLED=true and " "please set the configuration item CACHE_LOGIN_PASSWORD_ENABLED=true and "
"restart the service to enable it." "restart the service to enable it."
msgstr "同じアカウントを使用してホストに接続します。セキュリティ上の理由から、構成項目 CACHE_LOGIN_PASSWORD_ENABLED=true を設定してサービスを再起動して有効にしてください。" msgstr ""
"同じアカウントを使用してホストに接続します。セキュリティ上の理由から、構成項"
"目 CACHE_LOGIN_PASSWORD_ENABLED=true を設定してサービスを再起動して有効にして"
"ください。"
#: terminal/serializers/command.py:19 #: terminal/serializers/command.py:19
msgid "Session ID" msgid "Session ID"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n" "Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-10-30 11:28+0800\n" "POT-Creation-Date: 2023-10-31 14:04+0800\n"
"PO-Revision-Date: 2021-05-20 10:54+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n"
"Last-Translator: ibuler <ibuler@qq.com>\n" "Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n" "Language-Team: JumpServer team<ibuler@qq.com>\n"
@ -40,7 +40,7 @@ msgstr "密码"
msgid "SSH key" msgid "SSH key"
msgstr "SSH 密钥" msgstr "SSH 密钥"
#: accounts/const/account.py:8 authentication/models/access_key.py:37 #: accounts/const/account.py:8 authentication/models/access_key.py:42
msgid "Access key" msgid "Access key"
msgstr "Access key" msgstr "Access key"
@ -841,7 +841,7 @@ msgstr "关联平台,可配置推送参数,如果不关联,将使用默认
#: assets/models/group.py:20 common/db/models.py:36 ops/models/adhoc.py:26 #: assets/models/group.py:20 common/db/models.py:36 ops/models/adhoc.py:26
#: ops/models/job.py:145 ops/models/playbook.py:31 rbac/models/role.py:37 #: ops/models/job.py:145 ops/models/playbook.py:31 rbac/models/role.py:37
#: settings/models.py:37 terminal/models/applet/applet.py:45 #: settings/models.py:37 terminal/models/applet/applet.py:45
#: terminal/models/applet/applet.py:321 terminal/models/applet/host.py:143 #: terminal/models/applet/applet.py:317 terminal/models/applet/host.py:143
#: terminal/models/component/endpoint.py:24 #: terminal/models/component/endpoint.py:24
#: terminal/models/component/endpoint.py:104 #: terminal/models/component/endpoint.py:104
#: terminal/models/session/session.py:46 tickets/models/comment.py:32 #: terminal/models/session/session.py:46 tickets/models/comment.py:32
@ -1011,7 +1011,7 @@ msgstr "优先级可选范围为 1-100 (数值越小越优先)"
msgid "Reviewers" msgid "Reviewers"
msgstr "审批人" msgstr "审批人"
#: acls/models/base.py:43 authentication/models/access_key.py:20 #: acls/models/base.py:43 authentication/models/access_key.py:25
#: authentication/models/connection_token.py:53 #: authentication/models/connection_token.py:53
#: authentication/templates/authentication/_access_key_modal.html:32 #: authentication/templates/authentication/_access_key_modal.html:32
#: perms/models/asset_permission.py:81 terminal/models/session/sharing.py:29 #: perms/models/asset_permission.py:81 terminal/models/session/sharing.py:29
@ -1705,7 +1705,7 @@ msgstr "资产自动化任务"
#: assets/models/automations/base.py:113 audits/models.py:207 #: assets/models/automations/base.py:113 audits/models.py:207
#: audits/serializers.py:51 ops/models/base.py:49 ops/models/job.py:220 #: audits/serializers.py:51 ops/models/base.py:49 ops/models/job.py:220
#: terminal/models/applet/applet.py:320 terminal/models/applet/host.py:140 #: terminal/models/applet/applet.py:316 terminal/models/applet/host.py:140
#: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 #: terminal/models/component/status.py:30 terminal/serializers/applet.py:18
#: terminal/serializers/applet_host.py:124 tickets/models/ticket/general.py:283 #: terminal/serializers/applet_host.py:124 tickets/models/ticket/general.py:283
#: tickets/serializers/super_ticket.py:13 #: tickets/serializers/super_ticket.py:13
@ -2632,7 +2632,7 @@ msgid "Added on"
msgstr "附加" msgstr "附加"
#: authentication/backends/passkey/models.py:14 #: authentication/backends/passkey/models.py:14
#: authentication/models/access_key.py:21 #: authentication/models/access_key.py:26
#: authentication/models/private_token.py:8 #: authentication/models/private_token.py:8
msgid "Date last used" msgid "Date last used"
msgstr "最后使用日期" msgstr "最后使用日期"
@ -2925,6 +2925,11 @@ msgstr "该 MFA ({}) 方式没有启用"
msgid "Please change your password" msgid "Please change your password"
msgstr "请修改密码" msgstr "请修改密码"
#: authentication/models/access_key.py:22
#: terminal/models/component/endpoint.py:95
msgid "IP group"
msgstr "IPグループ"
#: authentication/models/connection_token.py:38 #: authentication/models/connection_token.py:38
#: terminal/serializers/storage.py:113 #: terminal/serializers/storage.py:113
msgid "Account name" msgid "Account name"
@ -3077,7 +3082,13 @@ msgstr "邮箱"
msgid "The {} cannot be empty" msgid "The {} cannot be empty"
msgstr "{} 不能为空" msgstr "{} 不能为空"
#: authentication/serializers/token.py:86 perms/serializers/permission.py:37 #: authentication/serializers/token.py:23
#, fuzzy
#| msgid "Access key"
msgid "AccessIP"
msgstr "アクセスIP"
#: authentication/serializers/token.py:93 perms/serializers/permission.py:37
#: perms/serializers/permission.py:59 users/serializers/user.py:98 #: perms/serializers/permission.py:59 users/serializers/user.py:98
#: users/serializers/user.py:168 #: users/serializers/user.py:168
msgid "Is valid" msgid "Is valid"
@ -4563,7 +4574,7 @@ msgid "My assets"
msgstr "我的资产" msgstr "我的资产"
#: rbac/tree.py:56 terminal/models/applet/applet.py:52 #: rbac/tree.py:56 terminal/models/applet/applet.py:52
#: terminal/models/applet/applet.py:317 terminal/models/applet/host.py:30 #: terminal/models/applet/applet.py:313 terminal/models/applet/host.py:30
#: terminal/serializers/applet.py:15 #: terminal/serializers/applet.py:15
msgid "Applet" msgid "Applet"
msgstr "远程应用" msgstr "远程应用"
@ -6256,7 +6267,7 @@ msgstr "只支持自定义平台"
msgid "Missing type in platform.yml" msgid "Missing type in platform.yml"
msgstr "在 platform.yml 中缺少类型" msgstr "在 platform.yml 中缺少类型"
#: terminal/models/applet/applet.py:319 terminal/models/applet/host.py:36 #: terminal/models/applet/applet.py:315 terminal/models/applet/host.py:36
#: terminal/models/applet/host.py:138 #: terminal/models/applet/host.py:138
msgid "Hosting" msgid "Hosting"
msgstr "宿主机" msgstr "宿主机"
@ -6333,10 +6344,6 @@ msgstr "Redis 端口"
msgid "Endpoint" msgid "Endpoint"
msgstr "端点" msgstr "端点"
#: terminal/models/component/endpoint.py:95
msgid "IP group"
msgstr "IP 组"
#: terminal/models/component/endpoint.py:108 #: terminal/models/component/endpoint.py:108
msgid "Endpoint rule" msgid "Endpoint rule"
msgstr "端点规则" msgstr "端点规则"
@ -6637,7 +6644,9 @@ msgid ""
"Connect to the host using the same account first. For security reasons, " "Connect to the host using the same account first. For security reasons, "
"please set the configuration item CACHE_LOGIN_PASSWORD_ENABLED=true and " "please set the configuration item CACHE_LOGIN_PASSWORD_ENABLED=true and "
"restart the service to enable it." "restart the service to enable it."
msgstr "优先使用同名账号连接发布机。为了安全,需配置文件中开启配置 CACHE_LOGIN_PASSWORD_ENABLED=true 修改后重启服务" msgstr ""
"优先使用同名账号连接发布机。为了安全,需配置文件中开启配置 "
"CACHE_LOGIN_PASSWORD_ENABLED=true 修改后重启服务"
#: terminal/serializers/command.py:19 #: terminal/serializers/command.py:19
msgid "Session ID" msgid "Session ID"