IP Subnet Calculator on Google App Engine

Summary

Over the past few days I put a few hours towards writing a web app to perform some basic CIDR subnet calculations. I wanted to both share the link to it ( https://tools.woohoosvcs.com/subnetcalculator/ ). I also wanted to walk through how I deployed it to Google App Engine.

What is a Subnet Calculator?

When provisioning subnets you typically need to determine your requirements. Determining the size of the subnet based on the number of hosts or what the mask will be can take a little bit of time. This is particularly so when you do not calculate this often. Subnet calculators help save some time on this.

Why did I do it?

I have written a few over the years. My early ones were written in C. Golang has a few libraries though in the new package to help with this. I had to use one the other day and figured why not quite my own. While doing this, why not share how to deploy your own app?

Deploying Golang on Google App Engine

In this, we assume you already have the Google SDK. We walked through this in Hello World From Google App Engine Via PHP.

As with any App Engine app, we need an app.yaml. Mine looks like this

runtime: go112

service: tools

handlers:
 - url: /view\.html
   static_files: view.html
   upload: view.html

I have a main.go file and a static view.html file. Since I already have a default service I am calling this one service. That requires I use a dispatch for my custom domain name.

Don’t forget to add the custom domain to the Settings section!

dispatch:
- url: "tools.woohoosvcs.com/*"
  service: tools

The easiest way to get a Golang app running is as follow. The Google App Engine will run the main function in the main package. Google App Engine sets the PORT environment variable.

package main

func main() {
        port := os.Getenv("PORT")
        if port == "" {
                port = "8080"
                log.Printf("Defaulting to port %s", port)
        }

        http.HandleFunc("/subnetcalculator/", viewSubnetCalculator)

        log.Printf("Listening on port %s", port)
        if err := http.ListenAndServe(":"+port, nil); err != nil {
                log.Fatal(err)
        }
}

// This is the main page to view the Subnet Calculator
func viewSubnetCalculator(w http.ResponseWriter, r *http.Request) {
        http.ServeFile(w, r, "./view.html")
}

The best practice for this would dictate a separate main.go importing your own package but if you were familiar enough with that, you may not need this article.

It is out of scope for this article but “http.HandleFunc(“/subnetcalculator/”, viewSubnetCalculator)” tells Golang that the “/subnetcalculator/” URI be directed to the “viewSubnetCalculator” function. That function simply displays the view.html file

From there we simply run the following. I have tools.woohoosvcs.com under the same parent as dispatch.yaml

cd tools.woohoosvcs.com 
gcloud app deploy

cd ..
gcloud app deploy dispatch/dispatch.yaml

Securing Google App Engine

If you use a Web Application Firewall like Cloudflare, don’t forget to ACL GAE to only allow connections from it. Otherwise, it can be completely bypassed

Google App Engine - Firewall
Google App Engine – Firewall

Final Words

This was just a quick and dirty deploy of a golang app to Google App Engine. As with all the apps though, I run them through Cloudflare or at least some sort of Web Application Firewall.

Hello World From Google App Engine via PHP

Summary

This article builds upon the Running Google App Engine Behind Cloudflare article. Based on that article we have a default service for static content. In Google App Engine, there is only one instance of GAE per project. We can however have multiple services. All of the above is fairly trivial but this article is nuanced with using custom domains.

The Google Docs have various automatic routing that is supposed to work but does not quite seem to work properly. In all fairness, the documentation indicates custom domains only work on http not https. With that said, I could not even get it to work properly without configuring a dispatch.

Configuring Custom Domain

From the last article, we configured www-test.woohoosvcs.com previously but we will walk through it again.

We want to “Add” a custom domain. Walk through the normal setup and in this case it was “www-test.woohoosvcs.com”. DNS records were already in place but it lists the CNAME that should be put in place.

This time we were able to select the domain name and click “disable managed security” to stop that process.

Then flip over to SSL certificates and assign the wildcard to this domain. If you get tired of doing this you can assign a “*.woohoosvcs.com” domain but don’t delete the “wooohoosvcs.com” if you do.

Google App Engine - Custom domains
Google App Engine – Custom domains

Service Routing

Google has quite a few documents I will list below on how it “should work”. And maybe I just had bad luck. I played with it for a good hour and came to the conclusion that I had to use a dispatch for custom domains.

Here are a few articles I went through.

Setting up the Service

Please keep in mind, you must already have a default service to create a non default. We have done that in the previous article.

www-test is fairly simple. The structure is flat. Create a directory “www-test.woohoosvcs.com” and create the following files

index.php

<html>
  <head>
    <title>Hello World from Google App Engine in PHP</title>
  </head>
    <body>
    <h1>Hello World from Google App Engine in PHP!</h1>
    <p>
     <? echo "This output is in php!" ?>
    </p>
  </body>
</html>

app.yaml

runtime: php73

service: www-test

We also need a dispatch/dispatch.yaml

dispatch:
- url: "www-test.woohoosvcs.com/"
  service: www-test

Now deploy!

% gcloud app deploy www-test.woohoosvcs.com/app.yaml dispatch/dispatch.yaml

Your services should then look like this!

Google App Engine / Services
Google App Engine / Services

You can see the default as well as www-test with a dispatch route.

Testing!

Now I pull up https://www-test.woohoosvs.com and expect my php page but I am sadly displeased. My default service responds. This is where I was for about an hour. Even the dispatch was giving me strange results until I loaded it up in curl and checked the headers

...
> GET / HTTP/2
> Host: www-test.woohoosvcs.com
> User-Agent: curl/7.64.1
> Accept: */*
...
< age: 30
< cache-control: public, max-age=600
...

For static pages like the default, Google App Engine appears to cache the results. The dynamic pages like php do not appear to have cache (as expected).

I was able to hit another google app engine front end that did not cache the results to test and it worked

curl -4 -kv https://www-test.woohoosvcs.com/ --resolve www-test.woohoosvcs.com:443:216.58.193.21

By the time I checked it again with the browser the cache had cleared and it was working as expected.

Final Words

Google App Engine is fairly flexible but it does have some nuances when trying to use custom domain names. If you are thinking about deploying something more complex outside of the default service, please do your testing ahead of time.

Running Google App Engine Behind Cloudflare

Summary

I had the need of a fairly static site in my infrastructure ecosystem. I thought, why not write an article about it with the nuance of putting it behind Cloudflare. There are much easier solutions for this static site, including running it off my WordPress server. In any case, this makes a neat introduction to Google App Engine.

In all fairness, this article is derived from Hosting a static website on Google App Engine but puts a slight spin with Cloudflare.

This is also not a usual use case for static hosting.

Requirements

Having a Google Cloud account is a must but that is already assumed. Next we need to download and install the Google Cloud SDK.

For MacOS its fairly simple. It is available here – Google Cloud SDK documentation

Installing the SDK

% pwd
/Users/dwcjr

% curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-270.0.0-darwin-x86_64.tar.gz           

% tar xzf google-cloud-sdk-270.0.0-darwin-x86_64.tar.gz

% ./google-cloud-sdk/install.sh
Welcome to the Google Cloud SDK!
....

# We want to install this because our static site will be PHP based
% ./google-cloud-sdk/bin/gcloud components install app-engine-php

% ./google-cloud-sdk/bin/gcloud init
....
You are logged in as: [[email protected]].

Pick cloud project to use: 
 [1] woohoo-blog-2414
 [2] Create a new project
Please enter numeric choice or text value (must exactly match list 
item):  1
...

Deploying the App

Here I setup a www structure as per the Google article.

% pwd
/Users/dwcjr/Downloads/woohoosvcs.com
% find ./ -type d
./
.//www
.//www/css
.//www/images
.//www/js

% cat app.yaml 
runtime: php73

handlers:
- url: /
  static_files: www/index.html
  upload: www/index.html

- url: /(.*)
  static_files: www/\1
  upload: www/(.*) 

We need to make sure www/index.html exists. Make a simple hello world in it. Something like the following would suffice

<html>
  <head>
    <title>Hello, world!</title>
  </head>
  <body>
    <h1>Hello, world!</h1>
    <p>
      This is a simple static HTML file that will be served from Google App
      Engine.
    </p>
  </body>
</html>

It produces a bit of output but a few minutes later, the app is deployed

% gcloud app deploy
Services to deploy:

descriptor:      [/Users/dwcjr/Downloads/woohoosvcs.com/app.yaml]
source:          [/Users/dwcjr/Downloads/woohoosvcs.com]
target project:  [woohoo-blog-2414]
target service:  [default]
target version:  [20191107t164351]
target url:      [https://woohoo-blog-2414.appspot.com]


Do you want to continue (Y/n)?  Y

Beginning deployment of service [default]...
Default Service Deployed
Default Service Deployed

Custom Hostnames

This is where some trickery happens. Not really, it is fairly straight forward, particularly with Cloudflare. We need to navigate to Settings / Custom Domains and add one. We will use www-test.woohoosvcs.com for this demo.

GAE / Settings / Custom Domains / Add
GAE / Settings / Custom Domains / Add
Walk through the setup with your domains
Walk through the setup with your domains

We then need to hop over to Cloudflare and add the CNAME as requested. Make sure it is setup as a “DNS only”. We do this so Google can validate the domain for its managed certificate. We do not want to use it but it will not allow us to place a custom one otherwise.

www-test CNAME - DNS only
www-test CNAME – DNS only

We are then going to hop on over to GAE’s Certificates section

I will then upload my stored copy from the WordPress site. After doing so an interesting issue happened.

The private key you've selected does not appear to be valid.
“The private key you’ve selected does not appear to be valid.”

This ended up being “XXX PRIVATE KEY” not being “XXX RSA PRIVATE KEY” so I simply modified the BEGIN and END to have RSA and it went through!

We then want to hop back over to Custom Names and disable managed security. This auto generates a certificate and we will be using the origin certificate instead.

Now if we click back on SSL Certificates it will allow us to drill into Cloudflare-Origin and assign.

SSL certificates - Cloudflare-Origin
SSL certificates – Cloudflare-Origin
Save!
Save!

We can then set the www-test.woohoosvcs.com back to Proxy to protect it

www-test / Proxied
www-test / Proxied

The Test!

We can test with curl to make sure it is going through Cloudflare.

% curl -v https://www-test.woohoosvcs.com
*   Trying 2606:4700:20::681a:d78...
* TCP_NODELAY set
* Connected to www-test.woohoosvcs.com (2606:4700:20::681a:d78) port 443 (#0)
.....
*  subject: C=US; ST=CA; L=San Francisco; O=Cloudflare, Inc.; CN=sni.cloudflaressl.com
.....
*  issuer: C=US; ST=CA; L=San Francisco; O=CloudFlare, Inc.; CN=CloudFlare Inc ECC CA-2
.....
* Using Stream ID: 1 (easy handle 0x7fd338005400)
> GET / HTTP/2
> Host: www-test.woohoosvcs.com
> User-Agent: curl/7.64.1
> Accept: */*
> 
* Connection state changed (MAX_CONCURRENT_STREAMS == 256)!
.....
< server: cloudflare
.....

Final Words

It may be a convoluted way but we now have a hosted static site behind Cloudflare using Strict TLS. All of that running on Google App Engine

In this article for the Custom Hostname, it should be possible to disable managed security so that www-test.woohoosvcs.com could start out as a Proxied entry and then associate the certificate. I had issues with that but it could have been me rebuilding the environment too quickly for the lab I was doing.