# HG changeset patch # User Peter Mikkelsen # Date 1595349650 -7200 # Tue Jul 21 18:40:50 2020 +0200 # Node ID 02d9cdddb193f4af304b8fcc3ace9c2683e9b4bc # Parent 5f6b0d956b58b89131a96d478d7fada4038f8993 Updates again: write about a simple fs for trello. write about tls certificates on 9front. change page sub-title. diff --git a/sites/pmikkelsen.com/_werc/config b/sites/pmikkelsen.com/_werc/config --- a/sites/pmikkelsen.com/_werc/config +++ b/sites/pmikkelsen.com/_werc/config @@ -1,3 +1,3 @@ masterSite=pmikkelsen.com siteTitle='Peter's website' -siteSubTitle='A blog' +siteSubTitle='random notes' diff --git a/sites/pmikkelsen.com/plan9/lets_encrypt.md b/sites/pmikkelsen.com/plan9/lets_encrypt.md new file mode 100644 --- /dev/null +++ b/sites/pmikkelsen.com/plan9/lets_encrypt.md @@ -0,0 +1,55 @@ +## How I get tls certificates for 9front + +First of all, I use linux and drawterm for this for now, but +I would like to be able to do it all from 9front at some point. + +## Generate the certificate + +Install certbot on linux and run the following command + + certbot certonly --manual -d pmikkelsen.com -d vps1.pmikkelsen.com + +and do the challenges, they should be easy. + +## Importing the cert and private key + +Start drawterm and login as the hostowner. After this, the filesystem of the linux +system is available at `/mnt/term`. Run the following: + + cd /sys/lib/tls/ + cp /mnt/term/etc/letsencrypt/live/pmikkelsen.com/privkey.pem ./ + cp /mnt/term/etc/letsencrypt/live/pmikkelsen.com/fullchain.pem ./cert + +Now the private key must be converted to one that can be loaded into factotum + + auth/pemdecode 'PRIVATE KEY' privkey.pem | auth/asn12rsa -t 'service=tls role=client' > key + rm privkey.pem + chmod 400 key + +Add the following to `/cfg/$sysname/cpurc` to load the private key on boot. + + cat /sys/lib/tls/key >> /mnt/factotum/ctl + +Done. + +## SMTP over TLS + +I have the following in `/bin/service.auth/tcp25` + + #!/bin/rc + + user=`{cat /dev/user} + exec upas/smtpd -c /sys/lib/tls/cert -n $3 + +Notice I had to put it in the `/bin/service.auth` folder so that it could find the private key. + +## Https with rc-httpd + +I have the following in `/bin/service.auth/tcp443` + + #!/bin/rc + + exec tlssrv -c /sys/lib/tls/cert -l /sys/log/https /bin/service/tcp80 $* + +Again, in the `/bin/service.auth` folder. It simply wraps the plain http service +in a tls wrapper. diff --git a/sites/pmikkelsen.com/plan9/trellofs.md b/sites/pmikkelsen.com/plan9/trellofs.md new file mode 100644 --- /dev/null +++ b/sites/pmikkelsen.com/plan9/trellofs.md @@ -0,0 +1,347 @@ +## A filesystem for viewing trello + +Since my last note [here](http://pmikkelsen.com/plan9/basic_9p_server) +was very simple, here comes +a small program (around 250 lines of C), which makes it +possible to view trello as a read-only filesystem. + +## Usage example + +First set the `trellokey` and `trellotoken` environment +variables to the API key and token from the trello website. + + term% trellokey=1298791723987937123..... + term% trellotoken=923748237497ab798798999999..... + +Then `webfs` must be started so the program can make +requests to the trello REST api. + + term% webfs + +Then `trellofs` itself must be started. It mounts the filesystem under `/mnt/trello`. + + term% trellofs + +If I now go and see which boards I have, I get the following + + term% cd /mnt/trello + term% ls + Beaglebone_Black + Fly + Fysik_Rapport + Guld_kobber + Ideer_til_2._produkt + Lektier + P0 + P1 + P2 + Projekt_Materialer + Spil + Welcome_Board + +The names of the folders are almost like the original board names, except +that that some characters have been replaced by `_`. Lets look at `P1` + + term% cd P1 + term% ls + Backlog + Done + Estimat + In-progress + Info + Master.C_Top_Down_Ting + Sprint_1 + Test + Test_sprint_1 + Todo + +Todo must have something worth looking at + + term% ls + Færdig_rapport_aflevering + Korrekturlæsning + +Those two items are the actual trello "cards", which I think of as tasks. The first +one means "Final report hand in", so let's have a look at that. + + term% cd Færdig_rapport_aflevering + term% ls + description + duedate + url + +There are always three files inside each card folder, but since the information in them +is not mandatory on trello, some of them will be empty. + + term% cat description + + term% cat duedate + 2019-12-18T13:00:00.000Z + term% cat url + https://trello.com/c/u23ZcNAm/9-f%C3%A6rdig-rapport-aflevering + +## Limitations + +The key and token are right now in environment variables, which is somewhat +awkward. Also I would like to be able to actually create and move cards directly +from the filesystem just by creating files, but that is not something I will implement +right now. + +## The code + +The code for the `trellofs` program is listed below. + + #include + #include + #include + #include + #include <9p.h> + #include + + #define MAX_RESPONSE_SIZE (1024 * 1024 * 8) + + char *key; + char *token; + + void + fsread(Req *r) + { + char *str; + + if (r->fid->file->aux == nil) { + readstr(r, ""); + } else { + str = malloc(strlen(r->fid->file->aux) + 2); + sprint(str, "%s\n", r->fid->file->aux); + readstr(r, str); + } + respond(r, nil); + } + + Srv fs = { + .read = fsread, + }; + + JSON * + trelloget(char *endpoint, char *params) + { + int ctlfd, bodyfd; + int n; + int err; + char *buf; + JSON *res; + + res = nil; + bodyfd = -1; + + buf = malloc(MAX_RESPONSE_SIZE + 1); + if(buf == nil) { + perror("malloc"); + return nil; + } + + ctlfd = open("/mnt/web/clone", ORDWR); + if(ctlfd < 0) { + perror("open"); + goto fail; + } + + if(read(ctlfd, buf, 32) < 0) { + perror("read"); + goto fail; + } + + n = atoi(buf); + + sprint(buf, "url https://api.trello.com/%s?key=%s&token=%s", endpoint, key, token); + if(params) + sprint(buf + strlen(buf), "\&%s\n", params); + else + sprint(buf + strlen(buf), "\n"); + + err = write(ctlfd, buf, strlen(buf)); + if(err < 0){ + perror("write"); + goto fail; + } + + sprint(buf, "/mnt/web/%d/body", n); + bodyfd = open(buf, OREAD); + if(bodyfd < 0){ + perror("open"); + goto fail; + } + + err = readn(bodyfd, buf, MAX_RESPONSE_SIZE); + if (err < 0) { + perror("read"); + goto fail; + } + + res = jsonparse(buf); + + fail: + close(ctlfd); + close(bodyfd); + free(buf); + + return res; + } + + char * + escapename(char *str) + { + char *new; + int i, len; + + len = strlen(str); + + new = malloc(len + 1); + for(i = 0; i <= len; i++){ + switch(str[i]){ + case '/': + case ' ': + case ',': + case ':': + case '"': + case '\'': + new[i] = '_'; + break; + default: + new[i] = str[i]; + } + } + return new; + } + + void + addcard(File *dir, JSON *card) + { + JSON *element; + char *filename; + File *carddir; + char *description; + char *url; + char *duedate; + + element = jsonbyname(card, "name"); + filename = escapename(element->s); + carddir = createfile(dir, filename, nil, DMDIR|0555, nil); + if(carddir == nil){ + perror("createfile"); + return; + } else { + element = jsonbyname(card, "desc"); + description = strdup(element->s); + element = jsonbyname(card, "url"); + url = strdup(element->s); + element = jsonbyname(card, "due"); + if (element->t == JSONString) + duedate = strdup(element->s); + else + duedate = nil; + createfile(carddir, "description", nil, 0444, description); + createfile(carddir, "url", nil, 0444, url); + createfile(carddir, "duedate", nil, 0444, duedate); + } + free(filename); + } + + void + addlist(File *dir, JSON *list) + { + JSON *element; + char *filename; + JSON *cards; + JSONEl *card; + char cardsEndpoint[128]; + File *listdir; + + element = jsonbyname(list, "name"); + + filename = escapename(element->s); + listdir = createfile(dir, filename, nil, DMDIR|0555, nil); + if(listdir == nil){ + perror("createfile"); + return; + } else { + element = jsonbyname(list, "id"); + sprint(cardsEndpoint, "1/lists/%s/cards", element->s); + cards = trelloget(cardsEndpoint, "fields=desc,name,url,due"); + if(cards == nil) + return; + + for(card = cards->first; card != nil; card = card->next){ + addcard(listdir, card->val); + } + } + jsonfree(cards); + free(filename); + } + + void + addboard(File *root, JSON *board) + { + JSON *element; + JSONEl *list; + char *filename; + File *boarddir; + + element = jsonbyname(board, "name"); + filename = escapename(element->s); + boarddir = createfile(root, filename, nil, DMDIR|0555, nil); + if(boarddir == nil) { + perror("createfile"); + return; + } + + element = jsonbyname(board, "lists"); + for(list = element->first; list != nil; list = list->next){ + addlist(boarddir, list->val); + } + + free(filename); + } + + void + trelloinit(File *root) + { + JSON *result; + JSONEl *board; + + result = trelloget("1/members/me/boards", "fields=name,lists&lists=open"); + + for(board = result->first; board != nil; board = board->next){ + addboard(root, board->val); + } + jsonfree(result); + } + + void + main(void) + { + JSONfmtinstall(); + + key = getenv("trellokey"); + token = getenv("trellotoken"); + + Tree *tree; + + tree = alloctree(nil, nil, DMDIR|0555, nil); + + fs.tree = tree; + trelloinit(tree->root); + + postmountsrv(&fs, nil, "/mnt/trello", MREPL | MCREATE); + } + +The program can be compiled by hand or by using the following `mkfile`. + + BIN=/usr/glenda/bin/amd64 + + TARG=trellofs + + OFILES=\ + main.$O\ + +