Oauth2 Part 2

Findings for part 2

Part one here

ApprovalForce

url := a.conf.AuthCodeURL("state", oauth2.AccessTypeOffline, oauth2.ApprovalForce) this line fixes everything. If you dont use the ApprovalForce option then a refresh_token isn’t created in the token that is returned.

With this token we can now load the token from a file, check the expiry, and create a new token source from that token to create a new valid token.

Google API hates numbers

Under ‘API & Services’ -> Credentials Tab is where you create the OAuth2 Client Ids. In there you specify what Authorized redirect URI’s you want to use. For whatever reason, probably a good one, they do not like using IP’s. Since we need to test against a remote machine running Nester, that means we need some DNS. More on that later.

 // openBrowser(url)
 fmt.Println(url)

That’s all the code change needed to remove the browser opening and instead print the link to follow and initiate the Auth Flow.

Down the rabbit hole

So those parts above basically solve most of the issues caused by the Go OAuth2 library(or my lack of reading ability). Now we get a refresh token that we store locally to use for auth when needed and after the initial login flow, we just run nester run on whatever interval we want and that spits our metrics to influxdb. Sweet.

Except, testing this and also setting was a pain in the ass. Now to make this work remotely, we need a public DNS record that points back to our server running the nester cli. I luckily have a bunch of random domains that I bought over the years and thought it was a perfect time to pull out the poop.software domain. Ok, easy part done. Now we need an SSL certificate because Google hates fun. Rather than buy a cert or worse, buy a wildcard - I decided on using Let’s Encrypt. Eventually, but probably not, we could add the functionality into the cli tool to expose an endpoint with a cert and grab one with Let’s encrypt blah blah. We’re on a mission to get this working so that’s future me problems.

This time we will just use Caddy as a TLS reverse proxy. It can autogenerate the SSL cert we need for our domain, we just have to add a few steps.

Step 1 - Get Namecheap API credentials

This parts pretty straightforward. Using Namecheap we need to generate some API creds so that Caddy can use those for the Let’s Encrypt calls for our cert. Intro docs are here and they’re easy to follow. At the end of it you should have these 3 things:

If you need an easy way to grab that just run curl icanhazip.com and go with that.

Step 2 - Custom Build Caddy Server

We can’t just use the default caddy server because we need a way for it to use our Namecheap credentials so that it can create a txt verification record for Let’s Encrypt. This isn’t as bad as it seems, they have some good tooling for it.

You need to go get xcaddy and install that(I used the go install method). Once you have that installed, you can run:

GOOS=linux GOARCH=amd64 xcaddy build --with github.com/caddy-dns/namecheap

This will output the binary for x86_64 linux, which is what the server runs. Obviously use whatever env vars for you build environment you need. That command will spit out a binary that’s patched for Namecheap Let’s Encrypt. So move that binary to your server and create a Caddyfile:

nester.poop.software {
 reverse_proxy localhost:8080

 tls {
  dns namecheap {
   api_key AWESOME_API_KEY
   user MUH_USERNAME
   api_endpoint https://api.namecheap.com/xml.response
   client_ip IP_ADDRESS_PLZ_DONT_HACK_ME
  }
 }
}

Pretty simple config here. Top part is our domain name we want the server to listen on. Set to reverse proxy to localhost:8080 which is where the nester tool listens to receive the token back after sending the code and the user going through the auth flow. The tls section just copy pasta your api key, username, and ip you setup for namecheap and we should be good.

Cool, so now the flow is this:

  1. Run nester run and it returns a url to open in your browser
  2. You go through the google auth flow which then creates a token with a refresh token.
  3. The redirect url is now https://nester.poop.software which points to a local IP on the network.
  4. That server on that IP is listening on port 443 as a reverse proxy to the nester listener on localhost:8080.
  5. Now the return token has a path back to the nester cli to be able to save the token locally.

Step 2.1 - Linux aside

Oh don’t forget to add firewall rules to your server to allow 80/443 inbound:

sudo firewall-cmd --list-all
sudo firewall-cmd --zone=public --permanent --add-service=http
sudo firewall-cmd --zone=public --permanent --add-service=https
sudo firewall-cmd --reload

Also influxDB if you want to get to the server ui:

sudo firewall-cmd --zone=public --permanent --add-port 8086/tcp
sudo firewall-cmd --reload

Step 3 - Setup InfluxDB

Easiest way to get moving is to use docker-compose. I won’t go over any docker stuff here, google around there’s enough info on it. Here’s my compose file:

version: "3"
services:
  influxdb:
    restart: always
    container_name: influxdb2
    image: influxdb:2.7
    ports:
      - "8086:8086"
      - "8088:8088"
    volumes:
      - influxdb-storage:/var/lib/influxdb
    environment:
      - INFLUXDB_DB=${INFLUXDB_DB}
      - INFLUXDB_HTTP_AUTH_ENABLED=${INFLUXDB_HTTP_AUTH_ENABLED}
      - DOCKER_INFLUXDB_INIT_MODE=setup
      - DOCKER_INFLUXDB_INIT_USERNAME=${INFLUXDB_ADMIN_USER}
      - DOCKER_INFLUXDB_INIT_PASSWORD=${INFLUXDB_ADMIN_PASSWORD}
      - DOCKER_INFLUXDB_INIT_ORG=${INFLUXDB_ORG}
      - DOCKER_INFLUXDB_INIT_BUCKET=${INFLUXDB_BUCKET}
      #- DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=${INFLUXDB_ADMIN_TOKEN}
      - INFLUXDB_LOGGING_LEVEL=debug
      - INFLUXDB_DATA_QUERY_LOG_ENABLED=true

volumes:
  influxdb-storage:

And my .env file - This is automatically picked up by docker-compose

#influxdb
INFLUXDB_HTTP_AUTH_ENABLED=true
INFLUXDB_ADMIN_USER=admin
INFLUXDB_ADMIN_PASSWORD=admin
INFLUXDB_DB=nester
INFLUXDB_ORG=nester
INFLUXDB_BUCKET=nester
INFLUXDB_USERNAME=donald
INFLUXDB_PASSWORD=donald
INFLUXDB_ADMIN_TOKEN=donaldadminsecret

Feel free to change those as needed :D

Step 4 - Finish line

So now you’ll have to go into the InfluxDB UI and create a token to use for nester to push to. In the .nester.yaml file there are fields there to fill in and test it out.

Conclusion

So after this long and ridiculous adventure, we have a tool that can run in a cron, grab data from the Nest API, and push it to an InfluxDb using OAuth2. Now to actually clean this thing up, add some logging, maybe figure out how to deploy all this from Github using Tailscale. Next post will be about nester in general, the design, code, etc.