-
Introduction
In my previous job we used to use pms, as our password manager to store account on our servers. But it is a bit buggy at some times, not ergonomic(eg: no search...) and completely outdated (and even its successor - cpm - is still Beta software, for many years, and is probably an other abandonned project).
With the password manager of our dream, we would be able to:
- Use it in a CLI environment
- Order the different accounts into group, and be able to work on a given group
- View an account while someone else is editing an other one
I first came up with kedpm which is a nice password manager, but we cannot use it the way we want to. Let me First explain how do we want to use it.
The PM is hosted on a computer on our lan, and we can only connect to it via ssh with a key, then laumch the PM utility.
This means at the same time, we might have several instances of the PM.
Now, if someone modifies the database in one instance of the PM, theses changes have to take effects on every other PM instances.
Unfortunetly it wasn't the case with kedpm, which leads to problem like this:
Say UserA and UserB are using kedpm at the same time (two instances).
- UserA makes a change
- shortly after, UserB saves the Database
Changes from UserA won't be saved. And this is a unacceptable
Kedpm interface is like a shell, and we liked it. I especially liked it cause I can make use of gnuscreen to automatically retrieve some information in the window running the PM.
Since I wasn't able to find an other PM which could fill our needs, I started to make something on my own, which will exactly behaves as we want. It has to be simple, (no dependency), light. and should not fall where the other did... That's for the how sampm was borned.
-
What's the idea behind sampm?
The password manager is, for us, a way to store private information about our servers. Here is an exemple of sensitive data we can have:
-------------------------------------- Title: truc@dnts.net Username: truc URL: A.B.C.D Password: eiC0gei5A Notes: this account actually doesn't exist.. --------------------------------------We want the PM to behave as a kedpm, and more precisely as a shell, for that we'll simply make use of a bash (yes I make use of bash specific features) and custom functions to work with the database:
- view/search
- export
- (import)
- set the Database Read-Only/Read-Write
- modify
- backup
The database will actually only be a normal filesystem, the device is encrypted using LUKS, and to be able to save the encrypted DB, we'll use a loopfile. That way the whole database will be on a single file.
To sum up:
A loop file -> which 'is' an encrypted partition -> with ext2 as the File System -> mounted in the user's home directory -> using unix arborescence as the way to classify accounts -> special shell fonctions to view/edit the database -
Setup
-
Required utilities
- cryptsetup (with LUKS)
- loop support
- losetup
- bash (yes bash)
- sed, awk...(common CLI utilities)
-
Commands
-
The pm group
You'll find in this section the different commands needed to setup the whole thing, you can, of course, adapt them to better suits your needs.
Let's first create a group to restrict the usage of sampm to the people you want:
# addgroup pmWe now create the users we want to use the sampm, or just add existing one(s) to the pm group:
# useradd -m truc -d /home/truc -G pm # or if the user truc already exists # gpasswd -a truc pm -
Preparing the loopfile
We now have to prepare the loop file, with appropriate rights. Note that root privileges are not required for this step.
$ umask 077 $ dd if=/dev/urandom of=/home/truc/trucDB.img bs=1k count=10000 10000+0 records in 10000+0 records out 10240000 bytes (10 MB) copied, 2.3127 seconds, 4.4 MB/s $ ls -l trucDB.img -rw------- 1 truc truc 10240000 2008-09-29 11:34 trucDB.imgWe've created a 10MB files, which is probably enough for most use.
We're now going to 'mount' this loopfile, for that you may have to load the loop module, as well as creating the /dev/loop0 device:
# modprobe loop # mknod /dev/loop0 b 7 0Then,
# losetup /dev/loop0 /home/truc/trucDB.img -
Preparing the encrypted device
We also want to encrypt the content of this device. That's were cryptsetup, with its LUKS enter the game:
# cryptsetup -y luksFormat /dev/loop0 WARNING! ======== This will overwrite data on /dev/loop0 irrevocably. Are you sure? (Type uppercase yes): YES Enter LUKS passphrase: Verify passphrase: Command successful.You may want to create an additionnal passphrase just in case.
# cryptsetup -y luksAddKey /dev/loop0 Enter any LUKS passphrase: key slot 0 unlocked. Enter new passphrase for key slot: Verify passphrase: Command successful.The encrypted device is ready, to begin working with it, we need to open it with cryptsetup:
# cryptsetup luksOpen /dev/loop0 trucDB Enter LUKS passphrase: key slot 0 unlocked. Command successful.Which creates this 'map', that we can use as any other block device:
# ls -l /dev/mapper/ total 0 crw-rw---- 1 root root 10, 62 2008-09-09 11:50 control brw-rw---- 1 root disk 254, 0 2008-09-25 12:37 trucDB -
Initialasing the device
I've chosen ext2 as the filesystem. But use the FS of your choice :)
Given that every file is small (on more than 610 accounts I have (@work), the biggest file is 162 bytes, while the average is of 73 bytes), we'll inevitably waste some space. I try to limit this waste by specifying the smallest blocksize I can on this filesystem.
# mke2fs -m 1 -b 1024 -i 1024 /dev/mapper/trucDB mke2fs 1.40-WIP (14-Nov-2006) Filesystem label= OS type: Linux Block size=1024 (log=0) Fragment size=1024 (log=0) 9488 inodes, 9484 blocks 0 blocks (0.00%) reserved for the super user First data block=1 Maximum filesystem blocks=9961472 2 block groups 8192 blocks per group, 8192 fragments per group 4744 inodes per group Superblock backups stored on blocks: 8193 Writing inode tables: done Writing superblocks and filesystem accounting information: done This filesystem will be automatically checked every 29 mounts or 180 days, whichever comes first. Use tune2fs -c or -i to override. -
Configuring sudo
To be able to mount/open and to close/umount the encrypted loopfile, the user needs root rights to execute some commands. We'll use the sudoers file to configure what the user can do via sudo:
# visudoThen, append these lines to the sudoers file:
Cmnd_Alias PM_TRUC_MOUNT = /bin/mount /dev/mapper/trucDB, /bin/umount /dev/mapper/trucDB Cmnd_Alias PM_TRUC_REMOUNT = /bin/mount -o remount?rw?nodev?noexec?noauto?sync /dev/mapper/trucDB, /bin/mount -o remount?ro?nodev?noexec?noauto?sync /dev/mapper/trucDB Cmnd_Alias PM_TRUC_START = /sbin/losetup /dev/loop0 /homuue/truc/trucDB.img, /sbin/cryptsetup luksOpen /dev/loop0 trucDB Cmnd_Alias PM_TRUC_STOP = /sbin/losetup -d /dev/loop0, /sbin/cryptsetup luksClose trucDB truc ALL=NOPASSWD: PM_TRUC_START, PM_TRUC_STOP, PM_TRUC_MOUNT, PM_TRUC_REMOUNT -
Preparing the mountpoint
To be able to mount the DB, you first need to add this to /etc/fstab:
/dev/mapper/trucDB /home/truc/trucDB ext2 ro,nodev,noexec,noauto,sync 0 0We need to allow the user the write to its database..
$ umask 077 $ mkdir ~/trucDB $ sudo mount /dev/mapper/trucDB # chmod 700 /home/truc/trucDB # chown truc:truc /home/truc/trucDB
-
The pm group
-
The script sampm
You can find the script here, copy it to e.g.: /usr/local/bin, then:
# chgrp pm /usr/local/bin/sampm # chmod 650 /usr/local/bin/sampm -
Tada!!
We're done with the setup, now I'm going to show you how to use it.
-
Required utilities
-
Usage
-
Command option
For now, every command will be run as a user of the pm group.
Here is what you got if you don't specify an action to do for the script:
$ sampm You failed! [backup|start|softStop|shell|fixrcfile|forceStop|version]Let's talk a bit about these options:
- backup
- make a backup and deletes [too] old ones
- start
- Do everything needed to get the encrypted loop device mounted on the ${USER}DB directory in the user's home
- softstop
- Umount the encrypted loop file unless there are other interactive sampm instances(shell)
- shell
- Do a start then start a modified interactive shell to start working on the password database
- fixrcfile
- If you change the script so that the .bashrc-sampm is changed, use this option to update the MD5SUM_OK variable in the script(see dedicated section below)
- forcestop
- Kill all other instances of interactive sampm(shell), and then umount the encrypted loop file
- version
- print the version, as well as my nickname as the author (samlt aka truc)
-
sampm shell
-
Basics
Inline help is rather short, that's why I'm going to say a few words about the commnds here:
sampm: > help List of available commands: Usual commands: cd, rm, rmdir, mv, mkdir, pwd, help ls [ -r ] [file|dir[ file|dir[ ...]]] : ls [recursive] More specific ones: new [title] : add a new account show [-r] : show an account [search for it recursively] edit title : edit an account changeURL title [...] : change the URL (or the IP) of one or more account For the commands requiring the FS to be writable, you might need these: _setDB_rw : remount the FS Read-Write _setDB_ro : remount the FS Read-Only Administrative tasks: save : make a backup of today of the loop file to ~/pmBackup/ showFriends : show who is also 'connected' to sampm checkDB [-v] : check and report uncorrect file in the DB [verbose] samexport [csv|html] [DIR1[ DIR2[...]]] : make a csv(default)/html export (to STDOUT unless redirected) [ Read-Only DB ] sampm: >When starting sampm, it'll only ask for the LUKS passphrase if the encrypted is not aleady opened with cryptsetup.
One important thing, you may wana know, is that the shell you're in when using sampm shell, is a normal shell, it's actually bash. The only two big differences, is that the environment variable PATH is empty, and that you're not supposed to use anything else than the function (shell functions) mentionned in the help. This will make modification to the database far less error prone.
Some of the shell builtin have been redefined, while others have been hidden (you can still use them with the 'builtin' shell command.
You'll also notice that the Database is mounted READ-ONLY by default, to modify it you need to set it READ-WRITE (some of the commands do it for you (new/edit..). You cannot edit the password database, in two different sampm session at the same time. If you try, a warning will tell you who has already set the DB READ-WRITE. You have to wait till it, set it READ-ONLY again( you can --force if you're sure, nobody's really editing the DB.
Being in a normal shell also means, you can do some crasy things with all the unix command line utility. REMEMBER THAT YOU SHOULD NOT USE ANYTHING ELSE THAN THE COMMANDS LISTED IN THE HELP TO EDIT THE DATABASE. But sometimes, you just need more power into your hands.. And it's good to know it's there.
If you modify the password database without the utility made on that purpose, you can make some mistakes in the account files, and the script can fix them for you, you'll have to fix them yourself. Anyway, I'll show you later an example of what you can easily do with the command line.
In the following, if a command description contains [Need READ-WRITE], this means it will first check, if the DB is not already RW, if it is, it will tell you who has locked it, if it's not you(in which case the action can't continue). If it's not, it will either ask you to set it RW prior to retry the command (commands rm,mkdir,rmdir..), or it will set the DB READ-WRITE just the time you need to modify the DB, and set it READ ONLY again as soon as possible (commands new,edit...)
-
cd, ls, scd, show, mkdir, mv, rm, rmdir
- cd
- If you go outside the database, it will bring you back in, to avoid unwanted action
- ls
- Can take one or more file/dir as argument as well as the -r switch to list recursively the account(need to be in first position). 'ls' shows the title, the Username and the URL of every listed account
- show
- Can take a regex to match a the title of a given account in the current directory, and its subdirectories if the swith -r is added (need to be in first position)
- mkdir
- [Need READ-WRITE] The same as the usual mkdir command
- mv
- [Need READ-WRITE] The same as the usual mv command, except that it check the title is correct if you use it as a way to rename an account
- rm
- [Need READ-WRITE] The same as the usual rm commnad
Additionnally to these basic commands, we'll see in the next section the commands to view/edit the accounts
-
new, edit, changeURL
- new
- [Need READ-WRITE] Takes nothing or the title of the account as an argument (will ask for it if no argument specified)
- edit
- [Need READ-WRITE] Takes the title of the account to edit as argument, the account has to exist and to be correct (no syntax error) in order to edit it
- changeURL
- [Need READ-WRITE] Takes a list of account as argument, ask for the old and the new URL, and make the change where it has to
For the two first commands, you're asked for each field one by one, with the old value specified in square brackets when it makes sens.
For the edit command, if nothing is entered, then old value is kept.
-
_set_DB_ro, _set_DB_rw
As I said before, if you want to edit the password database, you first need to set it READ-WRITE, but, in order to let people also editing the DB, you have to set it READ-ONLY after as soon as you can(ie remove the lock)
- _set_DB_ro
- Set the DB READ-ONLY. Note that you cannot do this if you're not the one who set it READ-WRITE, unless you add the --force flag
- _set_DB_rw
- Set the DB READ-WRITE. Note that you can't do this, if it is already set READ-WRITE by someone else
Tips&Tricks: You can press ctrl-c whenever you want, it will check if the DB is set RW, and set it back RO if it's possible (warns you otherwise). This also works while using interactive commands such as edit or new.
-
samexport
This was something we need at my job, and that's why that's in. But this can sometimes be usefull. if the first argument to this function is csv or html, then the output format will be either csv (comma seperated field) or html (strict).
Further arguments are supposed to be the different directory to include in the export, eg:
samexport html dir{2..3} ../otherdir ../dirA/directo*iesIf the export type is not specified then it is set to csv by default
if no directories are given as argument, then the export is made on the current working directory(and its subdirectories)
If export type is set to html, and the output is redirected to a file (stdout no connected to a terminal), then the whole html source is generated instead of just the lines <tr>.
If data contains 'comma's then it is replace with __C:o:M:a__ in the csv output.
I'll show you below how/why the csv export can be so much usefull:)
sampm: > samexport test/ samlt@12.35.49.47,12.35.49.47,samlt,pio1EU!ue#,ssh account truc@fooforums.com,http://forums.foo.com,truc,jlrç9812U,No longer used__C:o:M:a__ kept for historical purpose truc@mailbox.org,https://mailbox.org,truc,ouednts,best webmail ever [ Read-Only DB ] sampm: >In the csv export, fields are '''Title - URL - Username - Password - Notes''' respectively.
-
Via ssh
In my previous job, this PM was intended to be used, by several different users connecting via ssh to the host hosting the DB, as a specific user for which sampm was configured.
For this to work (sampm is interactive), it needs to have a tty allocated. that's why we force it via ssh options:
ssh -t truc@myhost sampm shell
-
Basics
-
Backups
-
Background
You should not do clear text backup, so the only file you should backup is the loop file (in our example it's trucDB.img, in truc's home)
In order not to have too many backups, at each connection to the sampm shell, every backup from the last month, but the last one, is deleted. The one kept is renamed to only keep the year and the month in its name.(YYYY-MM).
You can also do that via the command line by issuing:
sampm backupThis is also doable within the sampm shell, see next section.
-
save
Those who've payed attention to what I said, probably noticed I haven't talked about one (shell) command yet: save. This command does the same as issuing 'sampm backup' on the command line, (see previous section), So I won't detail it more.
-
Background
-
Command option
-
Advanced usage
-
Introduction
Now it's time for the fun part, taking advantage of the real shell environment, unix utilities and such.. :). You won't find here an introduction to sed/awk or anything, but a few examples on how powerfull theses utilities can be.
I will also show how to set up sampm for several users on the same machine, and how you can add some functionnalities to one while keeping them untouched for others
-
Redundant data
Imagine, you've imported your account from an other package manager, and now, you'd like to know if you have redundant data between fields (Username, URL, Notes...). Here is how you can find account with, for example, redundant information between the Notes and the Username fields. (equal?)
To achieve this, we'll use the 'csv' export feature, parse the output with awk, and print the first field (Title, if the fifth field (Notes) is equal to the third one (Username):
sampm: > samexport some/dir | /usr/bin/awk -v FS=',' '{ if ($5 == $3) print $1 }'We have to specify the full path to awk because PATH is empty in the sampm shell.
If we wanted to find out which account verified Notes == URL, then we would only have to replace $3 with $2 in the above command.
-
Security policy
Imagine now you have a lot of account for different machines on your LAN, You may want to avoid having the same password set for more than one account, (eg: identical root password for two different machines).
Here is how you can get the list of account with their identical password (grouped together):
samexport some/dir | /usr/bin/awk -v FS=',' '{ print $1,$4 }' | /usr/bin/sort -k 2 | /usr/bin/uniq -f 1 --all-repeated=separateYou can do almost everything you want now, just feel the power;)
-
Taking advantage of the multiser feature
If you configure sampm to be use for different user, you can also define some functions, specific to one given user. Let say this user is sam, you first need to add this line:
#add the following line: MD5SUM_SAM_OK= MD5SUM_OK=...at the beginning of the script, then in the checkrc function, add a case for the user sam, so the MD5 checksum is made with the right variable (MD5SUM_SAM_OK)
Now to add some specific code/function, search forward for the line # User specific settings/functions..., add a case for the user sam, with the function you want, and also define the USER_SPECIFIC_HELP so that those changes also appear in the help
Once you've got what you want, you have to update the MD5SUM_SAM_OK= line, added earlier at the beginning of the script, you can do it manually, or use the fixrcfile switch for sampm I'll describe in the next section
-
The fixrcfile switch
If you modified the the checkrc function, chances are you also need to update the MD5SUM_*OK at the beginning of the script to reflect the changes. The fixrcfile is there to help you to do so:
Suppose you modified it in a way that is not specific to one given user, you can update the line by issuing:
# sampm fixrcfile "" old line: MD5SUM_OK=43fd3719a929dc3c8277e02a15d270ff new line: MD5SUM_OK=29d270ffc43fd3719a93c8277e02a15dSuppose now the changes you've made are user specific, e.g. for the user sam, you'd have to run:
# sampm fixrcfile sam old line: MD5SUM_SAM_OK=429dc33fd3719a9c8277e02a15d432fa new line: MD5SUM_SAM_OK=f3c7fcbacfa3134c44f8ddd320af5503You'll note that I ran the two above command as root, but you just need to run them with enough right to write to the script
-
Introduction
-
Syntax
Since you're asking, you'll find here the syntax of an account file. You may need it, if you want to make a massive import, without the tools provided with the sampm shell:
The name of the file is actually the Title of the account, the fourth first lines (other are ignored) should be like this:
Ligne 1 => 'Username: ' Ligne 2 => 'URL: ' Ligne 3 => 'Password: ' Ligne 4 => 'Notes: 'Note that there is exactly one space after the colon, otherwise the file is considered to be incorrect, and will be ignored by sampm.
Here are other rules, theses lines should follow:
- Title
- Only the following characters are allowed in the title: [A-Za-z0-9@=_.-]. Can't be empty!
- Password
- Nothing special...
- Username
- Can't be empty
- URL
- Can't be empty
- Notes
- Nothing special...