зеркало из https://github.com/Azure/ARO-RP.git
373 строки
9.7 KiB
Go
373 строки
9.7 KiB
Go
package env
|
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the Apache License 2.0.
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
mgmtcompute "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2020-06-01/compute"
|
|
"github.com/Azure/go-autorest/autorest/adal"
|
|
"github.com/Azure/go-autorest/autorest/azure"
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"github.com/Azure/ARO-RP/pkg/proxy"
|
|
"github.com/Azure/ARO-RP/pkg/util/azureclient/mgmt/compute"
|
|
"github.com/Azure/ARO-RP/pkg/util/clientauthorizer"
|
|
"github.com/Azure/ARO-RP/pkg/util/computeskus"
|
|
"github.com/Azure/ARO-RP/pkg/util/keyvault"
|
|
"github.com/Azure/ARO-RP/pkg/util/liveconfig"
|
|
"github.com/Azure/ARO-RP/pkg/util/refreshable"
|
|
"github.com/Azure/ARO-RP/pkg/util/version"
|
|
)
|
|
|
|
type prod struct {
|
|
Core
|
|
proxy.Dialer
|
|
ARMHelper
|
|
|
|
liveConfig liveconfig.Manager
|
|
|
|
armClientAuthorizer clientauthorizer.ClientAuthorizer
|
|
adminClientAuthorizer clientauthorizer.ClientAuthorizer
|
|
|
|
acrDomain string
|
|
vmskus map[string]*mgmtcompute.ResourceSku
|
|
|
|
fpCertificateRefresher CertificateRefresher
|
|
fpClientID string
|
|
|
|
clusterKeyvault keyvault.Manager
|
|
serviceKeyvault keyvault.Manager
|
|
|
|
clusterGenevaLoggingCertificate *x509.Certificate
|
|
clusterGenevaLoggingPrivateKey *rsa.PrivateKey
|
|
clusterGenevaLoggingAccount string
|
|
clusterGenevaLoggingConfigVersion string
|
|
clusterGenevaLoggingEnvironment string
|
|
clusterGenevaLoggingNamespace string
|
|
|
|
gatewayDomains []string
|
|
|
|
log *logrus.Entry
|
|
|
|
features map[Feature]bool
|
|
}
|
|
|
|
func newProd(ctx context.Context, log *logrus.Entry) (*prod, error) {
|
|
for _, key := range []string{
|
|
"AZURE_FP_CLIENT_ID",
|
|
"DOMAIN_NAME",
|
|
} {
|
|
if _, found := os.LookupEnv(key); !found {
|
|
return nil, fmt.Errorf("environment variable %q unset", key)
|
|
}
|
|
}
|
|
|
|
if !IsLocalDevelopmentMode() {
|
|
for _, key := range []string{
|
|
"CLUSTER_MDSD_CONFIG_VERSION",
|
|
"CLUSTER_MDSD_ACCOUNT",
|
|
"GATEWAY_DOMAINS",
|
|
"GATEWAY_RESOURCEGROUP",
|
|
"MDSD_ENVIRONMENT",
|
|
"CLUSTER_MDSD_NAMESPACE",
|
|
} {
|
|
if _, found := os.LookupEnv(key); !found {
|
|
return nil, fmt.Errorf("environment variable %q unset", key)
|
|
}
|
|
}
|
|
}
|
|
|
|
core, err := NewCore(ctx, log)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
dialer, err := proxy.NewDialer(core.IsLocalDevelopmentMode())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p := &prod{
|
|
Core: core,
|
|
Dialer: dialer,
|
|
|
|
fpClientID: os.Getenv("AZURE_FP_CLIENT_ID"),
|
|
|
|
clusterGenevaLoggingAccount: os.Getenv("CLUSTER_MDSD_ACCOUNT"),
|
|
clusterGenevaLoggingConfigVersion: os.Getenv("CLUSTER_MDSD_CONFIG_VERSION"),
|
|
clusterGenevaLoggingEnvironment: os.Getenv("MDSD_ENVIRONMENT"),
|
|
clusterGenevaLoggingNamespace: os.Getenv("CLUSTER_MDSD_NAMESPACE"),
|
|
|
|
log: log,
|
|
|
|
features: map[Feature]bool{},
|
|
}
|
|
|
|
features := os.Getenv("RP_FEATURES")
|
|
if features != "" {
|
|
for _, feature := range strings.Split(features, ",") {
|
|
f, err := FeatureString("Feature" + feature)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.features[f] = true
|
|
}
|
|
}
|
|
|
|
msiAuthorizer, err := p.NewMSIAuthorizer(MSIContextRP, p.Environment().ResourceManagerEndpoint)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
msiKVAuthorizer, err := p.NewMSIAuthorizer(MSIContextRP, p.Environment().ResourceIdentifiers.KeyVault)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
serviceKeyvaultURI, err := keyvault.URI(p, ServiceKeyvaultSuffix)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.serviceKeyvault = keyvault.NewManager(msiKVAuthorizer, serviceKeyvaultURI)
|
|
|
|
resourceSkusClient := compute.NewResourceSkusClient(p.Environment(), p.SubscriptionID(), msiAuthorizer)
|
|
err = p.populateVMSkus(ctx, resourceSkusClient)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.fpCertificateRefresher = newCertificateRefresher(log, 1*time.Hour, p.serviceKeyvault, RPFirstPartySecretName)
|
|
err = p.fpCertificateRefresher.Start(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
localFPKVAuthorizer, err := p.FPAuthorizer(p.TenantID(), p.Environment().ResourceIdentifiers.KeyVault)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
clusterKeyvaultURI, err := keyvault.URI(p, ClusterKeyvaultSuffix)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.clusterKeyvault = keyvault.NewManager(localFPKVAuthorizer, clusterKeyvaultURI)
|
|
|
|
clusterGenevaLoggingPrivateKey, clusterGenevaLoggingCertificates, err := p.serviceKeyvault.GetCertificateSecret(ctx, ClusterLoggingSecretName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.clusterGenevaLoggingPrivateKey = clusterGenevaLoggingPrivateKey
|
|
p.clusterGenevaLoggingCertificate = clusterGenevaLoggingCertificates[0]
|
|
|
|
var acrDataDomain string
|
|
if p.ACRResourceID() != "" { // TODO: ugh!
|
|
acrResource, err := azure.ParseResourceID(p.ACRResourceID())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.acrDomain = acrResource.ResourceName + "." + p.Environment().ContainerRegistryDNSSuffix
|
|
acrDataDomain = acrResource.ResourceName + "." + p.Location() + ".data." + p.Environment().ContainerRegistryDNSSuffix
|
|
} else {
|
|
p.acrDomain = "arointsvc." + azure.PublicCloud.ContainerRegistryDNSSuffix // TODO: make cloud aware once this is set up for US Gov Cloud
|
|
acrDataDomain = "arointsvc." + p.Location() + ".data." + azure.PublicCloud.ContainerRegistryDNSSuffix // TODO: make cloud aware once this is set up for US Gov Cloud
|
|
}
|
|
|
|
if !p.IsLocalDevelopmentMode() {
|
|
gatewayDomains := os.Getenv("GATEWAY_DOMAINS")
|
|
if gatewayDomains != "" {
|
|
p.gatewayDomains = strings.Split(gatewayDomains, ",")
|
|
}
|
|
|
|
for _, rawurl := range []string{
|
|
p.Environment().ActiveDirectoryEndpoint,
|
|
p.Environment().ResourceManagerEndpoint,
|
|
} {
|
|
u, err := url.Parse(rawurl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.gatewayDomains = append(p.gatewayDomains, u.Hostname())
|
|
}
|
|
|
|
p.gatewayDomains = append(p.gatewayDomains, p.acrDomain, acrDataDomain)
|
|
}
|
|
|
|
p.ARMHelper, err = newARMHelper(ctx, log, p)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.liveConfig, err = p.Core.NewLiveConfigManager(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return p, nil
|
|
}
|
|
|
|
func (p *prod) InitializeAuthorizers() error {
|
|
if !p.FeatureIsSet(FeatureEnableDevelopmentAuthorizer) {
|
|
p.armClientAuthorizer = clientauthorizer.NewARM(p.log, p.Core)
|
|
} else {
|
|
armClientAuthorizer, err := clientauthorizer.NewSubjectNameAndIssuer(
|
|
p.log,
|
|
"/etc/aro-rp/arm-ca-bundle.pem",
|
|
os.Getenv("ARM_API_CLIENT_CERT_COMMON_NAME"),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
p.armClientAuthorizer = armClientAuthorizer
|
|
}
|
|
|
|
adminClientAuthorizer, err := clientauthorizer.NewSubjectNameAndIssuer(
|
|
p.log,
|
|
"/etc/aro-rp/admin-ca-bundle.pem",
|
|
os.Getenv("ADMIN_API_CLIENT_CERT_COMMON_NAME"),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
p.adminClientAuthorizer = adminClientAuthorizer
|
|
return nil
|
|
}
|
|
|
|
func (p *prod) ArmClientAuthorizer() clientauthorizer.ClientAuthorizer {
|
|
return p.armClientAuthorizer
|
|
}
|
|
|
|
func (p *prod) AdminClientAuthorizer() clientauthorizer.ClientAuthorizer {
|
|
return p.adminClientAuthorizer
|
|
}
|
|
|
|
func (p *prod) ACRResourceID() string {
|
|
return os.Getenv("ACR_RESOURCE_ID")
|
|
}
|
|
|
|
func (p *prod) ACRDomain() string {
|
|
return p.acrDomain
|
|
}
|
|
|
|
func (p *prod) AROOperatorImage() string {
|
|
return fmt.Sprintf("%s/aro:%s", p.acrDomain, version.GitCommit)
|
|
}
|
|
|
|
func (p *prod) populateVMSkus(ctx context.Context, resourceSkusClient compute.ResourceSkusClient) error {
|
|
// Filtering is poorly documented, but currently (API version 2019-04-01)
|
|
// it seems that the API returns all SKUs without a filter and with invalid
|
|
// value in the filter.
|
|
// Filtering gives significant optimisation: at the moment of writing,
|
|
// we get ~1.2M response in eastus vs ~37M unfiltered (467 items vs 16618).
|
|
filter := fmt.Sprintf("location eq '%s'", p.Location())
|
|
skus, err := resourceSkusClient.List(ctx, filter)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
p.vmskus = computeskus.FilterVMSizes(skus, p.Location())
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *prod) ClusterGenevaLoggingAccount() string {
|
|
return p.clusterGenevaLoggingAccount
|
|
}
|
|
|
|
func (p *prod) ClusterGenevaLoggingConfigVersion() string {
|
|
return p.clusterGenevaLoggingConfigVersion
|
|
}
|
|
|
|
func (p *prod) ClusterGenevaLoggingEnvironment() string {
|
|
return p.clusterGenevaLoggingEnvironment
|
|
}
|
|
|
|
func (p *prod) ClusterGenevaLoggingNamespace() string {
|
|
return p.clusterGenevaLoggingNamespace
|
|
}
|
|
|
|
func (p *prod) ClusterGenevaLoggingSecret() (*rsa.PrivateKey, *x509.Certificate) {
|
|
return p.clusterGenevaLoggingPrivateKey, p.clusterGenevaLoggingCertificate
|
|
}
|
|
|
|
func (p *prod) ClusterKeyvault() keyvault.Manager {
|
|
return p.clusterKeyvault
|
|
}
|
|
|
|
func (p *prod) Domain() string {
|
|
return os.Getenv("DOMAIN_NAME")
|
|
}
|
|
|
|
func (p *prod) FeatureIsSet(f Feature) bool {
|
|
return p.features[f]
|
|
}
|
|
|
|
func (p *prod) FPAuthorizer(tenantID, resource string) (refreshable.Authorizer, error) {
|
|
oauthConfig, err := adal.NewOAuthConfig(p.Environment().ActiveDirectoryEndpoint, tenantID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fpPrivateKey, fpCertificates := p.fpCertificateRefresher.GetCertificates()
|
|
|
|
sp, err := adal.NewServicePrincipalTokenFromCertificate(*oauthConfig, p.fpClientID, fpCertificates[0], fpPrivateKey, resource)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return refreshable.NewAuthorizer(sp), nil
|
|
}
|
|
|
|
func (p *prod) FPClientID() string {
|
|
return p.fpClientID
|
|
}
|
|
|
|
func (p *prod) Listen() (net.Listener, error) {
|
|
return net.Listen("tcp", ":8443")
|
|
}
|
|
|
|
func (p *prod) GatewayDomains() []string {
|
|
gatewayDomains := make([]string, len(p.gatewayDomains))
|
|
|
|
copy(gatewayDomains, p.gatewayDomains)
|
|
|
|
return gatewayDomains
|
|
}
|
|
|
|
func (p *prod) GatewayResourceGroup() string {
|
|
return os.Getenv("GATEWAY_RESOURCEGROUP")
|
|
}
|
|
|
|
func (p *prod) ServiceKeyvault() keyvault.Manager {
|
|
return p.serviceKeyvault
|
|
}
|
|
|
|
func (p *prod) VMSku(vmSize string) (*mgmtcompute.ResourceSku, error) {
|
|
vmsku, found := p.vmskus[vmSize]
|
|
if !found {
|
|
return nil, fmt.Errorf("sku information not found for vm size %q", vmSize)
|
|
}
|
|
return vmsku, nil
|
|
}
|
|
|
|
func (p *prod) LiveConfig() liveconfig.Manager {
|
|
return p.liveConfig
|
|
}
|