Expand description

This crate implements a libunftp Authenticator that authenticates against credentials in a JSON format.

It supports both plaintext as well as PBKDF2 encoded passwords.

Plaintext example

[
  {
    "username": "alice",
    "password": "I am in Wonderland!"
  }
]

PBKDF2 encoded Example

Both the salt and key need to be base64 encoded. Currently only HMAC_SHA256 is supported by libunftp (more will be supported later).

There are various tools that can be used to generate the key.

In this example we show two ways to generate the PBKDF2. First we show how to use the common tool nettle-pbkdf2 directly.

Generate a secure salt:

salt=$(dd if=/dev/random bs=1 count=8)

Generate the base64 encoded PBKDF2 key, to be copied into the pbkdf2_key field of the JSON structure.

When using nettle directly, make sure not to exceed the output length of the digest algorithm (256 bit, 32 bytes in our case):

echo -n "mypassword" | nettle-pbkdf2 -i 500000 -l 32 --hex-salt $(echo -n $salt | xxd -p -c 80) --raw |openssl base64 -A

Convert the salt into base64 to be copied into the pbkdf2_salt field of the JSON structure:

echo -n $salt | openssl base64 -A

Alternatively to using nettle directly, you may use our convenient docker image: bolcom/unftp-key-generator

docker run -ti bolcom/unftp-key-generator -h

Running it without options, will generate a PBKDF2 key and a random salt from a given password. If no password is entered, a secure password will be generated with default settings for the password complexity and number of iterations.

Now write these to the JSON file, as seen below. If you use our unftp-key-generator, you can use the -u switch, to generate the JSON output directly. Otherwise, make sure that pbkdf2_iter in the example below, matches the iterations (-i) used with nettle-pbkdf2.

[
  {
    "username": "bob",
    "pbkdf2_salt": "<<BASE_64_RANDOM_SALT>>",
    "pbkdf2_key": "<<BASE_64_KEY>>",
    "pbkdf2_iter": 500000
  },
]

Mixed example

It is possible to mix plaintext and pbkdf2 encoded type passwords.

[
  {
    "username": "alice",
    "pbkdf2_salt": "<<BASE_64_RANDOM_SALT>>",
    "pbkdf2_key": "<<BASE_64_KEY>>",
    "pbkdf2_iter": 500000
  },
  {
    "username": "bob",
    "password": "This password is a joke"
  }
]

Using it with libunftp

Use JsonFileAuthenticator::from_file to load the JSON structure directly from a file. See the example examples/jsonfile_auth.rs.

Alternatively use another source for your JSON credentials, and use JsonFileAuthenticator::from_json instead.

Preventing unauthorized access with allow lists

[
  {
    "username": "bob",
    "password": "it is me",
    "allowed_ip_ranges": ["192.168.178.0/24", "127.0.0.0/8"]
  },
]

Per user certificate validation

The JSON authenticator can also check that the CN of a client certificate matches a certain string or substring. Furthermore, password-less; certificate only; authentication can be configured per user when libunftp is configured to use TLS and specifically also configured to request or require a client certificate through the Server.ftps_client_auth method. For this to work correctly a trust store with the root certificate also needs to be configured with Server.ftps_trust_store.

Given this example configuration:

[
  {
   "username": "eve",
   "pbkdf2_salt": "dGhpc2lzYWJhZHNhbHR0b28=",
   "pbkdf2_key": "C2kkRTybDzhkBGUkTn5Ys1LKPl8XINI46x74H4c9w8s=",
   "pbkdf2_iter": 500000,
   "client_cert": {
     "allowed_cn": "i.am.trusted"
   }
 },
 {
   "username": "freddie",
   "client_cert": {}
 },
 {
   "username": "santa",
   "password": "clara",
   "client_cert": {}
 }
]

we can see that Eve needs to present a valid client certificate with a CN matching “i.am.trusted” and then also needs to provide the correct password. Freddie just needs to present a valid certificate that is signed by a certificate in the trust store. No password is required for him when logging in. Santa needs to provide a valid certificate and password but the CN can be anything.

Structs

This structure implements the libunftp Authenticator trait