- you are running SensorsIot/IOTstack with InfluxDB as one of the containers
- you want to enable authentication for your InfluxDB databases.
Note: This tutorial is specific to SensorsIot/IOTstack.
- the environment means environment variables in either:
~/IOTstack/services/influxdb/influxdb.env
; or~/IOTstack/docker-compose.yml
I define the following alias in my .profile
and I use it throughout this tutorial:
alias influx='docker exec -it influxdb influx -precision=rfc3339'
Having that alias installed lets me type influx
, press return, and get straight into the influx CLI.
Typing
exit
or pressing Control+D terminates the influx CLI and returns to the normal user shell.
The alias is a shortcut for the following commands:
$ docker exec -it influxdb bash
# influx -precision=rfc3339
The -precision=rfc3339
argument tells the influx CLI that I would prefer to see dates in human-readable form. If you omit that argument then you will see nanoseconds since 1970-01-01 which, to me, is as close to utterly useless as makes no difference.
This tutorial also assumes that you do not have any existing databases so it starts by creating two. One database will be provided with access controls but the other will be left alone so that the behaviour can be compared.
However, you need to understand that enabling authentication in InfluxDB is all-or-nothing. If you have any existing InfluxDB databases, you will need to:
- define access rights for all of your databases; and
- provide credentials to processes like Node-Red and Grafana that access your databases.
If you do not do this, your existing Node-Red flows, Grafana dashboards and other processes that write to or query your databases will stop working as soon as you activate authentication in Step 4 below.
Create two databases named "mydatabase1" and "mydatabase2":
$ influx
> CREATE DATABASE "mydatabase1"
> CREATE DATABASE "mydatabase2"
Typing
influx
didn't work? See useful alias above.
Define an administrative user. In this example, that user is "dba" (database administrator) with the password "supremo":
> CREATE USER "dba" WITH PASSWORD 'supremo' WITH ALL PRIVILEGES
- Key point: the mixture of "double" and 'single' quotes is intentional and required.
Define some garden-variety users:
> CREATE USER "nodered_user" WITH PASSWORD 'nodered_user_pw'
> CREATE USER "grafana_user" WITH PASSWORD 'grafana_user_pw'
You can define any usernames you like. The reason for using "nodered_" and "grafana_" prefixes in these examples is because those are common candidates in an IOTstack environment. The reason for the "_user" suffixes is to make it clear that a username is separate and distinct from a container name.
The user "dba" already has access to everything but, for all other users, you need to state which database(s) the user can access, and whether that access is:
- READ (aka read-only)
- WRITE (aka write-only)
- ALL (implies both READ and WRITE)
> GRANT WRITE ON "mydatabase1" TO "nodered_user"
> GRANT READ ON "mydatabase1" TO "grafana_user"
- Key point: you CREATE a user once but you need to GRANT access to every database to which that user needs access.
Once you have finished defining users and assigning access rights, drop out of the influx CLI:
> exit
$
Make sure you read the warning above, then edit the environment to add this key:
INFLUXDB_HTTP_AUTH_ENABLED=true
Put the change into effect by "upping" the container:
$ cd ~/IOTstack
$ docker-compose up -d influxdb
Recreating influxdb ... done
The up
causes docker-compose
to notice that the environment has changed, and to rebuild the container with the new settings.
-
Note: You should always wait for 30 seconds after a rebuild for InfluxDB to become available. Any time you see a message like this:
Failed to connect to http://localhost:8086: Get http://localhost:8086/ping: dial tcp 127.0.0.1:8086: connect: connection refused Please check your connection settings and ensure 'influxd' is running.
it simply means that you did not wait long enough. Be patient!
Start the influx CLI:
$ influx
Unless you have also set up the INFLUX_USERNAME
and INFLUX_PASSWORD
environment variables (described later under Authentication Hints), your session will not be authenticated as any user so you will not be able to access either database:
> USE mydatabase1
ERR: unable to parse authentication credentials
DB does not exist!
> USE mydatabase2
ERR: unable to parse authentication credentials
DB does not exist!
- Key point: This is what will happen to any of your pre-existing databases if you enable authentication without a lot of care. You must define users and access rights for all of your databases, and you must provide those credentials to the relevant processes like Node-Red and Grafana.
Authenticate as "nodered_user" and try again:
> AUTH
username: nodered_user
password:
> USE mydatabase1
Using database mydatabase1
> USE mydatabase2
ERR: Database mydatabase2 doesn't exist. Run SHOW DATABASES for a list of existing databases.
DB does not exist!
The "nodered_user" can access "mydatabase1" but not "mydatabase2", albeit with slightly different error messages. You will get the same behaviour for the "grafana_user" (try it).
Authenticate as the "dba" and try again:
> AUTH
username: dba
password:
> USE mydatabase1
Using database mydatabase1
> USE mydatabase2
Using database mydatabase2
The super-user can access both databases.
To get a list of users:
> SHOW USERS
user admin
---- -----
dba true
nodered_user false
grafana_user false
- Key point: you must be authenticated as the "dba" to run SHOW USERS.
To find out what privileges a user has on a database:
> SHOW GRANTS FOR "nodered_user"
database privilege
-------- ---------
mydatabase1 WRITE
- Key point: you must be authenticated as the "dba" to run SHOW GRANTS.
To test grants, you can try things like this:
AUTH
username: nodered_user
password:
> USE "mydatabase1"
Using database mydatabase1
> INSERT example somefield=123
"nodered_user" has WRITE access to "mydatabase1".
> SELECT * FROM example
ERR: error authorizing query: nodered_user not authorized to execute statement 'SELECT * FROM example', requires READ on mydatabase1
"nodered_user" does not have READ access to "mydatabase1".
Authenticate as "grafana_user" and try the query again:
> AUTH
username: grafana_user
password:
> SELECT * FROM example
name: example
time somefield
---- ---------
2020-09-19T01:41:09.6390883Z 123
"grafana_user" has READ access to "mydatabase1". Try an insertion as "grafana_user":
> INSERT example somefield=456
ERR: {"error":"\"grafana_user\" user is not authorized to write to database \"mydatabase1\""}
"grafana_user" does not have WRITE access to "mydatabase1".
Change the privileges for "nodered_user" to ALL then try both an insertion and a query. Note that changing privileges requires first authenticating as "dba":
> AUTH
username: dba
password:
> GRANT ALL ON "mydatabase1" TO "nodered_user"
> AUTH
username: nodered_user
password:
> INSERT example somefield=456
> SELECT * FROM example
name: example
time somefield
---- ---------
2020-09-19T01:41:09.6390883Z 123
2020-09-19T01:42:36.85766382Z 456
"nodered_user" has both READ and WRITE access to "mydatabase1".
-
Some inferences to draw from the above:
- user definitions are global rather than per-database. Grants are what tie users to particular databases.
- setting
INFLUXDB_HTTP_AUTH_ENABLED=true
is how authentication is activated and enforced. If it is false, all enforcement goes away (a handy thing to know if you lose passwords or need to recover from a mess). - as the "HTTP" in
INFLUXDB_HTTP_AUTH_ENABLED
suggests, it applies to access via HTTP. This includes the influx CLI and processes like Node-Red and Grafana.
-
Always keep in mind that the InfluxDB log is your friend:
$ docker logs influxdb
After you enable authentication, there are a couple of ways of speeding-up your daily activities. You can pass the dba username and password on the end of the influx alias:
$ influx -database mydatabase1 -username dba -password supremo
but this is probably sub-optimal because of the temptation to hard-code your dba password into scripts. An alternative is to add these lines to the environment:
INFLUX_USERNAME=dba
INFLUX_PASSWORD=supremo
and then "up" the container as explained above to apply the changes.
Misunderstandings about the scope and purpose of INFLUX_USERNAME
and INFLUX_PASSWORD
are quite common so make sure you realise that the variables:
- do not "set" any username or password within InfluxDB
- only apply to starting the influx CLI – they are just synonyms for the
-username
and-password
parameters on theinflux
CLI command - are not some kind of general-access credentials that apply to everything.
In other words, with INFLUX_USERNAME
and INFLUX_PASSWORD
added to the environment, the following two commands are identical:
$ influx -database mydatabase1 -username dba -password supremo
$ influx -database mydatabase1
The INFLUX_USERNAME
and INFLUX_PASSWORD
variables also work if you start a shell into the InfluxDB container and then invoke the influx CLI from there:
$ docker exec -it influxdb bash
# influx
>
To undo the steps in this tutorial, first set INFLUXDB_HTTP_AUTH_ENABLED=false
and then "up" influxdb. Then:
$ influx
> DROP USER "dba"
> DROP USER "nodered_user"
> DROP USER "grafana_user"
> DROP DATABASE "mydatabase1"
> DROP DATABASE "mydatabase2"
> exit