diff --git a/07-Other/AI/AI Agent/WY/团队服务器.md b/07-Other/AI/AI Agent/WY/团队服务器.md index 0a6242b..91985d8 100644 --- a/07-Other/AI/AI Agent/WY/团队服务器.md +++ b/07-Other/AI/AI Agent/WY/团队服务器.md @@ -16,4 +16,273 @@ - 域名(内网):`nos-gzdev.163nos.com` ## 文档 -- S3:https://sa.nie.netease.com/console/webconsole/idc/ \ No newline at end of file +- S3:https://sa.nie.netease.com/console/webconsole/idc/ + + +# 垃圾电脑服务 +我现在想在局域网里的另一台Ubuntu电脑上部署Unreal Horde服务,存储 缓存以及管理各个worker、节点信息;本机性能强劲,作为worker进行具体工作。 +- 电脑ip:10.219.36.57 +- 用户名:netease +- 密码:123 + +官方文档(UE5.5) + - https://dev.epicgames.com/documentation/zh-cn/unreal-engine/horde-in-unreal-engine?application_version=5.5 +- Horde README.md https://github.com/EpicGames/UnrealEngine/blob/release/Engine/Source/Programs/Horde/README.md + - [**Deploying Horde**](https://github.com/EpicGames/UnrealEngine/blob/release/Engine/Source/Programs/Horde/Docs/Deployment.md) + - - 有关 Horde 的架构和组件的信息,以及部署它们的最佳实践。 + **受众:** IT、系统管理员、打算修改 Horde 的程序员。 + - [**Configuring and Operating Horde**](https://github.com/EpicGames/UnrealEngine/blob/release/Engine/Source/Programs/Horde/Docs/Config.md) + - - 描述如何设置和管理 Horde。 + **受众:**构建/开发运营团队、管理员。 + - [**Horde Internals**](https://github.com/EpicGames/UnrealEngine/blob/release/Engine/Source/Programs/Horde/Docs/Internals.md) + - - 描述如何构建和修改 Horde 及其架构。 + **受众:**希望扩展 Horde 的开发人员。 +- 视频 + - [Horde and Unreal Build Accelerator: Operating at Epic Scale](https://youtu.be/ZUlwqNbYWBQ?si=DgFvrvepK67v-iTi) + +## 下载地址 +- 官方下载地址 + - 服务端:https://github.com/EpicGames/UnrealEngine/releases/download/5.5.0-release/UnrealHordeServer.msi + +# 大致步骤 +1. 使用docker部署服务。 +2. 服务器配置。 + 1. 默认端口Http 13340、Http 2 13342。 + 1. 默认情况下,Horde配置为使用端口5000通过未加密的HTTP提供数据。在默认情况下,代理通过端口5002使用未加密的HTTP/2上的gRPC与Horde服务器通信。这些设置在服务器启动时显示在控制台上。 + 2. [相关配置](https://dev.epicgames.com/documentation/zh-cn/unreal-engine/horde-orientation-for-unreal-engine?application_version=5.5) + - [服务器配置](https://dev.epicgames.com/documentation/zh-cn/unreal-engine/horde-settings-for-unreal-engine?application_version=5.5#%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%AE%BE%E7%BD%AE)将配置该服务器与其他服务器的通信,定义静态参数等。它由与该服务器一同部署的 `Server.json` 文件驱动。 + - [完整配置参数清单](https://dev.epicgames.com/documentation/zh-cn/unreal-engine/horde-settings-for-unreal-engine?application_version=5.5#%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%AE%BE%E7%BD%AE) + - [全局配置](https://dev.epicgames.com/documentation/zh-cn/unreal-engine/horde-schema-for-unreal-engine?application_version=5.5#globals)在部署后控制所有面向用户的元素,该配置存于名为 `Globals.json` 的文件中。设置好部署参数后,大多数配置都在此处完成。 + 3. [验证相关配置](https://dev.epicgames.com/documentation/zh-cn/unreal-engine/horde-authentication-tutorial-for-unreal-engine?application_version=5.5) + 1. [[#OIDC身份验证]] +3. 代理机配置。详见 http://10.219.103.35:13340/docs/Landing.md + 1. 配置文件位置:安装目录\Agent\Defaults\agent.json + 2. +# 部署笔记 + +## 部署流程 +- [Horde服务器](https://dev.epicgames.com/documentation/zh-cn/unreal-engine/horde-server-for-unreal-engine?application_version=5.5) + +# Perforce服务器 +ssl:inner02-commit.perforce.nie.netease.com:1667 + +# OIDC +## Netease OIDC信息 +| | | +| ------------- | ---------------------------------------------------------------- | +| client id | 4884b03e951711f0ad370242ac120002 | +| client secret | f2862b8c5ab24085ab0883d119b631604884b354951711f0ad370242ac120002 | +## OIDC 参考代码 +```python +#coding:UTF-8 +""" +Requirements: + 1. Flask >= 0.10.1 + 2. requests + 3. jwkest >= 1.1.7 + +Usage: + python oidc_code_demo.py -H {listen_address} -p {listen_port} + +Help: + python oidc_code_demo.py -h +""" +import os +import uuid +from hashlib import md5 +import datetime +from urllib import urlencode +import json +import requests +from flask import Flask, request, jsonify, session, redirect + +from jwkest.jwk import SYMKey +from jwkest.jws import JWS +from jwkest.jwk import load_jwks_from_url +from jwkest.jws import NoSuitableSigningKeys + +__revision__ = "0.01" +__author__ = "chenxs@corp.netease.com" + +OIDC_CLIENT_ID = "" +OIDC_CLIENT_SECRET = "" +OIDC_PROVIDER = "https://login.netease.com/connect" +OIDC_AUTHORIZATION_SERVER = "https://login.netease.com/connect/authorize" +OIDC_TOKEN_ENDPOINT = "https://login.netease.com/connect/token" +OIDC_USERINFO_ENDPOINT = "https://login.netease.com/connect/userinfo" +OIDC_SCOPE = "openid nickname email fullname dep title empno" +OIDC_REDIRECT_URI = "https://127.0.0.1:5000/finish" +OIDC_JWKS_URI = "https://login.netease.com/connect/jwks" +OIDC_ALG = "HS256" + +PYTHON_OIDC_DEMO = Flask(__name__) + +@PYTHON_OIDC_DEMO.route("/", methods=['GET']) +def index(): + """index""" + if 'username' in session: + body = ( + u"

OpenID Connect 鐧诲綍鎴愬姛銆�


" + u"鎮ㄧ殑鐢ㄦ埛鍚嶆槸锛�%s
" + u"鎮ㄧ殑鍏ㄥ悕鏄細%s
" + u"鎮ㄧ殑閭鏄細%s
" + u"鎮ㄧ殑鑱屼綅鏄細%s
" + u"鎮ㄧ殑閮ㄩ棬鏄細%s
" + u"鎮ㄧ殑宸ュ彿鏄細%s
") % ( + session['username'], session.get('fullname', ''), + session.get('email', ''), session.get('title', ''), + session.get('dep', ''), session.get('empno', '')) + body += u"鎴虫垜閲嶆柊鐧诲綍" + return body + else: + return u"鎴虫垜鐧诲綍" + +@PYTHON_OIDC_DEMO.route("/login", methods=['GET']) +def login(): + """AuthN Request""" + session.clear() + now = datetime.datetime.now().strftime("%s") + session['uid'] = uuid.uuid4().hex + session['state'] = session['uid'] + session['nonce'] = md5(session['uid'] + now).hexdigest() + + authn_request_params = { + 'response_type': 'code', + 'client_id': OIDC_CLIENT_ID, + 'state': session['state'], + 'nonce': session['nonce'], + 'scope': OIDC_SCOPE, + 'redirect_uri': OIDC_REDIRECT_URI, + #'prompt': 'login', + 'display': 'touch', + } + + redirect_url = "?".join([ + OIDC_AUTHORIZATION_SERVER, urlencode(authn_request_params)]) + + return redirect(redirect_url) + + +def token_request(code): + """2. Token Request""" + params = { + 'grant_type': 'authorization_code', + 'code': code, + 'redirect_uri': OIDC_REDIRECT_URI, + 'client_id': OIDC_CLIENT_ID, + 'client_secret': OIDC_CLIENT_SECRET, + } + _resp = requests.post(OIDC_TOKEN_ENDPOINT, data=params) + return json.loads(_resp.text) + +def id_token_verify(id_token, nonce=None): + """3. id token verify""" + + now = int(datetime.datetime.now().strftime("%s")) + if OIDC_ALG == "HS256": + signed_keys = [SYMKey(key=OIDC_CLIENT_SECRET)] + else: + signed_keys = load_jwks_from_url(OIDC_JWKS_URI) + + try: + plain_id_token = JWS().verify_compact(id_token, signed_keys) + except NoSuitableSigningKeys: + # logger the id_token please + return {'error': 'can not verify the id token'} + + print "idtoken: %s" % plain_id_token + if nonce: + if (not plain_id_token.has_key('nonce')) or ( + plain_id_token['nonce'] != nonce): + return {'error': 'id token nonce not correct'} + if plain_id_token['iss'] != OIDC_PROVIDER: + return {'error': 'id token iss not correct'} + if plain_id_token['aud'] != OIDC_CLIENT_ID: + return {'error': 'id token aud not correct'} + if now >= int(plain_id_token['exp']): + return {'error': 'id token expired'} + + return {'id_token': plain_id_token} + + +@PYTHON_OIDC_DEMO.route("/finish", methods=['GET']) +def finish(): + """ + 1. AuthN Response + 2. Token Request + 3. id token verify + 4. userinfo request + 5. login user + """ + + # 1. AuthN Response + try: + code = request.args.get('code') + if session['state']: + state = request.args.get('state') + if state != session['state']: + return u"闈炴硶璇锋眰" + except ValueError: + return u"闈炴硶璇锋眰" + # 2. Token Request + token = token_request(code) + print "token: %s" % token + if token.has_key('error'): + return u"鍑洪敊浜嗭細%s" % str(token) + # 3. id token verify + id_token_verified = id_token_verify(token['id_token']) + if id_token_verified.has_key('error'): + return id_token_verified['error'] + else: + id_token = id_token_verified['id_token'] + + print "id_token: %s" % id_token + + # 4. userinfo request + _req_session = requests.Session() + _req_session.headers.update({ + "Authorization": "Bearer %s" % token['access_token']}) + userinfo_req = _req_session.get(OIDC_USERINFO_ENDPOINT) + userinfo = json.loads(userinfo_req.text) + # login the user + session['username'] = userinfo['nickname'] + session['email'] = userinfo['email'] + session['title'] = userinfo.get('title', '') + session['empno'] = userinfo.get('empno', '') + session['dep'] = userinfo.get('dep', '') + session['fullname'] = userinfo.get('fullname', '') + return redirect("/") + +if __name__ == "__main__": + import sys + import argparse + parser = argparse.ArgumentParser( + usage='%(prog)s [options]', version='%(prog)s ' + str(__revision__)) + parser.add_argument( + '-H', '--host', dest='host', type=str, + help="Specify listening adress, default is 127.0.0.1") + parser.add_argument( + '-p', '--port', dest='port', type=int, + help="Specify listening port, default is 5000") + parser.add_argument( + '-c', '--client_id', dest='client_id', type=str, + help="oidc client_id is required.") + parser.add_argument( + '-s', '--client_secret', dest='client_secret', type=str, + help="oidc client_secret is required.") + + args = parser.parse_args() + + host = args.host or '127.0.0.1' + port = args.port or 5000 + OIDC_REDIRECT_URI = "http://%s:%s/finish" % (host, port) + OIDC_CLIENT_ID = args.client_id + OIDC_CLIENT_SECRET = args.client_secret + if not OIDC_CLIENT_ID or not OIDC_CLIENT_SECRET: + parser.print_help() + parser.exit() + + PYTHON_OIDC_DEMO.secret_key = "this is a random secret" + PYTHON_OIDC_DEMO.debug = True + PYTHON_OIDC_DEMO.run(host=host, port=port) +```