Deploying Google Upvote in 2021

The current state of Upvote

Google Upvote is a lightweight management UI and ‘sync server’ for Google’s Santa. Together these two are designed to allow an organization to deploy application allowlisting in a manner that causes minimal interruption.

While Google Santa is under constant development, Google Upvote’s latest change was in 2019. This reflects the reality of enterprises maintaining software. A note on a recent open issue cites “There has been a significant amount of work put toward this on the internal codebase. However, for unrelated technical reasons, GitHub releases have been put on hold for some time now. I’ll leave this open as a tracker though, because my understanding is that the eng team intends to resume GitHub releases at some point in the future” (https://github.com/google/upvote/issues/65).

Although alternatives ‘sync-servers’ are offered (https://github.com/google/santa#sync-servers) they are either much less featured, such as Moroz, or try to pull multiple services together. With this in mind in is often useful to deploy the old Upvote to see what its features are to build parity. The unfortunate state however is that Google Upvote won’t simply build anymore.

There are several reports about the Bazel configuration provided with Upvote not being supported any longer (https://github.com/google/upvote/issues/62, https://github.com/google/upvote/issues/59, https://github.com/google/upvote/issues/46 just to name a few). None of these issues have been brought to a successful conclusion and seem to be a mix of incompatibility with new versions of Bazel and incompatibility with certain libraries on old versions of Bazel.

Additionally, there is a ‘staging’ branch that is present, also not updated, and has been merged into without passing builds. This includes references to libraries that can’t be pulled in by Bazel along with half built functionality.

The following are the steps taken to allow Upvote to build locally. I would not suggest that Upvote is production ready software at this point as it relies on outdated packages that likely contain vulnerabilities. However, it may be of interest to some to see how it works.

Deploying the Upvote server

Upvote at this point is best deployed as a Docker container. This is done to ensure you get the specific versions of libraries needed to run the software. There is some nice previous work at https://www.praetorian.com/blog/implementing-application-whitelisting-with-google-santa-and-upvote-part-2, however this work currently won’t deploy.

You’ll need to install Docker if you haven’t on your base OS. The topic of installation is covered in many other places and is not covered here. After that you can create a Dockerfile similar to the following

from ubuntu:18.04

ARG project_id="upvote-exampe"
ENV PROJ_ID=$project_id

RUN apt-get update
RUN apt-get install -y openjdk-11-jdk curl git vim gnupg
RUN apt-get install -y --no-install-recommends \
   bash-completion \
   g++ \
   zlib1g-dev \
   unzip \
   python-pip python-dev \
 && curl -LO "https://github.com/bazelbuild/bazel/releases/download/0.26.0/bazel_0.26.0-linux-x86_64.deb" \
 && dpkg -i bazel_*.deb


RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-xenial main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg |  apt-key add -
RUN apt-get update && apt-get install google-cloud-sdk -y
RUN mkdir /app

RUN pip install six

WORKDIR /app

RUN git clone https://github.com/google/upvote.git
#RUN cd upvote && git checkout staging
RUN cd upvote && sed -i 's/USER_EMAIL_DOMAIN.*$/USER_EMAIL_DOMAIN = "gmail.com"/g' upvote/gae/settings.py
RUN cd upvote && sed -i "s/XXX\-REPLACE\-WITH\-PROJECT\-ID\-XXX/$project_id/g" upvote/gae/settings.py

# Change this to use python not infer it
RUN sed -i 's/\.\/manage\_crons\.py/python manage_crons.py/g' upvote/init_project.sh
# Remove the last line
RUN sed -i '$ d' upvote/init_project.sh
# Append our new build string
RUN echo "bazel run upvote/gae:monolith_binary.deploy --incompatible_depset_union=false" >> upvote/init_project.sh
# Remove broken deps
# Remove //net/proto2/python/public dep
RUN sed -i '/.*proto2.*/d' upvote/upvote/gae/cron/BUILD
# Remove //apphosting/api/app_identity dep
RUN sed -i '/.*app_identity.*/d' upvote/upvote/gae/cron/BUILD

Setting up the Google Cloud Project

Google Cloud is a pretty straight forward platform and we won’t be configuring much here. What you will need to do is the following:

  • open Google Cloud (it requires an account if you haven’t made one before).
  • Select the down arrow next to current in use project
  • Select ‘New Project from the prompt that appears
  • Then give the project a memorable name, you’re going to need this name in the next step.

Deploying the container

Once the project is done being created, change the ENV PROJ_ID value within the Dockerfile to match the name of the project you just created.

  • Build your Docker image: docker build -t upvote_build .
  • Run your Docker container (this is designed to deploy upvote, no ports are needed): docker run -it upvote_build

Once you do this you should be dropped to a bash shell (the default entry point for the Ubuntu upstream image). You’ll have to initialize your Google Cloud environment on the container.

  • Run the command: gcloud init
  • Answer any questions about the account used to setup your Google Cloud project
  • Navigate to the URL provided by the previous command to authorize access to your project in your hosts browser.

At this point you’re now ready to initialize the project. This will create the necessary Google Cloud resources as well as run Bazel to build and deploy the project.

  • If you have never initialized the project before (on any container) run: ./init_project.sh
  • However, if you HAVE initialized the project previously, you won’t be able to overwrite the existing keystore, as these must be globally unique. To resolve this do the following before running ./init_project.sh.
    • Open the init_project.sh in a text editor: vi init_project.sh
    • Find the following two lines and comment them out by adding a ‘#’ in front of them (started at line 35)
gcloud kms keyrings create ring --location=global
gcloud kms keys create virustotal --purpose=encryption --keyring=ring --location=global

If all went well, you should be informed that project was deployed succesfully and you can navigate to it

Changes to make this work

We automatically fill in the email domain to gmail in upvote/gae/settings.py. This is the most common use case for testing.

USER_EMAIL_DOMAIN = 'gmail.com'

We also automatically fill in the required production settings in upvote/gae/settings.py. They will look similar to the following:

HOSTNAME = 'upvote-example.appspot.com'
PROJECT_ID = 'upvote-example'
DATASTORE_BACKUP_BUCKET = 'upvote-example'

Additionally, we remove some of the Bazel requirements that don’t seem to work

  • Remove upvote/gae/cron/BUILD: “//net/proto2/python/public”
  • Remove cron/BUILD: “//apphosting/api/app_identity”,

Lastly we modify the init_project.sh file to update python’s path and run the following build command:

bazel run upvote/gae:monolith_binary.deploy --incompatible_depset_union=false

After running init_project.sh the build/deploy process will take around 15 minutes. When completed you’ll get a message like:

You can stream logs from the command line by running:
  $ gcloud app logs tail -s default

To view your application in the web browser run:
  $ gcloud app browse


Updates are available for some Cloud SDK components.  To install them,
please run:
  $ gcloud components updatepspot.com

As it indicates running gcloud app browse will get you the URL where your upvote app is now hosted!

Giving yourself admin access

One last thing that may be of interest is to modify the upvote/gae/utils/group_utils.py file

  • Open this file up in a text editor: vi upvote/gae/utils/group_utils.py
  • Find the following lines (Starts at line 45)
class GroupManager(AbstractGroupManager):
  """An static implementation of the groups interface."""
  _GROUPS = {
      'admin-users': []
  }
  • Modify it to add the name of the user you’re logging in with (typically this is the same as your google account. For example:
class GroupManager(AbstractGroupManager):
  """An static implementation of the groups interface."""
  _GROUPS = {
      'admin-users': ['example@gmail.com']
  }
  • Run python manage_crons.py enable groups

You may also want to manually run the cron job in GCloud. You can find this option under your project->Compute->App Engine->Cron Jobs.

Once there you should be able to run the cron/roles/sync job by clicking ‘Run now’. After this you should be able to access the /admin/ endpoint of the UI. Happy Hunting!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s