# IP - TA.Netease.com - SSH:10.145.96.67:32200 - ssh -p 32200 -i C:\Users\loujiajie\.ssh\netease_loujiajie_id_rsa loujiajie@10.145.96.67 - Artlib - SSH:10.145.96.68:32200 - ssh -p 32200 -i C:\Users\loujiajie\.ssh\netease_loujiajie_id_rsa loujiajie@10.145.96.68 - Artlib S3 - NOS 桶信息 - 名称:matrixaita - 项目:artct - 成本项目:artct - 区域:GA - Endpoint:gzdev - 桶用户:p-artct-matrixaita - 域名(内网):`nos-gzdev.163nos.com` - Access Key:57SCV9Q4MPLXQ3JCL5K9 - Secret Key:4dzMcakyxW2vEhCGjEfiXHtDgxZUiy57D9NqKOOm ## 文档 - 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) ```