푸시 알림을 위한 서버 설정

Google Play에서는 발생하여 기업에 영향을 미칠 수 있는 이벤트에 대한 응답으로 알림을 생성합니다. EMM 솔루션 제공업체는 이러한 알림을 수신하고 고객 관리자에게 알림 또는 기타 메커니즘 등을 제공하여 조치를 취할 수 있습니다.

이 가이드에서는 EMM push 알림만 수신하고 처리하는 데 필요한 서버 인프라를 구성하는 방법을 설명합니다. 가져오기 알림은 이 가이드에 설명된 설정이 필요하지 않습니다.

이 가이드에 설명된 서버 설정 외에도 푸시 알림의 경우 Google API 콘솔에서 적절한 권한을 부여하고 다른 구성 작업을 수행해야 합니다. 자세한 내용은 EMM 푸시 알림 사용 설정을 참고하세요.

예시를 포함한 Google Cloud Pub/Sub에 대한 자세한 내용은 Cloud Pub/Sub 문서를 참조하세요.

푸시 알림이 지정된 HTTPS 엔드포인트 또는 알림 전송을 대기하는 서버로 전송되도록 시스템을 구성할 수 있습니다.

푸시 엔드포인트 구성 정보

푸시 엔드포인트를 구성하려면 유효한 SSL 인증서가 있는 서버가 필요합니다. 이 예시에서는 SSL 인증서를 만들어 인증 기관(CA)에 업로드한 다음 NGINX 서버를 구성합니다. 마지막으로 테스트 코드를 컴파일하고 실행하여 설정이 올바른지 확인합니다.

이 예에서는 리버스 프록시 모드에서 NGINX 서버를 구성하여 Ubuntu 14.04를 사용해 포트 8093에서 실행되는 구독자 앱 (PushSubscriber.java)에 연결하는 방법을 보여줍니다. 기업에서 다른 서버를 사용할 수도 있지만 샘플 설정은 모든 Debian 기반 배포판에서 변경 없이 작동합니다. 다른 배포 (예: RedHat 기반 배포)도 비슷하지만 구성 파일의 위치는 다를 수 있습니다.

알림을 받으려면 먼저 다음 기준을 충족하는 엔드포인트를 구성해야 합니다.

  • 사용자가 도메인을 소유하고 Google API 콘솔에서 소유권을 확인해야 합니다.

  • 포트 443 (SSL)에서 서비스를 실행할 수 있어야 합니다.

  • CA 서명 SSL 인증서가 있어야 합니다. 자체 서명된 인증서는 작동하지 않습니다.

  • 실행 중인 웹 서버는 웹훅을 지원해야 합니다.

엔드포인트가 Google App Engine에서 실행될 필요는 없지만 실행할 수 있습니다.

SSL 인증서 만들기 및 업로드

1. 보안 소켓 레이어 (SSL) 인증서를 생성합니다.

    myusername@myhost:/tmp$ sudo openssl req -x509 -nodes -days 365 \
      -newkey rsa:2048 -keyout cert.key -out cert.crt

그러면 다음과 같은 응답이 생성됩니다. 다음 코드에서 샘플 값(예: push.solarmora.commyusername@myhost)을 실제 서버 이름, 회사, 주소 등으로 바꿉니다. 이 하위 도메인의 A 레코드가 서버를 가리키면 모든 하위 도메인을 사용할 수 있습니다.

    Generating a 2048 bit RSA private key
    ...........................................................................
    .....+++
    writing new private key to 'cert.key'
    -----
    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter '.', the field will be left blank.
    -----
    Country Name (2 letter code) [AU]:GB
    State or Province Name (full name) [Some-State]:England
    Locality Name (eg, city) []:London
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:Solarmora, Inc.
    Organizational Unit Name (eg, section) []:Creative Publications
    Common Name (e.g. server FQDN or YOUR name) []:push.solarmora.com
    Email Address []:admin@solarmora.com

2. 인증서 파일이 생성되었는지 확인합니다.

$ myusername@myhost:/tmp$ ls cert*
cert.crt  cert.key

3. 이 인증서에 서명을 받으려면 서명자에게 업로드할 인증서 서명 요청 (CSR)을 생성합니다.

myusername@myhost:/tmp$ sudo openssl x509 -x509toreq -in cert.crt \
  -out cert.csr -signkey cert.key
Getting request Private Key
Generating certificate request
myusername@myhost:/tmp$ ls cert.*
cert.crt  cert.csr  cert.key

4. CSR 파일의 콘텐츠가 다음과 같은지 확인합니다.

    Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: C=GB, ST=England, L=London, O=Solarmora, Inc.,
        OU=Creative Publications,
        CN=push.solarmora.com/emailAddress=admin@solarmora.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:cc:0f:54:26:3d:d9:17:eb:8f:6c:f7:27:5e:77:
                    64:65:00:db:fe:2a:1f:fa:ea:de:21:7a:c5:5d:87:
                    ...
                    ...
                Exponent: 65537 (0x10001)
        Attributes:
            a0:00
    Signature Algorithm: sha256WithRSAEncryption
         1d:ea:12:b8:c2:6a:d6:f4:6e:92:2f:b9:12:5e:e3:91:15:a0:
         06:b5:81:ce:c5:cf:b7:d2:a7:dd:f2:78:ca:28:8e:21:cd:6d:
         ...
         ...
    -----BEGIN CERTIFICATE REQUEST-----
    MIIC6zCCAdMCAQAwgaUxCzAJBgNVBAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5kMQ8w
    DQYDVQQHDAZMb25kb24xGDAWBgNVBAoMD0FDTUUgQ29ycCwgSW5jLjEYMBYGA1UE
    CwwPQ3JlYXRpdmUgQW52aWxzMRswGQYDVQQDDBJwdXNoLmFjbWUtY29ycC5jb20x
    IjAgBgkqhkiG9w0BCQEWE2FkbWluQGFjbWUtY29ycC5jb20wggEiMA0GCSqGSIb3
    ...
    ...
    -----END CERTIFICATE REQUEST-----

5. BEGIN 줄과 END 줄 사이의 인증서 부분을 CA에 업로드합니다. 정확한 프로세스는 CA에 따라 다르지만 다음 단계가 포함됩니다.

  1. CSR 파일을 CA 사이트에 업로드하거나 파일 콘텐츠를 CA 사이트에 붙여넣습니다. 그러면 CA가 이 데이터를 검증하고 처리합니다.
  2. CA에서 생성한 서명된 인증서를 다운로드합니다.

6. CA의 출력에는 서명된 인증서 자체 및 인증서에 서명할 수 있음을 확인하는 CA 인증서 등 여러 파일이 포함됩니다. 다운로드한 번들의 모든 *.crt 인증서 파일을 단일 번들 파일(예: bundle.push.solarmora.com.crt)에 연결합니다.

$ cat *.crt > bundle.push.solarmora.com.crt

프록시 서버 구성

이 섹션에서는 엔드포인트를 제공하고 모든 수신 요청을 구독자 서버로 전달하도록 NGINX 오픈소스 웹 서버와 역방향 프록시 서버를 구성합니다. 예시로 NGINX를 사용했지만 대신 다른 HTTP 프록시를 사용할 수 있습니다.

1. 서버에 NGINX를 설치합니다.

    $ sudo apt-get update
    $ sudo apt-get install nginx
    $ nginx -v
    $ nginx version: nginx/1.4.6 (Ubuntu)

2. sites-enabled 디렉터리에서 만든 추가 서버 구성 파일이 NGINX에서 처리되도록 하려면 /etc/nginx/nginx.conf를 수정하고 다음을 포함합니다.

    $ include /etc/nginx/conf.d/*.conf;
    $ include /etc/nginx/sites-enabled/*;

3. www-data 사용자는 읽을 수 있지만 다른 사용자는 읽을 수 없는 안전한 위치에 인증서 파일을 복사합니다. 웹 서버를 다른 사용자로 실행하는 경우 사용자 이름을 조정해야 할 수 있습니다.

    $ sudo mkdir -p /var/openssl/push.solarmora.com
    $ sudo mv /tmp/cert.key 
/var/openssl/push.solarmora.com/push.solarmora.com.key $ sudo mv /tmp/bundle.push.solarmora.com.crt
/var/openssl/push.solarmora.com/bundle.push.solarmora.com.crt

4. 새 server 구성을 만듭니다. /etc/nginx/sites-enabled에서 push.solarmora.com을 수정하고 실제 서버의 정규화된 도메인 이름을 파일 이름으로 사용합니다.

server {
   listen 443;
   server_name push.solarmora.com;

   ssl on;
   ssl_certificate
     /var/openssl/push.solarmora.com/bundle.push.solarmora.com.crt;
   ssl_certificate_key
     /var/openssl/push.solarmora.com/push.solarmora.com.key;

   # it is usually very convenient to have separate files for your
   # access and error log to analyze for possible problems
   access_log /var/log/nginx/nginx.push.solarmora.com.log;
   error_log /var/log/nginx/nginx.push.solarmora.com.log;

   location / {
            # assuming the subscriber will run on the same machine
            # on port 8093
            proxy_pass http://localhost:8093;
   }
}

5. NGINX를 다시 시작하여 변경사항을 구현합니다.

    myusername@myhost:/etc/nginx$ sudo service nginx restart
    * Restarting nginx nginx
    ...done.

6. 이제 서버가 구성되었습니다. 구성을 확인하려면 curl를 사용하여 서버를 쿼리해 보세요.

    [myusername@myhost:~]$ curl push.solarmora.com
    <html>
    <head><title>502 Bad Gateway</title></head>
    <body bgcolor="white">
    <center><h1>502 Bad Gateway</h1></center>
    <hr><center>nginx/1.4.6 (Ubuntu)</center>
    </body>
    </html>

다운스트림 서버가 구성되지 않은 경우 예상되는 응답(구성 파일의 localhost:8093)

컴파일 및 실행 예시

이 섹션의 예를 실행하려면 활성 상태의 Google API 콘솔 프로젝트가 필요합니다. 테스트용으로 특별히 프로젝트를 만들고 프로덕션 프로젝트와 분리하는 것이 좋습니다. 테스트 프로젝트를 만든 후에는 서비스 계정을 만들어야 합니다. 서비스 계정 이메일 주소를 기록하고 연결된 .p12 파일을 서버에 저장합니다.

소스 코드 트리 설정

1. play-work.git 저장소를 클론합니다.

    myusername@myhost:~/code$ git clone
    https://github.com/google/play-work.git
    Cloning into 'play-work'...
    Username for 'https://github.com': username
    Password for 'https://username@github.com':
    remote: Counting objects: 110, done.
    remote: Compressing objects: 100% (60/60), done.
    remote: Total 110 (delta 24), reused 95 (delta 9), pack-reused 0
    Receiving objects: 100% (110/110), 23.88 KiB | 0 bytes/s, done.
    Resolving deltas: 100% (24/24), done.
    Checking connectivity... done.
2. Debian 기반 시스템에서는 MavenGoogle 프로토콜 버퍼 컴파일러를 모두 설치합니다.
    $ sudo apt-get install maven protobuf-compiler

3. Maven 및 Google 프로토콜 버퍼 컴파일러가 모두 올바르게 설치되었는지 확인합니다.

    myusername@myhost:~$ mvn -v
    Apache Maven 3.0.5
    Maven home: /usr/share/maven
    Java version: 1.7.0_75, vendor: Oracle Corporation
    Java home: /usr/lib/jvm/java-7-openjdk-amd64/jre
    Default locale: en_US, platform encoding: UTF-8
    OS name: "linux", version: "3.16.0-30-generic", arch: "amd64", family: "unix"
    myusername@myhost:~$ protoc --version
    libprotoc 2.5.0

4. Maven 구성 파일 pom.xml은 프로토콜 버퍼 컴파일러가 /usr/bin/protoc 디렉터리에 설치되어 있다고 가정합니다.

    myusername@myhost:~$ which protoc
    /usr/bin/protoc
그렇지 않은 경우 pom.xml 또는 심볼릭 링크 protoc을 수정하면 됩니다.
    $ sudo ln -s which protoc /usr/bin/protoc
5. 예를 컴파일합니다. mvn clean compile assembly:single를 실행하여 코드를 빌드할 수 있는지 확인합니다. 그러면 emm-notifications-[version-number]-jar-with-dependencies.jar이라는 파일이 생성됩니다. 여기서 [version number]은 예시의 현재 버전입니다. 예를 들면 다음과 같습니다. 1.0-SNAPSHOT
    myusername@myhost:~/code/play-work/examples/emm-notifications$ ls target/*
    target/emm-notifications-1.0-SNAPSHOT-jar-with-dependencies.jar
6. 컴파일된 TestPublisher 코드를 실행할 수 있는지 확인합니다. 코드가 실패할 것으로 예상됩니다.

    myusername@myhost:~/code/play-work/examples/emm-notifications$ java -cp \
      target/emm-notifications-1.0-SNAPSHOT-jar-with-dependencies.jar \
      com.google.android.work.emmnotifications.TestPublisher
    Exception in thread "main" java.lang.IllegalArgumentException:
    You must specify non-default ServiceAccountEmail in
    settings.properties
        at com.google.api.client.repackaged.com.google.common.base.Preconditions.checkArgument(Preconditions.java:119)
        at com.google.api.client.util.Preconditions.checkArgument(Preconditions.java:69)
        at com.google.android.work.emmnotifications.Settings.verifyVariable(Settings.java:129)
        at com.google.android.work.emmnotifications.Settings.getSettings(Settings.java:103)
        at com.google.android.work.emmnotifications.TestPublisher.main(TestPublisher.java:39)

7. settings.properties 파일의 일부 값을 재정의해야 합니다. 이렇게 하려면 파일의 사본을 만들고 다음과 같이 사본의 속성을 수정합니다.

    # This should be your own service account's email address
    ServiceAccountEmail=368628613713-t4hfexampledn5lhpdcu1qqfgio01626@developer.gserviceaccount.com
    ServiceAccountP12KeyFile=/opt/secret/secret.p12
    # This will be the name of the service account
    ProjectName=enterprise-cloud-pub-sub
    SubscriptionName=projects/enterprise-cloud-pub-sub/subscriptions/default
    TopicName=projects/enterprise-cloud-pub-sub/topics/default
    # The push endpoint in your API Console project
    PushEndpoint=https://push.solarmora.com

8. TestPublisher 코드를 다시 실행하여 더 이상 비정상 종료되지 않도록 합니다. 로그 출력에 단일 오류가 표시될 수 있습니다.

게시자 테스트 코드 실행

구독자가 읽을 메시지를 갖도록 알림 게시를 위한 샘플 코드를 실행해야 합니다.

다음 예에서 코드는 my_settings.properties에 지정된 주제를 찾지만 찾지 못하여 주제를 만듭니다. 그런 다음 주제에 메시지를 게시합니다. 이 예에서는 Google Play EMM API에서 보낸 메시지를 에뮬레이션할 수 있는 유용한 테스트 도구를 제공합니다.

    myusername@myhost:~/code/play-work/examples/emm-notifications$ DEVELOPER_CONSOLE_SETTINGS=./my_settings.properties java -cp \
      target/emm-notifications-1.0-SNAPSHOT-jar-with-dependencies.jar com.google.android.work.emmnotifications.TestPublisher
    Feb 27, 2015 1:39:59 PM com.google.android.work.emmnotifications.RetryHttpInitializerWrapper$1 handleResponse
    INFO: RetryHandler: {
      "error": {
        "code": 404,
        "message": "Resource not found (resource=default).",
        "errors": [
          {
            "message": "Resource not found (resource=default).",
            "domain": "global",
            "reason": "notFound"
          }
        ],
        "status": "NOT_FOUND"
      }
    }

    Feb 27, 2015 1:39:59 PM com.google.android.work.emmnotifications.TestPublisher main
    INFO: Topic projects/enterprise-cloud-pub-sub/topics/default doesn't exists, creating it
    Feb 27, 2015 1:40:02 PM com.google.android.work.emmnotifications.TestPublisher main
    INFO: Topic projects/enterprise-cloud-pub-sub/topics/default created
    Feb 27, 2015 1:40:02 PM com.google.android.work.emmnotifications.TestPublisher main
    INFO: Publishing a request: {messages=[{data=CjEKFQoIMTIzMjEzMjESCXJpZ2h0IG5vdxIWY29tLmdvb2dsZS5hbmRyb2lkLmdtcxgA}]}

구독자 테스트 코드 실행

구독자 테스트 코드는 TestPublisher 코드로 게시된 메시지를 수신할 수 있는지 확인합니다.

1. 코드가 최신 상태이고 컴파일되었는지 확인한 후 구독자 테스트 코드를 실행합니다.

    myusername@myhost:~/code/play-work/examples/emm-notifications$ DEVELOPER_CONSOLE_SETTINGS=./my_settings.properties 
java -cp target/emm-notifications-1.0-SNAPSHOT-jar-with-dependencies.jar
com.google.android.work.emmnotifications.PushSubscriber Feb 27, 2015 1:46:37 PM com.google.android.work.emmnotifications.PushSubscriber main INFO: Will be using topic name: projects/enterprise-cloud-pub-sub/topics/default, subscription name:
projects/enterprise-cloud-pub-sub/subscriptions/default Feb 27, 2015 1:46:38 PM com.google.android.work.emmnotifications.PushSubscriber main INFO: Trying to get subscription named projects/enterprise-cloud-pub-sub/subscriptions/default Feb 27, 2015 1:46:38 PM com.google.android.work.emmnotifications.RetryHttpInitializerWrapper$1 handleResponse INFO: RetryHandler: { "error": { "code": 404, "message": "Resource not found (resource=default).", "errors": [ { "message": "Resource not found (resource=default).", "domain": "global", "reason": "notFound" } ], "status": "NOT_FOUND" } } Feb 27, 2015 1:46:38 PM com.google.android.work.emmnotifications.PushSubscriber main INFO: Subscription doesn't exist, will try to create projects/enterprise-cloud-pub-sub/subscriptions/default Feb 27, 2015 1:46:43 PM com.google.android.work.emmnotifications.PushSubscriber main INFO: Created: { "ackDeadlineSeconds" : 600, "name" : "projects/enterprise-cloud-pub-sub/subscriptions/default", "pushConfig" : { "pushEndpoint" : "https://push.solarmora.com" }, "topic" : "projects/enterprise-cloud-pub-sub/topics/default" }
이제 구독자가 실행 중이며 수신 메시지를 수락할 준비가 되었습니다.

2. 게시자를 다시 실행하면 새 메시지가 로그에 추가됩니다.

    Feb 27, 2015 1:47:24 PM com.google.android.work.emmnotifications.PushSubscriber$1 handle
    INFO: Raw request: {"message":{"data":"CjEKFQoIMTIzMjEzMjESCXJpZ2h0IG5vdxIWY29tLmdvb2dsZS5hbmRyb2lkLmdtcxgA",
"attributes":{},"message_id":"71571141246"},"subscription":"/subscriptions/enterprise-cloud-pub-sub/default"} Feb 27, 2015 1:47:24 PM com.google.android.work.emmnotifications.PushSubscriber$1 handle INFO: Pubsub message received: { "attributes" : { }, "data" : "CjEKFQoIMTIzMjEzMjESCXJpZ2h0IG5vdxIWY29tLmdvb2dsZS5hbmRyb2lkLmdtcxgA", "message_id" : "71571141246" } Feb 27, 2015 1:47:24 PM com.google.android.work.emmnotifications.PushSubscriber$1 handle INFO: Message received: product_approval_event { common_event_information { enterprise_id: "12321321" event_notification_sent_timestamp: "right now" } product_id: "com.google.android.gms" approved: false }
메시지가 제대로 수신되고 처리되었습니다.