ce7cb702218693d08ea6d83464d2457c9250027c

This commit is contained in:
ms2k 2026-11-12 20:46:28 +09:00
commit 34c5f8963c

View file

@ -0,0 +1,154 @@
package main
import (
"context"
"encoding/base64"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"github.com/oracle/oci-go-sdk/v65/common"
"github.com/oracle/oci-go-sdk/v65/common/auth"
"github.com/oracle/oci-go-sdk/v65/objectstorage"
"github.com/oracle/oci-go-sdk/v65/secrets"
"github.com/oracle/oci-go-sdk/v65/vault"
)
const maxAdmissionBytes = 64 << 20
const maxSMTPDenyBytes = 16 << 20
type ociClients struct {
objects objectstorage.ObjectStorageClient
secrets secrets.SecretsClient
vault vault.VaultsClient
}
func newOCIClients() (ociClients, error) {
provider, err := auth.InstancePrincipalConfigurationProvider()
if err != nil {
return ociClients{}, err
}
objectClient, err := objectstorage.NewObjectStorageClientWithConfigurationProvider(provider)
if err != nil {
return ociClients{}, err
}
secretClient, err := secrets.NewSecretsClientWithConfigurationProvider(provider)
if err != nil {
return ociClients{}, err
}
vaultClient, err := vault.NewVaultsClientWithConfigurationProvider(provider)
if err != nil {
return ociClients{}, err
}
return ociClients{objects: objectClient, secrets: secretClient, vault: vaultClient}, nil
}
func (clients ociClients) getSecret(ctx context.Context, secretOCID string) ([]byte, error) {
response, err := clients.secrets.GetSecretBundle(ctx, secrets.GetSecretBundleRequest{
SecretId: common.String(secretOCID),
Stage: secrets.GetSecretBundleStageCurrent,
})
if err != nil {
return nil, err
}
var encoded *string
switch content := response.SecretBundleContent.(type) {
case secrets.Base64SecretBundleContentDetails:
encoded = content.Content
case *secrets.Base64SecretBundleContentDetails:
encoded = content.Content
}
if encoded == nil {
return nil, errors.New("secret has no base64 content")
}
decoded, err := base64.StdEncoding.DecodeString(*encoded)
if err != nil {
return nil, fmt.Errorf("decode secret: %w", err)
}
return decoded, nil
}
func (clients ociClients) publishSecret(ctx context.Context, secretOCID string, value []byte) error {
current, err := clients.getSecret(ctx, secretOCID)
if err == nil && string(current) == string(value) {
return nil
}
content := base64.StdEncoding.EncodeToString(value)
_, err = clients.vault.UpdateSecret(ctx, vault.UpdateSecretRequest{
SecretId: common.String(secretOCID),
UpdateSecretDetails: vault.UpdateSecretDetails{
SecretContent: vault.Base64SecretContentDetails{
Content: common.String(content),
Stage: vault.SecretContentDetailsStageCurrent,
},
},
})
return err
}
func (clients ociClients) downloadAdmission(ctx context.Context, configuration config) (string, func(), error) {
return clients.downloadObject(
ctx,
webAllowStateDirectory,
configuration.Admission.Namespace,
configuration.Admission.Bucket,
configuration.Admission.Object,
maxAdmissionBytes,
"web-allow",
)
}
func (clients ociClients) downloadSMTPDeny(ctx context.Context, configuration config) (string, func(), error) {
if configuration.SMTPDeny == nil {
return "", nil, errors.New("SMTP deny configuration is inactive")
}
return clients.downloadObject(
ctx,
smtpDenyStateDirectory,
configuration.SMTPDeny.Namespace,
configuration.SMTPDeny.Bucket,
configuration.SMTPDeny.Object,
maxSMTPDenyBytes,
"smtp-deny",
)
}
func (clients ociClients) downloadObject(ctx context.Context, stateDirectory, namespace, bucket, object string, maximumBytes int64, label string) (string, func(), error) {
directory, err := os.MkdirTemp(stateDirectory, ".download-")
if err != nil {
return "", nil, err
}
cleanup := func() { _ = os.RemoveAll(directory) }
response, err := clients.objects.GetObject(ctx, objectstorage.GetObjectRequest{
NamespaceName: common.String(namespace),
BucketName: common.String(bucket),
ObjectName: common.String(object),
})
if err != nil {
cleanup()
return "", nil, err
}
defer func() { _ = response.Content.Close() }()
path := filepath.Join(directory, "bundle.json.gz")
file, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600)
if err != nil {
cleanup()
return "", nil, err
}
written, copyErr := io.Copy(file, io.LimitReader(response.Content, maximumBytes+1))
closeErr := file.Close()
if copyErr != nil || closeErr != nil || written > maximumBytes {
cleanup()
return "", nil, errors.Join(copyErr, closeErr, sizeError(label, written, maximumBytes))
}
return path, cleanup, nil
}
func sizeError(label string, size, maximum int64) error {
if size > maximum {
return fmt.Errorf("%s bundle exceeds %d bytes", label, maximum)
}
return nil
}