美文网首页
Android模拟器认证流程

Android模拟器认证流程

作者: AI科技智库 | 来源:发表于2020-04-06 15:27 被阅读0次

    1 分析create_web_container.sh脚本

    通过执行create_web_container.sh来创建Android模拟器和Web环境的dockerCompose

    DOCKER_YAML=js/docker/docker-compose-build.yaml
    PASSWDS="$USER,hello"
    
    while getopts 'hasip:' flag; do
        case "${flag}" in
        a) DOCKER_YAML="${DOCKER_YAML} -f js/docker/development.yaml" ;;
        p) PASSWDS="${OPTARG}" ;;
        h) help ;;
        s) START='yes' ;;
        i) INSTALL='yes' ;;
        *) help ;;
        esac
    done
    
    # Now generate the public/private keys and salt the password
    cd js/jwt-provider
    pip install -r requirements.txt >/dev/null
    python gen-passwords.py --pairs "${PASSWDS}" || exit 1
    cp jwt_secrets_pub.jwks ../docker/certs/jwt_secrets_pub.jwks
    
    
    # compose the container
    pip install docker-compose >/dev/null
    docker-compose -f ${DOCKER_YAML} build
    
    • 如果执行create_web_container.sh脚本时不指定-p选项,默认创建一套账户密码体系,其中账户名是$USER,默认的密码是hello
    • 如果执行脚本时指定了-p选项,${PASSWDS}会将账户密码传递给gen-passwords.py脚本
    • 通过pip安装requirements.txt文件中指定了库,例如JWCrypto用来创建公私钥文件
    • 执行gen-passwords.py脚本,根据传入的${PASSWDS}创建passwd文件保存账户信息,并创建公钥和私钥文件
    • 将gen-passwords.py脚本生成的公钥文件jwt_secrets_pub.jwks拷贝到../docker/certs/目录
    • 执行docker-compose -f js/docker/docker-compose-build.yaml build

    2 分析gen-passwords.py脚本

    gen-passwords.py脚本的作用是生成passwd、jwt_secrets_pub.jwks和jwt_secrets_priv.jwks三个文件。

    FLAGS = flags.FLAGS
    
    flags.DEFINE_list("pairs", [getpass.getuser() + "@localhost", "hello"], "List of user name password pairs. user1,passwd1,user2,passwd2,...")
    flags.DEFINE_string("passwords", "passwd", "File with json dictionary of users -> password hash")
    flags.DEFINE_string("jwks", "jwt_secrets", "File name with generated  JWKS secrets.")
    
    flags.register_validator(
        "pairs", lambda value: len(value) % 2 == 0, message="--pairs must have an even number of user,password pairs"
    )
    
    
    def pairwise(iterable):
        "s -> (s0, s1), (s2, s3), (s4, s5), ..."
        a = iter(iterable)
        return zip(a, a)
    
    
    def main(argv):
        if len(argv) > 1:
            raise app.UsageError("Too many command-line arguments.")
    
        # Create salted passwords
        unsalted = pairwise(FLAGS.pairs)
        salted = {}
        for pair in unsalted:
            logging.info("%s : %s", pair[0], pair[1])
            salted[pair[0]] = generate_password_hash(pair[1])
    
        # And write them to a file
        with open(FLAGS.passwords, "w") as f:
            f.write(json.dumps(salted))
    
        # Create the jwks secrets and export them
        keys = jwk.JWK.generate(kty="RSA", size=2048)
    
        # Python 2 does signed crc32, unlike Python 3
        kid = hex(binascii.crc32(keys.export_public().encode('utf-8')) & 0xFFFFFFFF)
    
        public = json.loads(keys.export_public())
        private = json.loads(keys.export_private())
        public["kid"] = kid
        private["kid"] = kid
        public_jwks = {"keys": [public]}
        private_jwks = {"keys": [private]}
    
        with open(FLAGS.jwks + '_pub.jwks', 'w') as f:
            f.write(json.dumps(public_jwks, indent=2))
    
        with open(FLAGS.jwks + '_priv.jwks', 'w') as f:
            f.write(json.dumps(private_jwks, indent=2))
    
    
    if __name__ == "__main__":
        app.run(main)
    
    • 调用generate_password_hash函数对传入的用户密码执行hash加密,passwd文件最后保存了用户名和加密后的用户密码
    • 调用JWCrypto库来创建jwt_secrets_pub.jwks公钥文件和jwt_secrets_priv.jwks私钥文件

    passwd文件

    {"wxudong": "pbkdf2:sha256:150000$QAhcdEXl$194fa653eb2234ae929a40b460d723f5845de2b85cdf7fc57fe87d8c1f9d8468"}
    

    jwt_secrets_pub.jwks公钥文件

    {
      "keys": [
        {
          "e": "AQAB",
          "kty": "RSA",
          "n": "s6CohlzVmDPjn9kat4lusYkvN0hpnAnJ5Hf6IYNqNq1jQUQD6B47k7HT_3XWr2MvRZYC59J5mK73IYLGpQIio9WEikoQog2sV8m7GRuSeo44qcLELpgJku8NJ3dQG0OflwVVWgRaS525i62jgc8MYl_eujKWtqOMesEI3UsDYEp83kNXIoHpO1t_g6XPwFaZTg3k0hG5PfSM5aaB79YfFKYi3GCdxjM4MjJR_-YPjZFDCVoikcYVnNu52w4gPUldBJv2svRsnQM6Xps5VugqkkxDszq5zxP-TRdwalP17EAdPWfs2GrRuC8fEevRgNZODgLwQpeJbRoYkQz3U8rYvw",
          "kid": "0x10034a08"
        }
      ]
    }
    

    jwt_secrets_priv.jwk私钥文件

    {
      "keys": [
        {
          "d": "Dvv48vRtkPvLIjt_Ig5h4Id8G9V7kduzLs7fW8pVougF3pzo4oUbHS_5alcPKKRSfjCMX4BMSnNWBEKfhYZPE3GtU8fn6UzQsqYOaILHTlfs3CR2LxjZu5sbcs5eLVgPyQ5V12ODkMlAgClk-WAnPVGYB9pOfj_YaSkPLz6hsneAXq4NGAUwQ1OaDDT28VC-RsQpC9geOt-xaZkfpO_66DCrjeMJrGm_v5tNdLyO1eX4tBTic9W7nDwHUEvAjst9U1s6bomwfVvdOxv7oVOR18plWIQKpjbwZ44Eu09PPHhAQkI3XTNwHVy6lSDZY4m3XayQrvKJk5wsDGNSTkE2AQ",
          "dp": "LnqJIuSX-d_vFRdupWqF3e92jGzIf6BKQf60dZTUsf8F0jpIdh2wVPIfFIxMzIJsvDH6lMeOfvOoqPhVK_dLHto5mbtpgQ3G9XE1m8e_HzDaqeciV0JZDn-rjC1vjX6X-NKt3aXWVu9EdIfgxYaA1zpa8aeB6psYpW-cN8jqZgE",
          "dq": "m95utBuxolIEzpmKUorC3ot-cvPhgTW5L9RnbBi8dbETnSpITBP0bFBvTZsK2Snt-wC1UJeRC4DR4ENIOXwsj9a_x_jY8OE8DC2a82O1PTTpOl6wVFlGpIwPMX_ZMZOPJDJEdXeYauJEHX7FScXvQZUARNUoO1LrIahF50uQIZ8",
          "e": "AQAB",
          "kty": "RSA",
          "n": "s6CohlzVmDPjn9kat4lusYkvN0hpnAnJ5Hf6IYNqNq1jQUQD6B47k7HT_3XWr2MvRZYC59J5mK73IYLGpQIio9WEikoQog2sV8m7GRuSeo44qcLELpgJku8NJ3dQG0OflwVVWgRaS525i62jgc8MYl_eujKWtqOMesEI3UsDYEp83kNXIoHpO1t_g6XPwFaZTg3k0hG5PfSM5aaB79YfFKYi3GCdxjM4MjJR_-YPjZFDCVoikcYVnNu52w4gPUldBJv2svRsnQM6Xps5VugqkkxDszq5zxP-TRdwalP17EAdPWfs2GrRuC8fEevRgNZODgLwQpeJbRoYkQz3U8rYvw",
          "p": "57X-KInt0h5RzyGbOJzxe0s3a3E8zffblwRe9XhuHq9WNurnbpztUVSh_pX5FUn8KAM_a7sMoPjJhm0YCUdLZ5Y6a4OWdk7AYk5X4lk-4QlYMPPkZOL9kBRyvEo6ZmUisMtHZYeTMk_oZOE31rOrrI82VN_wJpPUO9Pxp9h43wE",
          "q": "xnT_v8Ep6-pUx_IULsvAKrlYwQW_JCsVD3r8SYxy61zbPgD99StETVoSpAwJv8Gl71USrpBJ8OtzkvyvY38Y-M9VJQVPrcTzEi0lGiYcJP5WBxd-RFVrZDlfbxZ_nd30gpIiU12imbQwcMXrKfAAHfEPFXAVK2Y7b5tf1PTzd78",
          "qi": "A0tQFhLjDGCoiYXIYtBdAPEmk_EW9nnz2VF8d1-dNvVtyfSAIHg3Clp1MdH6kmZ6ZzFUgNcex0kHe2CD_oopow2t7XOiZzlfA29SOJbkSocgwopFV13X5Dj9iVUH-wShaNelg44VleV7k5rUus74gbJafAujSsImm9IpfE2k2jA",
          "kid": "0x10034a08"
        }
      ]
    }
    

    3 执行docker-compose-build.yaml文件

    version: "3.7"
    services:
      front-envoy:
        image: emulator_envoy:latest
        build:
          context: .
          dockerfile: envoy.Dockerfile
        networks:
          - envoymesh
        expose:
          - "8080"
          - "8001"
          - "8443"
        ports:
          - "80:8080"
          - "443:8443"
          - "8001:8001"
    
      emulator:
        image: emulator_emulator:latest
        build:
          context: ../../src
          dockerfile: Dockerfile
        networks:
          envoymesh:
            aliases:
              - emulator
        devices: [/dev/kvm]
        shm_size: 128M
        expose:
          - "8556"
    
      jwt_signer:
        image: emulator_jwt_signer:latest
        build:
          context: ../jwt-provider
          dockerfile: Dockerfile
        networks:
          envoymesh:
            aliases:
              - jwt_signer
        expose:
          - "8080"
    
      nginx:
        image: emulator_nginx:latest
        build:
          context: ..
          dockerfile: docker/nginx.Dockerfile
        networks:
          envoymesh:
            aliases:
              - nginx
        expose:
          - "80"
    
    networks:
      envoymesh: {}
    

    Docker Compose 配置文件语法:
    image 指定使用的镜像
    build 指定Dockerfile构建,context指定了路径,dockerfile指定了文件名
    networks 网络配置,容器部署在名为envoymesh的网络中
    ports 端口映射
    expose 暴露端口
    devices 设置映射列表
    shm_size 设置容器 /dev/shm 分区大小

    3.1 创建emulator_nginx:latest镜像

    nginx.Dockerfile指定了两个FROM,第一个FROM指定了编译镜像,将web程序的代码拷贝到app目录,建立npm环境来编译web程序。第二个FORM指定了最终要生成nginx镜像,将Build后的文件拷贝到该镜像中,Dockerfile执行完成后会删除编译镜像保留nginx镜像。nginx暴露了80端口

    # Stage 0, "build-stage", based on Node.js, to build and compile the frontend
    FROM tiangolo/node-frontend:10 as build-stage
    WORKDIR /app
    COPY package*.json /app/
    RUN npm install
    COPY ./ /app/
    ARG configuration=production
    
    RUN npm run build
    # Stage 1, based on Nginx, to have only the compiled app, ready for production with Nginx
    FROM nginx:1.16
    COPY --from=build-stage /app/build/ /usr/share/nginx/html
    # Copy the default nginx.conf provided by tiangolo/node-frontend
    COPY --from=build-stage /nginx.conf /etc/nginx/conf.d/default.conf
    

    3.2 创建emulator_jwt_signer:latest镜像

    创建该镜像时会将jwt-provider目录中的文件(包括公钥文件、私钥文件、passwd文件)拷贝到app目录,使用pip安装了认证程序所需要的库文件,最后执行jwt-provider.py脚本。该镜像被用作JWT认证服务器(JWTS),暴露了8080端口。
    当Dockerfile中指定ENTRYPOINT后, CMD的含义不再是直接运行其命令,而是将CMD的内容作为参数 传给ENTRYPOINT ,实际执行变为<ENTRYPOINT> "<CMD>"

    FROM debian:stretch-slim
    COPY sources.list /etc/apt/
    RUN apt-get update -y
    RUN apt-get install -y python-pip python-dev build-essential
    COPY . /app
    WORKDIR /app
    COPY pip.conf /etc/pip.conf
    RUN pip install -r requirements.txt
    ENTRYPOINT ["python"]
    CMD ["jwt-provider.py"]
    

    3.3 创建emulator_emulator:latest镜像

    将项目src目录的文件拷贝到docker镜像中指定的目录,包括指定模拟器版本编译后的可执行文件,指定Android rom编译后的image文件,启动模拟器脚本launch-emulator.sh等。emulator暴露了8556端口用于gRPC。启动后执行脚本CMD ["/android/sdk/launch-emulator.sh"]

    FROM debian:stretch-slim AS emulator
    
    RUN sed -i 's|security.debian.org/debian-security|mirrors.ustc.edu.cn/debian-security|g' /etc/apt/sources.list
    RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
    
    # Install all the required emulator dependencies.
    # You can get these by running ./android/scripts/unix/run_tests.sh --verbose --verbose --debs | grep apt | sort -u
    # pulse audio is needed due to some webrtc dependencies.
    RUN apt-get update && apt-get install -y --no-install-recommends \
    # Emulator & video bridge dependencies
        libc6 libdbus-1-3 libfontconfig1 libgcc1 \
        libpulse0 libtinfo5 libx11-6 libxcb1 libxdamage1 \
        libnss3 libxcomposite1 libxcursor1 libxi6 \
        libxext6 libxfixes3 zlib1g libgl1 pulseaudio socat \
    # Enable turncfg through usage of curl
        curl ca-certificates && \
        apt-get clean && \
        rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
    
    # Now we configure the user account under which we will be running the emulator
    RUN mkdir -p /android/sdk/platforms && \
        mkdir -p /android/sdk/platform-tools && \
        mkdir -p /android/sdk/system-images/android && \
        mkdir -p /android-home
    
    # Make sure to place files that do not change often in the higher layers
    # as this will improve caching.
    COPY launch-emulator.sh /android/sdk/
    COPY platform-tools/adb /android/sdk/platform-tools/adb
    COPY default.pa /etc/pulse/default.pa
    
    RUN gpasswd -a root audio && \
        chmod +x /android/sdk/launch-emulator.sh /android/sdk/platform-tools/adb
    
    COPY avd/ /android-home
    COPY emu/ /android/sdk/
    COPY sys/ /android/sdk/system-images/android/
    # Create an initial snapshot so we will boot fast next time around,
    # This is currently an experimental feature, and is not easily configurable//
    # RUN --security=insecure cd /android/sdk && ./launch-emulator.sh -quit-after-boot 120
    
    # This is the console port, you usually want to keep this closed.
    EXPOSE 5554
    
    # This is the ADB port, useful.
    EXPOSE 5555
    
    # This is the gRPC port, also useful, we don't want ADB to incorrectly identify this.
    EXPOSE 8556
    
    ENV ANDROID_SDK_ROOT /android/sdk
    ENV ANDROID_AVD_HOME /android-home
    WORKDIR /android/sdk
    
    # You will need to make use of the grpc snapshot/webrtc functionality to actually interact with
    # the emulator.
    CMD ["/android/sdk/launch-emulator.sh"]
    
    # Note we should use gRPC status endpoint to check for health once the canary release is out.
    HEALTHCHECK --interval=30s \
                --timeout=30s \
                --start-period=30s \
                --retries=3 \
                CMD /android/sdk/platform-tools/adb shell getprop dev.bootcomplete | grep "1"
    
    # Date frequently changes, so we place this in the last layer.
    LABEL maintainer="wxudong@pc" \
          SystemImage.Abi=x86_64 \
          SystemImage.TagId=android \
          SystemImage.GpuSupport=true \
          AndroidVersion.ApiLevel=28 \
          com.google.android.emulator.build-date="2020-03-10T03:36:04.968500Z" \
          com.google.android.emulator.description="Pixel 2 Emulator, running API 28" \
          com.google.android.emulator.version="android-28-x86_64/30.0.0"
    

    3.4 创建emulator_envoy:latest镜像

    拷贝配置文件envoy.yaml,拷贝证书文件、key、公钥文件到指定目录,容器启动后执行envoy.yaml配置文件。

    FROM envoyproxy/envoy:v1.12.0
    COPY ./envoy.yaml /etc/envoy/envoy.yaml
    ADD certs/self_sign.crt /etc/cert.crt
    ADD certs/self_sign.key /etc/key.key
    ADD certs/jwt_secrets_pub.jwks /etc/jwt_secrets_pub.jwks
    CMD /usr/local/bin/envoy -c /etc/envoy/envoy.yaml
    

    4 认证流程

    认证流程图.png

    1:web client获取用户输入的账户和密码,发送一个带有"/token"的GET网络请求
    2:envoy过滤"/token",转发到jwt_signer容器
    3:jwt验证账户和密码,并将私钥加密的token信息(包含iss、aud属性)返回给web client
    4:web client进行gRPC远程调用时,携带加密的token信息
    5:envoy使用http filter过滤器,过滤"/android.emulation.control.EmulatorController"(gPRC请求),公钥解密token信息后,验证iss、aud属性,如果匹配网络请求再转发到emulator。

    4.1 web client发送获取token的网络请求

    web应用程序启动时调用TokenAuthService,设置auth_uri为http://localhost:443/token
    文件App.js

    var EMULATOR_GRPC =
      window.location.protocol +
      "//" +
      window.location.hostname +
      ":" +
      window.location.port;
    if (development) {
      EMULATOR_GRPC = "http://localhost:8080";
    }
    
    export default class App extends Component {
      constructor(props) {
        super(props);
        if (development) {
          this.auth = new NoAuthService();
        } else {
          this.auth = new TokenAuthService(EMULATOR_GRPC + "/token");
        }
        this.emulator = new EmulatorControllerService(EMULATOR_GRPC, this.auth, this.onError);
      }
    }
    

    当用户输入用户账号密码后,调用login获取token信息。

      doLogin = () => {
        const { auth } = this.props;
        const { email, password } = this.state;
        auth.login(email, password).catch(e => {
          this.setState({ displayErrorSnack: true });
        });
      };
    
      // Login when we press the enter key
      handleTextFieldKeyDown = event => {
        if (event.key === "Enter") this.doLogin();
      };
    

    传入账号和密码,根据设置的auth_uri发送axios.get网络请求,response.data返回token信息。

      login = (email, password) => {
        return axios
          .get(this.auth_uri, {
            auth: {
              username: email,
              password: password
            }
          })
          .then(response => {
            this.token = "Bearer " + response.data;
            this.events.emit("authorized", true);
          });
      };
    

    4.2 envoy转发获取token的请求

    envoy会过滤根据/token过滤网络请求,转发到jwt_signer容器上去。

    virtual_hosts:
                - name: local_service
                  domains: ["*"]
                  routes:
                    # This is the emulator endpoint for grpc requests.
                  - match: { prefix: "/android.emulation.control.EmulatorController" }
                    route:
                       cluster: emulator_service_grpc
                       max_grpc_timeout: 0s
    
                    # This is the JWT token provider, responsible for handing our secure tokens.
                  - match: { prefix: "/token" }
                    route: { cluster: jwt_signer }
    
                    # The rest will be available under our webapp.
                  - match: { prefix: "/" }
                    route: { cluster: nginx }
                  cors:
                    allow_origin: ["*"]
                    allow_methods: GET, PUT, DELETE, POST, OPTIONS
                    allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout
                    max_age: "1728000"
                    expose_headers: custom-header-1,grpc-status,grpc-message
    
    - name: jwt_signer
        connect_timeout: 0.250s
        type: strict_dns
        lb_policy: round_robin
        load_assignment:
          cluster_name: jwt_signer
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: jwt_signer
                    port_value: 8080
    

    4.3 jwt_signer生成token信息并返回

    verify_password根据传入的用户名和密码,验证有效性,passwd文件中保存了用户名和经过hash算法加密的密码,该文件在创建jwt镜像时已经被拷贝。
    get_token会设置token信息,并将token信息返回

    • kid envoy用来获取解密的密钥
    • iss、aud 这两个属性需要跟envoy.yaml配置文件中的issuer和audiences匹配,用于网络过滤
    • exp 有效登录的截止时间
    • iat 有效登录的起始时间
    • name 用户名
    flags.DEFINE_string('passwd', 'passwd', 'The json password file used to verify access, generated by running gen-passwords.py')
    flags.DEFINE_string('jwk', 'jwt_secrets_priv.jwks', 'The jwk webkey used for signing, generated by running gen-passwords.py')
    
    @auth.verify_password
    def verify_password(username, password):
      logging.info("user: %s - exists: %s",username, username in users)
      if username in users:
        return check_password_hash(users.get(username), password)
      return False
    
    @api.route('/token', methods=['GET'])
    @auth.login_required
    def get_token():
      token = {
               # The KeyID, envoy will use this to pick the proper decryption key.
               'kid' : private_key[0],
               # Both the 'iss' and 'aud' must match what is expected
               # in the envoy.yaml configuration
               # under "issuer" and "audiences", without it the token will be rejected.
               'iss' : 'android-emulator@jwt-provider.py',
               'aud' : 'android.emulation.control.EmulatorController',
               # we give users 2 hours of access.
               'exp' : datetime.now() + timedelta(hours=2),
               'iat' : datetime.now(),
               'name' : auth.username()
              }
      return jwt.encode(token, private_key[1], algorithm='RS256')
    

    4.4 web client gRPC远程调用时携带token信息

    当web应用程序每次进行gRPC调用时会添加token信息,例如调用sendKey

    emulator_controller_grpc_web_pb.js

    proto.android.emulation.control.EmulatorControllerPromiseClient.prototype.sendKey =
        function(request, metadata) {
      return this.client_.rpcCall(this.hostname_ +
          '/android.emulation.control.EmulatorController/sendKey',
          request,
          metadata || {},
          methodInfo_EmulatorController_sendKey);
    };
    

    emulator_web_client.js

    class EmulatorWebClient extends GrpcWebClientBase {
    ...
      rpcCall = (method, request, metadata, methodinfo, callback) => {
        const authHeader = this.auth.authHeader();
        const meta = { ...metadata, ...authHeader };
        const self = this;
        return super.rpcCall(method, request, meta, methodinfo, (err, res) => {
          if (err) {
            if (err.code === 401) self.auth.unauthorized();
            if (self.events)
              self.events.emit("error", err);
          }
          if (callback) callback(err, res);
        });
      };
    ...
    }
    

    4.5 envoy验证token信息,并转发到emulator

    envoy支持JWT Authentication,HTTP filter过滤器可以被用来验证JSON Web Token (JWT),验证signature、audiences和issuer,还可以用来检测有效的登录时间,如果JWT验证失败,这次网络请求会被拒绝访问。filter的名字需要设置成"envoy.filters.http.jwt_authn"。

    • providers 指定了JWT应该如何验证,JWKS公钥的位置等
    • rules 指定了match匹配规则

    envoy会过滤"/android.emulation.control.EmulatorController"的网络请求,转发到 emulator-jwt provider处,通过本地公钥文件/etc/jwt_secrets_pub.jwks解密token信息,验证issuer和audiences属性是否匹配,如果验证成功则网路请求会被放过。

    envoy.yaml

    
    static_resources:
      listeners:
      - name: tls_redirect
        address:
          socket_address:
            address: 0.0.0.0
            port_value: 8080
        filter_chains:
        - filters:
          - name: envoy.http_connection_manager
            config:
              stream_idle_timeout: 0s  # Needed for streaming support.
              codec_type: auto
              stat_prefix: ingress_http
              route_config:
                virtual_hosts:
                - name: backend
                  domains:
                  - ["*"]
                  routes:
                  - match:
                      prefix: "/"
                    redirect:
                      path_redirect: "/"
                      https_redirect: true
              http_filters:
              - name: envoy.router
                config: {}
      - name: tls_secure
        address:
          socket_address: { address: 0.0.0.0, port_value: 8443 }
        filter_chains:
        - filters:
          - name: envoy.http_connection_manager
            config:
              codec_type: auto
              stat_prefix: ingress_http
              route_config:
                name: local_route
                virtual_hosts:
                - name: local_service
                  domains: ["*"]
                  routes:
                    # This is the emulator endpoint for grpc requests.
                  - match: { prefix: "/android.emulation.control.EmulatorController" }
                    route:
                       cluster: emulator_service_grpc
                       max_grpc_timeout: 0s
    
                    # This is the JWT token provider, responsible for handing our secure tokens.
                  - match: { prefix: "/token" }
                    route: { cluster: jwt_signer }
    
                    # The rest will be available under our webapp.
                  - match: { prefix: "/" }
                    route: { cluster: nginx }
                  cors:
                    allow_origin: ["*"]
                    allow_methods: GET, PUT, DELETE, POST, OPTIONS
                    allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout
                    max_age: "1728000"
                    expose_headers: custom-header-1,grpc-status,grpc-message
              http_filters:
              # We setup a JWT authentication endpoint in front of the gRPC engine.
              # This will enforce JWT token validation before we forward the gRPC call
              - name: envoy.filters.http.jwt_authn
                config:
                  providers:
                    emulator-jwt:
                      issuer: android-emulator@jwt-provider.py
                      audiences:
                      - android.emulation.control.EmulatorController
                      local_jwks:
                        # The secrets that are used by the token service to properly
                        # sign the jwt tokens.
                        filename: /etc/jwt_secrets_pub.jwks
                  rules:
                  - match: { prefix: "/android.emulation.control.EmulatorController" }
                    requires: { provider_name: "emulator-jwt" }
              - name: envoy.grpc_web
              - name: envoy.cors
              - name: envoy.router
    
          tls_context:
            common_tls_context:
              tls_certificates:
                - certificate_chain:
                    filename: "/etc/cert.crt"
                  private_key:
                    filename: "/etc/key.key"
      clusters:
      - name: emulator_service_grpc
        connect_timeout: 0.250s
        type: strict_dns
        lb_policy: round_robin
        http2_protocol_options: {}
        load_assignment:
          cluster_name: emulator_service_grpc
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: emulator
                    port_value: 8556
      - name: jwt_signer
        connect_timeout: 0.250s
        type: strict_dns
        lb_policy: round_robin
        load_assignment:
          cluster_name: jwt_signer
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: jwt_signer
                    port_value: 8080
      - name: nginx
        connect_timeout: 0.25s
        type: strict_dns
        lb_policy: round_robin
        load_assignment:
          cluster_name: nginx
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: nginx
                    port_value: 80
    admin:
      access_log_path: "/dev/stdout"
      address:
        socket_address:
          address: 0.0.0.0
          port_value: 8001
    

    5 多账户多模拟器认证

    5.1 Header消息头中添加标识

    根据不同的用户名,返回不同的Header标识

      authAndroidVersion = () => {
        if(this.user == "USER1"){
          return { AndroidVersion: "android9" };
        }
        if(this.user == "USER2"){
          return { AndroidVersion: "android10" };
        }
    }
    

    grpc请求时添加Header标识

      rpcCall = (method, request, metadata, methodinfo, callback) => {
        const authHeader = this.auth.authHeader();
        const authAndroidVersion = this.auth.authAndroidVersion();
        const meta = { ...metadata, ...authHeader, ...authAndroidVersion};
        const self = this;
        return super.rpcCall(method, request, meta, methodinfo, (err, res) => {
          if (err) {
            if (err.code === 401) self.auth.unauthorized();
            if (self.events)
              self.events.emit("error", err);
          }
          if (callback) callback(err, res);
        });
      };
    

    5.2 envoy过滤转发

    根据不同额Header标识,转发到不同的cluster,对应到不同的模拟器版本

    - match:
                      prefix: "/android.emulation.control.EmulatorController"
                      headers:
                      - name: AndroidVersion
                        exact_match: android9
                    route:
                       cluster: emulator_service_grpc_9
                       max_grpc_timeout: 0s
    
                  - match:
                      prefix: "/android.emulation.control.EmulatorController"
                      headers:
                      - name: AndroidVersion
                        exact_match: android10
                    route:
                       cluster: emulator_service_grpc_10
                       max_grpc_timeout: 0s
    

    5.3 docker-compose-build启动多个模拟器

    emulator_9:
        image: emulator_emulator_9:latest
        build:
          context: ../../src_9
          dockerfile: Dockerfile
        networks:
          envoymesh:
            aliases:
              - emulator_9
        devices: [/dev/kvm]
        shm_size: 128M
        expose:
          - "8556"
    
      emulator_10:
        image: emulator_emulator_10:latest
        build:
          context: ../../src_10
          dockerfile: Dockerfile
        networks:
          envoymesh:
            aliases:
              - emulator_10
        devices: [/dev/kvm]
        shm_size: 128M
        expose:
          - "8557"
    

    相关文章

      网友评论

          本文标题:Android模拟器认证流程

          本文链接:https://www.haomeiwen.com/subject/fmohphtx.html