desired config types + api types
This commit is contained in:
Otto Bittner 2023-07-28 09:40:06 +02:00
parent 9669bc8afa
commit 269773f044
13 changed files with 223 additions and 97 deletions

View File

@ -1790,6 +1790,14 @@ def go_dependencies():
sum = "h1:Q2TI34V/NCLGQQkdc0/KmPx/7ix9YnGDUQDT+gqvDw0=",
version = "v0.0.0-20230530085549-fd2878a4dead",
)
go_repository(
name = "com_github_edgelesssys_sev_snp_measure_go",
build_file_generation = "on",
build_file_proto_mode = "disable_global",
importpath = "github.com/edgelesssys/sev-snp-measure-go",
sum = "h1:M+QLxYKZm+fbPEH8G47GOmHbbL8k1ct04GYZbq0R3VY=",
version = "v0.0.0-20230727111429-1dabef884532",
)
go_repository(
name = "com_github_edsrzf_mmap_go",
@ -5710,8 +5718,8 @@ def go_dependencies():
build_file_generation = "on",
build_file_proto_mode = "disable_global",
importpath = "github.com/stretchr/testify",
sum = "h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=",
version = "v1.8.3",
sum = "h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=",
version = "v1.8.4",
)
go_repository(
name = "com_github_subosito_gotenv",

3
go.mod
View File

@ -73,6 +73,7 @@ require (
github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api v0.0.0
github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f
github.com/edgelesssys/go-tdx-qpl v0.0.0-20230530085549-fd2878a4dead
github.com/edgelesssys/sev-snp-measure-go v0.0.0-20230727111429-1dabef884532
github.com/fsnotify/fsnotify v1.6.0
github.com/go-playground/locales v0.14.1
github.com/go-playground/universal-translator v0.18.1
@ -99,7 +100,7 @@ require (
github.com/sigstore/sigstore v1.6.4
github.com/spf13/afero v1.9.5
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.3
github.com/stretchr/testify v1.8.4
go.uber.org/goleak v1.2.1
go.uber.org/zap v1.24.0
golang.org/x/crypto v0.9.0

6
go.sum
View File

@ -431,6 +431,8 @@ github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f
github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f/go.mod h1:hX9gZBSvliJcBEAyrJDh7990hLRg/Is+6PBpDZWSMoc=
github.com/edgelesssys/go-tdx-qpl v0.0.0-20230530085549-fd2878a4dead h1:Q2TI34V/NCLGQQkdc0/KmPx/7ix9YnGDUQDT+gqvDw0=
github.com/edgelesssys/go-tdx-qpl v0.0.0-20230530085549-fd2878a4dead/go.mod h1:IC72qyykUIWl0ZmSk53L4xbLCFDBEGZVaujUmPQOEyw=
github.com/edgelesssys/sev-snp-measure-go v0.0.0-20230727111429-1dabef884532 h1:M+QLxYKZm+fbPEH8G47GOmHbbL8k1ct04GYZbq0R3VY=
github.com/edgelesssys/sev-snp-measure-go v0.0.0-20230727111429-1dabef884532/go.mod h1:uvD6SNOxfV1BYqPv1PYTVBjsc+NoiDynRr/X4RjGutM=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ=
github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
@ -1332,8 +1334,8 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tedsuo/ifrit v0.0.0-20180802180643-bea94bb476cc/go.mod h1:eyZnKCc955uh98WQvzOm0dgAeLnf2O0Rz0LPoC5ze+0=
github.com/theupdateframework/go-tuf v0.5.2 h1:habfDzTmpbzBLIFGWa2ZpVhYvFBoK0C1onC3a4zuPRA=

View File

@ -48,7 +48,7 @@ require (
github.com/hexops/gotextdiff v1.0.3
github.com/spf13/afero v1.9.5
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.3
github.com/stretchr/testify v1.8.4
go.uber.org/goleak v1.2.1
go.uber.org/zap v1.24.0
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
@ -129,6 +129,7 @@ require (
github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api v0.0.0 // indirect
github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f // indirect
github.com/edgelesssys/go-tdx-qpl v0.0.0-20230530085549-fd2878a4dead // indirect
github.com/edgelesssys/sev-snp-measure-go v0.0.0-20230727111429-1dabef884532 // indirect
github.com/emicklei/go-restful/v3 v3.10.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect

View File

@ -398,6 +398,8 @@ github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f
github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f/go.mod h1:hX9gZBSvliJcBEAyrJDh7990hLRg/Is+6PBpDZWSMoc=
github.com/edgelesssys/go-tdx-qpl v0.0.0-20230530085549-fd2878a4dead h1:Q2TI34V/NCLGQQkdc0/KmPx/7ix9YnGDUQDT+gqvDw0=
github.com/edgelesssys/go-tdx-qpl v0.0.0-20230530085549-fd2878a4dead/go.mod h1:IC72qyykUIWl0ZmSk53L4xbLCFDBEGZVaujUmPQOEyw=
github.com/edgelesssys/sev-snp-measure-go v0.0.0-20230727111429-1dabef884532 h1:M+QLxYKZm+fbPEH8G47GOmHbbL8k1ct04GYZbq0R3VY=
github.com/edgelesssys/sev-snp-measure-go v0.0.0-20230727111429-1dabef884532/go.mod h1:uvD6SNOxfV1BYqPv1PYTVBjsc+NoiDynRr/X4RjGutM=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ=
github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
@ -1322,8 +1324,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tedsuo/ifrit v0.0.0-20180802180643-bea94bb476cc/go.mod h1:eyZnKCc955uh98WQvzOm0dgAeLnf2O0Rz0LPoC5ze+0=
github.com/theupdateframework/go-tuf v0.5.2 h1:habfDzTmpbzBLIFGWa2ZpVhYvFBoK0C1onC3a4zuPRA=

View File

@ -5,6 +5,7 @@ go_library(
name = "attestationconfigapi",
srcs = [
"attestationconfigapi.go",
"aws.go",
"azure.go",
"client.go",
"fetcher.go",
@ -19,6 +20,7 @@ go_library(
"//internal/logger",
"//internal/sigstore",
"//internal/staticupload",
"@com_github_edgelesssys_sev_snp_measure_go//ovmf",
],
)

View File

@ -0,0 +1,84 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package attestationconfigapi
import (
"fmt"
"path"
"strings"
"time"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/sev-snp-measure-go/ovmf"
)
// FirmwareMetadata contains information to precalculate the launchmeasurement of one firmware version.
type FirmwareMetadata struct {
Data ovmf.MetadataWrapper `json:"metadata"`
FirstSeenOn time.Time `json:"firstSeenOn"`
}
// AWSFirmwareMetadata tracks the latest version of each component of the Azure SEVSNP.
type AWSFirmwareMetadata struct {
Version string `json:"-"`
Metadata []FirmwareMetadata `json:"metadata"`
}
func NewAWSFirmwareMetadata() AWSFirmwareMetadata {
return AWSFirmwareMetadata{
Version: "1.0.json",
}
}
// URL returns the URL for the request to the config api.
func (i AWSFirmwareMetadata) URL() (string, error) {
return getURL(i)
}
// JSONPath returns the path to the JSON file for the request to the config api.
func (i AWSFirmwareMetadata) JSONPath() string {
return path.Join(attestationURLPath, variant.AWSSEVSNP{}.String(), i.Version+".json")
}
// ValidateRequest validates the request.
func (i AWSFirmwareMetadata) ValidateRequest() error {
if !strings.HasSuffix(i.Version, ".json") {
return fmt.Errorf("version has no .json suffix")
}
return nil
}
// Validate is a No-Op at the moment.
func (i AWSFirmwareMetadata) Validate() error {
return nil
}
// AWSFirmwareMetadataList is the request to list all versions in the config api.
type AWSFirmwareMetadataList []string
// URL returns the URL for the request to the config api.
func (i AWSFirmwareMetadataList) URL() (string, error) {
return getURL(i)
}
// JSONPath returns the path to the JSON file for the request to the config api.
func (i AWSFirmwareMetadataList) JSONPath() string {
return path.Join(attestationURLPath, variant.AzureSEVSNP{}.String(), "list")
}
// ValidateRequest is a NoOp as there is no input.
func (i AWSFirmwareMetadataList) ValidateRequest() error {
return nil
}
// Validate validates the response.
func (i AWSFirmwareMetadataList) Validate() error {
if len(i) < 1 {
return fmt.Errorf("no versions found in /list")
}
return nil
}

View File

@ -28,6 +28,9 @@ type Fetcher interface {
FetchAzureSEVSNPVersion(ctx context.Context, azureVersion AzureSEVSNPVersionAPI) (AzureSEVSNPVersionAPI, error)
FetchAzureSEVSNPVersionList(ctx context.Context, attestation AzureSEVSNPVersionList) (AzureSEVSNPVersionList, error)
FetchAzureSEVSNPVersionLatest(ctx context.Context, now time.Time) (AzureSEVSNPVersionAPI, error)
FetchAWSFirmwareMetadata(ctx context.Context, firmwareMetadata AWSFirmwareMetadata) (AWSFirmwareMetadata, error)
FetchAWSFirmwareMetadataList(ctx context.Context, attestation AWSFirmwareMetadataList) (AWSFirmwareMetadataList, error)
FetchAWSFirmwareMetadataLatest(ctx context.Context, now time.Time) (AWSFirmwareMetadata, error)
}
// fetcher fetches AttestationCfg API resources without authentication.

View File

@ -6,6 +6,7 @@ go_library(
srcs = [
"attestation.go",
"attestationversion.go",
"aws.go",
"azure.go",
"config.go",
"config_doc.go",
@ -30,6 +31,7 @@ go_library(
"//internal/constants",
"//internal/file",
"//internal/versions",
"@com_github_edgelesssys_sev_snp_measure_go//ovmf",
"@com_github_go_playground_locales//en",
"@com_github_go_playground_universal_translator//:universal-translator",
"@com_github_go_playground_validator_v10//:validator",

107
internal/config/aws.go Normal file
View File

@ -0,0 +1,107 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package config
import (
"fmt"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/sev-snp-measure-go/ovmf"
)
// AWSSEVSNP is the configuration for AWS SEV-SNP attestation.
type AWSSEVSNP struct {
// description: |
// Expected TPM measurements.
Measurements measurements.M `json:"measurements" yaml:"measurements" validate:"required,no_placeholders"`
// TODO (derpsteb): reenable launchMeasurement once we have a way to generate the expected value dynamically.
// description: |
// Expected launch measurement in SNP report. Not in use right now.
OVMFFirmware OVMFConfig
// description: |
// AMD Root Key certificate used to verify the SEV-SNP certificate chain.
AMDRootKey Certificate `json:"amdRootKey" yaml:"amdRootKey"`
}
type OVMFConfig struct {
WantLatest bool
Metadata []ovmf.MetadataWrapper `json:"metadata"`
}
// DefaultForAWSSEVSNP provides a valid default configuration for AWS SEV-SNP attestation.
func DefaultForAWSSEVSNP() *AWSSEVSNP {
return &AWSSEVSNP{
Measurements: measurements.DefaultsFor(cloudprovider.AWS, variant.AWSSEVSNP{}),
// LaunchMeasurement: measurements.PlaceHolderMeasurement(48),
AMDRootKey: mustParsePEM(constants.AMDRootKey),
}
}
// GetVariant returns aws-sev-snp as the variant.
func (AWSSEVSNP) GetVariant() variant.Variant {
return variant.AWSSEVSNP{}
}
// GetMeasurements returns the measurements used for attestation.
func (c AWSSEVSNP) GetMeasurements() measurements.M {
return c.Measurements
}
// SetMeasurements updates a config's measurements using the given measurements.
func (c *AWSSEVSNP) SetMeasurements(m measurements.M) {
c.Measurements = m
}
// EqualTo returns true if the config is equal to the given config.
func (c AWSSEVSNP) EqualTo(other AttestationCfg) (bool, error) {
otherCfg, ok := other.(*AWSSEVSNP)
if !ok {
return false, fmt.Errorf("cannot compare %T with %T", c, other)
}
// TODO (derpsteb): reenable launchMeasurement once we have a way to generate the expected value dynamically.
// if !bytes.Equal(c.LaunchMeasurement.Expected, otherCfg.LaunchMeasurement.Expected) {
// return false, nil
// }
// if c.LaunchMeasurement.ValidationOpt != otherCfg.LaunchMeasurement.ValidationOpt {
// return false, nil
// }
return c.Measurements.EqualTo(otherCfg.Measurements), nil
}
// AWSNitroTPM is the configuration for AWS Nitro TPM attestation.
type AWSNitroTPM struct {
// description: |
// Expected TPM measurements.
Measurements measurements.M `json:"measurements" yaml:"measurements" validate:"required,no_placeholders"`
}
// GetVariant returns aws-nitro-tpm as the variant.
func (AWSNitroTPM) GetVariant() variant.Variant {
return variant.AWSNitroTPM{}
}
// GetMeasurements returns the measurements used for attestation.
func (c AWSNitroTPM) GetMeasurements() measurements.M {
return c.Measurements
}
// SetMeasurements updates a config's measurements using the given measurements.
func (c *AWSNitroTPM) SetMeasurements(m measurements.M) {
c.Measurements = m
}
// EqualTo returns true if the config is equal to the given config.
func (c AWSNitroTPM) EqualTo(other AttestationCfg) (bool, error) {
otherCfg, ok := other.(*AWSNitroTPM)
if !ok {
return false, fmt.Errorf("cannot compare %T with %T", c, other)
}
return c.Measurements.EqualTo(otherCfg.Measurements), nil
}

View File

@ -782,92 +782,6 @@ func (c *Config) WithOpenStackProviderDefaults(openStackProvider string) *Config
return c
}
// AWSSEVSNP is the configuration for AWS SEV-SNP attestation.
type AWSSEVSNP struct {
// description: |
// Expected TPM measurements.
Measurements measurements.M `json:"measurements" yaml:"measurements" validate:"required,no_placeholders"`
// TODO (derpsteb): reenable launchMeasurement once we have a way to generate the expected value dynamically.
// description: |
// Expected launch measurement in SNP report. Not in use right now.
// LaunchMeasurement measurements.Measurement `json:"launchMeasurement" yaml:"launchMeasurement" validate:"required"`
// description: |
// AMD Root Key certificate used to verify the SEV-SNP certificate chain.
AMDRootKey Certificate `json:"amdRootKey" yaml:"amdRootKey"`
}
// DefaultForAWSSEVSNP provides a valid default configuration for AWS SEV-SNP attestation.
func DefaultForAWSSEVSNP() *AWSSEVSNP {
return &AWSSEVSNP{
Measurements: measurements.DefaultsFor(cloudprovider.AWS, variant.AWSSEVSNP{}),
// LaunchMeasurement: measurements.PlaceHolderMeasurement(48),
AMDRootKey: mustParsePEM(constants.AMDRootKey),
}
}
// GetVariant returns aws-sev-snp as the variant.
func (AWSSEVSNP) GetVariant() variant.Variant {
return variant.AWSSEVSNP{}
}
// GetMeasurements returns the measurements used for attestation.
func (c AWSSEVSNP) GetMeasurements() measurements.M {
return c.Measurements
}
// SetMeasurements updates a config's measurements using the given measurements.
func (c *AWSSEVSNP) SetMeasurements(m measurements.M) {
c.Measurements = m
}
// EqualTo returns true if the config is equal to the given config.
func (c AWSSEVSNP) EqualTo(other AttestationCfg) (bool, error) {
otherCfg, ok := other.(*AWSSEVSNP)
if !ok {
return false, fmt.Errorf("cannot compare %T with %T", c, other)
}
// TODO (derpsteb): reenable launchMeasurement once we have a way to generate the expected value dynamically.
// if !bytes.Equal(c.LaunchMeasurement.Expected, otherCfg.LaunchMeasurement.Expected) {
// return false, nil
// }
// if c.LaunchMeasurement.ValidationOpt != otherCfg.LaunchMeasurement.ValidationOpt {
// return false, nil
// }
return c.Measurements.EqualTo(otherCfg.Measurements), nil
}
// AWSNitroTPM is the configuration for AWS Nitro TPM attestation.
type AWSNitroTPM struct {
// description: |
// Expected TPM measurements.
Measurements measurements.M `json:"measurements" yaml:"measurements" validate:"required,no_placeholders"`
}
// GetVariant returns aws-nitro-tpm as the variant.
func (AWSNitroTPM) GetVariant() variant.Variant {
return variant.AWSNitroTPM{}
}
// GetMeasurements returns the measurements used for attestation.
func (c AWSNitroTPM) GetMeasurements() measurements.M {
return c.Measurements
}
// SetMeasurements updates a config's measurements using the given measurements.
func (c *AWSNitroTPM) SetMeasurements(m measurements.M) {
c.Measurements = m
}
// EqualTo returns true if the config is equal to the given config.
func (c AWSNitroTPM) EqualTo(other AttestationCfg) (bool, error) {
otherCfg, ok := other.(*AWSNitroTPM)
if !ok {
return false, fmt.Errorf("cannot compare %T with %T", c, other)
}
return c.Measurements.EqualTo(otherCfg.Measurements), nil
}
// SNPFirmwareSignerConfig is the configuration for validating the firmware signer.
type SNPFirmwareSignerConfig struct {
// description: |

View File

@ -23,7 +23,7 @@ require (
github.com/onsi/ginkgo/v2 v2.9.7
github.com/onsi/gomega v1.27.7
github.com/spf13/afero v1.9.5
github.com/stretchr/testify v1.8.3
github.com/stretchr/testify v1.8.4
go.etcd.io/etcd/api/v3 v3.5.7
go.etcd.io/etcd/client/pkg/v3 v3.5.7
go.etcd.io/etcd/client/v3 v3.5.7

View File

@ -318,8 +318,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=