all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: alexandre derumier <aderumier@odiso.com>
To: Proxmox VE development discussion <pve-devel@lists.proxmox.com>
Subject: Re: [pve-devel] [RFC qemu-server 2/2] fix #3075: add TPM v1.2 and v2.0 support via swtpm
Date: Fri, 16 Jul 2021 16:47:14 +0200	[thread overview]
Message-ID: <fd79b9f13f8d2fe59e0274cc6916b1a88c470926.camel@odiso.com> (raw)
In-Reply-To: <20210715142319.1457131-3-s.reiter@proxmox.com>

Hi, I have found old post where a nvram device (using a qcow2 or raw
file as backend) was used with tpm

https://libvir-list.redhat.narkive.com/eOrJPdYX/libvirt-how-libvirt-address-qemu-command-line-args
also dev doc from 2011 mention it

https://wiki.qemu.org/Features/TPM

(I don't have tested to verify)


Le jeudi 15 juillet 2021 à 16:23 +0200, Stefan Reiter a écrit :
> Starts an instance of swtpm per VM in it's systemd scope, it will
> terminate by itself if the VM exits, or be terminated manually if
> startup fails.
> 
> Before first use, a TPM state is created via swtpm_setup. The state
> lives in "/etc/pve/priv/tpm/<vmid>-<version>/".
> 
> TPM state is cleared if the 'tpm' config option is removed or the
> version changed, effectively clearing any stored keys/data.
> 
> Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
> ---
>  PVE/QemuServer.pm | 134
> +++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 133 insertions(+), 1 deletion(-)
> 
> diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
> index b0fe257..76a25ae 100644
> --- a/PVE/QemuServer.pm
> +++ b/PVE/QemuServer.pm
> @@ -686,6 +686,12 @@ EODESCR
>         description => "Configure a VirtIO-based Random Number
> Generator.",
>         optional => 1,
>      },
> +    tpm => {
> +       optional => 1,
> +       type => 'string',
> +       enum => [ qw(v1.2 v2.0) ],
> +       description => "Configure an emulated Trusted Platform
> Module.",
> +    },
>  };
>  
>  my $cicustom_fmt = {
> @@ -2945,6 +2951,116 @@ sub audio_devs {
>      return $devs;
>  }
>  
> +sub get_tpm_paths {
> +    my ($vmid, $version) = @_;
> +    return {
> +       state => "/etc/pve/priv/tpm/$vmid-$version/",
> +       socket => "/var/run/qemu-server/$vmid.swtpm",
> +       pid => "/var/run/qemu-server/$vmid.swtpm.pid",
> +       filename => $version eq "v1.2" ? "tpm-00.permall" : "tpm2-
> 00.permall",
> +    };
> +}
> +
> +sub print_tpm_device {
> +    my ($vmid, $version) = @_;
> +    my $paths = get_tpm_paths($vmid, $version);
> +
> +    my $devs = [];
> +
> +    push @$devs, "-chardev", "socket,id=tpmchar,path=$paths-
> >{socket}";
> +    push @$devs, "-tpmdev", "emulator,id=tpmdev,chardev=tpmchar";
> +    push @$devs, "-device", "tpm-tis,tpmdev=tpmdev";
> +
> +    return $devs;
> +}
> +
> +sub start_swtpm {
> +    my ($vmid, $version, $migration) = @_;
> +    my $paths = get_tpm_paths($vmid, $version);
> +
> +    if ($migration) {
> +       # we will get migration state from remote, so remove any pre-
> existing
> +       clear_tpm_states($vmid);
> +       File::Path::make_path($paths->{state});
> +    } else {
> +       # run swtpm_setup to create a new TPM state if it doesn't
> exist yet
> +       if (! -f "$paths->{state}/$paths->{filename}") {
> +           print "Creating new TPM state\n";
> +
> +           # swtpm_setup does not like /etc/pve/priv, so create in
> tempdir
> +           my $tmppath = "/tmp/tpm-$vmid-$$";
> +           File::Path::make_path($tmppath, mode => 0600);
> +           my $setup_cmd = [
> +               "swtpm_setup",
> +               "--tpmstate",
> +               "$tmppath",
> +               "--createek",
> +               "--create-ek-cert",
> +               "--create-platform-cert",
> +               "--lock-nvram",
> +               "--config",
> +               "/etc/swtpm_setup.conf", # do not use XDG configs
> +               "--runas",
> +               "0", # force creation as root, error if not possible
> +           ];
> +
> +           push @$setup_cmd, "--tpm2" if $version eq 'v2.0';
> +           # TPM 2.0 supports ECC crypto, use if possible
> +           push @$setup_cmd, "--ecc" if $version eq 'v2.0';
> +
> +           # produces a lot of verbose output, only show on error
> +           my $tpmout = "";
> +           run_command($setup_cmd, outfunc => sub {
> +               $tpmout .= $1 . "\n";
> +           });
> +
> +           File::Path::make_path($paths->{state});
> +           my $res = File::Copy::move("$tmppath/$paths->{filename}",
> +               "$paths->{state}/$paths->{filename}");
> +           File::Path::rmtree($tmppath);
> +           if (!$res) {
> +               my $err = $!;
> +               File::Path::rmtree($tmppath);
> +               print "swtpm_setup reported:\n$tpmout";
> +               die "couldn't move TPM state into '$paths->{state}' -
> $err\n";
> +           }
> +       }
> +    }
> +
> +    my $emulator_cmd = [
> +       "swtpm",
> +       "socket",
> +       "--tpmstate",
> +       "dir=$paths->{state},mode=0600",
> +       "--ctrl",
> +       "type=unixio,path=$paths->{socket},mode=0600",
> +       "--pid",
> +       "file=$paths->{pid}",
> +       "--terminate", # terminate on QEMU disconnect
> +       "--daemon",
> +    ];
> +    push @$emulator_cmd, "--tpm2" if $version eq 'v2.0';
> +    run_command($emulator_cmd);
> +
> +    # return untainted PID of swtpm daemon so it can be killed on
> error
> +    file_read_firstline($paths->{pid}) =~ m/(\d+)/;
> +    return $1;
> +}
> +
> +# clear any TPM states other than the ones relevant for $version
> +sub clear_tpm_states {
> +    my ($vmid, $keep_version) = @_;
> +
> +    my $clear = sub {
> +       my ($v) = @_;
> +       my $paths = get_tpm_paths($vmid, $v);
> +       rmtree $paths->{state};
> +    };
> +
> +    &$clear("v1.2") if !$keep_version || $keep_version ne "v1.2";
> +    &$clear("v2.0") if !$keep_version || $keep_version ne "v2.0";
> +}
> +
>  sub vga_conf_has_spice {
>      my ($vga) = @_;
>  
> @@ -3446,6 +3562,11 @@ sub config_to_command {
>         push @$devices, @$audio_devs;
>      }
>  
> +    if (my $tpmver = $conf->{tpm}) {
> +       my $tpmdev = print_tpm_device($vmid, $tpmver);
> +       push @$devices, @$tpmdev;
> +    }
> +
>      my $sockets = 1;
>      $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer
> iused
>      $sockets = $conf->{sockets} if  $conf->{sockets};
> @@ -4829,6 +4950,8 @@ sub vmconfig_apply_pending {
>         }
>      }
>  
> +    PVE::QemuServer::clear_tpm_states($vmid, $conf->{tpm});
> +
>      # write all changes at once to avoid unnecessary i/o
>      PVE::QemuConfig->write_config($vmid, $conf);
>  }
> @@ -5329,8 +5452,17 @@ sub vm_start_nolock {
>         PVE::Tools::run_fork sub {
>             PVE::Systemd::enter_systemd_scope($vmid, "Proxmox VE VM
> $vmid", %properties);
>  
> +           my $tpmpid;
> +           if (my $tpmver = $conf->{tpm}) {
> +               # start the TPM emulator so QEMU can connect on start
> +               $tpmpid = start_swtpm($vmid, $tpmver, $migratedfrom);
> +           }
> +
>             my $exitcode = run_command($cmd, %run_params);
> -           die "QEMU exited with code $exitcode\n" if $exitcode;
> +           if ($exitcode) {
> +               kill 'TERM', $tpmpid if $tpmpid;
> +               die "QEMU exited with code $exitcode\n";
> +           }
>         };
>      };
>  






  reply	other threads:[~2021-07-16 14:47 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-07-15 14:23 [pve-devel] [RFC 0/2] Initial TPM support for VMs Stefan Reiter
2021-07-15 14:23 ` [pve-devel] [RFC edk2-firmware 1/2] enable TPM and TPM2 support Stefan Reiter
2021-07-15 14:23 ` [pve-devel] [RFC qemu-server 2/2] fix #3075: add TPM v1.2 and v2.0 support via swtpm Stefan Reiter
2021-07-16 14:47   ` alexandre derumier [this message]
2021-08-09 18:17   ` Nick Chevsky
2021-08-10  7:48     ` Stefan Reiter
2021-07-16  9:48 ` [pve-devel] [RFC 0/2] Initial TPM support for VMs Thomas Lamprecht

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=fd79b9f13f8d2fe59e0274cc6916b1a88c470926.camel@odiso.com \
    --to=aderumier@odiso.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal