Android模拟器认证流程

2020-04-06  本文已影响0人  AI科技智库

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

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)

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信息返回

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"。

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"
上一篇下一篇

猜你喜欢

热点阅读