Is it possible to create a new user from a command on the image itself?

My use-case:

  • I am building a service which will run alongside Drone and expose to Prometheus some metrics that are not provided by Drone itself (specifically - per-repo, the latest build-state: running, success, failure, or unknown).
    • It will do this by consuming Webhook events about state changes, and by periodically (and at startup) calling the “Repos List” and “Build Info” APIs to sync status in case an event is missed.
  • In order to do so, the service needs an Token with which to call the Drone API
  • If this service is to be cold-started automatically alongside Drone (say, with Kubernetes), then either it must be possible to create a user with a known token (sourced from a Kubernetes secret, which both the server and the metrics server could access), or it must be possible to create a user and write the resulting token out to a shared volume (which the metrics service can then consume)
  • Technically, drone user add <name> --machine --token=<token> would achieve the first case. But the drone CLI does not appear to exist on the Drone image, cannot be installed on it (since it appears to be based on Busybox. Citation: a StackOverflow question, linking to which is forbidden on this forum. Google the obvious form of the related question to find it) - and in any case, in order to be able to “call itself” correctly, a drone CLI installation on the drone image would still need to also use sqlite to query the user_hash (column that holds the token) out of the database.

If I really reached, I could set up Drone so that the database is in a shared volume, then start a separate image (with access to apt; and thus both to sqlite3, and curl and so drone) that runs sqlite3 on /data/database.sqlite to query out the user_hash for the user created due to DRONE_USER_CREATE=username:<username>, then use that token to carry out appropriate drone commands - but it seems really wrong to rely on directly reading a database from a shared volume to bootstrap authentication. Is there a better way that I’m missing?

(Note that, in my particular case, I do concede that I could drop the periodic sync behaviour, solely rely on webhook events, and be probably-mostly-accurate. Nevertheless, I believe that the problem of “cold-start creating a user that an external service can use to authenticate API requests, without a human needing to copy-paste anything from a webbrowser” is still a valid request)

Hello @scubbo

This definitely sounds like an interesting project, please share it if possible!

DRONE_USER_CREATE allows the token to be specified DRONE_USER_CREATE | Drone, so you should be able to read the token however you like, and pass it to the drone server along with username.

Let me know if this doesn’t do what you need.

Ah, neat - DRONE_USER_CREATE will do exactly what I want, thanks!

Just looping back here to say that I haven’t forgotten this (though I’ve been on vacation, so development hasn’t been a priority), but it does look a little more challenging than I first thought.

As you say, DRONE_USER_CREATE does permit defining the token to be used to create a user. It should be possible to auto-generate a token in a secret (as here), and have that secret referenced in two places:

  • In the DRONE_USER_CREATE environment variable for Drone itself, which will result in the creation of a machine-user with that token
  • By the “sidecar” service, which will use it to make API calls to create a “monitoring” user for its own use

However, in order for the value of a secret to form part of the environment DRONE_USER_CREATE, it would be necessary for the secret-value to itself be an environment variable, and then Kubernetes’ dependent variables feature. That is, we would set the following envionmenbt variables:

primary-user-token=<...>
DRONE_USER_CREATE=username:foo,admin:true,machine:true,token:$(primary-user-token)

Unfortunately, AFAICT, Drone’s Helm chart’s extraSecretNamesForEnvFrom value (for inserting env values from secrets) gets inserted below the env configmap (I’m limited to only 2 links in a post, so check drone/charts/blob/master/charts/drone/templates/deployment.yaml lines 53-59 in the drone repo), and also supported by experimentation), and since Dependent Environment Variables are order-dependent, this means that the DRONE_USER_CREATE env variable will contain a literal $(primary-user-token), not the contents of that environment variable.

There are probably ways around this - I might be able to create a Helm template which reads-or-auto-generates the appropriate token, and then insert that template into both the “raw” secret that the monitoring service will use, and into the DRONE_USER_CREATE env variable; or I could just have a single Kubernetes secret containing the full DRONE_USER_CREATE string, and have the monitoring service parse out the token by splitting on colons and commas - but this really seems like I’m missing an easier option.

I’ll keep poking away at this, but I’d be grateful for a second perspective if I’ve missed anything obvious.

EDIT: I tried renaming the variable aa-primary-user-token in case being first lexicographically would result in the desired behaviour - no luck!

Some further thoughts on dependent variable ordering: An Advanced API for Environment Variables in Helm Charts | by dastrobu | Medium