Bluish Coder

Programming Languages, Martials Arts and Computers. The Weblog of Chris Double.


2012-12-18

Authentication and Encryption in Inferno OS

In my post about sharing resources in Inferno I used the -A command line switch to commands to disable authentication. This allowed anyone to make connections and the data was passed across them unencrypted. This post explains how to set up an Inferno system to use authentication and to encrypt connections.

Signing Server

For authentication to work each machine that wishes to communicate securely must have a certificate signed by a common signing authority. That signing authority should be a separate machine with the sole task of running the signing processes.

The signing server needs to have a public and private key used for signing certificates. To generate these keys run the command auth/createsignerkey from within the Inferno shell. The command takes a name argument which is the name that will appear as the signer name in the certificates generated by the server:

; auth/createsignerkey example.com

This command creates a /keydb/signerkey file containing the keys. It's important to note that if this file is regenerated then any existing certificates issued by the server will be invalidated as the private key will have been lost. Keep this file safe!

The act of authentication involves checking that the signing server and the user know a shared secret. The secrets are kept in the /keydb/keys file. The file, which starts off empty, is protected by a password. Run the svc/auth program to start the services which manage this file. You will need to provide the password, or create one if none exists:

; svc/auth
Key: ...enter password/passphrase...
Confirm key: ...confirm password/passphrase...

Each user to be authenticated must be set up on the signer by running the auth/changelogin command. This prompts for a password and an expiry date for the user. The expiry date is used as the maximum valid date for the certificates created for that user. At least one user must exist. Create a user on the signer for the current user on the signing machine (This might be inferno or your host username, depending on your setup). You need to run auth/changelogin for each remote or local user to be authenticated. That user can then run passwd command at a later time to change the password.

; auth/changelogin myuser
new account
secret: ...enter password/passphrase... 
confirm: ...confirm password/passphrase...
expires [DDMMYYYY/permanent, return = 17122013]: ...enter expiry date or confirm default...
change written

Obtaining a client certificate

A client of authenticated Inferno services needs to have a certificate obtained from the signing server. The request for this certificate is done using getauthinfo. There is a also a wm/getauthinfo variant that uses the Inferno GUI to prompt for required data. Other that that it is functionally the same as getauthinfo.

The keyname argument to getauthinfo should either be default or of the form net!example.com where 'example.com' is the hostname of the server with the resources being accessed. This must exactly match the name given to bind or mount when accessing the service. This is how those commands know to find the certificate file. The default keyname is used by file servers as the default certificate file for incoming connections.

When running getauthinfo you are prompted for the signing server to be used, the username on the signing server that you wish to get the certificate for, a password for that username (which was created with auth/changelogin previously, and whether you want to save the certificates in a file. If you answer yes to 'save in file?' then an actual physical file is created in the users /usr/username/keyring directory. If 'no' is answered then a fileserver is started on the client which serves a secure temporary file bound over the name in the given directory. This is removed when the file is unbound or Inferno is stopped.

For the following example I create a default file on a new server so it can act as a file server to remote clients. I'm using file.example.com as the hostname for this file server and example.com as the hostname for the signing server - replace this with the actual servers name. In these examples I assume that ndb/cs has been run to provide network services:

; getauthinfo default
use signer [$SIGNER]: example.com
remote user name [myuser]: ...enter username...
password: ...enter a password....
save in file [yes]: yes

On a client machine that wants to access resources on the file server run getauthinfo, but using the net!hostname form of the keyname, so bind and mount can find it.

; getauthinfo net!file.example.com
use signer [$SIGNER]: example.com
remote user name [myuser]: ...enter username...
password: ...enter password for user...
save in file [yes]: yes

The request for certificates only needs to be done once, assuming 'save in file?' was set to 'yes'. The certificate information is valid until the expiry date set for the user.

Initiating secure connections

Both server and client can now connect securely now that they have certificates provided by the common signing server. An example from my previous post on sharing resources was sharing a directory. On the file server:

; listen 'tcp!*!8000' { export '/usr/myuser' & }

This no longer has the -A switch to disable authentication. To connect from the client:

; mount 'tcp!file.example.com!8000' /mnt/remote

This command will actually fail with a message saying it couldn't find 'keyring/default' if we haven't generated a default certificate. This is because our connection string includes a port and our getauthinfo previously did not. Our getauthinfo used 'net!file.example.com' but we're connecting to 'net!file.example.com!8000'. The default for mount is to look for a file with the name exactly as specified with the host portion of the connection string and falling back to 'default'.

We can solve this by generating a default file with getauthinfo, or one for net!file.example.com!8000 which includes the port, or specifying the exact file on the mount command line by using the -k switch:

; mount -k 'net!file.example.com' 'tcp!file.example.com!8000' /mnt/remote

These mount examples will authenticate using the certificates but will not encrypt the session. To encrypt as well as authenticate use the -C switch to mount to set the hashing and encryption algorithm to use. See the ssl documentation for the supported algorithms. For example:

; mount -C sha1/rc4_256 'tcp!file.example.com!8000' /mnt/remote

All the examples in my previous post can now be done with authenticated and encrypted sessions.

Conclusion and additional notes

The examples in this post show explicitly passing the hostname of the signing server in places. The file /lib/ndb/local contains information for default servers. Changing the SIGNER key to the signing servers hostname will avoid the need of always specifying this information.

The signing server should have the sole task of running svc/auth. It shouldn't be used for other tasks. Doing so results in issues where parts of svc/auth exit when a client fails to authenticate resulting in the signing server no longer handling authentication. This is briefly warned about in svc/keyfs:

Keyfs should be started only on the machine acting as authentication server (signer), before a listener is started for signer(8). Note that signer and keyfs must share the name space. Furthermore, no other application except the console should see that name space.

This mailing list post writes more about this. This is why I use a separate file server to export the path in the listen statement in the examples here.

The following sources have additional information and examples on using authentication and encryption in Inferno. In particular the man pages for the commands have a lot of detail:

Tags


This site is accessable over tor as hidden service mh7mkfvezts5j6yu.onion, or Freenet using key:
USK@1ORdIvjL2H1bZblJcP8hu2LjjKtVB-rVzp8mLty~5N4,8hL85otZBbq0geDsSKkBK4sKESL2SrNVecFZz9NxGVQ,AQACAAE/bluishcoder/-44/


Tags

Archives
Links