You've already forked aws-iam-anywhere-refresher
Compare commits
7 Commits
daddb1c287
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
739f786c3d
|
|||
|
8b9d57bba9
|
|||
|
db068f5c6a
|
|||
|
8f813e1a6e
|
|||
|
6f05021af0
|
|||
|
41062e61b6
|
|||
|
b12df2a4c1
|
@@ -1 +1,5 @@
|
||||
.idea/
|
||||
.gitea/
|
||||
aws-iam-anywhere-refresher
|
||||
LICENSE
|
||||
README.md
|
||||
51
.gitea/workflows/build.yml
Normal file
51
.gitea/workflows/build.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
name: 🏗️✨ Build Workflow
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
name: 🖥️ 🔨 Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 🛡️ 🔒 Add Siteworx CA Certificates
|
||||
run: |
|
||||
apt update && apt install -yq ca-certificates curl
|
||||
curl -Ls https://siteworxpro.com/hosted/Siteworx+Root+CA.pem -o /usr/local/share/ca-certificates/sw.crt
|
||||
update-ca-certificates
|
||||
|
||||
- name: 📖 🔍 Checkout Repository Code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: 🔑 🔐 Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: 🏗️ 🔧 Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: 🐳 🔨 Build Backend Container
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
provenance: true
|
||||
sbom: true
|
||||
push: true
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
tags: siteworxpro/aws-iam-anywhere:${{ gitea.ref_name }}
|
||||
|
||||
- name: 🐳 🔨 Build Backend Container - Latest Tag
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
provenance: true
|
||||
sbom: true
|
||||
push: true
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
tags: siteworxpro/aws-iam-anywhere:latest
|
||||
43
.gitea/workflows/manual-build.yml
Normal file
43
.gitea/workflows/manual-build.yml
Normal file
@@ -0,0 +1,43 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "Tag to build"
|
||||
required: true
|
||||
|
||||
name: 🏗️✨ Build Workflow
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
name: 🖥️ 🔨 Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 🛡️ 🔒 Add Siteworx CA Certificates
|
||||
run: |
|
||||
apt update && apt install -yq ca-certificates curl
|
||||
curl -Ls https://siteworxpro.com/hosted/Siteworx+Root+CA.pem -o /usr/local/share/ca-certificates/sw.crt
|
||||
update-ca-certificates
|
||||
|
||||
- name: 📖 🔍 Checkout Repository Code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: 🔑 🔐 Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: 🏗️ 🔧 Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: 🐳 🔨 Build Backend Container
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
provenance: true
|
||||
sbom: true
|
||||
push: true
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
tags: siteworxpro/aws-iam-anywhere:${{ gitea.event.inputs.tag }}
|
||||
17
Dockerfile
17
Dockerfile
@@ -1,21 +1,22 @@
|
||||
FROM siteworxpro/golang:1.24.0 AS build
|
||||
|
||||
ENV GOPRIVATE=git.s.int
|
||||
ENV GOPROXY=direct
|
||||
FROM siteworxpro/golang:1.25.3 AS build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ADD . .
|
||||
|
||||
RUN go mod tidy && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -o /app/aws-iam-anywhere-refresher
|
||||
ENV GOPRIVATE=git.siteworxpro.com
|
||||
|
||||
FROM alpine:latest AS runtime
|
||||
RUN go mod tidy && go build -o aws-iam-anywhere-refresher .
|
||||
|
||||
FROM siteworxpro/alpine:3.21.4 AS runtime
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=build /app/aws-iam-anywhere-refresher aws-iam-anywhere-refresher
|
||||
COPY --from=build /app/aws-iam-anywhere-refresher /app/aws-iam-anywhere-refresher
|
||||
|
||||
RUN adduser -D -H iam && \
|
||||
RUN apk add --no-cache gcompat
|
||||
|
||||
RUN adduser -Dh /app iam && \
|
||||
chown iam:iam /app/aws-iam-anywhere-refresher
|
||||
USER iam
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ This image runs in a kubernetes cronjob and will create and save new IAM credent
|
||||
- `TRUSTED_ANCHOR_ARN` ***required*** : the trusted anchor arn
|
||||
- `PRIVATE_KEY` ***required*** : iam private key base64 encoded
|
||||
- `CERTIFICATE` ***required*** : iam certificate base64 encoded
|
||||
- `CA_CHAIN` : the certificate chain bundle if needed
|
||||
|
||||
```yaml
|
||||
|
||||
|
||||
547
aws_signing_helper/cert_store_signer_darwin.go
Normal file
547
aws_signing_helper/cert_store_signer_darwin.go
Normal file
@@ -0,0 +1,547 @@
|
||||
//go:build darwin
|
||||
|
||||
package aws_signing_helper
|
||||
|
||||
// This code is based on the smimesign repository at
|
||||
// https://github.com/github/smimesign
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework CoreFoundation -framework Security
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <Security/Security.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"sort"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type DarwinCertStoreSigner struct {
|
||||
identRef C.SecIdentityRef
|
||||
keyRef C.SecKeyRef
|
||||
certRef C.SecCertificateRef
|
||||
cert *x509.Certificate
|
||||
certChain []*x509.Certificate
|
||||
}
|
||||
|
||||
// osStatus wraps a C.OSStatus
|
||||
type osStatus C.OSStatus
|
||||
|
||||
const (
|
||||
errSecItemNotFound = osStatus(C.errSecItemNotFound)
|
||||
)
|
||||
|
||||
// Gets the matching identity and certificate for this CertIdentifier
|
||||
// If there is more than one, only a list of the matching certificates is returned
|
||||
func GetMatchingCertsAndIdentity(certIdentifier CertIdentifier) ([]C.SecIdentityRef, []C.SecCertificateRef, []CertificateContainer, error) {
|
||||
queryMap := map[C.CFTypeRef]C.CFTypeRef{
|
||||
C.CFTypeRef(C.kSecClass): C.CFTypeRef(C.kSecClassIdentity),
|
||||
C.CFTypeRef(C.kSecReturnRef): C.CFTypeRef(C.kCFBooleanTrue),
|
||||
C.CFTypeRef(C.kSecMatchLimit): C.CFTypeRef(C.kSecMatchLimitAll),
|
||||
}
|
||||
|
||||
query := mapToCFDictionary(queryMap)
|
||||
if query == 0 {
|
||||
return nil, nil, nil, errors.New("error creating CFDictionary")
|
||||
}
|
||||
defer C.CFRelease(C.CFTypeRef(query))
|
||||
|
||||
var absResult C.CFTypeRef
|
||||
if err := osStatusError(C.SecItemCopyMatching(query, &absResult)); err != nil {
|
||||
if err == errSecItemNotFound {
|
||||
return nil, nil, nil, errors.New("unable to find matching identity in cert store")
|
||||
}
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
defer C.CFRelease(C.CFTypeRef(absResult))
|
||||
aryResult := C.CFArrayRef(absResult)
|
||||
|
||||
// identRefs aren't owned by us initially
|
||||
numIdentRefs := C.CFArrayGetCount(aryResult)
|
||||
identRefs := make([]C.CFTypeRef, numIdentRefs)
|
||||
C.CFArrayGetValues(aryResult, C.CFRange{0, numIdentRefs}, (*unsafe.Pointer)(unsafe.Pointer(&identRefs[0])))
|
||||
var certContainers []CertificateContainer
|
||||
var certRefs []C.SecCertificateRef
|
||||
var outputIdentRefs []C.SecIdentityRef
|
||||
var isMatch bool
|
||||
certContainerIndex := 0
|
||||
for _, curIdentRef := range identRefs {
|
||||
curCertRef, err := getCertRef(C.SecIdentityRef(curIdentRef))
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.New("unable to get cert ref")
|
||||
}
|
||||
curCert, err := exportCertRef(curCertRef)
|
||||
if err != nil {
|
||||
if Debug {
|
||||
log.Printf("unable to parse certificate with error (%s) - skipping\n", err)
|
||||
}
|
||||
goto nextIteration
|
||||
}
|
||||
|
||||
// Find whether there is a matching certificate
|
||||
isMatch = certMatches(certIdentifier, *curCert)
|
||||
if isMatch {
|
||||
certContainers = append(certContainers, CertificateContainer{certContainerIndex, curCert, ""})
|
||||
certContainerIndex += 1
|
||||
// Assign to certRef and identRef at most once in the loop
|
||||
// Both values are only useful if there is exactly one match in the certificate store
|
||||
// When creating a signer, there has to be exactly one matching certificate
|
||||
|
||||
certRefs = append(certRefs, curCertRef)
|
||||
// Note that only the SecIdentityRef needs to be retained since it was neither created nor copied
|
||||
C.CFRetain(C.CFTypeRef(curIdentRef))
|
||||
outputIdentRefs = append(outputIdentRefs, C.SecIdentityRef(curIdentRef))
|
||||
}
|
||||
|
||||
nextIteration:
|
||||
}
|
||||
|
||||
if Debug {
|
||||
log.Printf("found %d matching identities\n", len(certContainers))
|
||||
}
|
||||
|
||||
// It's the caller's responsibility to release each SecIdentityRef after use.
|
||||
return outputIdentRefs, certRefs, certContainers, nil
|
||||
}
|
||||
|
||||
// Gets the certificates that match the CertIdentifier
|
||||
func GetMatchingCerts(certIdentifier CertIdentifier) ([]CertificateContainer, error) {
|
||||
identRefs, certRefs, certContainers, err := GetMatchingCertsAndIdentity(certIdentifier)
|
||||
for i, identRef := range identRefs {
|
||||
C.CFRelease(C.CFTypeRef(identRef))
|
||||
identRefs[i] = 0
|
||||
}
|
||||
for i, certRef := range certRefs {
|
||||
C.CFRelease(C.CFTypeRef(certRef))
|
||||
certRefs[i] = 0
|
||||
}
|
||||
return certContainers, err
|
||||
}
|
||||
|
||||
// Creates a DarwinCertStoreSigner based on the identifying certificate
|
||||
func GetCertStoreSigner(certIdentifier CertIdentifier, useLatestExpiringCert bool) (signer Signer, signingAlgorithm string, err error) {
|
||||
var (
|
||||
selectedCertContainer CertificateContainer
|
||||
cert *x509.Certificate
|
||||
identRef C.SecIdentityRef
|
||||
certRef C.SecCertificateRef
|
||||
keyRef C.SecKeyRef
|
||||
)
|
||||
|
||||
identRefs, certRefs, certContainers, err := GetMatchingCertsAndIdentity(certIdentifier)
|
||||
if err != nil {
|
||||
goto fail
|
||||
}
|
||||
if len(certContainers) == 0 {
|
||||
err = errors.New("no matching identities")
|
||||
goto fail
|
||||
}
|
||||
if useLatestExpiringCert {
|
||||
sort.Sort(CertificateContainerList(certContainers))
|
||||
// Release the `SecIdentityRef`s and `SecCertificateRef`s that won't be used
|
||||
for i, certContainer := range certContainers {
|
||||
if i != len(certContainers)-1 {
|
||||
C.CFRelease(C.CFTypeRef(identRefs[certContainer.Index]))
|
||||
C.CFRelease(C.CFTypeRef(certRefs[certContainer.Index]))
|
||||
|
||||
identRefs[certContainer.Index] = 0
|
||||
certRefs[certContainer.Index] = 0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if len(certContainers) > 1 {
|
||||
err = errors.New("multiple matching identities")
|
||||
goto fail
|
||||
}
|
||||
}
|
||||
selectedCertContainer = certContainers[len(certContainers)-1]
|
||||
if Debug {
|
||||
log.Print(fmt.Sprintf("selected certificate: %s", DefaultCertContainerToString(selectedCertContainer)))
|
||||
}
|
||||
cert = selectedCertContainer.Cert
|
||||
certRef = certRefs[selectedCertContainer.Index]
|
||||
identRef = identRefs[selectedCertContainer.Index]
|
||||
|
||||
// Find the signing algorithm
|
||||
switch cert.PublicKey.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
signingAlgorithm = aws4X509EcdsaSha256
|
||||
case *rsa.PublicKey:
|
||||
signingAlgorithm = aws4X509RsaSha256
|
||||
default:
|
||||
err = errors.New("unsupported algorithm")
|
||||
goto fail
|
||||
}
|
||||
|
||||
keyRef, err = getKeyRef(identRef)
|
||||
if err != nil {
|
||||
err = errors.New("unable to get key reference")
|
||||
goto fail
|
||||
}
|
||||
|
||||
return &DarwinCertStoreSigner{identRef, keyRef, certRef, cert, nil}, signingAlgorithm, nil
|
||||
|
||||
fail:
|
||||
for i, identRef := range identRefs {
|
||||
if identRef != 0 {
|
||||
C.CFRelease(C.CFTypeRef(identRef))
|
||||
identRefs[i] = 0
|
||||
}
|
||||
}
|
||||
for i, certRef := range certRefs {
|
||||
if certRef != 0 {
|
||||
C.CFRelease(C.CFTypeRef(certRef))
|
||||
certRefs[i] = 0
|
||||
}
|
||||
}
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// Gets the certificate associated with this DarwinCertStoreSigner
|
||||
func (signer *DarwinCertStoreSigner) Certificate() (*x509.Certificate, error) {
|
||||
if signer.cert != nil {
|
||||
return signer.cert, nil
|
||||
}
|
||||
|
||||
certRef, err := signer.getCertRef()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cert, err := exportCertRef(certRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signer.cert = cert
|
||||
|
||||
return signer.cert, nil
|
||||
}
|
||||
|
||||
// Gets the certificate chain associated with this DarwinCertStoreSigner
|
||||
func (signer *DarwinCertStoreSigner) CertificateChain() ([]*x509.Certificate, error) {
|
||||
if signer.certChain != nil {
|
||||
return signer.certChain, nil
|
||||
}
|
||||
|
||||
certRef, err := signer.getCertRef()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
policy := C.SecPolicyCreateSSL(0, 0)
|
||||
|
||||
var trustRef C.SecTrustRef
|
||||
if err := osStatusError(C.SecTrustCreateWithCertificates(C.CFTypeRef(certRef), C.CFTypeRef(policy), &trustRef)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer C.CFRelease(C.CFTypeRef(trustRef))
|
||||
|
||||
// var status C.SecTrustResultType
|
||||
var cfErrRef C.CFErrorRef
|
||||
if C.SecTrustEvaluateWithError(trustRef, &cfErrRef) {
|
||||
return nil, cfErrorError(cfErrRef)
|
||||
}
|
||||
|
||||
var (
|
||||
nChain = C.SecTrustGetCertificateCount(trustRef)
|
||||
certChain = make([]*x509.Certificate, 0, int(nChain))
|
||||
)
|
||||
|
||||
certChainArr := C.SecTrustCopyCertificateChain(trustRef)
|
||||
defer C.CFRelease(C.CFTypeRef(certChainArr))
|
||||
for i := C.CFIndex(0); i < nChain; i++ {
|
||||
chainCertRef := C.SecCertificateRef(C.CFArrayGetValueAtIndex(certChainArr, i))
|
||||
if chainCertRef == 0 {
|
||||
return nil, errors.New("nil certificate in chain")
|
||||
}
|
||||
|
||||
chainCert, err := exportCertRef(chainCertRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certChain = append(certChain, chainCert)
|
||||
}
|
||||
|
||||
certChain = certChain[1:]
|
||||
signer.certChain = certChain
|
||||
|
||||
return signer.certChain, nil
|
||||
}
|
||||
|
||||
// Public implements the crypto.Signer interface and returns the public key associated with the signer
|
||||
func (signer *DarwinCertStoreSigner) Public() crypto.PublicKey {
|
||||
cert, err := signer.Certificate()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return cert.PublicKey
|
||||
}
|
||||
|
||||
// Closes the DarwinCertStoreSigner
|
||||
func (signer *DarwinCertStoreSigner) Close() {
|
||||
if signer.identRef != 0 {
|
||||
C.CFRelease(C.CFTypeRef(signer.identRef))
|
||||
signer.identRef = 0
|
||||
}
|
||||
|
||||
if signer.keyRef != 0 {
|
||||
C.CFRelease(C.CFTypeRef(signer.keyRef))
|
||||
signer.keyRef = 0
|
||||
}
|
||||
|
||||
if signer.certRef != 0 {
|
||||
C.CFRelease(C.CFTypeRef(signer.certRef))
|
||||
signer.certRef = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Sign implements the crypto.Signer interface and signs the digest
|
||||
func (signer *DarwinCertStoreSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
|
||||
var hash []byte
|
||||
switch opts.HashFunc() {
|
||||
case crypto.SHA256:
|
||||
sum := sha256.Sum256(digest)
|
||||
hash = sum[:]
|
||||
case crypto.SHA384:
|
||||
sum := sha512.Sum384(digest)
|
||||
hash = sum[:]
|
||||
case crypto.SHA512:
|
||||
sum := sha512.Sum512(digest)
|
||||
hash = sum[:]
|
||||
default:
|
||||
return nil, ErrUnsupportedHash
|
||||
}
|
||||
|
||||
keyRef, err := signer.getKeyRef()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chash, err := bytesToCFData(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer C.CFRelease(C.CFTypeRef(chash))
|
||||
|
||||
cert, err := signer.Certificate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
algo, err := getAlgo(cert, opts.HashFunc())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// sign the digest
|
||||
var cfErrRef C.CFErrorRef
|
||||
cSig := C.SecKeyCreateSignature(keyRef, algo, chash, &cfErrRef)
|
||||
|
||||
if err := cfErrorError(cfErrRef); err != nil {
|
||||
C.CFRelease(C.CFTypeRef(cfErrRef))
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cSig == 0 {
|
||||
return nil, errors.New("nil signature from SecKeyCreateSignature")
|
||||
}
|
||||
defer C.CFRelease(C.CFTypeRef(cSig))
|
||||
|
||||
sig := cfDataToBytes(cSig)
|
||||
|
||||
return sig, nil
|
||||
}
|
||||
|
||||
// getAlgo decides which algorithm to use with this key type for the given hash.
|
||||
func getAlgo(cert *x509.Certificate, hash crypto.Hash) (algo C.SecKeyAlgorithm, err error) {
|
||||
switch cert.PublicKey.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
switch hash {
|
||||
case crypto.SHA1:
|
||||
algo = C.kSecKeyAlgorithmECDSASignatureDigestX962SHA1
|
||||
case crypto.SHA256:
|
||||
algo = C.kSecKeyAlgorithmECDSASignatureDigestX962SHA256
|
||||
case crypto.SHA384:
|
||||
algo = C.kSecKeyAlgorithmECDSASignatureDigestX962SHA384
|
||||
case crypto.SHA512:
|
||||
algo = C.kSecKeyAlgorithmECDSASignatureDigestX962SHA512
|
||||
default:
|
||||
err = ErrUnsupportedHash
|
||||
}
|
||||
case *rsa.PublicKey:
|
||||
switch hash {
|
||||
case crypto.SHA1:
|
||||
algo = C.kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1
|
||||
case crypto.SHA256:
|
||||
algo = C.kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA256
|
||||
case crypto.SHA384:
|
||||
algo = C.kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA384
|
||||
case crypto.SHA512:
|
||||
algo = C.kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA512
|
||||
default:
|
||||
err = ErrUnsupportedHash
|
||||
}
|
||||
default:
|
||||
err = errors.New("unsupported key type")
|
||||
}
|
||||
|
||||
return algo, err
|
||||
}
|
||||
|
||||
// exportCertRef gets a *x509.Certificate for the given SecCertificateRef.
|
||||
func exportCertRef(certRef C.SecCertificateRef) (*x509.Certificate, error) {
|
||||
derRef := C.SecCertificateCopyData(certRef)
|
||||
if derRef == 0 {
|
||||
return nil, errors.New("error getting certificate from identity")
|
||||
}
|
||||
defer C.CFRelease(C.CFTypeRef(derRef))
|
||||
|
||||
der := cfDataToBytes(derRef)
|
||||
crt, err := x509.ParseCertificate(der)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return crt, nil
|
||||
}
|
||||
|
||||
// getKeyRef gets the SecKeyRef for this identity's private key.
|
||||
func getKeyRef(ref C.SecIdentityRef) (C.SecKeyRef, error) {
|
||||
var keyRef C.SecKeyRef
|
||||
if err := osStatusError(C.SecIdentityCopyPrivateKey(ref, &keyRef)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return keyRef, nil
|
||||
}
|
||||
|
||||
// getKeyRef gets the SecKeyRef for this identity's private key.
|
||||
func (signer DarwinCertStoreSigner) getKeyRef() (C.SecKeyRef, error) {
|
||||
if signer.keyRef != 0 {
|
||||
return signer.keyRef, nil
|
||||
}
|
||||
|
||||
keyRef, err := getKeyRef(signer.identRef)
|
||||
signer.keyRef = keyRef
|
||||
|
||||
return signer.keyRef, err
|
||||
}
|
||||
|
||||
// getCertRef gets the SecCertificateRef for this identity's certificate.
|
||||
func getCertRef(ref C.SecIdentityRef) (C.SecCertificateRef, error) {
|
||||
var certRef C.SecCertificateRef
|
||||
if err := osStatusError(C.SecIdentityCopyCertificate(ref, &certRef)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return certRef, nil
|
||||
}
|
||||
|
||||
// getCertRef gets the identity's certificate reference
|
||||
func (signer *DarwinCertStoreSigner) getCertRef() (C.SecCertificateRef, error) {
|
||||
if signer.certRef != 0 {
|
||||
return signer.certRef, nil
|
||||
}
|
||||
|
||||
certRef, err := getCertRef(signer.identRef)
|
||||
signer.certRef = certRef
|
||||
|
||||
return signer.certRef, err
|
||||
}
|
||||
|
||||
// stringToCFData converts a Go string to a CFDataRef
|
||||
func stringToCFData(str string) (C.CFDataRef, error) {
|
||||
return bytesToCFData([]byte(str))
|
||||
}
|
||||
|
||||
// cfDataToBytes converts a CFDataRef to a Go byte slice
|
||||
func cfDataToBytes(cfdata C.CFDataRef) []byte {
|
||||
nBytes := C.CFDataGetLength(cfdata)
|
||||
bytesPtr := C.CFDataGetBytePtr(cfdata)
|
||||
return C.GoBytes(unsafe.Pointer(bytesPtr), C.int(nBytes))
|
||||
}
|
||||
|
||||
// bytesToCFData converts a Go byte slice to a CFDataRef
|
||||
func bytesToCFData(gobytes []byte) (C.CFDataRef, error) {
|
||||
var (
|
||||
cptr = (*C.UInt8)(nil)
|
||||
clen = C.CFIndex(len(gobytes))
|
||||
)
|
||||
|
||||
if len(gobytes) > 0 {
|
||||
cptr = (*C.UInt8)(&gobytes[0])
|
||||
}
|
||||
|
||||
cdata := C.CFDataCreate(0, cptr, clen)
|
||||
if cdata == 0 {
|
||||
return 0, errors.New("error creating cdata")
|
||||
}
|
||||
|
||||
return cdata, nil
|
||||
}
|
||||
|
||||
// cfErrorError returns an error for a CFErrorRef unless it is nil
|
||||
func cfErrorError(cerr C.CFErrorRef) error {
|
||||
if cerr == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
code := int(C.CFErrorGetCode(cerr))
|
||||
|
||||
if cdescription := C.CFErrorCopyDescription(cerr); cdescription != 0 {
|
||||
defer C.CFRelease(C.CFTypeRef(cdescription))
|
||||
|
||||
if cstr := C.CFStringGetCStringPtr(cdescription, C.kCFStringEncodingUTF8); cstr != nil {
|
||||
str := C.GoString(cstr)
|
||||
|
||||
return fmt.Errorf("CFError %d (%s)", code, str)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return fmt.Errorf("CFError %d", code)
|
||||
}
|
||||
|
||||
// mapToCFDictionary converts a Go map[C.CFTypeRef]C.CFTypeRef to a CFDictionaryRef
|
||||
func mapToCFDictionary(gomap map[C.CFTypeRef]C.CFTypeRef) C.CFDictionaryRef {
|
||||
var (
|
||||
n = len(gomap)
|
||||
keys = make([]unsafe.Pointer, 0, n)
|
||||
values = make([]unsafe.Pointer, 0, n)
|
||||
)
|
||||
|
||||
for k, v := range gomap {
|
||||
keys = append(keys, unsafe.Pointer(k))
|
||||
values = append(values, unsafe.Pointer(v))
|
||||
}
|
||||
|
||||
return C.CFDictionaryCreate(0, &keys[0], &values[0], C.CFIndex(n), nil, nil)
|
||||
}
|
||||
|
||||
// osStatusError returns an error for an OSStatus unless it is errSecSuccess
|
||||
func osStatusError(s C.OSStatus) error {
|
||||
if s == C.errSecSuccess {
|
||||
return nil
|
||||
}
|
||||
|
||||
return osStatus(s)
|
||||
}
|
||||
|
||||
// Error implements the error interface and returns a stringified error for this osStatus
|
||||
func (s osStatus) Error() string {
|
||||
return fmt.Sprintf("OSStatus %d", s)
|
||||
}
|
||||
15
aws_signing_helper/cert_store_signer_linux.go
Normal file
15
aws_signing_helper/cert_store_signer_linux.go
Normal file
@@ -0,0 +1,15 @@
|
||||
//go:build linux
|
||||
|
||||
package aws_signing_helper
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
func GetMatchingCerts(certIdentifier CertIdentifier) ([]CertificateContainer, error) {
|
||||
return nil, errors.New("unable to use cert store signer on linux")
|
||||
}
|
||||
|
||||
func GetCertStoreSigner(certIdentifier CertIdentifier, useLatestExpiringCert bool) (signer Signer, signingAlgorithm string, err error) {
|
||||
return nil, "", errors.New("unable to use cert store signer on linux")
|
||||
}
|
||||
@@ -1,20 +1,22 @@
|
||||
package aws_signing_helper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"github.com/aws/rolesanywhere-credential-helper/rolesanywhere"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
v1m "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"runtime"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/arn"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/aws/arn"
|
||||
awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http"
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/rolesanywhere-credential-helper/rolesanywhere"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
type CredentialsOpts struct {
|
||||
@@ -22,6 +24,7 @@ type CredentialsOpts struct {
|
||||
CertificateId string
|
||||
CertificateBundleId string
|
||||
CertIdentifier CertIdentifier
|
||||
UseLatestExpiringCertificate bool
|
||||
RoleArn string
|
||||
ProfileArnStr string
|
||||
TrustAnchorArnStr string
|
||||
@@ -34,9 +37,24 @@ type CredentialsOpts struct {
|
||||
Version string
|
||||
LibPkcs11 string
|
||||
ReusePin bool
|
||||
TpmKeyPassword string
|
||||
NoTpmKeyPassword bool
|
||||
ServerTTL int
|
||||
RoleSessionName string
|
||||
Pkcs8Password string
|
||||
}
|
||||
|
||||
func createCredHelperUserAgentMiddleware(userAgent string) middleware.BuildMiddleware {
|
||||
return middleware.BuildMiddlewareFunc("UserAgent", func(
|
||||
ctx context.Context, input middleware.BuildInput, next middleware.BuildHandler,
|
||||
) (middleware.BuildOutput, middleware.Metadata, error) {
|
||||
if req, ok := input.Request.(*smithyhttp.Request); ok {
|
||||
req.Header.Set("User-Agent", userAgent)
|
||||
}
|
||||
return next.HandleBuild(ctx, input)
|
||||
})
|
||||
}
|
||||
|
||||
// Function to create session and generate credentials
|
||||
func GenerateCredentials(opts *CredentialsOpts, signer Signer, signatureAlgorithm string) (CredentialProcessOutput, error) {
|
||||
// Assign values to region and endpoint if they haven't already been assigned
|
||||
trustAnchorArn, err := arn.Parse(opts.TrustAnchorArnStr)
|
||||
@@ -56,35 +74,37 @@ func GenerateCredentials(opts *CredentialsOpts, signer Signer, signatureAlgorith
|
||||
opts.Region = trustAnchorArn.Region
|
||||
}
|
||||
|
||||
mySession := session.Must(session.NewSession())
|
||||
|
||||
var logLevel aws.LogLevelType
|
||||
var logMode aws.ClientLogMode = 0
|
||||
if Debug {
|
||||
logLevel = aws.LogDebug
|
||||
} else {
|
||||
logLevel = aws.LogOff
|
||||
logMode = aws.LogSigning | aws.LogRetries | aws.LogRequestWithBody | aws.LogResponseWithBody | aws.LogRequestEventMessage | aws.LogResponseEventMessage
|
||||
}
|
||||
|
||||
var tr *http.Transport
|
||||
// Custom HTTP client with proxy and TLS settings
|
||||
httpClient := awshttp.NewBuildableClient().WithTransportOptions(func(tr *http.Transport) {
|
||||
tr.TLSClientConfig = &tls.Config{MinVersion: tls.VersionTLS12, InsecureSkipVerify: opts.NoVerifySSL}
|
||||
if opts.WithProxy {
|
||||
tr = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{MinVersion: tls.VersionTLS12, InsecureSkipVerify: opts.NoVerifySSL},
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
tr.Proxy = http.ProxyFromEnvironment
|
||||
}
|
||||
} else {
|
||||
tr = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{MinVersion: tls.VersionTLS12, InsecureSkipVerify: opts.NoVerifySSL},
|
||||
})
|
||||
ctx := context.TODO()
|
||||
cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(opts.Region), config.WithHTTPClient(httpClient), config.WithClientLogMode(logMode))
|
||||
if err != nil {
|
||||
return CredentialProcessOutput{}, err
|
||||
}
|
||||
}
|
||||
client := &http.Client{Transport: tr}
|
||||
config := aws.NewConfig().WithRegion(opts.Region).WithHTTPClient(client).WithLogLevel(logLevel)
|
||||
|
||||
// Override endpoint if specified
|
||||
if opts.Endpoint != "" {
|
||||
config.WithEndpoint(opts.Endpoint)
|
||||
cfg.BaseEndpoint = aws.String(opts.Endpoint)
|
||||
}
|
||||
rolesAnywhereClient := rolesanywhere.New(mySession, config)
|
||||
rolesAnywhereClient.Handlers.Build.RemoveByName("core.SDKVersionUserAgentHandler")
|
||||
rolesAnywhereClient.Handlers.Build.PushBackNamed(request.NamedHandler{Name: "v4x509.CredHelperUserAgentHandler", Fn: request.MakeAddToUserAgentHandler("CredHelper", opts.Version, runtime.Version(), runtime.GOOS, runtime.GOARCH)})
|
||||
rolesAnywhereClient.Handlers.Sign.Clear()
|
||||
|
||||
// Set a custom user agent
|
||||
userAgentStr := fmt.Sprintf("CredHelper/%s (%s; %s; %s)", opts.Version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
||||
cfg.APIOptions = append(cfg.APIOptions, func(stack *middleware.Stack) error {
|
||||
stack.Build.Remove("UserAgent")
|
||||
return stack.Build.Add(createCredHelperUserAgentMiddleware(userAgentStr), middleware.After)
|
||||
})
|
||||
|
||||
// Add custom request signer, implementing SigV4-X509
|
||||
certificate, err := signer.Certificate()
|
||||
if err != nil {
|
||||
return CredentialProcessOutput{}, errors.New("unable to find certificate")
|
||||
@@ -96,10 +116,21 @@ func GenerateCredentials(opts *CredentialsOpts, signer Signer, signatureAlgorith
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
rolesAnywhereClient.Handlers.Sign.PushBackNamed(request.NamedHandler{Name: "v4x509.SignRequestHandler", Fn: CreateRequestSignFunction(signer, signatureAlgorithm, certificate, certificateChain)})
|
||||
cfg.APIOptions = append(cfg.APIOptions, func(stack *middleware.Stack) error {
|
||||
// Remove middleware related to SigV4 signing
|
||||
stack.Finalize.Remove("Signing")
|
||||
stack.Finalize.Remove("setLegacyContextSigningOptions")
|
||||
stack.Finalize.Remove("GetIdentity")
|
||||
// Add middleware for SigV4-X509 signing
|
||||
stack.Finalize.Add(middleware.FinalizeMiddlewareFunc("Signing", CreateRequestSignFinalizeFunction(signer, opts.Region, signatureAlgorithm, certificate, certificateChain)), middleware.After)
|
||||
return nil
|
||||
})
|
||||
|
||||
// Create the Roles Anywhere client using the above-constructed Config
|
||||
rolesAnywhereClient := rolesanywhere.NewFromConfig(cfg)
|
||||
|
||||
certificateStr := base64.StdEncoding.EncodeToString(certificate.Raw)
|
||||
durationSeconds := int64(opts.SessionDuration)
|
||||
durationSeconds := int32(opts.SessionDuration)
|
||||
createSessionRequest := rolesanywhere.CreateSessionInput{
|
||||
Cert: &certificateStr,
|
||||
ProfileArn: &opts.ProfileArnStr,
|
||||
@@ -109,7 +140,10 @@ func GenerateCredentials(opts *CredentialsOpts, signer Signer, signatureAlgorith
|
||||
RoleArn: &opts.RoleArn,
|
||||
SessionName: nil,
|
||||
}
|
||||
output, err := rolesAnywhereClient.CreateSession(&createSessionRequest)
|
||||
if opts.RoleSessionName != "" {
|
||||
createSessionRequest.RoleSessionName = &opts.RoleSessionName
|
||||
}
|
||||
output, err := rolesAnywhereClient.CreateSession(ctx, &createSessionRequest)
|
||||
if err != nil {
|
||||
return CredentialProcessOutput{}, err
|
||||
}
|
||||
@@ -128,20 +162,3 @@ func GenerateCredentials(opts *CredentialsOpts, signer Signer, signatureAlgorith
|
||||
}
|
||||
return credentialProcessOutput, nil
|
||||
}
|
||||
|
||||
func (credentials CredentialProcessOutput) ToSecret(secretName string) *v1.Secret {
|
||||
return &v1.Secret{
|
||||
ObjectMeta: v1m.ObjectMeta{
|
||||
Name: secretName,
|
||||
Labels: map[string]string{
|
||||
"managed-by": "aws-iam-anywhere-refresher",
|
||||
},
|
||||
},
|
||||
StringData: map[string]string{
|
||||
"AWS_ACCESS_KEY_ID": credentials.AccessKeyId,
|
||||
"AWS_SECRET_ACCESS_KEY": credentials.SecretAccessKey,
|
||||
"AWS_SESSION_TOKEN": credentials.SessionToken,
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,18 +18,19 @@ type FileSystemSigner struct {
|
||||
certPath string
|
||||
isPkcs12 bool
|
||||
privateKeyPath string
|
||||
pkcs8Password string
|
||||
}
|
||||
|
||||
func (fileSystemSigner *FileSystemSigner) Public() crypto.PublicKey {
|
||||
privateKey, _, _ := fileSystemSigner.readCertFiles()
|
||||
{
|
||||
privateKey, ok := privateKey.(ecdsa.PrivateKey)
|
||||
privateKey, ok := privateKey.(*ecdsa.PrivateKey)
|
||||
if ok {
|
||||
return &privateKey.PublicKey
|
||||
}
|
||||
}
|
||||
{
|
||||
privateKey, ok := privateKey.(rsa.PrivateKey)
|
||||
privateKey, ok := privateKey.(*rsa.PrivateKey)
|
||||
if ok {
|
||||
return &privateKey.PublicKey
|
||||
}
|
||||
@@ -56,17 +57,17 @@ func (fileSystemSigner *FileSystemSigner) Sign(rand io.Reader, digest []byte, op
|
||||
return nil, ErrUnsupportedHash
|
||||
}
|
||||
|
||||
ecdsaPrivateKey, ok := privateKey.(ecdsa.PrivateKey)
|
||||
ecdsaPrivateKey, ok := privateKey.(*ecdsa.PrivateKey)
|
||||
if ok {
|
||||
sig, err := ecdsa.SignASN1(rand, &ecdsaPrivateKey, hash[:])
|
||||
sig, err := ecdsa.SignASN1(rand, ecdsaPrivateKey, hash[:])
|
||||
if err == nil {
|
||||
return sig, nil
|
||||
}
|
||||
}
|
||||
|
||||
rsaPrivateKey, ok := privateKey.(rsa.PrivateKey)
|
||||
rsaPrivateKey, ok := privateKey.(*rsa.PrivateKey)
|
||||
if ok {
|
||||
sig, err := rsa.SignPKCS1v15(rand, &rsaPrivateKey, opts.HashFunc(), hash[:])
|
||||
sig, err := rsa.SignPKCS1v15(rand, rsaPrivateKey, opts.HashFunc(), hash[:])
|
||||
if err == nil {
|
||||
return sig, nil
|
||||
}
|
||||
@@ -87,17 +88,17 @@ func (fileSystemSigner *FileSystemSigner) CertificateChain() ([]*x509.Certificat
|
||||
}
|
||||
|
||||
// GetFileSystemSigner returns a FileSystemSigner, that signs a payload using the private key passed in
|
||||
func GetFileSystemSigner(privateKeyPath string, certPath string, bundlePath string, isPkcs12 bool) (signer Signer, signingAlgorithm string, err error) {
|
||||
fsSigner := &FileSystemSigner{bundlePath: bundlePath, certPath: certPath, isPkcs12: isPkcs12, privateKeyPath: privateKeyPath}
|
||||
func GetFileSystemSigner(privateKeyPath string, certPath string, bundlePath string, isPkcs12 bool, pkcs8Password string) (signer Signer, signingAlgorithm string, err error) {
|
||||
fsSigner := &FileSystemSigner{bundlePath: bundlePath, certPath: certPath, isPkcs12: isPkcs12, privateKeyPath: privateKeyPath, pkcs8Password: pkcs8Password}
|
||||
privateKey, _, _ := fsSigner.readCertFiles()
|
||||
// Find the signing algorithm
|
||||
_, isRsaKey := privateKey.(rsa.PrivateKey)
|
||||
_, isRsaKey := privateKey.(*rsa.PrivateKey)
|
||||
if isRsaKey {
|
||||
signingAlgorithm = aws4_x509_rsa_sha256
|
||||
signingAlgorithm = aws4X509RsaSha256
|
||||
}
|
||||
_, isEcKey := privateKey.(ecdsa.PrivateKey)
|
||||
_, isEcKey := privateKey.(*ecdsa.PrivateKey)
|
||||
if isEcKey {
|
||||
signingAlgorithm = aws4_x509_ecdsa_sha256
|
||||
signingAlgorithm = aws4X509EcdsaSha256
|
||||
}
|
||||
if signingAlgorithm == "" {
|
||||
log.Println("unsupported algorithm")
|
||||
@@ -111,22 +112,48 @@ func (fileSystemSigner *FileSystemSigner) readCertFiles() (crypto.PrivateKey, *x
|
||||
if fileSystemSigner.isPkcs12 {
|
||||
chain, privateKey, err := ReadPKCS12Data(fileSystemSigner.certPath)
|
||||
if err != nil {
|
||||
log.Printf("Failed to read PKCS12 certificate: %s\n", err)
|
||||
log.Printf("failed to read PKCS12 certificate: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return privateKey, chain[0], chain
|
||||
} else {
|
||||
privateKey, err := ReadPrivateKeyData(fileSystemSigner.privateKeyPath)
|
||||
var privateKey crypto.PrivateKey
|
||||
var err error
|
||||
if len(fileSystemSigner.pkcs8Password) > 0 || isPKCS8EncryptedBlockType(fileSystemSigner.privateKeyPath) {
|
||||
passwordPromptInput := PasswordPromptProps{
|
||||
InitialPassword: fileSystemSigner.pkcs8Password,
|
||||
NoPassword: false,
|
||||
CheckPassword: func(password string) (interface{}, error) {
|
||||
return ReadPrivateKeyData(fileSystemSigner.privateKeyPath, password)
|
||||
},
|
||||
IncorrectPasswordMsg: "incorrect PKCS#8 private key password",
|
||||
Prompt: "Please enter your PKC8 private key password:",
|
||||
Reprompt: "Incorrect PKCS#8 private key password. Please try again:",
|
||||
ParseErrMsg: "unable to read your PKCS#8 private key password",
|
||||
CheckPasswordAuthorizationErrorMsg: "unable to parse private key",
|
||||
}
|
||||
password, signingPrivKey, err := PasswordPrompt(passwordPromptInput)
|
||||
if err != nil {
|
||||
log.Printf("Failed to read private key: %s\n", err)
|
||||
log.Printf("failed to read private key: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fileSystemSigner.pkcs8Password = password
|
||||
privateKey, _ = signingPrivKey.(crypto.PrivateKey)
|
||||
} else {
|
||||
privateKey, err = ReadPrivateKeyData(fileSystemSigner.privateKeyPath, "")
|
||||
if err != nil {
|
||||
log.Printf("failed to read private key: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var chain []*x509.Certificate
|
||||
if fileSystemSigner.bundlePath != "" {
|
||||
chain, err = GetCertChain(fileSystemSigner.bundlePath)
|
||||
if err != nil {
|
||||
privateKey = nil
|
||||
log.Printf("Failed to read certificate bundle: %s\n", err)
|
||||
log.Printf("failed to read certificate bundle: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -135,7 +162,7 @@ func (fileSystemSigner *FileSystemSigner) readCertFiles() (crypto.PrivateKey, *x
|
||||
_, cert, err = ReadCertificateData(fileSystemSigner.certPath)
|
||||
if err != nil {
|
||||
privateKey = nil
|
||||
log.Printf("Failed to read certificate: %s\n", err)
|
||||
log.Printf("failed to read certificate: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else if len(chain) > 0 {
|
||||
|
||||
1227
aws_signing_helper/pkcs11_signer.go
Normal file
1227
aws_signing_helper/pkcs11_signer.go
Normal file
File diff suppressed because it is too large
Load Diff
301
aws_signing_helper/pkcs8_utils.go
Normal file
301
aws_signing_helper/pkcs8_utils.go
Normal file
@@ -0,0 +1,301 @@
|
||||
package aws_signing_helper
|
||||
|
||||
// PKCS#8 is a cryptographic standard that defines a flexible format
|
||||
// for encoding private key information, including both the key data
|
||||
// and optional attributes such as encryption parameters. It also allows
|
||||
// private keys to be encrypted using password-based encryption methods,
|
||||
// providing an additional layer of security.
|
||||
//
|
||||
// This file contains implementations of PKCS#8 decryption, which decodes
|
||||
// a PKCS#8-encrypted private key using PBES2, as defined in PKCS #5 (RFC 8018).
|
||||
//
|
||||
// Not all ciphers or pseudo-random function(PRF) are supported. Please refer to the list below for the supported options.
|
||||
//
|
||||
// Supported ciphers: AES-128/192/256-CBC
|
||||
// Supported KDFs: PBKDF2, Scrypt
|
||||
// Supported PRFs: HMACWithSHA256, HMACWithSHA384, HMACWithSHA512
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
"hash"
|
||||
"os"
|
||||
)
|
||||
|
||||
// as defined in https://datatracker.ietf.org/doc/html/rfc8018#appendix-A.4
|
||||
type PBES2Params struct {
|
||||
KeyDerivationFunc pkix.AlgorithmIdentifier
|
||||
EncryptionScheme pkix.AlgorithmIdentifier
|
||||
}
|
||||
|
||||
// as defined in https://datatracker.ietf.org/doc/html/rfc5958#section-3
|
||||
type EncryptedPrivateKeyInfo struct {
|
||||
EncryptionAlgorithm pkix.AlgorithmIdentifier
|
||||
EncryptedData []byte
|
||||
}
|
||||
|
||||
// as defined in https://datatracker.ietf.org/doc/html/rfc8018#appendix-A.2
|
||||
type PBKDF2RPFParams struct {
|
||||
Algorithm asn1.ObjectIdentifier
|
||||
Params asn1.RawValue `asn1:"optional"`
|
||||
}
|
||||
|
||||
// as defined in https://datatracker.ietf.org/doc/html/rfc8018#appendix-A.2
|
||||
// and https://datatracker.ietf.org/doc/html/rfc8018#section-5.2
|
||||
type PBKDF2Params struct {
|
||||
Salt []byte
|
||||
Iteration int
|
||||
PRF PBKDF2RPFParams
|
||||
}
|
||||
|
||||
type ScryptParams struct {
|
||||
Salt []byte
|
||||
CostFactor int
|
||||
BlockSizeFactor int
|
||||
ParallelizationFactor int
|
||||
}
|
||||
|
||||
// Supported PRFs
|
||||
var (
|
||||
oidHMACWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 9}
|
||||
oidHMACWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 10}
|
||||
oidHMACWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 11}
|
||||
)
|
||||
|
||||
// Supported KDFs
|
||||
var (
|
||||
oidPBKDF2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 12}
|
||||
oidScrypt = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11591, 4, 11}
|
||||
)
|
||||
|
||||
// Supported Ciphers
|
||||
var cipherMap = map[string]struct {
|
||||
KeySize int
|
||||
NewCipher func([]byte) (cipher.Block, error)
|
||||
}{
|
||||
"2.16.840.1.101.3.4.1.42": {32, aes.NewCipher}, // AES-256-CBC
|
||||
"2.16.840.1.101.3.4.1.22": {24, aes.NewCipher}, // AES-192-CBC
|
||||
"2.16.840.1.101.3.4.1.2": {16, aes.NewCipher}, // AES-128-CBC
|
||||
}
|
||||
|
||||
const unencryptedBlockType = "PRIVATE KEY"
|
||||
const encryptedBlockType = "ENCRYPTED PRIVATE KEY"
|
||||
|
||||
// 'getNewHash' creates a new hash based on the specified PRF
|
||||
func getNewHash(oid asn1.ObjectIdentifier) (func() hash.Hash, error) {
|
||||
switch {
|
||||
case oid.Equal(oidHMACWithSHA256):
|
||||
return sha256.New, nil
|
||||
case oid.Equal(oidHMACWithSHA384):
|
||||
return sha512.New384, nil
|
||||
case oid.Equal(oidHMACWithSHA512):
|
||||
return sha512.New, nil
|
||||
default:
|
||||
return nil, errors.New("unsupported hash function")
|
||||
}
|
||||
}
|
||||
|
||||
// 'extractCipherParams' extracts and parses cipher parameters.
|
||||
// It identifies the encryption algorithm and returns the IV and key size based on the detected algorithm.
|
||||
func extractCipherParams(es pkix.AlgorithmIdentifier) ([]byte, int, error) {
|
||||
algo, exist := cipherMap[es.Algorithm.String()]
|
||||
if !exist {
|
||||
return nil, 0, errors.New("unsupported encryption algorithm")
|
||||
}
|
||||
|
||||
var iv []byte
|
||||
if _, err := asn1.Unmarshal(es.Parameters.FullBytes, &iv); err != nil {
|
||||
return nil, 0, errors.New("failed to parse the initialization vector")
|
||||
}
|
||||
return iv, algo.KeySize, nil
|
||||
}
|
||||
|
||||
// PBKDF2 is a cryptographic algorithm designed to derive strong cryptographic keys from weak passwords by
|
||||
// applying a hashing function iteratively.
|
||||
// It takes as input a password, a cryptographic salt, an iteration count, and a desired output key length.
|
||||
// PBKDF2 is formally specified in RFC 8018, which is part of the PKCS#5 standard.
|
||||
func deriveKeyUsingPBKDF2(parameterBytes []byte, keySize int, password []byte) ([]byte, error) {
|
||||
var kdfParams PBKDF2Params
|
||||
if _, err := asn1.Unmarshal(parameterBytes, &kdfParams); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse ASN.1 OID: %w", err)
|
||||
}
|
||||
|
||||
hashFunc, err := getNewHash(kdfParams.PRF.Algorithm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := pbkdf2.Key(password, kdfParams.Salt, kdfParams.Iteration, keySize, hashFunc)
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// Scrypt is a password-based key derivation function specifically designed to be computationally and memory-hard.
|
||||
// The algorithm was specifically designed to make it costly to perform large-scale custom hardware attacks by requiring large amounts of memory.
|
||||
// For more information about Scrypt:
|
||||
// https://en.wikipedia.org/wiki/Scrypt
|
||||
func deriveKeyUsingScrypt(parameterBytes []byte, keySize int, password []byte) ([]byte, error) {
|
||||
var kdfParams ScryptParams
|
||||
if _, err := asn1.Unmarshal(parameterBytes, &kdfParams); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse ASN.1 OID: %w", err)
|
||||
}
|
||||
|
||||
key, err := scrypt.Key(password, kdfParams.Salt, kdfParams.CostFactor, kdfParams.BlockSizeFactor,
|
||||
kdfParams.ParallelizationFactor, keySize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// 'parseDERFromPEMForPKCS8' parses a PEM file.
|
||||
// If the blockType matches the required PEM blockType, it returns the decoded bytes.
|
||||
// It is only called for verifying PKCS#8 private keys. If the blockType is "PRIVATE KEY",
|
||||
// it indicates that a PKCS#8 password was provided when it should not have been.
|
||||
// Conversely, if the bloclType is "ENCRYPTED PRIVATE KEY",
|
||||
// it means the password was not provided when required.
|
||||
func parseDERFromPEMForPKCS8(pemDataId string, blockType string) (*pem.Block, error) {
|
||||
bytes, err := os.ReadFile(pemDataId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var block *pem.Block
|
||||
for len(bytes) > 0 {
|
||||
block, bytes = pem.Decode(bytes)
|
||||
if block == nil {
|
||||
return nil, errors.New("unable to parse PEM data")
|
||||
}
|
||||
if block.Type == blockType {
|
||||
return block, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("requested block type could not be found. The block type detected is %s", block.Type)
|
||||
}
|
||||
|
||||
// 'isPKCS8EncryptedBlockType' tries to decode the PEM block
|
||||
// and determine if the PEM block type is 'ENCRYPTED PRIVATE KEY'.
|
||||
func isPKCS8EncryptedBlockType(pemDataId string) bool {
|
||||
bytes, err := os.ReadFile(pemDataId)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
var block *pem.Block
|
||||
for len(bytes) > 0 {
|
||||
block, bytes = pem.Decode(bytes)
|
||||
if block == nil {
|
||||
return false
|
||||
}
|
||||
if block.Type == encryptedBlockType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 'readPKCS8PrivateKey' reads and parses an unencrypted PKCS#8 private key.
|
||||
func readPKCS8PrivateKey(privateKeyId string) (crypto.PrivateKey, error) {
|
||||
block, err := parseDERFromPEMForPKCS8(privateKeyId, unencryptedBlockType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, errors.New("could not parse private key")
|
||||
}
|
||||
|
||||
rsaPrivateKey, ok := privateKey.(*rsa.PrivateKey)
|
||||
if ok {
|
||||
return rsaPrivateKey, nil
|
||||
}
|
||||
|
||||
ecPrivateKey, ok := privateKey.(*ecdsa.PrivateKey)
|
||||
if ok {
|
||||
return ecPrivateKey, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("could not parse PKCS#8 private key")
|
||||
}
|
||||
|
||||
// 'readPKCS8EncryptedPrivateKey' reads and parses an encrypted PKCS#8 private key, following the process defined in RFC 8018.
|
||||
// Note that the encryption scheme must be PBES2, and the supported key types are limited to RSA and ECDSA.
|
||||
func readPKCS8EncryptedPrivateKey(privateKeyId string, pkcs8Password []byte) (crypto.PrivateKey, error) {
|
||||
block, err := parseDERFromPEMForPKCS8(privateKeyId, encryptedBlockType)
|
||||
if err != nil {
|
||||
return nil, errors.New("could not parse PEM data")
|
||||
}
|
||||
|
||||
var privKey EncryptedPrivateKeyInfo
|
||||
if _, err := asn1.Unmarshal(block.Bytes, &privKey); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse PKCS#8 structure: %w", err)
|
||||
}
|
||||
|
||||
var pbes2 PBES2Params
|
||||
if _, err := asn1.Unmarshal(privKey.EncryptionAlgorithm.Parameters.FullBytes, &pbes2); err != nil {
|
||||
return nil, errors.New("invalid PBES2 parameters")
|
||||
}
|
||||
|
||||
iv, keySize, err := extractCipherParams(pbes2.EncryptionScheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kdfOid := pbes2.KeyDerivationFunc.Algorithm
|
||||
var key []byte
|
||||
// zeroing the derived key after use
|
||||
defer copy(key, make([]byte, len(key)))
|
||||
switch {
|
||||
case kdfOid.Equal(oidPBKDF2):
|
||||
key, _ = deriveKeyUsingPBKDF2(pbes2.KeyDerivationFunc.Parameters.FullBytes, keySize, pkcs8Password)
|
||||
case kdfOid.Equal(oidScrypt):
|
||||
key, _ = deriveKeyUsingScrypt(pbes2.KeyDerivationFunc.Parameters.FullBytes, keySize, pkcs8Password)
|
||||
default:
|
||||
return nil, errors.New("unsupported key derivation function")
|
||||
}
|
||||
|
||||
blockCipher, err := cipherMap[pbes2.EncryptionScheme.Algorithm.String()].NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create cipher: %w", err)
|
||||
}
|
||||
|
||||
ciphertext := privKey.EncryptedData
|
||||
mode := cipher.NewCBCDecrypter(blockCipher, iv)
|
||||
plaintext := make([]byte, len(ciphertext))
|
||||
mode.CryptBlocks(plaintext, ciphertext)
|
||||
|
||||
privateKey, err := x509.ParsePKCS8PrivateKey(plaintext)
|
||||
if err != nil {
|
||||
return nil, errors.New("incorrect password or invalid key format")
|
||||
}
|
||||
|
||||
switch privateKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
rsaPrivateKey, ok := privateKey.(*rsa.PrivateKey)
|
||||
if ok {
|
||||
return rsaPrivateKey, nil
|
||||
}
|
||||
case *ecdsa.PrivateKey:
|
||||
ecPrivateKey, ok := privateKey.(*ecdsa.PrivateKey)
|
||||
if ok {
|
||||
return ecPrivateKey, nil
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("could not parse PKCS#8 private key")
|
||||
}
|
||||
|
||||
return nil, errors.New("could not parse PKCS#8 private key")
|
||||
}
|
||||
@@ -16,10 +16,11 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/arn"
|
||||
"github.com/aws/aws-sdk-go-v2/aws/arn"
|
||||
)
|
||||
|
||||
const DefaultPort = 9911
|
||||
const DefaultHopLimit = 64
|
||||
const LocalHostAddress = "127.0.0.1"
|
||||
|
||||
var RefreshTime = time.Minute * time.Duration(5)
|
||||
@@ -325,6 +326,7 @@ func Serve(port int, credentialsOptions CredentialsOpts) {
|
||||
log.Println("failed to create listener")
|
||||
os.Exit(1)
|
||||
}
|
||||
listener = NewListenerWithTTL(listener, credentialsOptions.ServerTTL)
|
||||
endpoint.PortNum = listener.Addr().(*net.TCPAddr).Port
|
||||
log.Println("Local server started on port:", endpoint.PortNum)
|
||||
log.Println("Make it available to the sdk by running:")
|
||||
|
||||
@@ -2,10 +2,12 @@ package aws_signing_helper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
@@ -15,17 +17,23 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
v1m "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"log"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
"golang.org/x/crypto/pkcs12"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
type SignerParams struct {
|
||||
@@ -47,18 +55,9 @@ var (
|
||||
// algorithm isn't supported.
|
||||
ErrUnsupportedHash = errors.New("unsupported hash algorithm")
|
||||
|
||||
// Predefined system store names.
|
||||
// See: https://learn.microsoft.com/en-us/windows/win32/seccrypto/system-store-locations
|
||||
SystemStoreNames = []string{
|
||||
"MY",
|
||||
"Root",
|
||||
"Trust",
|
||||
"CA",
|
||||
}
|
||||
RolesanywhereSigningName = "rolesanywhere"
|
||||
)
|
||||
|
||||
// Interface that all signers will have to implement
|
||||
// (as a result, they will also implement crypto.Signer)
|
||||
type Signer interface {
|
||||
Public() crypto.PublicKey
|
||||
Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error)
|
||||
@@ -67,7 +66,6 @@ type Signer interface {
|
||||
Close()
|
||||
}
|
||||
|
||||
// Container for certificate data returned to the SDK as JSON.
|
||||
type CertificateData struct {
|
||||
// Type for the key contained in the certificate.
|
||||
// Passed back to the `sign-string` command
|
||||
@@ -82,7 +80,6 @@ type CertificateData struct {
|
||||
Algorithms []string `json:"supportedAlgorithms"`
|
||||
}
|
||||
|
||||
// Container that adheres to the format of credential_process output as specified by AWS.
|
||||
type CredentialProcessOutput struct {
|
||||
// This field should be hard-coded to 1 for now.
|
||||
Version int `json:"Version"`
|
||||
@@ -97,6 +94,8 @@ type CredentialProcessOutput struct {
|
||||
}
|
||||
|
||||
type CertificateContainer struct {
|
||||
// Index (can be useful in sorting)
|
||||
Index int
|
||||
// Certificate data
|
||||
Cert *x509.Certificate
|
||||
// Certificate URI (only populated in the case that the certificate is a PKCS#11 object)
|
||||
@@ -105,17 +104,15 @@ type CertificateContainer struct {
|
||||
|
||||
// Define constants used in signing
|
||||
const (
|
||||
aws4_x509_rsa_sha256 = "AWS4-X509-RSA-SHA256"
|
||||
aws4_x509_ecdsa_sha256 = "AWS4-X509-ECDSA-SHA256"
|
||||
aws4X509RsaSha256 = "AWS4-X509-RSA-SHA256"
|
||||
aws4X509EcdsaSha256 = "AWS4-X509-ECDSA-SHA256"
|
||||
timeFormat = "20060102T150405Z"
|
||||
shortTimeFormat = "20060102"
|
||||
x_amz_date = "X-Amz-Date"
|
||||
x_amz_x509 = "X-Amz-X509"
|
||||
x_amz_x509_chain = "X-Amz-X509-Chain"
|
||||
x_amz_content_sha256 = "X-Amz-Content-Sha256"
|
||||
xAmzDate = "X-Amz-Date"
|
||||
xAmzX509 = "X-Amz-X509"
|
||||
xAmzX509Chain = "X-Amz-X509-Chain"
|
||||
authorization = "Authorization"
|
||||
host = "Host"
|
||||
emptyStringSHA256 = `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`
|
||||
)
|
||||
|
||||
// Headers that aren't included in calculating the signature
|
||||
@@ -125,7 +122,153 @@ var ignoredHeaderKeys = map[string]bool{
|
||||
"X-Amzn-Trace-Id": true,
|
||||
}
|
||||
|
||||
var Debug bool = false
|
||||
var Debug = false
|
||||
|
||||
func GetPassword(ttyReadFile *os.File, ttyWriteFile *os.File, prompt string, parseErrMsg string) (string, error) {
|
||||
_, _ = fmt.Fprintln(ttyWriteFile, prompt)
|
||||
passwordBytes, err := term.ReadPassword(int(ttyReadFile.Fd()))
|
||||
if err != nil {
|
||||
return "", errors.New(parseErrMsg)
|
||||
}
|
||||
|
||||
password := string(passwordBytes[:])
|
||||
strings.Replace(password, "\r", "", -1) // Remove CR
|
||||
return password, nil
|
||||
}
|
||||
|
||||
type PasswordPromptProps struct {
|
||||
InitialPassword string
|
||||
NoPassword bool
|
||||
CheckPassword func(string) (interface{}, error)
|
||||
IncorrectPasswordMsg string
|
||||
Prompt string
|
||||
Reprompt string
|
||||
ParseErrMsg string
|
||||
CheckPasswordAuthorizationErrorMsg string
|
||||
}
|
||||
|
||||
func PasswordPrompt(passwordPromptInput PasswordPromptProps) (string, interface{}, error) {
|
||||
var (
|
||||
err error
|
||||
ttyReadPath string
|
||||
ttyWritePath string
|
||||
ttyReadFile *os.File
|
||||
ttyWriteFile *os.File
|
||||
parseErrMsg string
|
||||
prompt string
|
||||
reprompt string
|
||||
password string
|
||||
incorrectPasswordMsg string
|
||||
checkPasswordAuthorizationErrorMsg string
|
||||
checkPassword func(string) (interface{}, error)
|
||||
checkPasswordResult interface{}
|
||||
noPassword bool
|
||||
)
|
||||
|
||||
password = passwordPromptInput.InitialPassword
|
||||
noPassword = passwordPromptInput.NoPassword
|
||||
incorrectPasswordMsg = passwordPromptInput.IncorrectPasswordMsg
|
||||
prompt = passwordPromptInput.Prompt
|
||||
reprompt = passwordPromptInput.Reprompt
|
||||
parseErrMsg = passwordPromptInput.ParseErrMsg
|
||||
checkPassword = passwordPromptInput.CheckPassword
|
||||
checkPasswordAuthorizationErrorMsg = passwordPromptInput.CheckPasswordAuthorizationErrorMsg
|
||||
|
||||
ttyReadPath = "/dev/tty"
|
||||
ttyWritePath = ttyReadPath
|
||||
if runtime.GOOS == "windows" {
|
||||
ttyReadPath = "CONIN$"
|
||||
ttyWritePath = "CONOUT$"
|
||||
}
|
||||
|
||||
// If no password is required
|
||||
if noPassword {
|
||||
checkPasswordResult, err = checkPassword("")
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return "", checkPasswordResult, nil
|
||||
}
|
||||
|
||||
// If the password was provided explicitly, beforehand
|
||||
if password != "" {
|
||||
checkPasswordResult, err = checkPassword(password)
|
||||
if err != nil {
|
||||
return "", nil, errors.New(incorrectPasswordMsg)
|
||||
}
|
||||
return password, checkPasswordResult, nil
|
||||
}
|
||||
|
||||
ttyReadFile, err = os.OpenFile(ttyReadPath, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return "", nil, errors.New(parseErrMsg)
|
||||
}
|
||||
defer func(ttyReadFile *os.File) {
|
||||
_ = ttyReadFile.Close()
|
||||
}(ttyReadFile)
|
||||
|
||||
ttyWriteFile, err = os.OpenFile(ttyWritePath, os.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
return "", nil, errors.New(parseErrMsg)
|
||||
}
|
||||
defer func(ttyWriteFile *os.File) {
|
||||
_ = ttyWriteFile.Close()
|
||||
}(ttyWriteFile)
|
||||
|
||||
// The key has a password, so prompt for it
|
||||
password, err = GetPassword(ttyReadFile, ttyWriteFile, prompt, parseErrMsg)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
checkPasswordResult, err = checkPassword(password)
|
||||
for {
|
||||
// If we've found the right password, return both it and the result of `checkPassword`
|
||||
if err == nil {
|
||||
return password, checkPasswordResult, nil
|
||||
}
|
||||
// Otherwise, if the password was incorrect, prompt for it again
|
||||
if strings.Contains(err.Error(), checkPasswordAuthorizationErrorMsg) {
|
||||
password, err = GetPassword(ttyReadFile, ttyWriteFile, reprompt, parseErrMsg)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
checkPasswordResult, err = checkPassword(password)
|
||||
continue
|
||||
}
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultCertContainerToString(certContainer CertificateContainer) string {
|
||||
var certStr string
|
||||
|
||||
cert := certContainer.Cert
|
||||
|
||||
fingerprint := sha1.Sum(cert.Raw) // nosemgrep
|
||||
fingerprintHex := hex.EncodeToString(fingerprint[:])
|
||||
certStr = fmt.Sprintf("%s \"%s\"\n", fingerprintHex, cert.Subject.String())
|
||||
|
||||
// Only for PKCS#11
|
||||
if certContainer.Uri != "" {
|
||||
certStr += fmt.Sprintf("\tURI: %s\n", certContainer.Uri)
|
||||
}
|
||||
|
||||
return certStr
|
||||
}
|
||||
|
||||
type CertificateContainerList []CertificateContainer
|
||||
|
||||
func (certificateContainerList CertificateContainerList) Less(i, j int) bool {
|
||||
return certificateContainerList[i].Cert.NotAfter.Before(certificateContainerList[j].Cert.NotAfter)
|
||||
}
|
||||
|
||||
func (certificateContainerList CertificateContainerList) Swap(i, j int) {
|
||||
certificateContainerList[i], certificateContainerList[j] = certificateContainerList[j], certificateContainerList[i]
|
||||
}
|
||||
|
||||
func (certificateContainerList CertificateContainerList) Len() int {
|
||||
return len(certificateContainerList)
|
||||
}
|
||||
|
||||
// Find whether the current certificate matches the CertIdentifier
|
||||
func certMatches(certIdentifier CertIdentifier, cert x509.Certificate) bool {
|
||||
@@ -152,7 +295,7 @@ func certMatches(certIdentifier CertIdentifier, cert x509.Certificate) bool {
|
||||
// }
|
||||
//
|
||||
// This is defined in RFC3279 §2.2.3 as well as SEC.1.
|
||||
// I can't find anything which mandates DER but I've seen
|
||||
// I can't find anything which mandates DER, but I've seen
|
||||
// OpenSSL refusing to verify it with indeterminate length.
|
||||
func encodeEcdsaSigValue(signature []byte) (out []byte, err error) {
|
||||
sigLen := len(signature) / 2
|
||||
@@ -169,14 +312,13 @@ func encodeEcdsaSigValue(signature []byte) (out []byte, err error) {
|
||||
func GetSigner(opts *CredentialsOpts) (signer Signer, signatureAlgorithm string, err error) {
|
||||
var (
|
||||
certificate *x509.Certificate
|
||||
certificateChain []*x509.Certificate
|
||||
)
|
||||
|
||||
privateKeyId := opts.PrivateKeyId
|
||||
if privateKeyId == "" {
|
||||
if opts.CertificateId == "" {
|
||||
if Debug {
|
||||
log.Println("attempting to use CertStoreSigner")
|
||||
}
|
||||
return GetCertStoreSigner(opts.CertIdentifier, opts.UseLatestExpiringCertificate)
|
||||
}
|
||||
privateKeyId = opts.CertificateId
|
||||
}
|
||||
@@ -186,9 +328,7 @@ func GetSigner(opts *CredentialsOpts) (signer Signer, signatureAlgorithm string,
|
||||
if err == nil {
|
||||
certificate = cert
|
||||
} else if opts.PrivateKeyId == "" {
|
||||
if Debug {
|
||||
log.Println("not a PEM certificate, so trying PKCS#12")
|
||||
}
|
||||
|
||||
if opts.CertificateBundleId != "" {
|
||||
return nil, "", errors.New("can't specify certificate chain when" +
|
||||
" using PKCS#12 files; certificate bundle should be provided" +
|
||||
@@ -199,54 +339,72 @@ func GetSigner(opts *CredentialsOpts) (signer Signer, signatureAlgorithm string,
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return GetFileSystemSigner(opts.PrivateKeyId, opts.CertificateId, opts.CertificateBundleId, true)
|
||||
return GetFileSystemSigner(opts.PrivateKeyId, opts.CertificateId, opts.CertificateBundleId, true, opts.Pkcs8Password)
|
||||
} else {
|
||||
return nil, "", err
|
||||
}
|
||||
}
|
||||
|
||||
if opts.CertificateBundleId != "" {
|
||||
certificateChain, err = GetCertChain(opts.CertificateBundleId)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(privateKeyId, "pkcs11:") {
|
||||
if Debug {
|
||||
log.Println("attempting to use PKCS11Signer")
|
||||
}
|
||||
if certificate != nil {
|
||||
opts.CertificateId = ""
|
||||
}
|
||||
return GetPKCS11Signer(opts.LibPkcs11, certificate, certificateChain, opts.PrivateKeyId, opts.CertificateId, opts.ReusePin)
|
||||
} else if strings.HasPrefix(privateKeyId, "handle:") {
|
||||
return GetTPMv2Signer(
|
||||
GetTPMv2SignerOpts{
|
||||
certificate,
|
||||
certificateChain,
|
||||
nil,
|
||||
opts.TpmKeyPassword,
|
||||
opts.NoTpmKeyPassword,
|
||||
opts.PrivateKeyId,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
_, err = ReadPrivateKeyData(privateKeyId)
|
||||
tpmKey, err := parseDERFromPEM(privateKeyId, "TSS2 PRIVATE KEY")
|
||||
if err == nil {
|
||||
return GetTPMv2Signer(
|
||||
GetTPMv2SignerOpts{
|
||||
certificate,
|
||||
certificateChain,
|
||||
tpmKey,
|
||||
opts.TpmKeyPassword,
|
||||
opts.NoTpmKeyPassword,
|
||||
"",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if !isPKCS8EncryptedBlockType(privateKeyId) {
|
||||
_, err = ReadPrivateKeyData(privateKeyId, opts.Pkcs8Password)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
}
|
||||
|
||||
if certificate == nil {
|
||||
return nil, "", errors.New("undefined certificate value")
|
||||
}
|
||||
if Debug {
|
||||
log.Println("attempting to use FileSystemSigner")
|
||||
return GetFileSystemSigner(privateKeyId, opts.CertificateId, opts.CertificateBundleId, false, opts.Pkcs8Password)
|
||||
}
|
||||
return GetFileSystemSigner(privateKeyId, opts.CertificateId, opts.CertificateBundleId, false)
|
||||
}
|
||||
|
||||
return nil, "", errors.New("unknown certificate type")
|
||||
}
|
||||
|
||||
// Obtain the date-time, formatted as specified by SigV4
|
||||
func (signerParams *SignerParams) GetFormattedSigningDateTime() string {
|
||||
return signerParams.OverriddenDate.UTC().Format(timeFormat)
|
||||
}
|
||||
|
||||
// Obtain the short date-time, formatted as specified by SigV4
|
||||
func (signerParams *SignerParams) GetFormattedShortSigningDateTime() string {
|
||||
return signerParams.OverriddenDate.UTC().Format(shortTimeFormat)
|
||||
}
|
||||
|
||||
// Obtain the scope as part of the SigV4-X509 signature
|
||||
func (signerParams *SignerParams) GetScope() string {
|
||||
var scopeStringBuilder strings.Builder
|
||||
scopeStringBuilder.WriteString(signerParams.GetFormattedShortSigningDateTime())
|
||||
@@ -276,75 +434,46 @@ func certificateChainToString(certificateChain []*x509.Certificate) string {
|
||||
return x509ChainString.String()
|
||||
}
|
||||
|
||||
func CreateRequestSignFunction(signer crypto.Signer, signingAlgorithm string, certificate *x509.Certificate, certificateChain []*x509.Certificate) func(*request.Request) {
|
||||
return func(req *request.Request) {
|
||||
region := req.ClientInfo.SigningRegion
|
||||
if region == "" {
|
||||
region = aws.StringValue(req.Config.Region)
|
||||
func CreateRequestSignFinalizeFunction(signer crypto.Signer, signingRegion string, signingAlgorithm string, certificate *x509.Certificate, certificateChain []*x509.Certificate) func(context.Context, middleware.FinalizeInput, middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) {
|
||||
return func(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) (out middleware.FinalizeOutput, metadata middleware.Metadata, err error) {
|
||||
req, ok := in.Request.(*smithyhttp.Request)
|
||||
if !ok {
|
||||
return out, metadata, errors.New(fmt.Sprintf("unexpected request middleware type %T", in.Request))
|
||||
}
|
||||
|
||||
name := req.ClientInfo.SigningName
|
||||
if name == "" {
|
||||
name = req.ClientInfo.ServiceName
|
||||
}
|
||||
payloadHash := v4.GetPayloadHash(ctx)
|
||||
signRequest(signer, signingRegion, signingAlgorithm, certificate, certificateChain, req.Request, payloadHash)
|
||||
|
||||
signerParams := SignerParams{time.Now(), region, name, signingAlgorithm}
|
||||
return next.HandleFinalize(ctx, in)
|
||||
}
|
||||
}
|
||||
|
||||
func signRequest(signer crypto.Signer, signingRegion string, signingAlgorithm string, certificate *x509.Certificate, certificateChain []*x509.Certificate, req *http.Request, payloadHash string) {
|
||||
signerParams := SignerParams{time.Now(), signingRegion, RolesanywhereSigningName, signingAlgorithm}
|
||||
|
||||
// Set headers that are necessary for signing
|
||||
req.HTTPRequest.Header.Set(host, req.HTTPRequest.URL.Host)
|
||||
req.HTTPRequest.Header.Set(x_amz_date, signerParams.GetFormattedSigningDateTime())
|
||||
req.HTTPRequest.Header.Set(x_amz_x509, certificateToString(certificate))
|
||||
req.Header.Set(host, req.URL.Host)
|
||||
req.Header.Set(xAmzDate, signerParams.GetFormattedSigningDateTime())
|
||||
req.Header.Set(xAmzX509, certificateToString(certificate))
|
||||
if certificateChain != nil {
|
||||
req.HTTPRequest.Header.Set(x_amz_x509_chain, certificateChainToString(certificateChain))
|
||||
req.Header.Set(xAmzX509Chain, certificateChainToString(certificateChain))
|
||||
}
|
||||
|
||||
contentSha256 := calculateContentHash(req.HTTPRequest, req.Body)
|
||||
if req.HTTPRequest.Header.Get(x_amz_content_sha256) == "required" {
|
||||
req.HTTPRequest.Header.Set(x_amz_content_sha256, contentSha256)
|
||||
}
|
||||
|
||||
canonicalRequest, signedHeadersString := createCanonicalRequest(req.HTTPRequest, req.Body, contentSha256)
|
||||
canonicalRequest, signedHeadersString := createCanonicalRequest(req, payloadHash)
|
||||
|
||||
stringToSign := CreateStringToSign(canonicalRequest, signerParams)
|
||||
signatureBytes, err := signer.Sign(rand.Reader, []byte(stringToSign), crypto.SHA256)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
log.Println("could not sign request", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
signature := hex.EncodeToString(signatureBytes)
|
||||
|
||||
req.HTTPRequest.Header.Set(authorization, BuildAuthorizationHeader(req.HTTPRequest, req.Body, signedHeadersString, signature, certificate, signerParams))
|
||||
req.SignedHeaderVals = req.HTTPRequest.Header
|
||||
}
|
||||
}
|
||||
|
||||
// Find the SHA256 hash of the provided request body as a io.ReadSeeker
|
||||
func makeSha256Reader(reader io.ReadSeeker) []byte {
|
||||
hash := sha256.New()
|
||||
start, _ := reader.Seek(0, 1)
|
||||
defer reader.Seek(start, 0)
|
||||
|
||||
io.Copy(hash, reader)
|
||||
return hash.Sum(nil)
|
||||
}
|
||||
|
||||
// Calculate the hash of the request body
|
||||
func calculateContentHash(r *http.Request, body io.ReadSeeker) string {
|
||||
hash := r.Header.Get(x_amz_content_sha256)
|
||||
|
||||
if hash == "" {
|
||||
if body == nil {
|
||||
hash = emptyStringSHA256
|
||||
} else {
|
||||
hash = hex.EncodeToString(makeSha256Reader(body))
|
||||
}
|
||||
}
|
||||
|
||||
return hash
|
||||
req.Header.Set(authorization, BuildAuthorizationHeader(req, signedHeadersString, signature, certificate, signerParams))
|
||||
}
|
||||
|
||||
// Create the canonical query string.
|
||||
func createCanonicalQueryString(r *http.Request, body io.ReadSeeker) string {
|
||||
func createCanonicalQueryString(r *http.Request) string {
|
||||
rawQuery := strings.Replace(r.URL.Query().Encode(), "+", "%20", -1)
|
||||
return rawQuery
|
||||
}
|
||||
@@ -424,14 +553,14 @@ func stripExcessSpaces(vals []string) {
|
||||
}
|
||||
|
||||
// Create the canonical request.
|
||||
func createCanonicalRequest(r *http.Request, body io.ReadSeeker, contentSha256 string) (string, string) {
|
||||
func createCanonicalRequest(r *http.Request, contentSha256 string) (string, string) {
|
||||
var canonicalRequestStrBuilder strings.Builder
|
||||
canonicalHeaderString, signedHeadersString := createCanonicalHeaderString(r)
|
||||
canonicalRequestStrBuilder.WriteString("POST")
|
||||
canonicalRequestStrBuilder.WriteString("\n")
|
||||
canonicalRequestStrBuilder.WriteString("/sessions")
|
||||
canonicalRequestStrBuilder.WriteString("\n")
|
||||
canonicalRequestStrBuilder.WriteString(createCanonicalQueryString(r, body))
|
||||
canonicalRequestStrBuilder.WriteString(createCanonicalQueryString(r))
|
||||
canonicalRequestStrBuilder.WriteString("\n")
|
||||
canonicalRequestStrBuilder.WriteString(canonicalHeaderString)
|
||||
canonicalRequestStrBuilder.WriteString("\n\n")
|
||||
@@ -443,7 +572,6 @@ func createCanonicalRequest(r *http.Request, body io.ReadSeeker, contentSha256 s
|
||||
return hex.EncodeToString(canonicalRequestStringHashBytes[:]), signedHeadersString
|
||||
}
|
||||
|
||||
// Create the string to sign.
|
||||
func CreateStringToSign(canonicalRequest string, signerParams SignerParams) string {
|
||||
var stringToSignStrBuilder strings.Builder
|
||||
stringToSignStrBuilder.WriteString(signerParams.SigningAlgorithm)
|
||||
@@ -457,8 +585,7 @@ func CreateStringToSign(canonicalRequest string, signerParams SignerParams) stri
|
||||
return stringToSign
|
||||
}
|
||||
|
||||
// Builds the complete authorization header
|
||||
func BuildAuthorizationHeader(request *http.Request, body io.ReadSeeker, signedHeadersString string, signature string, certificate *x509.Certificate, signerParams SignerParams) string {
|
||||
func BuildAuthorizationHeader(_ *http.Request, signedHeadersString string, signature string, certificate *x509.Certificate, signerParams SignerParams) string {
|
||||
signingCredentials := certificate.SerialNumber.String() + "/" + signerParams.GetScope()
|
||||
credential := "Credential=" + signingCredentials
|
||||
signerHeaders := "SignedHeaders=" + signedHeadersString
|
||||
@@ -479,8 +606,8 @@ func BuildAuthorizationHeader(request *http.Request, body io.ReadSeeker, signedH
|
||||
func encodeDer(der []byte) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
encoder := base64.NewEncoder(base64.StdEncoding, &buf)
|
||||
encoder.Write(der)
|
||||
encoder.Close()
|
||||
_, _ = encoder.Write(der)
|
||||
_ = encoder.Close()
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
@@ -501,104 +628,64 @@ func parseDERFromPEM(pemDataId string, blockType string) (*pem.Block, error) {
|
||||
}
|
||||
|
||||
func ReadCertificateBundleData(certificateBundleId string) ([]*x509.Certificate, error) {
|
||||
bytes, err := os.ReadFile(certificateBundleId)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var derBytes []byte
|
||||
var block *pem.Block
|
||||
for len(bytes) > 0 {
|
||||
block, bytes = pem.Decode(bytes)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
block, _ = pem.Decode([]byte(certificateBundleId))
|
||||
|
||||
if block.Type != "CERTIFICATE" {
|
||||
return nil, errors.New("invalid certificate chain")
|
||||
}
|
||||
|
||||
blockBytes := block.Bytes
|
||||
derBytes = append(derBytes, blockBytes...)
|
||||
}
|
||||
|
||||
return x509.ParseCertificates(derBytes)
|
||||
}
|
||||
|
||||
func readECPrivateKey(privateKeyId string) (ecdsa.PrivateKey, error) {
|
||||
func readECPrivateKey(privateKeyId string) (*ecdsa.PrivateKey, error) {
|
||||
block, err := parseDERFromPEM(privateKeyId, "EC PRIVATE KEY")
|
||||
if err != nil {
|
||||
return ecdsa.PrivateKey{}, errors.New("could not parse PEM data")
|
||||
}
|
||||
|
||||
privateKey, err := x509.ParseECPrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return ecdsa.PrivateKey{}, errors.New("could not parse private key")
|
||||
}
|
||||
|
||||
return *privateKey, nil
|
||||
}
|
||||
|
||||
func readRSAPrivateKey(privateKeyId string) (rsa.PrivateKey, error) {
|
||||
block, err := parseDERFromPEM(privateKeyId, "RSA PRIVATE KEY")
|
||||
if err != nil {
|
||||
return rsa.PrivateKey{}, errors.New("could not parse PEM data")
|
||||
}
|
||||
|
||||
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return rsa.PrivateKey{}, errors.New("could not parse private key")
|
||||
}
|
||||
|
||||
return *privateKey, nil
|
||||
}
|
||||
|
||||
func readPKCS8PrivateKey(privateKeyId string) (crypto.PrivateKey, error) {
|
||||
block, err := parseDERFromPEM(privateKeyId, "PRIVATE KEY")
|
||||
if err != nil {
|
||||
return nil, errors.New("could not parse PEM data")
|
||||
}
|
||||
|
||||
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||
privateKey, err := x509.ParseECPrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, errors.New("could not parse private key")
|
||||
}
|
||||
|
||||
rsaPrivateKey, ok := privateKey.(*rsa.PrivateKey)
|
||||
if ok {
|
||||
return *rsaPrivateKey, nil
|
||||
}
|
||||
|
||||
ecPrivateKey, ok := privateKey.(*ecdsa.PrivateKey)
|
||||
if ok {
|
||||
return *ecPrivateKey, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("could not parse PKCS#8 private key")
|
||||
return privateKey, nil
|
||||
}
|
||||
|
||||
func readRSAPrivateKey(privateKeyId string) (*rsa.PrivateKey, error) {
|
||||
block, err := parseDERFromPEM(privateKeyId, "RSA PRIVATE KEY")
|
||||
if err != nil {
|
||||
return nil, errors.New("could not parse PEM data")
|
||||
}
|
||||
|
||||
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, errors.New("could not parse private key")
|
||||
}
|
||||
|
||||
return privateKey, nil
|
||||
}
|
||||
|
||||
// Reads and parses a PKCS#12 file (which should contain an end-entity
|
||||
// certificate, (optional) certificate chain, and the key associated with the
|
||||
// end-entity certificate). The end-entity certificate will be the first
|
||||
// certificate in the returned chain. This method assumes that there is
|
||||
// exactly one certificate that doesn't issue any others within the container
|
||||
// and treats that as the end-entity certificate. Also, the order of the other
|
||||
// certificates in the chain aren't guaranteed (it's also not guaranteed that
|
||||
// those certificates form a chain with the end-entity certificat either).
|
||||
func ReadPKCS12Data(certificateId string) (certChain []*x509.Certificate, privateKey crypto.PrivateKey, err error) {
|
||||
var (
|
||||
bytes []byte
|
||||
bts []byte
|
||||
pemBlocks []*pem.Block
|
||||
parsedCerts []*x509.Certificate
|
||||
certMap map[string]*x509.Certificate
|
||||
endEntityFoundIndex int
|
||||
)
|
||||
|
||||
bytes, err = os.ReadFile(certificateId)
|
||||
bts, err = os.ReadFile(certificateId)
|
||||
if err != nil {
|
||||
return nil, nil, nil
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pemBlocks, err = pkcs12.ToPEM(bytes, "")
|
||||
pemBlocks, err = pkcs12.ToPEM(bts, "")
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
@@ -614,16 +701,11 @@ func ReadPKCS12Data(certificateId string) (certChain []*x509.Certificate, privat
|
||||
privateKey = privateKeyTmp
|
||||
continue
|
||||
}
|
||||
// If neither a certificate nor a private key could be parsed from the
|
||||
// Block, ignore it and continue.
|
||||
if Debug {
|
||||
log.Println("unable to parse PEM block in PKCS#12 file - skipping")
|
||||
}
|
||||
}
|
||||
|
||||
certMap = make(map[string]*x509.Certificate)
|
||||
for _, cert := range parsedCerts {
|
||||
// pkix.Name.String() roughly following the RFC 2253 Distinguished Names
|
||||
// pkix.Name.String() roughly follows the RFC 2253 Distinguished Names
|
||||
// syntax, so we assume that it's canonical.
|
||||
issuer := cert.Issuer.String()
|
||||
certMap[issuer] = cert
|
||||
@@ -638,10 +720,6 @@ func ReadPKCS12Data(certificateId string) (certChain []*x509.Certificate, privat
|
||||
break
|
||||
}
|
||||
}
|
||||
if endEntityFoundIndex == -1 {
|
||||
return nil, "", errors.New("no end-entity certificate found in PKCS#12 file")
|
||||
}
|
||||
|
||||
for i, cert := range parsedCerts {
|
||||
if i != endEntityFoundIndex {
|
||||
certChain = append(certChain, cert)
|
||||
@@ -651,12 +729,19 @@ func ReadPKCS12Data(certificateId string) (certChain []*x509.Certificate, privat
|
||||
return certChain, privateKey, nil
|
||||
}
|
||||
|
||||
// Load the private key referenced by `privateKeyId`.
|
||||
func ReadPrivateKeyData(privateKeyId string) (crypto.PrivateKey, error) {
|
||||
func ReadPrivateKeyData(privateKeyId string, pkcs8Password ...string) (crypto.PrivateKey, error) {
|
||||
if len(pkcs8Password) > 0 && pkcs8Password[0] != "" {
|
||||
if key, err := readPKCS8EncryptedPrivateKey(privateKeyId, []byte(pkcs8Password[0])); err == nil {
|
||||
return key, nil
|
||||
}
|
||||
return nil, errors.New("unable to parse private key")
|
||||
}
|
||||
|
||||
if key, err := readPKCS8PrivateKey(privateKeyId); err == nil {
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// Try EC and RSA keys as a fallback
|
||||
if key, err := readECPrivateKey(privateKeyId); err == nil {
|
||||
return key, nil
|
||||
}
|
||||
@@ -668,7 +753,6 @@ func ReadPrivateKeyData(privateKeyId string) (crypto.PrivateKey, error) {
|
||||
return nil, errors.New("unable to parse private key")
|
||||
}
|
||||
|
||||
// Reads private key data from a *pem.Block.
|
||||
func ReadPrivateKeyDataFromPEMBlock(block *pem.Block) (key crypto.PrivateKey, err error) {
|
||||
key, err = x509.ParseECPrivateKey(block.Bytes)
|
||||
if err == nil {
|
||||
@@ -693,7 +777,6 @@ func ReadCertificateData(certificateId string) (CertificateData, *x509.Certifica
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
log.Println("could not parse certificate", err)
|
||||
return CertificateData{}, nil, errors.New("could not parse certificate")
|
||||
}
|
||||
|
||||
@@ -736,3 +819,20 @@ func GetCertChain(certificateBundleId string) ([]*x509.Certificate, error) {
|
||||
}
|
||||
return chain, nil
|
||||
}
|
||||
|
||||
func (credentials CredentialProcessOutput) ToSecret(secretName string) *v1.Secret {
|
||||
return &v1.Secret{
|
||||
ObjectMeta: v1m.ObjectMeta{
|
||||
Name: secretName,
|
||||
Labels: map[string]string{
|
||||
"managed-by": "aws-iam-anywhere-refresher",
|
||||
},
|
||||
},
|
||||
StringData: map[string]string{
|
||||
"AWS_ACCESS_KEY_ID": credentials.AccessKeyId,
|
||||
"AWS_SECRET_ACCESS_KEY": credentials.SecretAccessKey,
|
||||
"AWS_SESSION_TOKEN": credentials.SessionToken,
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -847,7 +847,7 @@ func TestStoreValidToken(t *testing.T) {
|
||||
t.Log("unable to create test http request")
|
||||
t.Fail()
|
||||
}
|
||||
httpRequest.Header.Add(EC2_METADATA_TOKEN_HEADER, token)
|
||||
httpRequest.Header.Add(Ec2MetadataTokenHeader, token)
|
||||
|
||||
err = CheckValidToken(nil, httpRequest)
|
||||
if err != nil {
|
||||
|
||||
446
aws_signing_helper/tpm_signer.go
Normal file
446
aws_signing_helper/tpm_signer.go
Normal file
@@ -0,0 +1,446 @@
|
||||
package aws_signing_helper
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
tpm3 "github.com/google/go-tpm/legacy/tpm2"
|
||||
"github.com/google/go-tpm/tpmutil"
|
||||
)
|
||||
|
||||
type tpm2_TPMPolicy struct {
|
||||
CommandCode int `asn1:"explicit,tag:0"`
|
||||
CommandPolicy []byte `asn1:"explicit,tag:1"`
|
||||
}
|
||||
|
||||
type tpm2_TPMAuthPolicy struct {
|
||||
Name string `asn1:"utf8,optional,explicit,tag:0"`
|
||||
Policy []tpm2_TPMPolicy `asn1:"explicit,tag:1"`
|
||||
}
|
||||
|
||||
type tpm2_TPMKey struct {
|
||||
Oid asn1.ObjectIdentifier
|
||||
EmptyAuth bool `asn1:"optional,explicit,tag:0"`
|
||||
Policy []tpm2_TPMPolicy `asn1:"optional,explicit,tag:1"`
|
||||
Secret []byte `asn1:"optional,explicit,tag:2"`
|
||||
AuthPolicy []tpm2_TPMAuthPolicy `asn1:"optional,explicit,tag:3"`
|
||||
Parent int
|
||||
Pubkey []byte
|
||||
Privkey []byte
|
||||
}
|
||||
|
||||
var oidLoadableKey = asn1.ObjectIdentifier{2, 23, 133, 10, 1, 3}
|
||||
var TPM_RC_AUTH_FAIL = "0x22"
|
||||
|
||||
type TPMv2Signer struct {
|
||||
cert *x509.Certificate
|
||||
certChain []*x509.Certificate
|
||||
tpmData tpm2_TPMKey
|
||||
public tpm3.Public
|
||||
private []byte
|
||||
password string
|
||||
emptyAuth bool
|
||||
handle tpmutil.Handle
|
||||
}
|
||||
|
||||
func handleIsPersistent(h int) bool {
|
||||
return (h >> 24) == int(tpm3.HandleTypePersistent)
|
||||
}
|
||||
|
||||
var primaryParams = tpm3.Public{
|
||||
Type: tpm3.AlgECC,
|
||||
NameAlg: tpm3.AlgSHA256,
|
||||
Attributes: tpm3.FlagUserWithAuth | tpm3.FlagRestricted | tpm3.FlagDecrypt | tpm3.FlagFixedTPM | tpm3.FlagFixedParent | tpm3.FlagNoDA | tpm3.FlagSensitiveDataOrigin,
|
||||
ECCParameters: &tpm3.ECCParams{
|
||||
Symmetric: &tpm3.SymScheme{
|
||||
Alg: tpm3.AlgAES,
|
||||
KeyBits: 128,
|
||||
Mode: tpm3.AlgCFB,
|
||||
},
|
||||
Sign: &tpm3.SigScheme{
|
||||
Alg: tpm3.AlgNull,
|
||||
},
|
||||
CurveID: tpm3.CurveNISTP256,
|
||||
KDF: &tpm3.KDFScheme{
|
||||
Alg: tpm3.AlgNull,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type GetTPMv2SignerOpts struct {
|
||||
certificate *x509.Certificate
|
||||
certificateChain []*x509.Certificate
|
||||
keyPem *pem.Block
|
||||
password string
|
||||
emptyAuth bool
|
||||
handle string
|
||||
}
|
||||
|
||||
// Returns the public key associated with this TPMv2Signer
|
||||
func (tpmv2Signer *TPMv2Signer) Public() crypto.PublicKey {
|
||||
ret, _ := tpmv2Signer.public.Key()
|
||||
return ret
|
||||
}
|
||||
|
||||
// Closes this TPMv2Signer
|
||||
func (tpmv2Signer *TPMv2Signer) Close() {
|
||||
tpmv2Signer.password = ""
|
||||
}
|
||||
|
||||
func checkCapability(rw io.ReadWriter, algo tpm3.Algorithm) error {
|
||||
descs, _, err := tpm3.GetCapability(rw, tpm3.CapabilityAlgs, 1, uint32(algo))
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("error trying to get capability from TPM for the algorithm (%s)", algo)
|
||||
return errors.New(errMsg)
|
||||
}
|
||||
if tpm3.Algorithm(descs[0].(tpm3.AlgorithmDescription).ID) != algo {
|
||||
errMsg := fmt.Sprintf("unsupported algorithm (%s) for TPM", algo)
|
||||
return errors.New(errMsg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Implements the crypto.Signer interface and signs the passed in digest
|
||||
func (tpmv2Signer *TPMv2Signer) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) {
|
||||
var (
|
||||
keyHandle tpmutil.Handle
|
||||
)
|
||||
|
||||
rw, err := openTPM()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rw.Close()
|
||||
|
||||
if tpmv2Signer.handle != 0 {
|
||||
keyHandle = tpmv2Signer.handle
|
||||
} else {
|
||||
parentHandle := tpmutil.Handle(tpmv2Signer.tpmData.Parent)
|
||||
if !handleIsPersistent(tpmv2Signer.tpmData.Parent) {
|
||||
// Parent and owner passwords aren't supported currently when creating a primary given a persistent handle for the parent
|
||||
parentHandle, _, err = tpm3.CreatePrimary(rw, tpmutil.Handle(tpmv2Signer.tpmData.Parent), tpm3.PCRSelection{}, "", "", primaryParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer tpm3.FlushContext(rw, parentHandle)
|
||||
}
|
||||
|
||||
keyHandle, _, err = tpm3.Load(rw, parentHandle, "", tpmv2Signer.tpmData.Pubkey[2:], tpmv2Signer.tpmData.Privkey[2:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer tpm3.FlushContext(rw, keyHandle)
|
||||
}
|
||||
|
||||
var algo tpm3.Algorithm
|
||||
var shadigest []byte
|
||||
|
||||
switch opts.HashFunc() {
|
||||
case crypto.SHA256:
|
||||
sha256digest := sha256.Sum256(digest)
|
||||
shadigest = sha256digest[:]
|
||||
algo = tpm3.AlgSHA256
|
||||
case crypto.SHA384:
|
||||
sha384digest := sha512.Sum384(digest)
|
||||
shadigest = sha384digest[:]
|
||||
algo = tpm3.AlgSHA384
|
||||
case crypto.SHA512:
|
||||
sha512digest := sha512.Sum512(digest)
|
||||
shadigest = sha512digest[:]
|
||||
algo = tpm3.AlgSHA512
|
||||
default:
|
||||
panic("unhandled default case")
|
||||
}
|
||||
|
||||
if tpmv2Signer.public.Type == tpm3.AlgECC {
|
||||
// Check to see that ECDSA is supported for signing
|
||||
err = checkCapability(rw, tpm3.AlgECC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// For an EC key we lie to the TPM about what the hash is.
|
||||
// It doesn't actually matter what the original digest was;
|
||||
// the algo we feed to the TPM is *purely* based on the
|
||||
// size of the curve itself. We truncate the actual digest,
|
||||
// or pad with zeroes, to the byte size of the key.
|
||||
pubKey, err := tpmv2Signer.public.Key()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ecPubKey, ok := pubKey.(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, errors.New("failed to obtain ecdsa.PublicKey")
|
||||
}
|
||||
bitSize := ecPubKey.Curve.Params().BitSize
|
||||
byteSize := (bitSize + 7) / 8
|
||||
if byteSize > sha512.Size {
|
||||
byteSize = sha512.Size
|
||||
}
|
||||
switch byteSize {
|
||||
case sha512.Size:
|
||||
algo = tpm3.AlgSHA512
|
||||
case sha512.Size384:
|
||||
algo = tpm3.AlgSHA384
|
||||
case sha512.Size256:
|
||||
algo = tpm3.AlgSHA256
|
||||
case sha1.Size:
|
||||
algo = tpm3.AlgSHA1
|
||||
default:
|
||||
return nil, errors.New("unsupported curve")
|
||||
}
|
||||
|
||||
if len(shadigest) > byteSize {
|
||||
shadigest = shadigest[:byteSize]
|
||||
}
|
||||
|
||||
for len(shadigest) < byteSize {
|
||||
shadigest = append([]byte{0}, shadigest...)
|
||||
}
|
||||
|
||||
sig, err := tpmv2Signer.signHelper(rw, keyHandle, shadigest,
|
||||
&tpm3.SigScheme{Alg: tpm3.AlgECDSA, Hash: algo})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signature, err = asn1.Marshal(struct {
|
||||
R *big.Int
|
||||
S *big.Int
|
||||
}{sig.ECC.R, sig.ECC.S})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Check to see that the requested hash function is supported
|
||||
err = checkCapability(rw, algo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check to see that RSASSA is supported for signing
|
||||
err = checkCapability(rw, tpm3.AlgRSASSA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sig, err := tpmv2Signer.signHelper(rw, keyHandle, shadigest,
|
||||
&tpm3.SigScheme{Alg: tpm3.AlgRSASSA, Hash: algo})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signature = sig.RSA.Signature
|
||||
}
|
||||
return signature, nil
|
||||
}
|
||||
|
||||
func (tpmv2Signer *TPMv2Signer) signHelper(rw io.ReadWriter, keyHandle tpmutil.Handle, digest tpmutil.U16Bytes, sigScheme *tpm3.SigScheme) (*tpm3.Signature, error) {
|
||||
passwordPromptInput := PasswordPromptProps{
|
||||
InitialPassword: tpmv2Signer.password,
|
||||
NoPassword: tpmv2Signer.emptyAuth,
|
||||
CheckPassword: func(password string) (interface{}, error) {
|
||||
return tpm3.Sign(rw, keyHandle, password, digest, nil, sigScheme)
|
||||
},
|
||||
IncorrectPasswordMsg: "incorrect TPM key password",
|
||||
Prompt: "Please enter your TPM key password:",
|
||||
Reprompt: "Incorrect TPM key password. Please try again:",
|
||||
ParseErrMsg: "unable to read your TPM key password",
|
||||
CheckPasswordAuthorizationErrorMsg: TPM_RC_AUTH_FAIL,
|
||||
}
|
||||
|
||||
password, sig, err := PasswordPrompt(passwordPromptInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tpmv2Signer.password = password
|
||||
return sig.(*tpm3.Signature), err
|
||||
}
|
||||
|
||||
// Gets the x509.Certificate associated with this TPMv2Signer
|
||||
func (tpmv2Signer *TPMv2Signer) Certificate() (*x509.Certificate, error) {
|
||||
return tpmv2Signer.cert, nil
|
||||
}
|
||||
|
||||
// Gets the certificate chain associated with this TPMv2Signer
|
||||
func (tpmv2Signer *TPMv2Signer) CertificateChain() (chain []*x509.Certificate, err error) {
|
||||
return tpmv2Signer.certChain, nil
|
||||
}
|
||||
|
||||
/*
|
||||
* DER forbids storing a BOOLEAN as anything but 0x00 or 0xFF,
|
||||
* 0x01, and the Go asn1 parser cannot be relaxed. But both
|
||||
* OpenSSL ENGINEs which produce these keys have at least in
|
||||
* the past emitted 0x01 as the value, leading to an Unmarshal
|
||||
* failure with 'asn1: syntax error: invalid boolean'. So...
|
||||
*/
|
||||
func fixupEmptyAuth(tpmData *[]byte) {
|
||||
var pos int = 0
|
||||
|
||||
// Skip the SEQUENCE tag and length
|
||||
if len(*tpmData) < 2 || (*tpmData)[0] != 0x30 {
|
||||
return
|
||||
}
|
||||
|
||||
// Don't care what the SEQUENCE length is, just skip it
|
||||
pos = 1
|
||||
lenByte := (*tpmData)[pos]
|
||||
if lenByte < 0x80 {
|
||||
pos = pos + 1
|
||||
} else if lenByte < 0x85 {
|
||||
pos = pos + 1 + int(lenByte) - 0x80
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
if len(*tpmData) <= pos {
|
||||
return
|
||||
}
|
||||
|
||||
// Use asn1.Unmarshal to eat the OID; we care about 'rest'
|
||||
var oid asn1.ObjectIdentifier
|
||||
rest, err := asn1.Unmarshal((*tpmData)[pos:], &oid)
|
||||
if err != nil || rest == nil || !oid.Equal(oidLoadableKey) || len(rest) < 5 {
|
||||
return
|
||||
}
|
||||
|
||||
// If the OPTIONAL EXPLICIT BOOLEAN [0] exists, it'll be here
|
||||
pos = len(*tpmData) - len(rest)
|
||||
|
||||
if (*tpmData)[pos] == 0xa0 && // Tag
|
||||
(*tpmData)[pos+1] == 0x03 && // length
|
||||
(*tpmData)[pos+2] == 0x01 &&
|
||||
(*tpmData)[pos+3] == 0x01 &&
|
||||
(*tpmData)[pos+4] == 0x01 {
|
||||
(*tpmData)[pos+4] = 0xff
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a TPMv2Signer, that can be used to sign a payload through a TPMv2-compatible
|
||||
// cryptographic device
|
||||
func GetTPMv2Signer(opts GetTPMv2SignerOpts) (signer Signer, signingAlgorithm string, err error) {
|
||||
var (
|
||||
certificate *x509.Certificate
|
||||
certificateChain []*x509.Certificate
|
||||
keyPem *pem.Block
|
||||
password string
|
||||
emptyAuth bool
|
||||
tpmData tpm2_TPMKey
|
||||
handle tpmutil.Handle
|
||||
public tpm3.Public
|
||||
private []byte
|
||||
)
|
||||
|
||||
certificate = opts.certificate
|
||||
certificateChain = opts.certificateChain
|
||||
keyPem = opts.keyPem
|
||||
password = opts.password
|
||||
emptyAuth = opts.emptyAuth
|
||||
|
||||
// If a handle is provided instead of a TPM key file
|
||||
if opts.handle != "" {
|
||||
handleParts := strings.Split(opts.handle, ":")
|
||||
if len(handleParts) != 2 {
|
||||
return nil, "", errors.New("invalid TPM handle format")
|
||||
}
|
||||
hexHandleStr := handleParts[1]
|
||||
if strings.HasPrefix(hexHandleStr, "0x") {
|
||||
hexHandleStr = hexHandleStr[2:]
|
||||
}
|
||||
handleValue, err := strconv.ParseUint(hexHandleStr, 16, 32)
|
||||
if err != nil {
|
||||
return nil, "", errors.New("invalid hex TPM handle value")
|
||||
}
|
||||
handle = tpmutil.Handle(handleValue)
|
||||
|
||||
// Read the public key from the loaded key within the TPM
|
||||
rw, err := openTPM()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
defer rw.Close()
|
||||
|
||||
public, _, _, err = tpm3.ReadPublic(rw, handle)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
} else {
|
||||
fixupEmptyAuth(&keyPem.Bytes)
|
||||
_, err = asn1.Unmarshal(keyPem.Bytes, &tpmData)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
emptyAuth = tpmData.EmptyAuth
|
||||
if emptyAuth && password != "" {
|
||||
return nil, "", errors.New("password is provided but TPM key file indicates that one isn't required")
|
||||
}
|
||||
|
||||
if !tpmData.Oid.Equal(oidLoadableKey) {
|
||||
return nil, "", errors.New("invalid OID for TPMv2 key:" + tpmData.Oid.String())
|
||||
}
|
||||
|
||||
if tpmData.Policy != nil || tpmData.AuthPolicy != nil {
|
||||
return nil, "", errors.New("TPMv2 policy not implemented yet")
|
||||
}
|
||||
if tpmData.Secret != nil {
|
||||
return nil, "", errors.New("TPMv2 key has 'secret' field which should not be set")
|
||||
}
|
||||
|
||||
if !handleIsPersistent(tpmData.Parent) &&
|
||||
tpmData.Parent != int(tpm3.HandleOwner) &&
|
||||
tpmData.Parent != int(tpm3.HandleNull) &&
|
||||
tpmData.Parent != int(tpm3.HandleEndorsement) &&
|
||||
tpmData.Parent != int(tpm3.HandlePlatform) {
|
||||
return nil, "", errors.New("invalid parent for TPMv2 key")
|
||||
}
|
||||
if len(tpmData.Pubkey) < 2 ||
|
||||
len(tpmData.Pubkey)-2 != (int(tpmData.Pubkey[0])<<8)+int(tpmData.Pubkey[1]) {
|
||||
return nil, "", errors.New("invalid length for TPMv2 PUBLIC blob")
|
||||
}
|
||||
|
||||
public, err = tpm3.DecodePublic(tpmData.Pubkey[2:])
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if len(tpmData.Privkey) < 2 ||
|
||||
len(tpmData.Privkey)-2 != (int(tpmData.Privkey[0])<<8)+int(tpmData.Privkey[1]) {
|
||||
return nil, "", errors.New("invalid length for TPMv2 PRIVATE blob")
|
||||
}
|
||||
private = tpmData.Privkey[2:]
|
||||
}
|
||||
|
||||
switch public.Type {
|
||||
case tpm3.AlgRSA:
|
||||
signingAlgorithm = aws4X509RsaSha256
|
||||
case tpm3.AlgECC:
|
||||
signingAlgorithm = aws4X509EcdsaSha256
|
||||
default:
|
||||
return nil, "", errors.New("unsupported TPMv2 key type")
|
||||
}
|
||||
|
||||
return &TPMv2Signer{
|
||||
certificate,
|
||||
certificateChain,
|
||||
tpmData,
|
||||
public,
|
||||
private,
|
||||
password,
|
||||
emptyAuth,
|
||||
handle,
|
||||
},
|
||||
signingAlgorithm, nil
|
||||
}
|
||||
17
aws_signing_helper/tpm_signer_helper.go
Normal file
17
aws_signing_helper/tpm_signer_helper.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package aws_signing_helper
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
tpm3 "github.com/google/go-tpm/legacy/tpm2"
|
||||
)
|
||||
|
||||
func openTPM() (io.ReadWriteCloser, error) {
|
||||
var paths []string
|
||||
tpmdev := os.Getenv("TPM_DEVICE")
|
||||
if tpmdev != "" {
|
||||
paths = append(paths, tpmdev)
|
||||
}
|
||||
return tpm3.OpenTPM(paths...)
|
||||
}
|
||||
42
aws_signing_helper/ttl_listener.go
Normal file
42
aws_signing_helper/ttl_listener.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package aws_signing_helper
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
type ttlListener struct {
|
||||
l net.Listener
|
||||
ttl int
|
||||
}
|
||||
|
||||
// NewListenerWithTTL wraps a net.Listener and sets the TTL on outgoing packets to the specififed value
|
||||
func NewListenerWithTTL(l net.Listener, ttl int) net.Listener {
|
||||
return &ttlListener{l, ttl}
|
||||
}
|
||||
|
||||
func (w *ttlListener) Accept() (net.Conn, error) {
|
||||
c, err := w.l.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.RemoteAddr().(*net.TCPAddr).IP.To16() != nil && c.RemoteAddr().(*net.TCPAddr).IP.To4() == nil {
|
||||
p := ipv6.NewConn(c)
|
||||
if err := p.SetHopLimit(w.ttl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if c.RemoteAddr().(*net.TCPAddr).IP.To4() != nil {
|
||||
p := ipv4.NewConn(c)
|
||||
if err := p.SetTTL(w.ttl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (w *ttlListener) Close() error { return w.l.Close() }
|
||||
|
||||
func (w *ttlListener) Addr() net.Addr { return w.l.Addr() }
|
||||
@@ -1,6 +1,6 @@
|
||||
package cmd
|
||||
|
||||
import helper "git.s.int/rrise/aws-iam-anywhere-refresher/aws_signing_helper"
|
||||
import helper "gitea.siteworxpro.com/Siteworxpro/aws-iam-anywhere-refresher/aws_signing_helper"
|
||||
|
||||
func Run(opts *helper.CredentialsOpts) (*helper.CredentialProcessOutput, error) {
|
||||
signer, signingAlgorithm, err := helper.GetSigner(opts)
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package config
|
||||
|
||||
import "git.s.int/packages/go/utilities/Env"
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"gitea.siteworxpro.com/golang-packages/utilities/Env"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
const (
|
||||
namespace Env.EnvironmentVariable = "NAMESPACE"
|
||||
@@ -10,8 +15,10 @@ const (
|
||||
trustedAnchorArn Env.EnvironmentVariable = "TRUSTED_ANCHOR_ARN"
|
||||
privateKey Env.EnvironmentVariable = "PRIVATE_KEY"
|
||||
certificate Env.EnvironmentVariable = "CERTIFICATE"
|
||||
bundleId Env.EnvironmentVariable = "CA_CHAIN"
|
||||
sessionDuration Env.EnvironmentVariable = "SESSION_DURATION"
|
||||
restartDeployments Env.EnvironmentVariable = "RESTART_DEPLOYMENTS"
|
||||
fetchOnly Env.EnvironmentVariable = "FETCH_ONLY"
|
||||
)
|
||||
|
||||
type Config struct{}
|
||||
@@ -20,6 +27,59 @@ func NewConfig() *Config {
|
||||
return &Config{}
|
||||
}
|
||||
|
||||
func (c Config) Valid() error {
|
||||
// Certificate Required
|
||||
if c.Certificate() == "" {
|
||||
return fmt.Errorf("certificate is required")
|
||||
}
|
||||
|
||||
// Private Key Required
|
||||
if c.PrivateKey() == "" {
|
||||
return fmt.Errorf("private Key is required")
|
||||
}
|
||||
|
||||
// Role ARN Required
|
||||
if c.RoleArn() == "" {
|
||||
return fmt.Errorf("role ARN is required")
|
||||
}
|
||||
|
||||
if !regexp.MustCompile(`^arn:aws:iam::[0-9]{10,13}:role/[\w\D]*$`).MatchString(c.RoleArn()) {
|
||||
return fmt.Errorf("role ARN %s is invalid", c.RoleArn())
|
||||
}
|
||||
|
||||
if c.ProfileArn() == "" {
|
||||
return fmt.Errorf("profile ARN is required")
|
||||
}
|
||||
|
||||
if !regexp.MustCompile(`^arn:aws:rolesanywhere:[\w-]*:\d{10,12}:profile/[\w\D]*$`).MatchString(c.ProfileArn()) {
|
||||
return fmt.Errorf("profile ARN %s is invalid", c.ProfileArn())
|
||||
}
|
||||
|
||||
// Trusted Anchor ARN Required
|
||||
if c.TrustedAnchor() == "" {
|
||||
return fmt.Errorf("trusted anchor ARN is required")
|
||||
}
|
||||
|
||||
if !regexp.MustCompile(`^arn:aws:rolesanywhere:[\w-]*:\d{10,12}:trust-anchor/[\w\D]*$`).MatchString(c.TrustedAnchor()) {
|
||||
return fmt.Errorf("trusted anchor %s ARN is invalid", c.TrustedAnchor())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (Config) BundleId() string {
|
||||
v, err := base64.StdEncoding.DecodeString(bundleId.GetEnvString(""))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return string(v)
|
||||
}
|
||||
|
||||
func (Config) FetchOnly() bool {
|
||||
return fetchOnly.GetEnvBool(false)
|
||||
}
|
||||
|
||||
func (Config) Namespace() string {
|
||||
return namespace.GetEnvString("")
|
||||
}
|
||||
@@ -41,11 +101,21 @@ func (Config) TrustedAnchor() string {
|
||||
}
|
||||
|
||||
func (Config) PrivateKey() string {
|
||||
return privateKey.GetEnvString("")
|
||||
v, err := base64.StdEncoding.DecodeString(privateKey.GetEnvString(""))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return string(v)
|
||||
}
|
||||
|
||||
func (Config) Certificate() string {
|
||||
return certificate.GetEnvString("")
|
||||
v, err := base64.StdEncoding.DecodeString(certificate.GetEnvString(""))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return string(v)
|
||||
}
|
||||
|
||||
func (Config) SessionDuration() int64 {
|
||||
|
||||
123
go.mod
123
go.mod
@@ -1,64 +1,97 @@
|
||||
module git.s.int/rrise/aws-iam-anywhere-refresher
|
||||
module gitea.siteworxpro.com/Siteworxpro/aws-iam-anywhere-refresher
|
||||
|
||||
go 1.24.0
|
||||
go 1.25.3
|
||||
|
||||
require (
|
||||
git.s.int/packages/go/utilities v1.2.2
|
||||
github.com/aws/aws-sdk-go v1.55.5
|
||||
github.com/aws/rolesanywhere-credential-helper v1.2.0
|
||||
github.com/charmbracelet/log v0.4.0
|
||||
golang.org/x/crypto v0.31.0
|
||||
k8s.io/api v0.31.1
|
||||
k8s.io/apimachinery v0.31.1
|
||||
k8s.io/client-go v0.31.1
|
||||
gitea.siteworxpro.com/golang-packages/utilities v1.0.0
|
||||
github.com/aws/aws-sdk-go v1.55.8
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.0
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.6
|
||||
github.com/aws/rolesanywhere-credential-helper v1.7.2
|
||||
github.com/aws/smithy-go v1.24.0
|
||||
github.com/charmbracelet/log v0.4.2
|
||||
github.com/google/go-tpm v0.9.7
|
||||
github.com/miekg/pkcs11 v1.1.1
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6
|
||||
golang.org/x/crypto v0.46.0
|
||||
golang.org/x/net v0.48.0
|
||||
golang.org/x/term v0.38.0
|
||||
k8s.io/api v0.35.0
|
||||
k8s.io/apimachinery v0.35.0
|
||||
k8s.io/client-go v0.35.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.10.0 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.4.1 // indirect
|
||||
github.com/charmbracelet/lipgloss v1.1.0 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.11.3 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.14 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.6.2 // indirect
|
||||
github.com/clipperhouse/stringish v0.1.1 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.4 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.1 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.22.4 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.4 // indirect
|
||||
github.com/go-openapi/swag v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/cmdutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/conv v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/fileutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/loading v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/mangling v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/netutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4 // indirect
|
||||
github.com/google/gnostic-models v0.7.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.15.2 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
|
||||
golang.org/x/net v0.26.0 // indirect
|
||||
golang.org/x/oauth2 v0.21.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/term v0.27.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect
|
||||
golang.org/x/oauth2 v0.34.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e // indirect
|
||||
k8s.io/utils v0.0.0-20251222233032-718f0e51e6d2 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect
|
||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||
)
|
||||
|
||||
330
go.sum
330
go.sum
@@ -1,195 +1,231 @@
|
||||
git.s.int/packages/go/utilities v1.2.2 h1:IXKdrTgRc7tnDUB4sOWD/kjwgw9luUzvsaPzX+Dhm7Y=
|
||||
git.s.int/packages/go/utilities v1.2.2/go.mod h1:1nIS3PzUaLiNBBkyme408XbI725PiureeTV7iBXfUI0=
|
||||
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
|
||||
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||
github.com/aws/rolesanywhere-credential-helper v1.2.0 h1:eLqJvSznH8nJk48dwFc0raWOpbTGgBeNYH3Q8UQFVx4=
|
||||
github.com/aws/rolesanywhere-credential-helper v1.2.0/go.mod h1:YRxmRrAaqbVVXPNH1gHT76nWaMGvpAziHAHw8UwKrpU=
|
||||
gitea.siteworxpro.com/golang-packages/utilities v1.0.0 h1:f5JqAeZWBn/HBO9k5dzg0Wm91a69uwU5UC2P9ebQ9J0=
|
||||
gitea.siteworxpro.com/golang-packages/utilities v1.0.0/go.mod h1:QNqclnfv/BT2D5tbXgsGm7uhhe2Baovi5F6j0pVvMGc=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ=
|
||||
github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.0 h1:tNvqh1s+v0vFYdA1xq0aOJH+Y5cRyZ5upu6roPgPKd4=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.0/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.6 h1:hFLBGUKjmLAekvi1evLi5hVvFQtSo3GYwi+Bx4lpJf8=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.6/go.mod h1:lcUL/gcd8WyjCrMnxez5OXkO3/rwcNmvfno62tnXNcI=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.6 h1:F9vWao2TwjV2MyiyVS+duza0NIRtAslgLUM0vTA1ZaE=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.6/go.mod h1:SgHzKjEVsdQr6Opor0ihgWtkWdfRAIwxYzSJ8O85VHY=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 h1:80+uETIWS1BqjnN9uJ0dBUaETh+P1XwFy5vwHwK5r9k=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16/go.mod h1:wOOsYuxYuB/7FlnVtzeBYRcjSRtQpAW0hCP7tIULMwo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 h1:rgGwPzb82iBYSvHMHXc8h9mRoOUBZIGFgKb9qniaZZc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16/go.mod h1:L/UxsGeKpGoIj6DxfhOWHWQ/kGKcd4I1VncE4++IyKA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 h1:1jtGzuV7c82xnqOVfx2F0xmJcOw5374L7N6juGW6x6U=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16/go.mod h1:M2E5OQf+XLe+SZGmmpaI2yy+J326aFf6/+54PoxSANc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 h1:oHjJHeUy0ImIV0bsrX0X91GkV5nJAyv1l1CC9lnO0TI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16/go.mod h1:iRSNGgOYmiYwSCXxXaKb9HfOEj40+oTKn8pTxMlYkRM=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 h1:HpI7aMmJ+mm1wkSHIA2t5EaFFv5EFYXePW30p1EIrbQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.4/go.mod h1:C5RdGMYGlfM0gYq/tifqgn4EbyX99V15P2V3R+VHbQU=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.8 h1:aM/Q24rIlS3bRAhTyFurowU8A0SMyGDtEOY/l/s/1Uw=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.8/go.mod h1:+fWt2UHSb4kS7Pu8y+BMBvJF0EWx+4H0hzNwtDNRTrg=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 h1:AHDr0DaHIAo8c9t1emrzAlVDFp+iMMKnPdYy6XO4MCE=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12/go.mod h1:GQ73XawFFiWxyWXMHWfhiomvP3tXtdNar/fi8z18sx0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 h1:SciGFVNZ4mHdm7gpD1dgZYnCuVdX1s+lFTg4+4DOy70=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.5/go.mod h1:iW40X4QBmUxdP+fZNOpfmkdMZqsovezbAeO+Ubiv2pk=
|
||||
github.com/aws/rolesanywhere-credential-helper v1.7.2 h1:DcouWSyzdjpCZNAQh/ajXlHPds2Kz2mM8FovCjzUy6k=
|
||||
github.com/aws/rolesanywhere-credential-helper v1.7.2/go.mod h1:0WSfaR9Jc/tsAWJUHdZSLxQKpqltdL0SMjmykDTT3G8=
|
||||
github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk=
|
||||
github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s=
|
||||
github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE=
|
||||
github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM=
|
||||
github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM=
|
||||
github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
|
||||
github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
|
||||
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
||||
github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig=
|
||||
github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
|
||||
github.com/charmbracelet/x/ansi v0.11.3 h1:6DcVaqWI82BBVM/atTyq6yBoRLZFBsnoDoX9GCu2YOI=
|
||||
github.com/charmbracelet/x/ansi v0.11.3/go.mod h1:yI7Zslym9tCJcedxz5+WBq+eUGMJT0bM06Fqy1/Y4dI=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.14 h1:iUEMryGyFTelKW3THW4+FfPgi4fkmKnnaLOXuc+/Kj4=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.14/go.mod h1:P447lJl49ywBbil/KjCk2HexGh4tEY9LH0/1QrZZ9rA=
|
||||
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
||||
github.com/clipperhouse/displaywidth v0.6.2 h1:ZDpTkFfpHOKte4RG5O/BOyf3ysnvFswpyYrV7z2uAKo=
|
||||
github.com/clipperhouse/displaywidth v0.6.2/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
|
||||
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
||||
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
|
||||
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
|
||||
github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE=
|
||||
github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4=
|
||||
github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80=
|
||||
github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8=
|
||||
github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4=
|
||||
github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU=
|
||||
github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ=
|
||||
github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4=
|
||||
github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=
|
||||
github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
|
||||
github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
|
||||
github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y=
|
||||
github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk=
|
||||
github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
|
||||
github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM=
|
||||
github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
|
||||
github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
|
||||
github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48=
|
||||
github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg=
|
||||
github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0=
|
||||
github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg=
|
||||
github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
|
||||
github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
|
||||
github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw=
|
||||
github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg=
|
||||
github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
|
||||
github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c=
|
||||
github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-tpm v0.9.7 h1:u89J4tUUeDTlH8xxC3CTW7OHZjbjKoHdQ9W7gCUhtxA=
|
||||
github.com/google/go-tpm v0.9.7/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
|
||||
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba h1:qJEJcuLzH5KDR0gKc0zcktin6KSAwL7+jWKBYceddTc=
|
||||
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba/go.mod h1:EFYHy8/1y2KfgTAsx7Luu7NGhoxtuVHnNo8jE7FikKc=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
|
||||
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
||||
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
|
||||
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
|
||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||
github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns=
|
||||
github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
|
||||
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
|
||||
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 h1:pnnLyeX7o/5aX8qUQ69P/mLojDqwda8hFOCBTmP/6hw=
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h1:39R/xuhNgVhi+K0/zst4TLrJrVmbm6LVgl4A0+ZFS5M=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=
|
||||
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
||||
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
|
||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
||||
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
||||
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU=
|
||||
k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI=
|
||||
k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
|
||||
k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
||||
k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0=
|
||||
k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg=
|
||||
k8s.io/api v0.35.0 h1:iBAU5LTyBI9vw3L5glmat1njFK34srdLmktWwLTprlY=
|
||||
k8s.io/api v0.35.0/go.mod h1:AQ0SNTzm4ZAczM03QH42c7l3bih1TbAXYo0DkF8ktnA=
|
||||
k8s.io/apimachinery v0.35.0 h1:Z2L3IHvPVv/MJ7xRxHEtk6GoJElaAqDCCU0S6ncYok8=
|
||||
k8s.io/apimachinery v0.35.0/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
|
||||
k8s.io/client-go v0.35.0 h1:IAW0ifFbfQQwQmga0UdoH0yvdqrbwMdq9vIFEhRpxBE=
|
||||
k8s.io/client-go v0.35.0/go.mod h1:q2E5AAyqcbeLGPdoRB+Nxe3KYTfPce1Dnu1myQdqz9o=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e h1:iW9ChlU0cU16w8MpVYjXk12dqQ4BPFBEgif+ap7/hqQ=
|
||||
k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
|
||||
k8s.io/utils v0.0.0-20251222233032-718f0e51e6d2 h1:OfgiEo21hGiwx1oJUU5MpEaeOEg6coWndBkZF/lkFuE=
|
||||
k8s.io/utils v0.0.0-20251222233032-718f0e51e6d2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.1 h1:JrhdFMqOd/+3ByqlP2I45kTOZmTRLBUm5pvRjeheg7E=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
|
||||
54
main.go
54
main.go
@@ -1,14 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
helper "git.s.int/rrise/aws-iam-anywhere-refresher/aws_signing_helper"
|
||||
"git.s.int/rrise/aws-iam-anywhere-refresher/cmd"
|
||||
appConfig "git.s.int/rrise/aws-iam-anywhere-refresher/config"
|
||||
"git.s.int/rrise/aws-iam-anywhere-refresher/kube_client"
|
||||
"github.com/charmbracelet/log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
helper "gitea.siteworxpro.com/Siteworxpro/aws-iam-anywhere-refresher/aws_signing_helper"
|
||||
"gitea.siteworxpro.com/Siteworxpro/aws-iam-anywhere-refresher/cmd"
|
||||
appConfig "gitea.siteworxpro.com/Siteworxpro/aws-iam-anywhere-refresher/config"
|
||||
"gitea.siteworxpro.com/Siteworxpro/aws-iam-anywhere-refresher/kube_client"
|
||||
"github.com/charmbracelet/log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -18,35 +18,21 @@ func main() {
|
||||
ReportTimestamp: true,
|
||||
TimeFormat: time.RFC3339,
|
||||
})
|
||||
|
||||
l.Info("Starting credentials refresh")
|
||||
|
||||
client, err := kube_client.NewKubeClient()
|
||||
if err != nil {
|
||||
l.Error("Failed to create kubernetes client", "error", err)
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
c := appConfig.NewConfig()
|
||||
|
||||
privateKey, err := base64.StdEncoding.DecodeString(c.PrivateKey())
|
||||
err := c.Valid()
|
||||
if err != nil {
|
||||
l.Error("Failed to decode private key", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
certificate, err := base64.StdEncoding.DecodeString(c.Certificate())
|
||||
if err != nil {
|
||||
l.Error("Failed to decode certificate", "error", err)
|
||||
l.Error("Invalid configuration", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
credentials, err := cmd.Run(&helper.CredentialsOpts{
|
||||
PrivateKeyId: string(privateKey),
|
||||
CertificateId: string(certificate),
|
||||
CertIdentifier: helper.CertIdentifier{
|
||||
SystemStoreName: "MY",
|
||||
},
|
||||
PrivateKeyId: c.PrivateKey(),
|
||||
CertificateId: c.Certificate(),
|
||||
CertificateBundleId: c.BundleId(),
|
||||
RoleArn: c.RoleArn(),
|
||||
ProfileArnStr: c.ProfileArn(),
|
||||
TrustAnchorArnStr: c.TrustedAnchor(),
|
||||
@@ -61,6 +47,22 @@ func main() {
|
||||
|
||||
l.Info("Credentials refreshed")
|
||||
|
||||
if c.FetchOnly() {
|
||||
l.Info("Fetch only mode, skipping secret update")
|
||||
|
||||
l.Info("AccessKeyId", "access-key-id", credentials.AccessKeyId)
|
||||
l.Info("SecretAccessKey", "secret-access-key", credentials.SecretAccessKey)
|
||||
l.Info("SessionToken", "session-token", credentials.SessionToken)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
client, err := kube_client.NewKubeClient()
|
||||
if err != nil {
|
||||
l.Error("Failed to create kubernetes client", "error", err)
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
_, err = client.GetSecret(c.Namespace(), c.Secret())
|
||||
if err != nil {
|
||||
l.Error("Failed to get secret", "error", err)
|
||||
|
||||
Reference in New Issue
Block a user