Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Unix系统下支持无免密sudo权限账号安装agent (closed #1675) #2422

Open
wants to merge 1 commit into
base: v2.4.8-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 25 additions & 5 deletions apps/backend/agent/solution_maker.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,20 @@ def choose_script_file(cls, host: models.Host, is_execute_on_target: bool) -> st
return script_file_name

@staticmethod
def get_gse_extra_config_dir(os_type: str):
def get_gse_extra_config_dir(host: models.Host):
extra_config_sub_dir: str = "user_conf"
os_type: str = host.os_type
extra_dir: str = settings.GSE_ENVIRON_DIR
if os_type.upper() == constants.OsType.WINDOWS:
return json.dumps(PathHandler(os_type).join(settings.GSE_ENVIRON_WIN_DIR, extra_config_sub_dir))[1:-1]
extra_dir = settings.GSE_ENVIRON_WIN_DIR

if not host.ap.is_use_sudo:
extra_dir: str = host.ap.get_agent_config(os_type)["setup_path"]

if os_type.upper() == constants.OsType.WINDOWS:
return json.dumps(PathHandler(os_type).join(extra_dir, extra_config_sub_dir))[1:-1]
else:
return PathHandler(os_type).join(settings.GSE_ENVIRON_DIR, extra_config_sub_dir)
return PathHandler(os_type).join(extra_dir, extra_config_sub_dir)


class BaseExecutionSolutionMaker(metaclass=abc.ABCMeta):
Expand Down Expand Up @@ -271,6 +279,12 @@ def get_run_cmd_base_params(self) -> typing.List[str]:
f"-s {self.pipeline_id}",
]

if self.host_ap.is_use_sudo:
run_dir = f'GSE_AGENT_RUN_DIR={self.agent_config["run_path"]}'
data_dir = f'GSE_AGENT_DATA_DIR={self.agent_config["data_path"]}'
log_dir = f'GSE_AGENT_LOG_DIR={self.agent_config["log_path"]}'
run_cmd_params.append(f"-v {run_dir} {data_dir} {log_dir}")

# 系统开启使用密码注册 Windows 服务时,需额外传入 -U -P 参数,用于注册 Windows 服务,详见 setup_agent.bat 脚本
if self.need_encrypted_password():
# GSE 密码注册场景暂不启用国密,使用固定 RSA 的方式
Expand Down Expand Up @@ -313,6 +327,7 @@ def add_sudo_to_cmds(self, execution_solution: ExecutionSolution):
self.host.os_type == constants.OsType.WINDOWS,
self.identity_data.account in [constants.LINUX_ACCOUNT],
self.script_file_name == constants.SetupScriptFileName.SETUP_PAGENT_PY.value,
self.host_ap.is_use_sudo is False,
]
):
return
Expand Down Expand Up @@ -366,7 +381,7 @@ def get_create_pre_dirs_step(self, is_shell_adapter: bool = False) -> ExecutionS

if not self.agent_setup_info.is_legacy:
# GSE 1.0 不需要创建额外配置目录
filepath_necessary_names.append(ExecutionSolutionTools.get_gse_extra_config_dir(self.host.os_type))
filepath_necessary_names.append(ExecutionSolutionTools.get_gse_extra_config_dir(self.host))

dirs_to_be_created: typing.Set[str] = {self.dest_dir}
for filepath_necessary_name in filepath_necessary_names:
Expand Down Expand Up @@ -533,6 +548,7 @@ def make(self) -> ExecutionSolution:
execution_solution: ExecutionSolution = self._make()
if self.is_combine_cmd_step:
self.combine_cmd_step(execution_solution)

self.add_sudo_to_cmds(execution_solution)
return execution_solution

Expand Down Expand Up @@ -587,7 +603,11 @@ def shell_cmd_adapter(
shell: str = "bash"
else:
shell: str = suffix
run_cmd = f"nohup {shell} {run_cmd} &> {self.dest_dir}nm.nohup.out &"

if self.host.os_type.lower() == backend_api_constants.OS.AIX:
run_cmd = f"nohup {shell} {run_cmd} > {self.dest_dir}nm.nohup.out 2>&1 &"
else:
run_cmd = f"nohup {shell} {run_cmd} &> {self.dest_dir}nm.nohup.out &"

curl_cmd: str = ("curl", f"{dest_dir}curl.exe")[self.host.os_type == constants.OsType.WINDOWS]
download_cmd = (
Expand Down
2 changes: 2 additions & 0 deletions apps/backend/components/collections/agent_new/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,8 @@ def handle_report_data(self, host: models.Host, sub_inst_id: int, success_callba
untreated_healthz_result = base64.b64decode(untreated_healthz_result.encode()).decode()
except binascii.Error:
pass
except UnicodeDecodeError:
pass

# 去除可能存在的前缀
if untreated_healthz_result.startswith("healthz:"):
Expand Down
16 changes: 16 additions & 0 deletions apps/backend/components/collections/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,22 @@ def request_single_job_and_create_map(
account_alias = (settings.BACKEND_UNIX_ACCOUNT, settings.BACKEND_WINDOWS_ACCOUNT)[
os_type == constants.OsType.WINDOWS
]

account_set: set = set()
for host in job_params["target_server"][host_interaction_from]:
if host_interaction_from == "host_id_list":
account = models.Host.objects.get(bk_host_id=host).identity.account
account_set.add(account)

if host_interaction_from == "ip_list":
account = models.Host.objects.get(inner_ip=host["ip"]).identity.account
account_set.add(account)

if len(account_set) > 1:
raise AppBaseException(_("目标机器账户不一致,请检查"))

account_alias = account_set.pop()

script_language = (constants.ScriptLanguageType.SHELL.value, constants.ScriptLanguageType.BAT.value)[
os_type == constants.OsType.WINDOWS
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def __post_init__(self):
]
)
],
extra_config_directory=ExecutionSolutionTools.get_gse_extra_config_dir(self.host.os_type),
extra_config_directory=ExecutionSolutionTools.get_gse_extra_config_dir(self.host),
),
context_dataclass.AccessConfigContext(
cluster_endpoints=",".join(
Expand Down
18 changes: 18 additions & 0 deletions apps/node_man/migrations/0084_accesspoint_is_use_sudo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.4 on 2024-08-26 08:39

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("node_man", "0083_subscription_operate_info"),
]

operations = [
migrations.AddField(
model_name="accesspoint",
name="is_use_sudo",
field=models.BooleanField(default=True, verbose_name="是否使用sudo"),
),
]
1 change: 1 addition & 0 deletions apps/node_man/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@ class AccessPoint(models.Model):
proxy_package = JSONField(_("Proxy上的安装包"), default=list)
outer_callback_url = models.CharField(_("节点管理外网回调地址"), max_length=128, blank=True, null=True, default="")
callback_url = models.CharField(_("节点管理内网回调地址"), max_length=128, blank=True, null=True, default="")
is_use_sudo = models.BooleanField(_("是否使用sudo"), default=True)

@property
def file_endpoint_info(self) -> EndpointInfo:
Expand Down
2 changes: 2 additions & 0 deletions apps/node_man/serializers/ap.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class ListSerializer(serializers.ModelSerializer):
is_default = serializers.BooleanField(label=_("是否默认接入点,不可删除"))
proxy_package = serializers.JSONField(label=_("Proxy上的安装包"))
file_cache_dirs = serializers.SerializerMethodField(label=_("文件缓存目录"))
is_use_sudo = serializers.BooleanField(label=_("是否使用sudo"))

def to_representation(self, instance):
ret = super(ListSerializer, self).to_representation(instance)
Expand Down Expand Up @@ -117,6 +118,7 @@ class ZKSerializer(serializers.Serializer):
bscp_config = serializers.DictField(_("BSCP配置"), required=False)
outer_callback_url = serializers.CharField(label=_("节点管理外网回调地址"), required=False, allow_blank=True)
callback_url = serializers.CharField(label=_("节点管理内网回调地址"), required=False, allow_blank=True)
is_use_sudo = serializers.BooleanField(label=_("是否使用sudo"), required=False, default=True)

def validate(self, data):
gse_version_list: List[str] = list(set(AccessPoint.objects.values_list("gse_version", flat=True)))
Expand Down
86 changes: 86 additions & 0 deletions docs/solution/how_use_no_sudo_user_install_agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# 使用无免密sudo账户安装agent注意事项

*请确保使用无免密sudo权限账户安装agent的机器未曾使用过非该账户安装过agent*

*或在使用无免密sudo权限账户安装agent前将原agent卸载并清理痕迹*

```shell
# 基于默认接入点包括但不限于
/tmp/nm*
/tmp/xuoasefasd.err
/tmp/bkjob
/usr/local/gse
/var/run/ipc.stat
/var/log/gse
/var/run/gse
/var/run/ipc.state.report
/var/lib/gse
```

*请在配置接入点时确认agent的安装账户拥有hostid文件路径的读写操作权限*

hostid文件的路径默认为`Linux: /var/lib/gse/host Windows: c:/gse/data/host`

> 二进制部署版本中默认位于cmdb的/data/bkee/cmdb/server/conf/common.yaml文件eventServer-hostIdentifier中配置
>
> 容器化版本中默认位于cmdb的/data/bkhelmfile/blueking/environments/default/bkcmdb-values.yaml.gotmpl文件中的common-eventServer-hostIdentifier中配置

*请确保所用账户拥有一下系统程序操作权限*

```shell
/usr/bin/curl,
/usr/bin/mkdir,
/usr/bin/ls,
/usr/bin/cat,
/usr/bin/which,
/usr/bin/ping,
/usr/bin/echo,
/usr/bin/chmod,
/usr/bin/nohup,
/usr/bin/tail,
/usr/bin/ps,
/usr/bin/date,
/usr/bin/tee,
/usr/bin/uname,
/usr/bin/rm,
/usr/bin/awk,
/usr/bin/lsof,
/usr/bin/stat,
/usr/bin/readlink,
/usr/bin/grep,
/usr/bin/read,
/usr/bin/hash,
/usr/bin/timeout,
/usr/bin/bash,
/usr/bin/sed,
/usr/bin/chattr,
/usr/bin/cd,
/usr/bin/cp,
/usr/bin/wait,
/usr/bin/tr,
/usr/bin/wc,
/usr/bin/mktemp,
/usr/bin/seq,
/usr/bin/sleep,
/usr/bin/df,
/usr/bin/pidof,
/usr/bin/tar,
/usr/bin/gzip,
/usr/bin/pgrep,
/usr/bin/xargs,
```

## 接入点配置

接入点配置中 `hostid路径` 必须与上述 cmdb 的配置文件中指定的路径一致

创建接入点后根据接入点页面中显示的接入点id到节点管理数据库中执行下述语句

```sql
use bk_nodeman;
update node_man_accesspoint set is_use_sudo=0 where id={接入点页面中显示的id};
```

## 作业平台

作业平台需要新建该无免密sudo权限的账户的执行账户
Loading