Create/update fleet variables from device

Hello!

Summary

I need some help with my project balenaRancher. This project builds a Rancher k3s server and k3s worker nodes. It uses release pinning to allow two different releases, a rancher and a ranch-hand, to operate in the same fleet.

Problem

I am trying to automate some of the setup that is currently a manual and confusing process. There are two variables that the ranch-hand workers need to configure themselves and connect to the rancher. One is a node-token (K3S_TOKEN) and the other is the server endpoint url (K3S_URL). The information to create these variables is on the server. I am trying to use the balena python sdk to create fleet wide device variables. Here is my clunky code…

import os
from time import sleep
from os.path import exists, isfile
import balena

TOKEN_PATH = '/var/lib/rancher/k3s/server/node-token'
BALENA_APP_ID = os.environ.get('BALENA_APP_ID')
BALENA_API_KEY = os.environ.get('BALENA_API_KEY')
api = balena.Balena()

def addK3SToken(token):
  api.auth.login_with_token(BALENA_API_KEY)
  fleet_vars = api.models.environment_variables.application.get_all(BALENA_APP_ID)
  list_from = [i['name'] for i in fleet_vars]
  var_id = [i['id'] for i in fleet_vars if i['name'] == 'K3S_TOKEN']
  if 'K3S_TOKEN' in list_from:
    api.models.environment_variables.application.update(var_id[0], token)
  else:
    api.models.environment_variables.application.create(BALENA_APP_ID, 'K3S_TOKEN', token)


while not exists(TOKEN_PATH):
  sleep(2)

if isfile(TOKEN_PATH):
  token = open(TOKEN_PATH, 'r').read().strip()
  addK3SToken(token)
else:
  print('I done effed up, Sarge.')

:skull_and_crossbones: If I run it as is, using the BALENA_API_KEY variable from the server device, I get the following error:

Traceback (most recent call last):
  File "token-wrangler.py", line 27, in <module>
    createSuperToken(token)
  File "token-wrangler.py", line 17, in createSuperToken
    api.models.environment_variables.application.update(var_id[0], token)
  File "/usr/lib/python3.6/site-packages/balena/models/environment_variables.py", line 432, in update
    endpoint=self.settings.get('pine_endpoint')
  File "/usr/lib/python3.6/site-packages/balena/base_request.py", line 197, in request
    raise exceptions.RequestError(response._content)
balena.exceptions.RequestError: b'Unauthorized'

:unicorn: It works perfectly if I change the BALENA_API_KEY to an API key that I generated in the balenaCloud console.

Questions

  1. Am I understanding correctly that the BALENA_API_KEY on the device should allow this action?
  2. Can this task be done another way?

Note

I have tried the same thing using the Balena JavaScript SDK… no joy :frowning: (I even tried a shell script using cURL commands! :bomb:)

I would appreciate any help I can get!
//Sam

UPDATE
I found my own answer in the docs [here] (Security - Balena Documentation). :exploding_head:

TL;DR -
The BALENA_API_KEY on the device is limited in scope and only grants read for the fleet variables. So I can’t use it to create/update fleet variables. Makes sense from a security standpoint.

I still think the effort is worthwhile. Maybe have the user create an api key and then remove after config? Is that any easier to figure out than the current instructions? I think I’m just talking to myself at this point… :expressionless:

1 Like

Glad you found the docs useful @sameureka! Please let me know if you were struggling to find what you needed in there the first time around? Maybe they can be made easier to navigate :blush:

@Lizzieepton thanks for reaching out! I actually read the relevant bits in the docs before posting my question but it didn’t really sink in until I reasoned out the different contexts the API operates in, device/local vs cloud/fleet actions. It makes sense to me now, you wouldn’t want to have an api key on the device that could have so much potential for bad actors to give you a really bad day. For my project I’m suggesting that users generate an API key in the console to use during setup of the Rancher server and worker nodes, then remove the key for added security. I’ve documented the new setup procedures and in my testing it works really well. It mostly configures itself automatically once you add in the API key.

The docs were clear and easy to find… my understanding just had to catch up!

Thanks again,
//Sam
balenaRancher

That makes sense, thank you for explaining! Good luck with the rest of your project it looks fantastic so far :slight_smile: