From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <aderumier@odiso.com>
Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68])
 (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
 key-exchange X25519 server-signature RSA-PSS (2048 bits))
 (No client certificate requested)
 by lists.proxmox.com (Postfix) with ESMTPS id 0316D76A78
 for <pve-devel@lists.proxmox.com>; Fri, 16 Jul 2021 16:47:55 +0200 (CEST)
Received: from firstgate.proxmox.com (localhost [127.0.0.1])
 by firstgate.proxmox.com (Proxmox) with ESMTP id EE0BF12AF4
 for <pve-devel@lists.proxmox.com>; Fri, 16 Jul 2021 16:47:24 +0200 (CEST)
Received: from mail-wm1-x32b.google.com (mail-wm1-x32b.google.com
 [IPv6:2a00:1450:4864:20::32b])
 (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)
 key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256)
 (No client certificate requested)
 by firstgate.proxmox.com (Proxmox) with ESMTPS id 7CA2512AEB
 for <pve-devel@lists.proxmox.com>; Fri, 16 Jul 2021 16:47:21 +0200 (CEST)
Received: by mail-wm1-x32b.google.com with SMTP id
 a23-20020a05600c2257b0290236ec98bebaso3577891wmm.1
 for <pve-devel@lists.proxmox.com>; Fri, 16 Jul 2021 07:47:21 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=odiso-com.20150623.gappssmtp.com; s=20150623;
 h=message-id:subject:from:to:date:in-reply-to:references:user-agent
 :mime-version:content-transfer-encoding;
 bh=wOUb6YfETTEKapuVjCZNReRaBT+CIcyBdpX2ZokKlsk=;
 b=I/TLcDqk2auXtXIZGMtxgmbEGyp7YQPs1QkUygBsPyZK2i6MOvaYKg5HSr7oBqXN9F
 BGIbpHMc5ZY6RvFNIgZt5YJJzKhiu0mShosBkM4MjIbYRK/QBu5IcMvXRmMMoxEzFjZn
 mEc9jRktcRX7N0ZwjVV1Dc9vSdxBRoZ2QqABDUuO1yiCET1nGecG/GUywpnwb1oWybGr
 0nncQzhy0WD0+AaViB8AvG69ml4u199U8CMzyWYOTpV4CZLETzJcw4oSeWHWD8hHjCuf
 it9y2ju20LuIglQWW8dm4jkJbGLpyRk3dG5R2KHPriYrEMI6Jf4caqNinL76iHcMF7nH
 maIw==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=1e100.net; s=20161025;
 h=x-gm-message-state:message-id:subject:from:to:date:in-reply-to
 :references:user-agent:mime-version:content-transfer-encoding;
 bh=wOUb6YfETTEKapuVjCZNReRaBT+CIcyBdpX2ZokKlsk=;
 b=cw6Swu12kh5ypHGJYEKhH1N5tXiytk54kGA4K7Cm+GLXmkGov8OFdOrgo4/9tae9xc
 RR5y80VtlNDQZHSQGTn4gojFDhP/5Gx0U84CE/JwP2hMWF/9QJuzD/UUyJmuhFQ0bn01
 bKZMMO4RVHTwyFNcxWjXNUPefCmx+9iB6sTOy+3sGYIYwX2+JJg/mF8clJB3HvqlGC9X
 hdHvJq0se5SoUBa4OV1sWrO+AMLB3A8g4saIL4GZYl5Y8ZVeyTLeYg/hM5FLdUsGdc87
 CVmfn1EaoU/NkVhEqIslQXu401nEEbtO2Dc24mDWOHJW+ApITHwrnb+KcyCrV6QU9BaI
 WGhw==
X-Gm-Message-State: AOAM532QSC5yOCVjGzAbrbMCr+nwXy2cRcs4AGReHMWGQsMYyZIjyGNh
 koyy6y6I4VrbCDG60RoRWp7Uk+EdxAnbGoHw
X-Google-Smtp-Source: ABdhPJwx0DMcMMNpEjSPpN/bsdBbSDCbsY4LowLIrSeYGVl2Owvtlrg4ULElg9zjFRkyhNe/UdaAkQ==
X-Received: by 2002:a1c:7410:: with SMTP id p16mr6509906wmc.6.1626446835034;
 Fri, 16 Jul 2021 07:47:15 -0700 (PDT)
Received: from [192.168.178.50] ([79.132.252.54])
 by smtp.gmail.com with ESMTPSA id 12sm12017044wme.28.2021.07.16.07.47.14
 for <pve-devel@lists.proxmox.com>
 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
 Fri, 16 Jul 2021 07:47:14 -0700 (PDT)
Message-ID: <fd79b9f13f8d2fe59e0274cc6916b1a88c470926.camel@odiso.com>
From: alexandre derumier <aderumier@odiso.com>
To: Proxmox VE development discussion <pve-devel@lists.proxmox.com>
Date: Fri, 16 Jul 2021 16:47:14 +0200
In-Reply-To: <20210715142319.1457131-3-s.reiter@proxmox.com>
References: <20210715142319.1457131-1-s.reiter@proxmox.com>
 <20210715142319.1457131-3-s.reiter@proxmox.com>
Content-Type: text/plain; charset="UTF-8"
User-Agent: Evolution 3.40.3 
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
X-SPAM-LEVEL: Spam detection results:  0
 AWL 0.797 Adjusted score from AWL reputation of From: address
 BAYES_00                 -1.9 Bayes spam probability is 0 to 1%
 DKIM_SIGNED               0.1 Message has a DKIM or DK signature,
 not necessarily valid
 DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature
 RCVD_IN_DNSWL_NONE     -0.0001 Sender listed at https://www.dnswl.org/,
 no trust
 SPF_HELO_NONE           0.001 SPF: HELO does not publish an SPF Record
 SPF_PASS               -0.001 SPF: sender matches SPF record
Subject: Re: [pve-devel] [RFC qemu-server 2/2] fix #3075: add TPM v1.2 and
 v2.0 support via swtpm
X-BeenThere: pve-devel@lists.proxmox.com
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: Proxmox VE development discussion <pve-devel.lists.proxmox.com>
List-Unsubscribe: <https://lists.proxmox.com/cgi-bin/mailman/options/pve-devel>, 
 <mailto:pve-devel-request@lists.proxmox.com?subject=unsubscribe>
List-Archive: <http://lists.proxmox.com/pipermail/pve-devel/>
List-Post: <mailto:pve-devel@lists.proxmox.com>
List-Help: <mailto:pve-devel-request@lists.proxmox.com?subject=help>
List-Subscribe: <https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel>, 
 <mailto:pve-devel-request@lists.proxmox.com?subject=subscribe>
X-List-Received-Date: Fri, 16 Jul 2021 14:47:55 -0000

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";
> +           }
>         };
>      };
>