Rename root, api and sdk module paths (imports, go.mod, go_package, ldflags, build scripts, buf configs) and regenerate protobuf/gateway code with the project's pinned toolchain. Wire identifiers unchanged. api gets a local replace for sdk; root/api/sdk require v0.1.0. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
358 lines
11 KiB
Go
358 lines
11 KiB
Go
// Copyright IBM Corp. 2020, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package targets
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/globals"
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/credential"
|
|
credstatic "repo.anhonesthost.net/CyberCoveLLC/threshold/internal/credential/static"
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/daemon/controller/handlers"
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/errors"
|
|
serverpb "repo.anhonesthost.net/CyberCoveLLC/threshold/internal/gen/controller/servers/services"
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/session"
|
|
pb "repo.anhonesthost.net/CyberCoveLLC/threshold/sdk/pbs/controller/api/resources/targets"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/types/known/structpb"
|
|
)
|
|
|
|
// dynamicToWorkerCredential converts the strongly typed credential.Dynamic into
|
|
// a session.Credential suitable for passing to a Boundary worker.
|
|
func dynamicToWorkerCredential(ctx context.Context, cred credential.Dynamic) (session.Credential, error) {
|
|
const op = "targets.dynamicToWorkerCredential"
|
|
var workerCred *serverpb.Credential
|
|
switch c := cred.(type) {
|
|
case credential.UsernamePasswordDomain:
|
|
workerCred = &serverpb.Credential{
|
|
Credential: &serverpb.Credential_UsernamePasswordDomain{
|
|
UsernamePasswordDomain: &serverpb.UsernamePasswordDomain{
|
|
Username: c.Username(),
|
|
Password: string(c.Password()),
|
|
Domain: c.Domain(),
|
|
},
|
|
},
|
|
}
|
|
case credential.UsernamePassword:
|
|
workerCred = &serverpb.Credential{
|
|
Credential: &serverpb.Credential_UsernamePassword{
|
|
UsernamePassword: &serverpb.UsernamePassword{
|
|
Username: c.Username(),
|
|
Password: string(c.Password()),
|
|
},
|
|
},
|
|
}
|
|
case credential.SshCertificate:
|
|
workerCred = &serverpb.Credential{
|
|
Credential: &serverpb.Credential_SshCertificate{
|
|
SshCertificate: &serverpb.SshCertificate{
|
|
Username: c.Username(),
|
|
PrivateKey: string(c.PrivateKey()),
|
|
Certificate: string(c.Certificate()),
|
|
},
|
|
},
|
|
}
|
|
case credential.SshPrivateKey:
|
|
workerCred = &serverpb.Credential{
|
|
Credential: &serverpb.Credential_SshPrivateKey{
|
|
SshPrivateKey: &serverpb.SshPrivateKey{
|
|
Username: c.Username(),
|
|
PrivateKey: string(c.PrivateKey()),
|
|
PrivateKeyPassphrase: string(c.PrivateKeyPassphrase()),
|
|
},
|
|
},
|
|
}
|
|
|
|
default:
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c))
|
|
}
|
|
|
|
data, err := proto.Marshal(workerCred)
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling dynamic secret to proto"))
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
func dynamicToSessionCredential(ctx context.Context, cred credential.Dynamic) (*pb.SessionCredential, error) {
|
|
const op = "targets.dynamicToSessionCredential"
|
|
l := cred.Library()
|
|
secret := cred.Secret()
|
|
// TODO: Access the json directly from the vault response instead of re-marshalling it.
|
|
jSecret, err := json.Marshal(secret)
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling secret to json"))
|
|
}
|
|
var sSecret *structpb.Struct
|
|
switch secret.(type) {
|
|
case map[string]any:
|
|
// In this case we actually have to re-decode it. The proto wrappers
|
|
// choke on json.Number and at the time I'm writing this I don't
|
|
// have time to write a walk function to dig through with reflect
|
|
// and find all json.Numbers and replace them. So we eat the
|
|
// inefficiency. So note that we are specifically _not_ using a
|
|
// decoder with UseNumber here.
|
|
var dSecret map[string]any
|
|
if err := json.Unmarshal(jSecret, &dSecret); err != nil {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("decoding json for proto marshaling"))
|
|
}
|
|
sSecret, err = structpb.NewStruct(dSecret)
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for secret"))
|
|
}
|
|
}
|
|
|
|
var credType string
|
|
var credData *structpb.Struct
|
|
if l.CredentialType() != globals.UnspecifiedCredentialType {
|
|
credType = string(l.CredentialType())
|
|
|
|
switch c := cred.(type) {
|
|
case credential.UsernamePasswordDomain:
|
|
credData, err = handlers.ProtoToStruct(
|
|
ctx,
|
|
&pb.UsernamePasswordDomainCredential{
|
|
Username: c.Username(),
|
|
Password: string(c.Password()),
|
|
Domain: c.Domain(),
|
|
},
|
|
)
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for credential"))
|
|
}
|
|
|
|
case credential.UsernamePassword:
|
|
credData, err = handlers.ProtoToStruct(
|
|
ctx,
|
|
&pb.UsernamePasswordCredential{
|
|
Username: c.Username(),
|
|
Password: string(c.Password()),
|
|
},
|
|
)
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for credential"))
|
|
}
|
|
|
|
case credential.PasswordOnly:
|
|
credData, err = handlers.ProtoToStruct(
|
|
ctx,
|
|
&pb.PasswordCredential{
|
|
Password: string(c.Password()),
|
|
},
|
|
)
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for credential"))
|
|
}
|
|
|
|
case credential.SshPrivateKey:
|
|
credData, err = handlers.ProtoToStruct(
|
|
ctx,
|
|
&pb.SshPrivateKeyCredential{
|
|
Username: c.Username(),
|
|
PrivateKey: string(c.PrivateKey()),
|
|
PrivateKeyPassphrase: string(c.PrivateKeyPassphrase()),
|
|
},
|
|
)
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for credential"))
|
|
}
|
|
|
|
default:
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c))
|
|
}
|
|
}
|
|
|
|
return &pb.SessionCredential{
|
|
CredentialSource: &pb.CredentialSource{
|
|
Id: l.GetPublicId(),
|
|
Name: l.GetName(),
|
|
Description: l.GetDescription(),
|
|
CredentialStoreId: l.GetStoreId(),
|
|
Type: globals.ResourceInfoFromPrefix(l.GetPublicId()).Subtype.String(),
|
|
CredentialType: credType,
|
|
},
|
|
Secret: &pb.SessionSecret{
|
|
Raw: base64.StdEncoding.EncodeToString(jSecret),
|
|
Decoded: sSecret,
|
|
},
|
|
Credential: credData,
|
|
}, nil
|
|
}
|
|
|
|
// staticToWorkerCredential converts the credential.Static into
|
|
// a session.Credential suitable for passing to a Boundary worker.
|
|
func staticToWorkerCredential(ctx context.Context, cred credential.Static) (session.Credential, error) {
|
|
const op = "targets.staticToWorkerCredential"
|
|
var workerCred *serverpb.Credential
|
|
switch c := cred.(type) {
|
|
case *credstatic.UsernamePasswordCredential:
|
|
workerCred = &serverpb.Credential{
|
|
Credential: &serverpb.Credential_UsernamePassword{
|
|
UsernamePassword: &serverpb.UsernamePassword{
|
|
Username: c.GetUsername(),
|
|
Password: string(c.GetPassword()),
|
|
},
|
|
},
|
|
}
|
|
|
|
case *credstatic.UsernamePasswordDomainCredential:
|
|
workerCred = &serverpb.Credential{
|
|
Credential: &serverpb.Credential_UsernamePasswordDomain{
|
|
UsernamePasswordDomain: &serverpb.UsernamePasswordDomain{
|
|
Username: c.GetUsername(),
|
|
Password: string(c.GetPassword()),
|
|
Domain: c.GetDomain(),
|
|
},
|
|
},
|
|
}
|
|
|
|
case *credstatic.SshPrivateKeyCredential:
|
|
workerCred = &serverpb.Credential{
|
|
Credential: &serverpb.Credential_SshPrivateKey{
|
|
SshPrivateKey: &serverpb.SshPrivateKey{
|
|
Username: c.GetUsername(),
|
|
PrivateKey: string(c.GetPrivateKey()),
|
|
PrivateKeyPassphrase: string(c.GetPrivateKeyPassphrase()),
|
|
},
|
|
},
|
|
}
|
|
|
|
default:
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c))
|
|
}
|
|
|
|
data, err := proto.Marshal(workerCred)
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling static secret to proto"))
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
func staticToSessionCredential(ctx context.Context, cred credential.Static) (*pb.SessionCredential, error) {
|
|
const op = "targets.staticToSessionCredential"
|
|
|
|
var credType string
|
|
var credData *structpb.Struct
|
|
var secret map[string]any
|
|
switch c := cred.(type) {
|
|
case *credstatic.UsernamePasswordCredential:
|
|
var err error
|
|
credType = string(globals.UsernamePasswordCredentialType)
|
|
credData, err = handlers.ProtoToStruct(
|
|
ctx,
|
|
&pb.UsernamePasswordCredential{
|
|
Username: c.GetUsername(),
|
|
Password: string(c.GetPassword()),
|
|
},
|
|
)
|
|
secret = map[string]any{
|
|
"username": c.GetUsername(),
|
|
"password": string(c.GetPassword()),
|
|
}
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for username password credential"))
|
|
}
|
|
case *credstatic.UsernamePasswordDomainCredential:
|
|
var err error
|
|
credType = string(globals.UsernamePasswordDomainCredentialType)
|
|
credData, err = handlers.ProtoToStruct(
|
|
ctx,
|
|
&pb.UsernamePasswordDomainCredential{
|
|
Username: c.GetUsername(),
|
|
Password: string(c.GetPassword()),
|
|
Domain: c.GetDomain(),
|
|
},
|
|
)
|
|
secret = map[string]any{
|
|
"username": c.GetUsername(),
|
|
"password": string(c.GetPassword()),
|
|
"domain": c.GetDomain(),
|
|
}
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for username password domain credential"))
|
|
}
|
|
case *credstatic.PasswordCredential:
|
|
var err error
|
|
credType = string(globals.PasswordCredentialType)
|
|
credData, err = handlers.ProtoToStruct(
|
|
ctx,
|
|
&pb.PasswordCredential{
|
|
Password: string(c.GetPassword()),
|
|
},
|
|
)
|
|
secret = map[string]any{
|
|
"password": string(c.GetPassword()),
|
|
}
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for password credential"))
|
|
}
|
|
case *credstatic.SshPrivateKeyCredential:
|
|
var err error
|
|
credType = string(globals.SshPrivateKeyCredentialType)
|
|
credData, err = handlers.ProtoToStruct(
|
|
ctx,
|
|
&pb.SshPrivateKeyCredential{
|
|
Username: c.GetUsername(),
|
|
PrivateKey: string(c.GetPrivateKey()),
|
|
PrivateKeyPassphrase: string(c.GetPrivateKeyPassphrase()),
|
|
},
|
|
)
|
|
secret = map[string]any{
|
|
"username": c.GetUsername(),
|
|
"private_key": string(c.GetPrivateKey()),
|
|
}
|
|
if len(c.GetPrivateKeyPassphrase()) > 0 {
|
|
secret["private_key_passphrase"] = string(c.GetPrivateKeyPassphrase())
|
|
}
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for ssh private key credential"))
|
|
}
|
|
case *credstatic.JsonCredential:
|
|
var err error
|
|
credType = string(globals.JsonCredentialType)
|
|
object := map[string]any{}
|
|
err = json.Unmarshal(c.GetObject(), &object)
|
|
if err != nil {
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "unmarshalling json")
|
|
}
|
|
|
|
credData, err = structpb.NewStruct(object)
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for json credential"))
|
|
}
|
|
secret = object
|
|
|
|
default:
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c))
|
|
}
|
|
|
|
jSecret, err := json.Marshal(secret)
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling static secret to json"))
|
|
}
|
|
sSecret, err := structpb.NewStruct(secret)
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for secret"))
|
|
}
|
|
|
|
return &pb.SessionCredential{
|
|
CredentialSource: &pb.CredentialSource{
|
|
Id: cred.GetPublicId(),
|
|
Name: cred.GetName(),
|
|
Description: cred.GetDescription(),
|
|
CredentialStoreId: cred.GetStoreId(),
|
|
Type: credstatic.Subtype.String(),
|
|
CredentialType: credType,
|
|
},
|
|
Secret: &pb.SessionSecret{
|
|
Raw: base64.StdEncoding.EncodeToString(jSecret),
|
|
Decoded: sSecret,
|
|
},
|
|
Credential: credData,
|
|
}, nil
|
|
}
|