Ir al contenido principal

Ralsina.Me — El sitio web de Roberto Alsina

Publicaciones sobre server

Cheap man's secret handling

I run a very cheap home serv­er. How cheap? Very, very cheap. Sub Rasp­ber­ry Pi 4 cheap.

It runs a ton of ser­vices, and it al­so works as my "Func­tion­s" serv­er.

What is a func­tions server?

It's a cheap man's AWS Lamb­da which al­lows me to cre­ate small "ser­vices" like this, and de­ploy them au­to­mat­i­cal­ly. It's re­al­ly a game chang­er for sim­ple code avail­abil­i­ty, and it's my favourite way to share func­tion­al­i­ty with oth­er­s.

But some­times a ser­vice re­lies on a 3rd par­ty API, and it needs things like a to­ken to be avail­able. Faasd sup­ports this us­ing their se­cret API. You cre­ate a se­cret like this:

faas-cli secret create whatever

And when you declare in your functions.yml that your function needs a secret:

  lang: python3-fastapi
  handler: ./myfunc
  - whatever

Your code reads a file in /var/openfaas/secrets/whatever and that's all, there is a secret on the server, your app can see it, it's not in the app's code, all good.

Ex­cept ... what hap­pens if you need to re­de­ploy faas­d? You will need to cre­ate the se­cret again! So you need to keep the se­cret some­where.

So­lu­tion: pass

I already use pass to keep many passwords, it's easy to also put secrets there. It manages everything using a git repo, so it's a known factor. You can even do things like add them all inside a faasd/ folder and then recreate them using scripts, like this:

pass faasd/whatever | faas-cli secret create whatever

pass will ask for your mas­ter passphrase, se­cret cre­at­ed. You can even pub­lish your pass re­po since ev­ery­thing in it is en­crypt­ed with gpg, so no­body can re­al­ly read it (don't do that).

So, this so­lu­tion us­es:

  • pass
  • gpg
  • git
  • faasd
  • shell
  • what­ev­er lan­guage and frame­work you use in your code

And ev­ery­thing is seam­less!

I think this is a nice ex­am­ple of how ran­dom tools can con­nect with each oth­er be­cause they all fol­low the unix con­ven­tion about mov­ing things around as tex­t.

How much energy does my office server use?

Just ran in­to this video where the host ex­plains how he's us­ing a home serv­er and how he made it use un­der 23W, and the steps he took to make it qui­et and ef­fi­cien­t.

So, know­ing that our servers are not com­pa­ra­ble I want­ed to check how much pow­er my serv­er used.

Yes, my serv­er is pret­ty un­usu­al, you can read about it here: 1 2 3 4 5

First let's look at MAX­I­MUM pow­er draw for all com­po­nents.

  • Com­put­er (Radxa Ze­ro): 900 mA at 5V
  • HD­D: 500 mA at 5V (x2)
  • Net­work USB adapter: 500 mA at 5V

There is al­so a USB 3.0 hub there but the pow­er us­age is neg­li­gi­ble, prob­a­bly be­low 100 mA at 5V.

So, the max­i­mum pow­er us­age is ~2400 mA at 5V, which is about 12 watts.

When more or less idle, mea­sured with a USB thingie, the Radxa Ze­ro us­es ~200 mA and the Net­work adapter nev­er seems to go above 150 mA, so a low­er bound is maybe about ~1300 mA, or about 6 watts.

And then there's cost. My whole sys­tem (which does ev­ery­thing I want nice­ly AFAIC­S) costs un­der 100 dol­lars. And my pow­er bill from it is, if it runs full throt­tle ALL THE TIME (it does­n't) ... $87

Mind you, that's 87 ar­gen­tini­an pe­sos, or be­tween 50 and 25 USD cents, de­pend­ing on your ex­change rate.

I think I'll man­age.

CORS config for FaaS

Be­cause I want to be able to de­ploy ran­dom python code eas­i­ly to my own server, I have set­up a "Func­tion as a Ser­vice" thing, called faasd (think of it as poor peo­ple's AWS lamb­da). More de­tails on how, why and how it turned out will come in the fu­ture. BUT: this ex­plains how to fix the un­avoid­able headache CORS will give you.


  • The Faasd serv­er runs in some ma­chine, which is prox­­ied by a Ng­inx serv­er avail­able at http­s://­­faas­d.ral­si­­

  • Apps are just HTML pages some­where in­­­side ei­ther http­s://ral­si­­ or some oth­­er sim­i­lar do­­main.

What will happen?

You will set­up your func­tion, test it out us­ing curl, be hap­py it work­s, then set it up in your web app and get an er­ror in the con­sole about how CORS is not al­low­ing the re­quest.

What is CORS and why is it annoying me?

CORS is a way for a ser­vice liv­ing in a cer­tain URL to say which oth­er URLs are al­lowed to call it. So, if the app are in, say, http­s://nom­bres.ralsi­ and the func­tion lives in http­s://­faas.ralsi­ then the ORI­GIN for the app is not the same as the ORI­GIN for the func­tion, so this is a "Cross Ori­gin Re­quest" and you are try­ing to do "Cross Ori­gin Re­source Shar­ing" (CORS) and the brows­er won't let you.

How do I fix it?

There are a num­ber of fix­es you can try, but they all come down to the same two ba­sic ap­proach­es:

Option 1

Make it so the re­quest is not cross-­source. To do that, move the func­tion some­how in­to the same URL as the page, and bob's your un­cle.

So, just change the proxy con­fig so nom­bres.ralsi­­func­tions is prox­ied to the faasd server's /func­tions and change the page to use a re­quest that is not cross-o­rig­in, and that's fixed.

I don't want to do this be­cause I don't want to have to set­up the proxy dif­fer­ent­ly for each ap­p.

Option 2

Have the func­tion re­turn a head­er that says "Ac­cess-­Con­trol-Al­low-O­rig­in: some­thing". That "some­thing" should be the ori­gin of the re­quest (in our ex­am­ple nom­bres.ralsi­ or "*" to say "I don't care".

So, you may say "Fine, I'll just add that head­er in my re­sponse and it will work!". Oh sweet sum­mer child. That will NOT work (at least not in the case of Faas­d)


Be­cause web browsers don't just make the re­quest they want and then look at the head­er­s. They do a spe­cial pre­flight re­quest, which is some­thing like "Hey, server, if I were to ask you to give me /func­tion­s/what­ev­er from this orig­in, would you give me a CORS per­mis­sion or not?"

That re­quest is done us­ing the OP­TIONS HTTP method, and Faasd (and, to be hon­est, most web frame­work­s) will not process those by pass­ing them to your code.

So, even if your func­tion says CORS is al­lowed, you still will get CORS er­rors.

You can see this if you ex­am­ine your browser's HTTP traf­fic us­ing the de­vel­op­er tool­s. There will be an OP­TIONS pre­flight re­quest, and that one does­n't have the head­er.

So, the eas­i­est thing is to add those in the proxy.

So, in my case, in the prox­y's ng­inx.­con­f, I had to add this in "the right place":

  add_header 'Access-Control-Allow-Origin' '*';

What is the right place will vary de­pend­ing on how you have con­fig­ured things. But hey, there you go.

Contents © 2000-2023 Roberto Alsina