mirror of
https://github.com/hanzoai/compute.git
synced 2026-10-30 14:50:41 +00:00
97 lines
3.6 KiB
Go
97 lines
3.6 KiB
Go
// Copyright 2023 Hanzo Industries Inc. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/hanzoai/egress/spend"
|
|
|
|
"github.com/hanzoai/compute/conf"
|
|
"github.com/hanzoai/compute/service"
|
|
)
|
|
|
|
// carry sends every cloud call through hanzoai/egress, so this process holds no
|
|
// cloud credential.
|
|
//
|
|
// Configured, the platform's cloud accounts are egress's: reading this pod's
|
|
// environment, config or memory yields nothing that spends, and Hanzo's hosted
|
|
// AWS account is signed there. A tenant's own Provider row is refused rather
|
|
// than carried, because the carrier spends as compute. Unconfigured, compute
|
|
// calls no cloud at all: there is no key here to call one with.
|
|
//
|
|
// What visor still holds is its OWN token, and that is the trade rather than an
|
|
// oversight. A stolen caller token buys metered calls through our meter — rate
|
|
// limited, attributed, audited, revocable in one place — instead of a vendor
|
|
// key that spends without limit, off our network, and takes a rotation across
|
|
// every cloud to undo.
|
|
func carry() error {
|
|
address := strings.TrimSpace(conf.GetConfigString("egressAddress"))
|
|
if address == "" {
|
|
return nil
|
|
}
|
|
// WHO IS CALLING IS ASKED OF IAM, not of a config value. visor already signs
|
|
// in with a clientId and clientSecret; egress verifies what those buy the
|
|
// same way every service verifies a caller — issuer, audience, JWKS. So
|
|
// there is no second credential to mint, paste or rotate, and the token
|
|
// expires on its own.
|
|
//
|
|
// Refusing to start is the point. An operator who set an address means the
|
|
// cloud keys to be gone from here, and coming up without an identity would
|
|
// serve every cloud call as a 401 whose obvious repair is to put the keys
|
|
// back.
|
|
id := strings.TrimSpace(conf.GetConfigString("clientId"))
|
|
secret := strings.TrimSpace(conf.GetConfigString("clientSecret"))
|
|
iam := strings.TrimSpace(conf.GetConfigString("iamEndpoint"))
|
|
if id == "" || secret == "" || iam == "" {
|
|
return fmt.Errorf("egressAddress is set but visor has no IAM identity "+
|
|
"(clientId=%q iamEndpoint=%q): it cannot say who it is to egress", id, iam)
|
|
}
|
|
who := service.NewIdentity(iam, id, secret, strings.TrimSpace(conf.GetConfigString("egressAudience")), nil)
|
|
network, address := dial(address)
|
|
service.RegisterCarrier(func(c service.Credential) (*http.Client, error) {
|
|
token, err := who.Token()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return spend.Client(spend.Config{
|
|
Network: network,
|
|
Address: address,
|
|
Token: token,
|
|
Provider: c.Provider,
|
|
Account: c.Name,
|
|
}), nil
|
|
})
|
|
return nil
|
|
}
|
|
|
|
// dial splits an egress address into the network and the address, so one
|
|
// configuration value covers a socket on this host and a service across the
|
|
// network. Bare means tcp, which is the common case.
|
|
//
|
|
// tcp://egress.hanzo.ai:9653
|
|
// unix:///run/hanzo/egress.sock
|
|
// egress.hanzo.ai:9653
|
|
func dial(address string) (string, string) {
|
|
for _, network := range []string{"unix", "tcp"} {
|
|
if rest, ok := strings.CutPrefix(address, network+"://"); ok {
|
|
return network, rest
|
|
}
|
|
}
|
|
return "tcp", address
|
|
}
|