# Encrypt swap file



## noobster (Jan 26, 2014)

The handbook is clear about how to create a swap file and also how to encrypt a swap partition, but is it possible to encrypt a swap file? A quick Google search did not result in anything useful.


----------



## Deleted member 9563 (Jan 26, 2014)

I can't answer you question, but I don't see the gains. Wouldn't encryption add so much overhead that the file would be useless in practice?


----------



## noobster (Jan 26, 2014)

OJ said:
			
		

> I can't answer you question, but I don't see the gains. Wouldn't encryption add so much overhead that the file would be useless in practice?



No, encrypting the data is negligible compared to the time it takes to write it to the disk. The gain is that no sensitive information can be leaked through the swap file.


----------



## NewGuy (Jan 26, 2014)

Since a partition and file are treated pretty much the same way I thnk you can use the same steps to encrypt a swap file as you would use to encrypt a partition. Just subsitute the /dev/device pathname for the path of a file you want to use as a swap file.


----------



## noobster (Jan 26, 2014)

I tried a few things but it does not seem to be possible. First of all, instead of using


```
swapfile="/swap"
```
in /etc/rc.conf as is suggested by the handbook, I added


```
md none swap sw,file=/swap 0 0
```
to /etc/fstab. This worked. Second, I tried to enable encryption by appending .eli to md (as you would do when encrypting a swap partition), however, this did not work. I also tried variations like md0.eli, /dev/md.eli, and /dev/md0.eli to no effect.

Any suggestions what else I might try?


----------



## wblock@ (Jan 27, 2014)

That code is new.  The combination of md and eli may just not have been considered.  A bug report would not be out of line.


----------



## noobster (Jan 29, 2014)

Bug report has been created: http://www.freebsd.org/cgi/query-pr.cgi?pr=186252


----------



## gesperon (Jan 31, 2014)

This worked for me:


```
$ dd if=/dev/zero of=swap0 count=100 bs=1m
$ mdconfig -a -t swap -f swap0 
$ kldload geom_eli
$ geom eli init md0
Enter new passphrase:
Reenter new passphrase:

Metadata backup can be found in /var/backups/md0.eli and
can be restored with the following command:

        # geli restore /var/backups/md0.eli md0

$ geom eli attach md0
Enter passphrase:
$ geom eli list
Geom name: md0.eli
State: ACTIVE
EncryptionAlgorithm: AES-XTS
KeyLength: 128
Crypto: software
Version: 7
UsedKey: 0
Flags: NONE
KeysAllocated: 1
KeysTotal: 1
Providers:
1. Name: md0.eli
   Mediasize: 104857088 (100M)
   Sectorsize: 512
   Mode: r0w0e0
Consumers:
1. Name: md0
   Mediasize: 104857600 (100M)
   Sectorsize: 512
   Mode: r1w1e1

$ swapon /dev/md0.eli

$ swapinfo
Device          1K-blocks     Used    Avail Capacity
/dev/gpt/swap0    4194304        0  4194304     0%
/dev/gpt/swap1    4194304        0  4194304     0%
/dev/md0.eli       102396        0   102396     0%
Total             8491004        0  8491004     0%
```

So to start up the system I added:

```
mdconfig -a -t vnode /var/swap0
geli attach md0
swapon /dev/md0.eli
```

to /etc/rc.local. It is ugly but works.

I noticed there is a /etc/rc.d/mdconfig script with:

```
# PROVIDE: mdconfig
# REQUIRE: swap root

and a geli script:
# PROVIDE: disks
# REQUIRE: initrandom
```

One solution would be to duplicate /etc/rc.d/geli script and change the REQUIRE to mdconfig to make it load after /etc/rc.d/mdconfig. Then add geli_devices and mdconfig_md0 to /etc/rc.conf.


EDIT: add geom_eli_load="YES" to /boot/loader.conf.


----------



## Neffi (Jul 3, 2015)

*Edit*: I've come across an issue with the previous script I had been using. I've edited this thread to explain the issue and the script has been modified.

Sorry for the bump, but this thread is still the top result on Google for "freebsd encrypted swap file" so I wanted to drop a line with my own solution, which is an improvement upon the post above.

Just like above I'm making use of rc.local to set up my encrypted swap. If that files does not exist, create it with +x permissions. It will be automatically executed upon bootup. If it already exists, simply append to the end of it.

First off, you may need to load the geom_eli module upon boot. In /boot/loader.conf:


```
geom_eli_load="YES"
```

If you have a CPU with AES-NI acceleration, you should also load the aesni driver upon boot, to get hardware accelerated AES -- this significantly improves the speed of the swap file we're creating. /boot/loader.conf:


```
aesni_load="YES"
```

Next you'll have to create the swap file. Previously we were using a sparse file created in the script itself, but it has come to my attention that your swap file should never be a sparse file, for unspecified reasons.


```
dd if=/dev/zero of=/swap bs=1M count=6144 && sync
chmod 0600 /swap
```

Alter `count=6144` to the desired size of your swap file, in number of megabytes.

The contents of my rc.local are as follows:


```
#!/bin/sh

# Create and mount one-time encrypted swap file
SWFILE="/swap";
if [ -f "$SWFILE" ]; then
  chmod 0600 "$SWFILE" &&
  SWMD=$(mdconfig -a -t vnode -f "$SWFILE");
  if [ $? -eq 0 ] && [ -n "$SWMD" ] && [ -e "/dev/$SWMD" ]; then
    chmod 0600 "/dev/$SWMD" &&
    geli onetime -e AES-XTS -l 256 -d "/dev/$SWMD" &&
    chmod 0600 "/dev/$SWMD.eli" &&
    swapon "/dev/$SWMD.eli";
  fi
fi
unset SWFILE;
unset SWMD;
```

Alter `SWFILE="/swap"` if you have placed the swap elsewhere.

The notable improvements are as follows:

We make sure the permissions on the swap file are strict. World cannot read or write.
Rather than depending on our swap file to exist at /dev/md0 we capture the actual ID of the file, ensure it exists, and then feed that to GELI. If any other scripts created an md device prior to this script, it won't break.
Instead of using `geli init` and `geli attach`, GELI provides a convenient `onetime` function which initiates and attaches the device with a random passphrase. This way, the user never has to enter a password during boot.
We use key length 256 because that's the longest key (currently) accelerated by AES-NI according to aesni(). The default is 128.

The above post was also using `mdconfig` incorrectly. We want a memory disk of type "vnode" (file backed, that is, a file on the hard drive) rather than "swap" (buffer backed, or, in other words, a RAM disk).

Reboot and check `swapinfo` to ensure everything functioned correctly. On my machine, everything appears good:


```
$ swapinfo
Device  1K-blocks  Used  Avail Capacity
/dev/md0.eli  6291456  0  6291456  0%
```

*Edit*: It has also come to my attention that, because of the level of indirection involved in handing 'swapon' an encrypted md, perhaps the kernel will not be aware of where the data eventually resides and may choose to buffer/cache the contents of the file, when we probably want it written directly to disk to act as an effective swap. I do not know if this will pose a problem, and if so, how to correct this.


----------



## wblock@ (Jul 3, 2015)

On FreeBSD 10, this can be done automatically with just an entry in /etc/fstab.  See fstab(5).


----------



## Neffi (Jul 3, 2015)

wblock@ said:


> On FreeBSD 10, this can be done automatically with just an entry in /etc/fstab.  See fstab(5).


Unless I'm looking it over, I see no way to handle an encrypted swap file there. It's pretty clear how to handle an encrypted swap partition, or a normal vnode-backed md as swap, but not an encrypted swap file as a vnode-backed md passed through GELI.


----------



## wblock@ (Jul 3, 2015)

Sorry, maybe I misremembered.  But maybe it allows "md99.eli".  If not, a bug report/feature request would not be a bad idea.


----------



## t1066 (Jul 3, 2015)

```
$ cat /etc/fstab
# Device  Mountpoint  FStype  Options  Dump  Pass#
/dev/gpt/swap0.eli  none  swap  sw  0  0

$ swapinfo
Device  1K-blocks  Used  Avail Capacity
/dev/gpt/swap0.eli  41943040  22508 41920532  0%
```


----------



## mb2015 (Oct 11, 2015)

Not sure what t1066 was trying say. This thread is about having an encrypted swap _file_ (via md(4)) not an encrypted swap _partition_ (via gpt).

Anyway...

No action has been taken on PR 186252, so it is still not possible to create an encrypted swap file via /etc/fstab, as would be the standard procedure in FreeBSD 10 and up.

Neffi's script, above, provides a great workaround for creating an encrypted swap device, but since there is no /etc/fstab entry for it, it is not destroyed at shutdown. If the swap is not empty, then the kernel will panic after unmounting the file systems. So we need to ensure `swapoff /dev/$SWMD` runs before the dismount. Any suggestions on how best to accomplish that would be appreciated! I'm hoping for something. Thanks.


----------



## asteriskRoss (Oct 11, 2015)

mb2015, you could achieve a graceful shutdown by creating a script that fits with the rc(8) framework.  Have a look at this article: Practical rc.d scripting in BSD.

Something like Neffi's script (that isn't currently compatible with the framework) above would be the code in the start() function, with the swapoff(8) and anything else necessary being handled in the stop() function.


----------



## mb2015 (Oct 19, 2015)

Alright, this is what I came up with. Save it to /usr/local/etc/rc.d/encrypted_swapfile. In /etc/rc.conf put `encrypted_swapfile="YES"`. If you want a different swap file location or size, there are variables for that, too. You can create and destroy the encrypted swap file with `service encrypted_swapfile start` and `service encrypted_swapfile stop`. Those commands will run automatically at boot & shutdown.

Any suggestions for improving it appreciated.


```
#!/bin/sh

# PROVIDE: encrypted_swapfile
# REQUIRE: swaplate
# BEFORE: LOGIN
# KEYWORD: shutdown

. /etc/rc.subr

name="encrypted_swapfile"
rcvar="encrypted_swapfile_enable"

start_cmd="${name}_start"
stop_cmd="${name}_stop"

load_rc_config $name
: ${encrypted_swapfile_enable:="NO"}
: ${encrypted_swapfile_file:="/usr/swap0"}
: ${encrypted_swapfile_size:="2G"}

SWFILE="$encrypted_swapfile_file";
SWFILEDIR=$(dirname "$SWFILE");
SWDEVLINK="/var/run/encrypted_swapfile_device";

encrypted_swapfile_start()
{
    # Create and mount a one-time encrypted swap file.
    # This is a workaround for the inability to do this via an /etc/fstab entry.
    # See https://forums.freebsd.org/threads/encrypt-swap-file.44519/#post-292933
    #
    if [ ! -e "$SWDEVLINK" ]; then
        if [ -w "$SWFILEDIR" ]; then
            truncate -s "$encrypted_swapfile_size" "$encrypted_swapfile_file" &&
            chmod 0600 "$encrypted_swapfile_file" &&
            SWMD=$(mdconfig -a -t vnode -f "$encrypted_swapfile_file") &&
            if [ $? -eq 0 ] && [ -n $SWMD ] && [ -e "/dev/$SWMD" ]; then
                chmod 0600 "/dev/$SWMD" &&
                geli onetime -e AES-XTS -l 256 -d "/dev/$SWMD" &&
                chmod 0600 "/dev/$SWMD.eli" &&
                swapon "/dev/$SWMD.eli" &&
                ln -f -s "/dev/$SWMD.eli" "$SWDEVLINK";
                unset SWMD;
            fi
        else
            echo "Could not create encrypted swap file in $SWFILEDIR; check permissions." &&
            return 1;
        fi
    else
        SWMD=$(readlink "$SWDEVLINK") &&
        swapinfo | grep -vq "^$SWMD " &&
        echo "Encrypted swap file already exists; enabling." &&
        swapon "$SWMD";
        unset SWMD;
    fi
    return 0;
}

encrypted_swapfile_stop()
{
    if [ -e "$SWDEVLINK" ]; then
        swapoff "$SWDEVLINK" &&
        rm "$SWDEVLINK" &&
        rm "$SWFILE";
    else
        echo "No encrypted swap file found; nothing to stop.";
        rm -f "$SWDEVLINK";
    fi
}

run_rc_command "$1"
```

update: added `-f` flag to `ln -s`
update: added `BEFORE: LOGIN` and return codes


----------

