From 67c9990ec122915e648160d627dbd70e7eaa64e0 Mon Sep 17 00:00:00 2001 From: Henry Avetisyan Date: Tue, 31 Dec 2024 10:39:13 -0800 Subject: [PATCH] consolidate sia config objects into a single module Signed-off-by: Henry Avetisyan --- libs/go/sia/Makefile | 2 +- libs/go/sia/access/tokens/tokens.go | 6 +- libs/go/sia/agent/agent.go | 31 +- libs/go/sia/agent/agent_test.go | 39 +- libs/go/sia/aws/agent/agent.go | 31 +- libs/go/sia/aws/agent/agent_test.go | 42 +- libs/go/sia/aws/attestation/attestation.go | 6 +- libs/go/sia/aws/options/options.go | 282 ++----------- libs/go/sia/aws/options/options_test.go | 84 ++-- libs/go/sia/aws/sds/creds.go | 78 ---- libs/go/sia/aws/sds/data/athenz.api.cert.pem | 1 - libs/go/sia/aws/sds/data/athenz.api.key.pem | 1 - libs/go/sia/aws/sds/data/ca.cert.pem | 1 - libs/go/sia/aws/sds/handler.go | 386 ------------------ libs/go/sia/aws/sds/handler_test.go | 279 ------------- libs/go/sia/aws/sds/info.go | 30 -- libs/go/sia/aws/sds/info_test.go | 36 -- libs/go/sia/aws/sds/sds.go | 73 ---- libs/go/sia/aws/sds/subscriber.go | 80 ---- libs/go/sia/aws/sds/subscriber_test.go | 68 ---- libs/go/sia/aws/sds/uds.go | 120 ------ libs/go/sia/config/config.go | 228 +++++++++++ libs/go/sia/options/options.go | 397 +++---------------- libs/go/sia/options/options_test.go | 86 ++-- libs/go/sia/sds/handler.go | 10 +- libs/go/sia/sds/handler_test.go | 54 +-- libs/go/sia/sds/sds.go | 4 +- provider/aws/sia-ec2/authn.go | 9 +- provider/aws/sia-eks/authn.go | 5 +- provider/aws/sia-fargate/authn.go | 9 +- provider/gcp/sia-gce/authn.go | 10 +- provider/gcp/sia-gce/cmd/siad/main.go | 7 +- provider/gcp/sia-gke/authn.go | 5 +- provider/gcp/sia-gke/cmd/siad/main.go | 3 +- provider/gcp/sia-run/authn.go | 6 +- provider/gcp/sia-run/cmd/siad/main.go | 3 +- utils/msd-agent/svc/service_aws_ec2.go | 3 +- 37 files changed, 554 insertions(+), 1961 deletions(-) delete mode 100644 libs/go/sia/aws/sds/creds.go delete mode 100644 libs/go/sia/aws/sds/data/athenz.api.cert.pem delete mode 100644 libs/go/sia/aws/sds/data/athenz.api.key.pem delete mode 100644 libs/go/sia/aws/sds/data/ca.cert.pem delete mode 100644 libs/go/sia/aws/sds/handler.go delete mode 100644 libs/go/sia/aws/sds/handler_test.go delete mode 100644 libs/go/sia/aws/sds/info.go delete mode 100644 libs/go/sia/aws/sds/info_test.go delete mode 100644 libs/go/sia/aws/sds/sds.go delete mode 100644 libs/go/sia/aws/sds/subscriber.go delete mode 100644 libs/go/sia/aws/sds/subscriber_test.go delete mode 100644 libs/go/sia/aws/sds/uds.go create mode 100644 libs/go/sia/config/config.go diff --git a/libs/go/sia/Makefile b/libs/go/sia/Makefile index a45e96d9fff..0b484298254 100644 --- a/libs/go/sia/Makefile +++ b/libs/go/sia/Makefile @@ -13,7 +13,7 @@ ifneq ($(patsubst %$(SIA_DIR),,$(lastword $(ATHENZ_DIR))),) endif SUBDIRS = access/config access/tokens agent aws/agent aws/attestation aws/doc aws/lambda aws/meta \ - aws/options aws/sds aws/stssession file futil gcp/attestation gcp/meta gcp/functions \ + aws/options aws/stssession file futil gcp/attestation gcp/meta gcp/functions \ host/hostdoc host/ip host/provider host/signature host/utils logutil options pki/cert \ sds ssh/hostcert ssh/hostkey util verify OS = darwin linux windows diff --git a/libs/go/sia/access/tokens/tokens.go b/libs/go/sia/access/tokens/tokens.go index 3572c24fa95..80e9530622c 100644 --- a/libs/go/sia/access/tokens/tokens.go +++ b/libs/go/sia/access/tokens/tokens.go @@ -31,7 +31,7 @@ import ( "github.com/AthenZ/athenz/clients/go/zts" "github.com/AthenZ/athenz/libs/go/sia/access/config" - "github.com/AthenZ/athenz/libs/go/sia/aws/options" + sc "github.com/AthenZ/athenz/libs/go/sia/config" siafile "github.com/AthenZ/athenz/libs/go/sia/file" "github.com/AthenZ/athenz/libs/go/sia/futil" tlsconfig "github.com/AthenZ/athenz/libs/go/tls/config" @@ -239,7 +239,7 @@ func makeTokenRequest(domain string, roles []string, expiryTime int, proxyPrinci return params.Encode() } -func NewTokenOptions(options *options.Options, ztsUrl string, userAgent string) (*config.TokenOptions, error) { +func NewTokenOptions(options *sc.Options, ztsUrl string, userAgent string) (*config.TokenOptions, error) { if options.AccessTokens == nil { return nil, fmt.Errorf("not configured to fetch access tokens") } @@ -272,7 +272,7 @@ func NewTokenOptions(options *options.Options, ztsUrl string, userAgent string) return tokenOpts, nil } -func toTokenServices(services []options.Service) []config.TokenService { +func toTokenServices(services []sc.Service) []config.TokenService { var tokenServices []config.TokenService for _, svc := range services { diff --git a/libs/go/sia/agent/agent.go b/libs/go/sia/agent/agent.go index 0b21d172d39..d5561d9ebf5 100644 --- a/libs/go/sia/agent/agent.go +++ b/libs/go/sia/agent/agent.go @@ -35,6 +35,7 @@ import ( "github.com/AthenZ/athenz/libs/go/athenzutils" "github.com/AthenZ/athenz/libs/go/sia/access/config" "github.com/AthenZ/athenz/libs/go/sia/access/tokens" + sc "github.com/AthenZ/athenz/libs/go/sia/config" "github.com/AthenZ/athenz/libs/go/sia/options" "github.com/AthenZ/athenz/libs/go/sia/sds" "github.com/AthenZ/athenz/libs/go/sia/ssh/hostkey" @@ -86,7 +87,7 @@ func RoleKey(rotateKey bool, roleKey, svcKey string) (*rsa.PrivateKey, error) { } } -func GetRoleCertificates(ztsUrl string, opts *options.Options) (int, []string) { +func GetRoleCertificates(ztsUrl string, opts *sc.Options) (int, []string) { //initialize our return state to success failures := make([]string, 0) @@ -171,7 +172,7 @@ func GetRoleCertificates(ztsUrl string, opts *options.Options) (int, []string) { return len(opts.Roles), failures } -func RegisterInstance(ztsUrl string, opts *options.Options, docExpiryCheck bool) error { +func RegisterInstance(ztsUrl string, opts *sc.Options, docExpiryCheck bool) error { //special handling for VM instances ( EC2 / GCE ) //before we process our register event we need to check to @@ -191,7 +192,7 @@ func RegisterInstance(ztsUrl string, opts *options.Options, docExpiryCheck bool) return nil } -func RefreshInstance(ztsUrl string, opts *options.Options) error { +func RefreshInstance(ztsUrl string, opts *sc.Options) error { for _, svc := range opts.Services { err := refreshSvc(svc, ztsUrl, opts) if err != nil { @@ -201,7 +202,7 @@ func RefreshInstance(ztsUrl string, opts *options.Options) error { return nil } -func getServiceHostname(opts *options.Options, svc options.Service, fqdn bool) string { +func getServiceHostname(opts *sc.Options, svc sc.Service, fqdn bool) string { if !opts.SanDnsHostname { return "" } @@ -235,7 +236,7 @@ func getServiceHostname(opts *options.Options, svc options.Service, fqdn bool) s return fmt.Sprintf("%s.%s.%s.%s", hostname, svc.Name, hyphenDomain, opts.HostnameSuffix) } -func registerSvc(svc options.Service, ztsUrl string, opts *options.Options) error { +func registerSvc(svc sc.Service, ztsUrl string, opts *sc.Options) error { key, err := util.GenerateKeyPair(2048) if err != nil { @@ -349,7 +350,7 @@ func registerSvc(svc options.Service, ztsUrl string, opts *options.Options) erro return nil } -func refreshSvc(svc options.Service, ztsUrl string, opts *options.Options) error { +func refreshSvc(svc sc.Service, ztsUrl string, opts *sc.Options) error { keyFile := util.GetSvcKeyFileName(opts.KeyDir, svc.KeyFilename, opts.Domain, svc.Name) certFile := util.GetSvcCertFileName(opts.CertDir, svc.CertFilename, opts.Domain, svc.Name) @@ -466,7 +467,7 @@ func refreshSvc(svc options.Service, ztsUrl string, opts *options.Options) error return nil } -func generateSshRequest(opts *options.Options, primaryServiceName, hostname string) (*zts.SSHCertRequest, string, error) { +func generateSshRequest(opts *sc.Options, primaryServiceName, hostname string) (*zts.SSHCertRequest, string, error) { var err error var sshCsr string var sshCertRequest *zts.SSHCertRequest @@ -560,7 +561,7 @@ func hostCertificateLinePresent(sshConfigFile, sshCertFile string) (bool, error) return false, nil } -func SetupAgent(opts *options.Options, siaAgentDir, siaLinkDir string) { +func SetupAgent(opts *sc.Options, siaAgentDir, siaLinkDir string) { //first, let's determine if we need to drop our privileges //since it requires us to create the directories with the @@ -617,7 +618,7 @@ func SetupAgent(opts *options.Options, siaAgentDir, siaLinkDir string) { } } -func RunAgent(siaCmds, ztsUrl string, opts *options.Options) { +func RunAgent(siaCmds, ztsUrl string, opts *sc.Options) { log.Printf("sia command line arguments specified: '%s'\n", siaCmds) cmds := strings.Split(siaCmds, ",") for _, cmd := range cmds { @@ -625,7 +626,7 @@ func RunAgent(siaCmds, ztsUrl string, opts *options.Options) { } } -func runAgentCommand(siaCmd, ztsUrl string, opts *options.Options) { +func runAgentCommand(siaCmd, ztsUrl string, opts *sc.Options) { //make sure the meta endpoint is configured by the caller if opts.MetaEndPoint == "" { @@ -888,9 +889,9 @@ func accessTokenRequest(tokenOpts *config.TokenOptions) error { return err } -func tokenOptions(opts *options.Options, ztsUrl string) (*config.TokenOptions, error) { +func tokenOptions(opts *sc.Options, ztsUrl string) (*config.TokenOptions, error) { userAgent := fmt.Sprintf("%s-%s", opts.Provider, opts.InstanceId) - tokenOpts, err := tokens.NewTokenOptions(options.LegacyOptions(opts), ztsUrl, userAgent) + tokenOpts, err := tokens.NewTokenOptions(opts, ztsUrl, userAgent) if err != nil { return nil, fmt.Errorf("processing access tokens: %s", err.Error()) } @@ -923,7 +924,7 @@ func fetchAccessToken(tokenOpts *config.TokenOptions) error { } } -func shouldSkipRegister(opts *options.Options) bool { +func shouldSkipRegister(opts *sc.Options) bool { if opts.EC2StartTime == nil { return false } @@ -932,13 +933,13 @@ func shouldSkipRegister(opts *options.Options) bool { return duration.Seconds() > 1800 } -func serviceAlreadyRegistered(opts *options.Options, svc options.Service) bool { +func serviceAlreadyRegistered(opts *sc.Options, svc sc.Service) bool { keyFile := util.GetSvcKeyFileName(opts.KeyDir, svc.KeyFilename, opts.Domain, svc.Name) certFile := util.GetSvcCertFileName(opts.CertDir, svc.CertFilename, opts.Domain, svc.Name) return util.FileExists(keyFile) && util.FileExists(certFile) } -func shouldExitRightAway(failedRefreshCount int, opts *options.Options) bool { +func shouldExitRightAway(failedRefreshCount int, opts *sc.Options) bool { // if the failed count already matches or exceeds our configured // value then we return right away if failedRefreshCount >= opts.FailCountForExit { diff --git a/libs/go/sia/agent/agent_test.go b/libs/go/sia/agent/agent_test.go index 2eb6143d8d9..a54b85ee4d3 100644 --- a/libs/go/sia/agent/agent_test.go +++ b/libs/go/sia/agent/agent_test.go @@ -32,12 +32,11 @@ import ( "github.com/AthenZ/athenz/libs/go/sia/access/config" "github.com/AthenZ/athenz/libs/go/sia/agent/devel/ztsmock" + sc "github.com/AthenZ/athenz/libs/go/sia/config" "github.com/AthenZ/athenz/libs/go/sia/host/ip" "github.com/AthenZ/athenz/libs/go/sia/host/signature" - "github.com/AthenZ/athenz/libs/go/sia/options" "github.com/AthenZ/athenz/libs/go/sia/ssh/hostkey" "github.com/AthenZ/athenz/libs/go/sia/util" - "github.com/stretchr/testify/assert" ) @@ -200,9 +199,9 @@ func TestRegisterInstance(test *testing.T) { tp := TestProvider{ Name: "athenz.aws.us-west-2", } - opts := &options.Options{ + opts := &sc.Options{ Domain: "athenz", - Services: []options.Service{ + Services: []sc.Service{ { Name: "hockey", Uid: util.ExecIdCommand("-u"), @@ -248,7 +247,7 @@ func copyFile(src, dst string) error { return os.WriteFile(dst, data, 0644) } -func refreshServiceCertSetup(test *testing.T) (*options.Options, string) { +func refreshServiceCertSetup(test *testing.T) (*sc.Options, string) { siaDir := test.TempDir() @@ -275,9 +274,9 @@ func refreshServiceCertSetup(test *testing.T) (*options.Options, string) { tp := TestProvider{ Name: "athenz.aws.us-west-2", } - opts := &options.Options{ + opts := &sc.Options{ Domain: "athenz", - Services: []options.Service{ + Services: []sc.Service{ { Name: "hockey", Uid: util.ExecIdCommand("-u"), @@ -344,9 +343,9 @@ func TestRoleCertificateRequest(test *testing.T) { tp := TestProvider{ Name: "athenz.aws.us-west-2", } - opts := &options.Options{ + opts := &sc.Options{ Domain: "athenz", - Services: []options.Service{ + Services: []sc.Service{ { Name: "hockey", Uid: util.ExecIdCommand("-u"), @@ -354,7 +353,7 @@ func TestRoleCertificateRequest(test *testing.T) { FileMode: 0400, }, }, - Roles: []options.Role{ + Roles: []sc.Role{ { Name: "athenz:role.writers", Service: "hockey", @@ -385,7 +384,7 @@ func TestRoleCertificateRequest(test *testing.T) { func TestShouldSkipRegister(test *testing.T) { startTime := time.Now() - opts := &options.Options{ + opts := &sc.Options{ EC2StartTime: &startTime, } //current time is valid @@ -471,7 +470,7 @@ func TestUpdateSSHConfigFile(test *testing.T) { } func TestNilTokenOptions(test *testing.T) { - opts := &options.Options{ + opts := &sc.Options{ Domain: "athenz", } token, err := tokenOptions(opts, "") @@ -480,7 +479,7 @@ func TestNilTokenOptions(test *testing.T) { } func TestTokenStoreOptions(test *testing.T) { - opts := &options.Options{ + opts := &sc.Options{ Domain: "athenz", AccessTokens: []config.AccessToken{ { @@ -530,13 +529,13 @@ func TestGetServiceHostname(test *testing.T) { Name: "testProvider", Hostname: tt.providerHostname, } - opts := options.Options{ + opts := sc.Options{ SanDnsHostname: tt.sanDnsHostname, HostnameSuffix: tt.hostnameSuffix, Domain: tt.domain, Provider: provider, } - svc := options.Service{ + svc := sc.Service{ Name: tt.service, } hostname := getServiceHostname(&opts, svc, false) @@ -551,7 +550,7 @@ func TestServiceAlreadyRegistered(test *testing.T) { keyDir := test.TempDir() certDir := test.TempDir() - opts := options.Options{ + opts := sc.Options{ KeyDir: keyDir, CertDir: certDir, Domain: "athenz", @@ -581,7 +580,7 @@ func TestServiceAlreadyRegistered(test *testing.T) { } for _, tt := range tests { test.Run(tt.name, func(t *testing.T) { - svc := options.Service{ + svc := sc.Service{ Name: "api", KeyFilename: tt.keyFileName, CertFilename: tt.certFileName, @@ -599,7 +598,7 @@ func TestGenerateSshRequest(test *testing.T) { tp := TestProvider{ Name: "athenz.aws.us-west-2", } - opts := options.Options{ + opts := sc.Options{ Ssh: false, Provider: tp, } @@ -610,7 +609,7 @@ func TestGenerateSshRequest(test *testing.T) { assert.Nil(test, err) // ssh enabled but not for primary service we should get success with nils and empty csr opts.Ssh = true - opts.Services = []options.Service{ + opts.Services = []sc.Service{ { Name: "api", }, @@ -653,7 +652,7 @@ func TestGenerateSshRequest(test *testing.T) { func TestShouldExitRightAwayCountsOnly(test *testing.T) { - opts := &options.Options{ + opts := &sc.Options{ FailCountForExit: 2, } diff --git a/libs/go/sia/aws/agent/agent.go b/libs/go/sia/aws/agent/agent.go index b5ee9444608..2ab5bb7a5f7 100644 --- a/libs/go/sia/aws/agent/agent.go +++ b/libs/go/sia/aws/agent/agent.go @@ -38,7 +38,8 @@ import ( "github.com/AthenZ/athenz/libs/go/sia/access/tokens" "github.com/AthenZ/athenz/libs/go/sia/aws/attestation" "github.com/AthenZ/athenz/libs/go/sia/aws/options" - "github.com/AthenZ/athenz/libs/go/sia/aws/sds" + sc "github.com/AthenZ/athenz/libs/go/sia/config" + "github.com/AthenZ/athenz/libs/go/sia/sds" "github.com/AthenZ/athenz/libs/go/sia/ssh/hostkey" "github.com/AthenZ/athenz/libs/go/sia/util" "github.com/ardielle/ardielle-go/rdl" @@ -88,7 +89,7 @@ func RoleKey(rotateKey bool, roleKey, svcKey string) (*rsa.PrivateKey, error) { } } -func GetRoleCertificates(ztsUrl string, opts *options.Options) (int, []string) { +func GetRoleCertificates(ztsUrl string, opts *sc.Options) (int, []string) { //initialize our return state to success failures := make([]string, 0) @@ -173,7 +174,7 @@ func GetRoleCertificates(ztsUrl string, opts *options.Options) (int, []string) { return len(opts.Roles), failures } -func RegisterInstance(data []*attestation.AttestationData, ztsUrl string, opts *options.Options, docExpiryCheck bool) error { +func RegisterInstance(data []*attestation.AttestationData, ztsUrl string, opts *sc.Options, docExpiryCheck bool) error { //special handling for EC2 instances //before we process our register event we need to check to @@ -193,7 +194,7 @@ func RegisterInstance(data []*attestation.AttestationData, ztsUrl string, opts * return nil } -func RefreshInstance(data []*attestation.AttestationData, ztsUrl string, opts *options.Options) error { +func RefreshInstance(data []*attestation.AttestationData, ztsUrl string, opts *sc.Options) error { for i, svc := range opts.Services { err := refreshSvc(svc, data[i], ztsUrl, opts) if err != nil { @@ -203,7 +204,7 @@ func RefreshInstance(data []*attestation.AttestationData, ztsUrl string, opts *o return nil } -func getServiceHostname(opts *options.Options, svc options.Service, fqdn bool) string { +func getServiceHostname(opts *sc.Options, svc sc.Service, fqdn bool) string { if !opts.SanDnsHostname { return "" } @@ -237,7 +238,7 @@ func getServiceHostname(opts *options.Options, svc options.Service, fqdn bool) s return fmt.Sprintf("%s.%s.%s.%s", hostname, svc.Name, hyphenDomain, opts.HostnameSuffix) } -func registerSvc(svc options.Service, data *attestation.AttestationData, ztsUrl string, opts *options.Options) error { +func registerSvc(svc sc.Service, data *attestation.AttestationData, ztsUrl string, opts *sc.Options) error { key, err := util.GenerateKeyPair(2048) if err != nil { @@ -349,7 +350,7 @@ func registerSvc(svc options.Service, data *attestation.AttestationData, ztsUrl return nil } -func refreshSvc(svc options.Service, data *attestation.AttestationData, ztsUrl string, opts *options.Options) error { +func refreshSvc(svc sc.Service, data *attestation.AttestationData, ztsUrl string, opts *sc.Options) error { keyFile := util.GetSvcKeyFileName(opts.KeyDir, svc.KeyFilename, opts.Domain, svc.Name) certFile := util.GetSvcCertFileName(opts.CertDir, svc.CertFilename, opts.Domain, svc.Name) @@ -462,7 +463,7 @@ func refreshSvc(svc options.Service, data *attestation.AttestationData, ztsUrl s return nil } -func generateSshRequest(opts *options.Options, primaryServiceName, hostname string) (*zts.SSHCertRequest, string, error) { +func generateSshRequest(opts *sc.Options, primaryServiceName, hostname string) (*zts.SSHCertRequest, string, error) { var err error var sshCsr string var sshCertRequest *zts.SSHCertRequest @@ -556,7 +557,7 @@ func hostCertificateLinePresent(sshConfigFile, sshCertFile string) (bool, error) return false, nil } -func SetupAgent(opts *options.Options, siaAgentDir, siaLinkDir string) { +func SetupAgent(opts *sc.Options, siaAgentDir, siaLinkDir string) { //first, let's determine if we need to drop our privileges //since it requires us to create the directories with the @@ -613,7 +614,7 @@ func SetupAgent(opts *options.Options, siaAgentDir, siaLinkDir string) { } } -func RunAgent(siaCmds, ztsUrl string, opts *options.Options) { +func RunAgent(siaCmds, ztsUrl string, opts *sc.Options) { log.Printf("sia command line arguments specified: '%s'\n", siaCmds) cmds := strings.Split(siaCmds, ",") for _, cmd := range cmds { @@ -621,7 +622,7 @@ func RunAgent(siaCmds, ztsUrl string, opts *options.Options) { } } -func runAgentCommand(siaCmd, ztsUrl string, opts *options.Options) { +func runAgentCommand(siaCmd, ztsUrl string, opts *sc.Options) { //make sure the meta endpoint is configured by the caller if opts.MetaEndPoint == "" { @@ -889,7 +890,7 @@ func accessTokenRequest(tokenOpts *config.TokenOptions) error { return err } -func tokenOptions(opts *options.Options, ztsUrl string) (*config.TokenOptions, error) { +func tokenOptions(opts *sc.Options, ztsUrl string) (*config.TokenOptions, error) { userAgent := fmt.Sprintf("%s-%s", opts.Provider, opts.InstanceId) tokenOpts, err := tokens.NewTokenOptions(opts, ztsUrl, userAgent) if err != nil { @@ -924,7 +925,7 @@ func fetchAccessToken(tokenOpts *config.TokenOptions) error { } } -func shouldSkipRegister(opts *options.Options) bool { +func shouldSkipRegister(opts *sc.Options) bool { if opts.EC2StartTime == nil { return false } @@ -933,13 +934,13 @@ func shouldSkipRegister(opts *options.Options) bool { return duration.Seconds() > 1800 } -func serviceAlreadyRegistered(opts *options.Options, svc options.Service) bool { +func serviceAlreadyRegistered(opts *sc.Options, svc sc.Service) bool { keyFile := util.GetSvcKeyFileName(opts.KeyDir, svc.KeyFilename, opts.Domain, svc.Name) certFile := util.GetSvcCertFileName(opts.CertDir, svc.CertFilename, opts.Domain, svc.Name) return util.FileExists(keyFile) && util.FileExists(certFile) } -func shouldExitRightAway(failedRefreshCount int, opts *options.Options) bool { +func shouldExitRightAway(failedRefreshCount int, opts *sc.Options) bool { // if the failed count already matches or exceeds our configured // value then we return right away if failedRefreshCount >= opts.FailCountForExit { diff --git a/libs/go/sia/aws/agent/agent_test.go b/libs/go/sia/aws/agent/agent_test.go index dfe64c87a1a..81519fbe982 100644 --- a/libs/go/sia/aws/agent/agent_test.go +++ b/libs/go/sia/aws/agent/agent_test.go @@ -29,16 +29,14 @@ import ( "testing" "time" - "github.com/AthenZ/athenz/libs/go/sia/ssh/hostkey" - "github.com/AthenZ/athenz/libs/go/sia/access/config" "github.com/AthenZ/athenz/libs/go/sia/aws/agent/devel/ztsmock" "github.com/AthenZ/athenz/libs/go/sia/aws/attestation" - "github.com/AthenZ/athenz/libs/go/sia/aws/options" + sc "github.com/AthenZ/athenz/libs/go/sia/config" "github.com/AthenZ/athenz/libs/go/sia/host/ip" "github.com/AthenZ/athenz/libs/go/sia/host/signature" + "github.com/AthenZ/athenz/libs/go/sia/ssh/hostkey" "github.com/AthenZ/athenz/libs/go/sia/util" - "github.com/stretchr/testify/assert" ) @@ -206,9 +204,9 @@ func TestRegisterInstance(test *testing.T) { tp := TestProvider{ Name: "athenz.aws.us-west-2", } - opts := &options.Options{ + opts := &sc.Options{ Domain: "athenz", - Services: []options.Service{ + Services: []sc.Service{ { Name: "hockey", Uid: util.ExecIdCommand("-u"), @@ -259,7 +257,7 @@ func copyFile(src, dst string) error { return os.WriteFile(dst, data, 0644) } -func refreshServiceCertSetup(test *testing.T) (*options.Options, *attestation.AttestationData, string) { +func refreshServiceCertSetup(test *testing.T) (*sc.Options, *attestation.AttestationData, string) { siaDir := test.TempDir() @@ -286,9 +284,9 @@ func refreshServiceCertSetup(test *testing.T) (*options.Options, *attestation.At tp := TestProvider{ Name: "athenz.aws.us-west-2", } - opts := &options.Options{ + opts := &sc.Options{ Domain: "athenz", - Services: []options.Service{ + Services: []sc.Service{ { Name: "hockey", Uid: util.ExecIdCommand("-u"), @@ -360,9 +358,9 @@ func TestRoleCertificateRequest(test *testing.T) { tp := TestProvider{ Name: "athenz.aws.us-west-2", } - opts := &options.Options{ + opts := &sc.Options{ Domain: "athenz", - Services: []options.Service{ + Services: []sc.Service{ { Name: "hockey", Uid: util.ExecIdCommand("-u"), @@ -370,7 +368,7 @@ func TestRoleCertificateRequest(test *testing.T) { FileMode: 0400, }, }, - Roles: []options.Role{ + Roles: []sc.Role{ { Name: "athenz:role.writers", Service: "hockey", @@ -401,7 +399,7 @@ func TestRoleCertificateRequest(test *testing.T) { func TestShouldSkipRegister(test *testing.T) { startTime := time.Now() - opts := &options.Options{ + opts := &sc.Options{ EC2StartTime: &startTime, } //current time is valid @@ -487,7 +485,7 @@ func TestUpdateSSHConfigFile(test *testing.T) { } func TestNilTokenOptions(test *testing.T) { - opts := &options.Options{ + opts := &sc.Options{ Domain: "athenz", } token, err := tokenOptions(opts, "") @@ -496,7 +494,7 @@ func TestNilTokenOptions(test *testing.T) { } func TestTokenStoreOptions(test *testing.T) { - opts := &options.Options{ + opts := &sc.Options{ Domain: "athenz", AccessTokens: []config.AccessToken{ { @@ -546,13 +544,13 @@ func TestGetServiceHostname(test *testing.T) { Name: "testProvider", Hostname: tt.providerHostname, } - opts := options.Options{ + opts := sc.Options{ SanDnsHostname: tt.sanDnsHostname, HostnameSuffix: tt.hostnameSuffix, Domain: tt.domain, Provider: provider, } - svc := options.Service{ + svc := sc.Service{ Name: tt.service, } hostname := getServiceHostname(&opts, svc, false) @@ -567,7 +565,7 @@ func TestServiceAlreadyRegistered(test *testing.T) { keyDir := test.TempDir() certDir := test.TempDir() - opts := options.Options{ + opts := sc.Options{ KeyDir: keyDir, CertDir: certDir, Domain: "athenz", @@ -597,7 +595,7 @@ func TestServiceAlreadyRegistered(test *testing.T) { } for _, tt := range tests { test.Run(tt.name, func(t *testing.T) { - svc := options.Service{ + svc := sc.Service{ Name: "api", KeyFilename: tt.keyFileName, CertFilename: tt.certFileName, @@ -615,7 +613,7 @@ func TestGenerateSshRequest(test *testing.T) { tp := TestProvider{ Name: "athenz.aws.us-west-2", } - opts := options.Options{ + opts := sc.Options{ Ssh: false, Provider: tp, } @@ -626,7 +624,7 @@ func TestGenerateSshRequest(test *testing.T) { assert.Nil(test, err) // ssh enabled but not for primary service we should get success with nils and empty csr opts.Ssh = true - opts.Services = []options.Service{ + opts.Services = []sc.Service{ { Name: "api", }, @@ -654,7 +652,7 @@ func TestGenerateSshRequest(test *testing.T) { func TestShouldExitRightAwayCountsOnly(test *testing.T) { - opts := &options.Options{ + opts := &sc.Options{ FailCountForExit: 2, } diff --git a/libs/go/sia/aws/attestation/attestation.go b/libs/go/sia/aws/attestation/attestation.go index 3c4c6c65df8..90e64450a42 100644 --- a/libs/go/sia/aws/attestation/attestation.go +++ b/libs/go/sia/aws/attestation/attestation.go @@ -24,8 +24,8 @@ import ( "os" "strings" - "github.com/AthenZ/athenz/libs/go/sia/aws/options" "github.com/AthenZ/athenz/libs/go/sia/aws/stssession" + sc "github.com/AthenZ/athenz/libs/go/sia/config" "github.com/aws/aws-sdk-go-v2/service/sts" ) @@ -42,7 +42,7 @@ type AttestationData struct { // New creates a new AttestationData with values fed to it and from the result of STS Assume Role. // This requires an identity document along with its signature. The aws account and region will // be extracted from the identity document. -func New(opts *options.Options, service string) (*AttestationData, error) { +func New(opts *sc.Options, service string) (*AttestationData, error) { commonName := fmt.Sprintf("%s.%s", opts.Domain, service) var role string if opts.OmitDomain { @@ -115,7 +115,7 @@ func GetECSTaskId() string { } // GetAttestationData fetches attestation data for all the services mentioned in the config file -func GetAttestationData(opts *options.Options) ([]*AttestationData, error) { +func GetAttestationData(opts *sc.Options) ([]*AttestationData, error) { data := []*AttestationData{} for _, svc := range opts.Services { a, err := New(opts, svc.Name) diff --git a/libs/go/sia/aws/options/options.go b/libs/go/sia/aws/options/options.go index 8fff4d8f6cf..5f47e37d35b 100644 --- a/libs/go/sia/aws/options/options.go +++ b/libs/go/sia/aws/options/options.go @@ -25,212 +25,16 @@ import ( "os" "strings" "syscall" - "time" ac "github.com/AthenZ/athenz/libs/go/sia/access/config" "github.com/AthenZ/athenz/libs/go/sia/aws/doc" "github.com/AthenZ/athenz/libs/go/sia/aws/meta" "github.com/AthenZ/athenz/libs/go/sia/aws/stssession" - "github.com/AthenZ/athenz/libs/go/sia/host/provider" + sc "github.com/AthenZ/athenz/libs/go/sia/config" "github.com/AthenZ/athenz/libs/go/sia/ssh/hostkey" "github.com/AthenZ/athenz/libs/go/sia/util" ) -// package options contains types for parsing sia_config file and options to carry those config values - -// ConfigService represents a service to be specified by user, and specify User/Group attributes for the service -type ConfigService struct { - KeyFilename string `json:"key_filename,omitempty"` - CertFilename string `json:"cert_filename,omitempty"` - User string `json:"user,omitempty"` - Group string `json:"group,omitempty"` - ExpiryTime int `json:"expiry_time,omitempty"` - SDSUdsUid int `json:"sds_uds_uid,omitempty"` - SDSNodeId string `json:"sds_node_id,omitempty"` - SDSNodeCluster string `json:"sds_node_cluster,omitempty"` - Threshold float64 `json:"cert_threshold_to_check,omitempty"` -} - -// ConfigRole represents a role to be specified by user, and specify attributes for the role -type ConfigRole struct { - Filename string `json:"filename,omitempty"` //filename for the generated role certificate file - ExpiryTime int `json:"expiry_time,omitempty"` //requested expiry time for the role certificate - Service string `json:"service,omitempty"` //principal with role access - User string `json:"user,omitempty"` //user owner on the role identity key - Group string `json:"group,omitempty"` //group owner on the role identity key - Threshold float64 `json:"cert_threshold_to_check,omitempty"` -} - -// ConfigAccount represents each of the accounts that can be specified in the config file -type ConfigAccount struct { - Name string `json:"name,omitempty"` //name of the service identity - User string `json:"user,omitempty"` //the username to chown the cert/key dirs to. If absent, then root. - Group string `json:"group,omitempty"` //the group name to chown the cert/key dirs to. If absent, then athenz. - Domain string `json:"domain,omitempty"` //name of the domain for the identity - Account string `json:"account,omitempty"` //name of the account - Service string `json:"service,omitempty"` //name of the service for the identity - Zts string `json:"zts,omitempty"` //the ZTS to contact - Roles map[string]ConfigRole `json:"roles,omitempty"` //map of roles to retrieve certificates for - Version string `json:"version,omitempty"` //sia version number - Threshold float64 `json:"cert_threshold_to_check,omitempty"` //threshold to verify for all certs - SshThreshold float64 `json:"sshcert_threshold_to_check,omitempty"` //threshold to verify for ssh certs - OmitDomain bool `json:"omit_domain,omitempty"` //attestation role only includes service name -} - -// Config represents entire sia_config file -type Config struct { - Version string `json:"version,omitempty"` //name of the provider - Service string `json:"service,omitempty"` //name of the service for the identity - Services map[string]ConfigService `json:"services,omitempty"` //names of the multiple services for the identity - Ssh *bool `json:"ssh,omitempty"` //ssh certificate support - SshHostKeyType hostkey.KeyType `json:"ssh_host_key_type,omitempty"` //ssh host key type - rsa, ecdsa, etc - SshPrincipals string `json:"ssh_principals,omitempty"` //ssh additional principals - SanDnsWildcard bool `json:"sandns_wildcard,omitempty"` //san dns wildcard support - SanDnsHostname bool `json:"sandns_hostname,omitempty"` //san dns hostname support - SanDnsX509Cnames string `json:"sandns_x509_cnames,omitempty"` //additional san dns entries to be added to the CSR - UseRegionalSTS bool `json:"regionalsts,omitempty"` //whether to use a regional STS endpoint (default is false) - Accounts []ConfigAccount `json:"accounts,omitempty"` //array of configured accounts - GenerateRoleKey bool `json:"generate_role_key,omitempty"` //private key to be generated for role certificate - RotateKey bool `json:"rotate_key,omitempty"` //rotate private key support - User string `json:"user,omitempty"` //the username to chown the cert/key dirs to. If absent, then root - Group string `json:"group,omitempty"` //the group name to chown the cert/key dirs to. If absent, then athenz - SDSUdsPath string `json:"sds_uds_path,omitempty"` //uds path if the agent should support uds connections - SDSUdsUid int `json:"sds_uds_uid,omitempty"` //uds connections must be from the given user uid - ExpiryTime int `json:"expiry_time,omitempty"` //service and role certificate expiry in minutes - RefreshInterval int `json:"refresh_interval,omitempty"` //specifies refresh interval in minutes - ZTSRegion string `json:"zts_region,omitempty"` //specifies zts region for the requests - DropPrivileges bool `json:"drop_privileges,omitempty"` //drop privileges to configured user instead of running as root - AccessTokens map[string]ac.Role `json:"access_tokens,omitempty"` //map of role name to token attributes - FileDirectUpdate bool `json:"file_direct_update,omitempty"` //update key/cert files directly instead of using rename - SiaKeyDir string `json:"sia_key_dir,omitempty"` //sia keys directory to override /var/lib/sia/keys - SiaCertDir string `json:"sia_cert_dir,omitempty"` //sia certs directory to override /var/lib/sia/certs - SiaTokenDir string `json:"sia_token_dir,omitempty"` //sia tokens directory to override /var/lib/sia/tokens - SiaBackupDir string `json:"sia_backup_dir,omitempty"` //sia backup directory to override /var/lib/sia/backup - HostnameSuffix string `json:"hostname_suffix,omitempty"` //hostname suffix in case we need to auto-generate hostname - AccessManagement bool `json:"access_management,omitempty"` //access management support - FailCountForExit int `json:"fail_count_for_exit,omitempty"` //number of failed counts before exiting program - RunAfterCerts string `json:"run_after,omitempty"` //execute the command mentioned after certs are created - RunAfterCertsErr string `json:"run_after_certs_err,omitempty"` //execute the command mentioned after role certs fail to refresh - RunAfterTokens string `json:"run_after_tokens,omitempty"` //execute the command mentioned after tokens are created - RunAfterTokensErr string `json:"run_after_tokens_err,omitempty"` //execute the command mentioned after tokens fail to refresh - SpiffeTrustDomain string `json:"spiffe_trust_domain,omitempty"` //spiffe trust domain - if configured used full spiffe uri with namespace - StoreTokenOption *int `json:"store_token_option,omitempty"` //store access token option - RunAfterFailExit bool `json:"run_after_fail_exit,omitempty"` //exit process if run_after script fails -} - -type AccessProfileConfig struct { - Profile string `json:"profile,omitempty"` - ProfileRestrictTo string `json:"profile_restrict_to,omitempty"` -} - -// Role contains role details. Attributes are set based on the config values -type Role struct { - Name string - Service string - SvcKeyFilename string - SvcCertFilename string - ExpiryTime int - RoleKeyFilename string - RoleCertFilename string - User string - Uid int - Gid int - FileMode int - Threshold float64 -} - -// Service represents service details. Attributes are filled in based on the config values -type Service struct { - Name string - KeyFilename string - CertFilename string - User string - Group string - Uid int - Gid int - FileMode int - ExpiryTime int - SDSUdsUid int - SDSNodeId string - SDSNodeCluster string - Threshold float64 -} - -// Options represents settings that are derived from config file and application defaults -type Options struct { - Provider provider.Provider //provider instance - MetaEndPoint string //meta data service endpoint - Name string //name of the service identity - User string //the username to chown the cert/key dirs to. If absent, then root - Group string //the group name to chown the cert/key dirs to. If absent, then athenz - Domain string //name of the domain for the identity - Account string //name of the account - Service string //name of the service for the identity - Zts string //the ZTS to contact - InstanceId string //instance id if ec2, task id if running within eks/ecs - Roles []Role //map of roles to retrieve certificates for - Region string //region name - SanDnsWildcard bool //san dns wildcard support - SanDnsHostname bool //san dns hostname support - Version string //sia version number - ZTSDomains []string //zts domain prefixes - Services []Service //array of configured services - Ssh bool //ssh certificate support - UseRegionalSTS bool //use regional sts endpoint - KeyDir string //private key directory path - CertDir string //x.509 certificate directory path - AthenzCACertFile string //filename to store Athenz CA certs - ZTSCACertFile string //filename for CA certs when communicating with ZTS - ZTSServerName string //ZTS server name, if necessary for tls - ZTSAWSDomains []string //list of domain prefixes for sanDNS entries - GenerateRoleKey bool //option to generate a separate key for role certificates - RotateKey bool //rotate the private key when refreshing certificates - BackupDir string //backup directory for key/cert rotation - CertCountryName string //generated x.509 certificate country name - CertOrgName string //generated x.509 certificate organization name - SshPubKeyFile string //ssh host public key file path - SshCertFile string //ssh host certificate file path - SshConfigFile string //sshd config file path - SshHostKeyType hostkey.KeyType //ssh host key type - rsa or ecdsa - PrivateIp string //instance private ip - EC2Document string //EC2 instance identity document - EC2Signature string //EC2 instance identity document pkcs7 signature - EC2StartTime *time.Time //EC2 instance start time - InstanceIdSanDNS bool //include instance id in a san dns entry (backward compatible option) - RolePrincipalEmail bool //include role principal in a san email field (backward compatible option) - SDSUdsPath string //UDS path if the agent should support uds connections - SDSUdsUid int //UDS connections must be from the given user uid - RefreshInterval int //refresh interval for certificates - default 24 hours - ZTSRegion string //ZTS region in case the client needs this information - DropPrivileges bool //Drop privileges to configured user instead of running as root - TokenDir string //Access tokens directory - AccessTokens []ac.AccessToken //Access tokens object - Profile string //Access profile name - ProfileRestrictTo string //Tag associated with access profile roles - Threshold float64 //threshold in number of days for cert expiry checks - SshThreshold float64 //threshold in number of days for ssh cert expiry checks - FileDirectUpdate bool //update key/cert files directly instead of using rename - HostnameSuffix string //hostname suffix in case we need to auto-generate hostname - SshPrincipals string //ssh additional principals - AccessManagement bool //access management support - AddlSanDNSEntries []string //additional san dns entries to be added to the CSR - FailCountForExit int //number of failed counts before exiting program - RunAfterCertsOkParts []string //run after certificate parsed parts for success - RunAfterCertsErrParts []string //run after certificate parsed parts for errors - RunAfterTokensOkParts []string //run after token parsed parts for success - RunAfterTokensErrParts []string //run after token parsed parts for errors - SpiffeTrustDomain string //spiffe uri trust domain - SpiffeNamespace string //spiffe uri namespace - OmitDomain bool //attestation role only includes service name - StoreTokenOption *int //store access token option - RunAfterFailExit bool //exit process if run_after script fails -} - -const ( - DefaultTokenExpiry = 28800 // 8 hrs - DefaultThreshold = float64(15) // 15 days -) - func GetInstanceTagValue(metaEndPoint, tagKey string) (string, error) { tagValue, err := meta.GetData(metaEndPoint, "/latest/meta-data/tags/instance/"+tagKey) return string(tagValue), err @@ -250,25 +54,25 @@ func GetAccountId(metaEndPoint string, useRegionalSTS bool, region string) (stri return doc.GetDocumentEntry(document, "accountId") } -func InitCredsConfig(roleSuffix, accessProfileSeparator string, useRegionalSTS bool, region string) (*ConfigAccount, *AccessProfileConfig, error) { +func InitCredsConfig(roleSuffix, accessProfileSeparator string, useRegionalSTS bool, region string) (*sc.ConfigAccount, *sc.AccessProfileConfig, error) { account, domain, service, profile, err := stssession.GetMetaDetailsFromCreds(roleSuffix, accessProfileSeparator, useRegionalSTS, region) if err != nil { return nil, nil, fmt.Errorf("unable to parse role arn: %v", err) } - return &ConfigAccount{ + return &sc.ConfigAccount{ Domain: domain, Service: service, Account: account, Name: fmt.Sprintf("%s.%s", domain, service), - Threshold: DefaultThreshold, - SshThreshold: DefaultThreshold, - }, &AccessProfileConfig{ + Threshold: sc.DefaultThreshold, + SshThreshold: sc.DefaultThreshold, + }, &sc.AccessProfileConfig{ Profile: profile, ProfileRestrictTo: "", }, nil } -func InitProfileConfig(metaEndPoint, roleSuffix, accessProfileSeparator string) (*ConfigAccount, *AccessProfileConfig, error) { +func InitProfileConfig(metaEndPoint, roleSuffix, accessProfileSeparator string) (*sc.ConfigAccount, *sc.AccessProfileConfig, error) { info, err := meta.GetData(metaEndPoint, "/latest/meta-data/iam/info") if err != nil { return nil, nil, err @@ -288,21 +92,21 @@ func InitProfileConfig(metaEndPoint, roleSuffix, accessProfileSeparator string) domain = string(athenzDomain) omitDomain = true } - return &ConfigAccount{ + return &sc.ConfigAccount{ Domain: domain, Service: service, Account: account, Name: fmt.Sprintf("%s.%s", domain, service), - Threshold: DefaultThreshold, - SshThreshold: DefaultThreshold, + Threshold: sc.DefaultThreshold, + SshThreshold: sc.DefaultThreshold, OmitDomain: omitDomain, - }, &AccessProfileConfig{ + }, &sc.AccessProfileConfig{ Profile: profile, ProfileRestrictTo: "", }, nil } -func InitFileConfig(fileName, metaEndPoint string, useRegionalSTS bool, region, account string) (*Config, *ConfigAccount, error) { +func InitFileConfig(fileName, metaEndPoint string, useRegionalSTS bool, region, account string) (*sc.Config, *sc.ConfigAccount, error) { confBytes, err := os.ReadFile(fileName) if err != nil { return nil, nil, err @@ -310,7 +114,7 @@ func InitFileConfig(fileName, metaEndPoint string, useRegionalSTS bool, region, if len(confBytes) == 0 { return nil, nil, errors.New("empty config bytes") } - var config Config + var config sc.Config err = json.Unmarshal(confBytes, &config) if err != nil { return nil, nil, err @@ -330,15 +134,15 @@ func InitFileConfig(fileName, metaEndPoint string, useRegionalSTS bool, region, } configAccount.Service = config.Service configAccount.Name = fmt.Sprintf("%s.%s", configAccount.Domain, configAccount.Service) - configAccount.Threshold = nonZeroValue(configAccount.Threshold, DefaultThreshold) - configAccount.SshThreshold = nonZeroValue(configAccount.SshThreshold, DefaultThreshold) + configAccount.Threshold = nonZeroValue(configAccount.Threshold, sc.DefaultThreshold) + configAccount.SshThreshold = nonZeroValue(configAccount.SshThreshold, sc.DefaultThreshold) return &config, &configAccount, nil } } return nil, nil, fmt.Errorf("missing account %s details from config file", account) } -func InitAccessProfileFileConfig(fileName string) (*AccessProfileConfig, error) { +func InitAccessProfileFileConfig(fileName string) (*sc.AccessProfileConfig, error) { confBytes, err := os.ReadFile(fileName) if err != nil { return nil, err @@ -346,7 +150,7 @@ func InitAccessProfileFileConfig(fileName string) (*AccessProfileConfig, error) if len(confBytes) == 0 { return nil, errors.New("empty config bytes") } - var config AccessProfileConfig + var config sc.AccessProfileConfig err = json.Unmarshal(confBytes, &config) if err != nil { return nil, err @@ -355,18 +159,18 @@ func InitAccessProfileFileConfig(fileName string) (*AccessProfileConfig, error) return nil, fmt.Errorf("missing required Profile field from the config file") } - return &AccessProfileConfig{ + return &sc.AccessProfileConfig{ Profile: config.Profile, ProfileRestrictTo: config.ProfileRestrictTo, }, nil } -func InitEnvConfig(config *Config) (*Config, *ConfigAccount, error) { +func InitEnvConfig(config *sc.Config) (*sc.Config, *sc.ConfigAccount, error) { // it is possible that the config object was already created the // config file in which case we're not going to override any // of the settings. if config == nil { - config = &Config{} + config = &sc.Config{} } if !config.SanDnsWildcard { config.SanDnsWildcard = util.ParseEnvBooleanFlag("ATHENZ_SIA_SANDNS_WILDCARD") @@ -482,7 +286,7 @@ func InitEnvConfig(config *Config) (*Config, *ConfigAccount, error) { if config.Service == "" { config.Service = service } - var configRoles map[string]ConfigRole + var configRoles map[string]sc.ConfigRole rolesEnv := os.Getenv("ATHENZ_SIA_ACCOUNT_ROLES") if rolesEnv != "" { err = json.Unmarshal([]byte(rolesEnv), &configRoles) @@ -505,11 +309,11 @@ func InitEnvConfig(config *Config) (*Config, *ConfigAccount, error) { } } - threshold := util.ParseEnvFloatFlag("ATHENZ_SIA_ACCOUNT_THRESHOLD", DefaultThreshold) - sshThreshold := util.ParseEnvFloatFlag("ATHENZ_SIA_ACCOUNT_SSH_THRESHOLD", DefaultThreshold) + threshold := util.ParseEnvFloatFlag("ATHENZ_SIA_ACCOUNT_THRESHOLD", sc.DefaultThreshold) + sshThreshold := util.ParseEnvFloatFlag("ATHENZ_SIA_ACCOUNT_SSH_THRESHOLD", sc.DefaultThreshold) omitDomain := util.ParseEnvBooleanFlag("ATHENZ_SIA_OMIT_DOMAIN") - return config, &ConfigAccount{ + return config, &sc.ConfigAccount{ Account: account, Domain: domain, Service: service, @@ -521,14 +325,14 @@ func InitEnvConfig(config *Config) (*Config, *ConfigAccount, error) { }, nil } -func InitAccessProfileEnvConfig() (*AccessProfileConfig, error) { +func InitAccessProfileEnvConfig() (*sc.AccessProfileConfig, error) { accessProfile := os.Getenv("ATHENZ_SIA_ACCESS_PROFILE") if accessProfile == "" { return nil, fmt.Errorf("athenz accessProfile variable not configured") } - return &AccessProfileConfig{ + return &sc.AccessProfileConfig{ Profile: accessProfile, ProfileRestrictTo: "", }, nil @@ -536,7 +340,7 @@ func InitAccessProfileEnvConfig() (*AccessProfileConfig, error) { // setOptions takes in sia_config objects and returns a pointer to Options after parsing and initializing the defaults // It uses profile arn for defaults when sia_config is empty or non-parsable. It populates "services" array -func setOptions(config *Config, account *ConfigAccount, profileConfig *AccessProfileConfig, siaDir, version string) (*Options, error) { +func setOptions(config *sc.Config, account *sc.ConfigAccount, profileConfig *sc.AccessProfileConfig, siaDir, version string) (*sc.Options, error) { //update regional sts and wildcard settings based on config settings useRegionalSTS := false @@ -647,11 +451,11 @@ func setOptions(config *Config, account *ConfigAccount, profileConfig *AccessPro } } - var services []Service + var services []sc.Service if config == nil || len(config.Services) == 0 { //There is no sia_config, or multiple services are not configured. //Populate services with the account information we gathered - s := Service{ + s := sc.Service{ Name: account.Service, User: account.User, Threshold: account.Threshold, @@ -666,12 +470,12 @@ func setOptions(config *Config, account *ConfigAccount, profileConfig *AccessPro return nil, fmt.Errorf("services: %+v mentioned, service: %q needs to be part of services", config.Services, config.Service) } // Populate config.Service into first - first := Service{ + first := sc.Service{ Name: config.Service, } // Populate the remaining into tail - var tail []Service + var tail []sc.Service for name, s := range config.Services { svcExpiryTime := expiryTime if s.ExpiryTime > 0 { @@ -701,7 +505,7 @@ func setOptions(config *Config, account *ConfigAccount, profileConfig *AccessPro first.SDSUdsUid = svcSDSUdsUid first.Threshold = nonZeroValue(s.Threshold, account.Threshold) } else { - ts := Service{ + ts := sc.Service{ Name: name, KeyFilename: s.KeyFilename, CertFilename: s.CertFilename, @@ -727,7 +531,7 @@ func setOptions(config *Config, account *ConfigAccount, profileConfig *AccessPro return nil, err } - var roles []Role + var roles []sc.Role for name, r := range account.Roles { if r.Filename != "" && r.Filename[0] == '/' { log.Println("when custom filepaths are specified, rotate_key and generate_role_key are not supported") @@ -735,7 +539,7 @@ func setOptions(config *Config, account *ConfigAccount, profileConfig *AccessPro rotateKey = false } roleService := getRoleServiceOwner(r.Service, services) - role := Role{ + role := sc.Role{ Name: name, Service: roleService.Name, SvcKeyFilename: util.GetSvcKeyFileName(keyDir, roleService.KeyFilename, account.Domain, roleService.Name), @@ -767,7 +571,7 @@ func setOptions(config *Config, account *ConfigAccount, profileConfig *AccessPro profileRestrictTo = profileConfig.ProfileRestrictTo } - return &Options{ + return &sc.Options{ Name: account.Name, User: account.User, Group: account.Group, @@ -814,7 +618,7 @@ func setOptions(config *Config, account *ConfigAccount, profileConfig *AccessPro }, nil } -func getRoleServiceOwner(serviceName string, services []Service) Service { +func getRoleServiceOwner(serviceName string, services []sc.Service) sc.Service { if serviceName == "" { return services[0] } @@ -827,7 +631,7 @@ func getRoleServiceOwner(serviceName string, services []Service) Service { return services[0] } -func processAccessTokens(config *Config, processedSvcs []Service) ([]ac.AccessToken, error) { +func processAccessTokens(config *sc.Config, processedSvcs []sc.Service) ([]ac.AccessToken, error) { if config == nil || config.AccessTokens == nil { return nil, nil } @@ -845,7 +649,7 @@ func processAccessTokens(config *Config, processedSvcs []Service) ([]ac.AccessTo if len(roles) == 0 { roles = []string{fileName} } - expiry := DefaultTokenExpiry + expiry := sc.DefaultTokenExpiry if t.Expiry != 0 { expiry = t.Expiry } @@ -876,17 +680,17 @@ func processAccessTokens(config *Config, processedSvcs []Service) ([]ac.AccessTo return accessTokens, nil } -func getSvc(name string, services []Service) (Service, error) { +func getSvc(name string, services []sc.Service) (sc.Service, error) { for _, s := range services { if s.Name == name { return s, nil } } - return Service{}, fmt.Errorf("%q not found in processed services", name) + return sc.Service{}, fmt.Errorf("%q not found in processed services", name) } // GetSvcNames returns comma separated list of service names -func GetSvcNames(svcs []Service) string { +func GetSvcNames(svcs []sc.Service) string { var b bytes.Buffer for _, svc := range svcs { b.WriteString(fmt.Sprintf("%s,", svc.Name)) @@ -900,7 +704,7 @@ func GetSvcNames(svcs []Service) string { // for keys and certs, then the tool can drop its access from root // to the specified user. If they're multiple users defined then // the return values would be -1/-1 -func GetRunsAsUidGid(opts *Options) (int, int) { +func GetRunsAsUidGid(opts *sc.Options) (int, int) { // first we want to check if the caller has specifically indicated // that they want to keep the privileges and not drop to another user if !opts.DropPrivileges { @@ -949,7 +753,7 @@ func GetRunsAsUidGid(opts *Options) (int, int) { return uid, gid } -func NewOptions(config *Config, configAccount *ConfigAccount, profileConfig *AccessProfileConfig, siaDir, siaVersion string, useRegionalSTS bool, region string) (*Options, error) { +func NewOptions(config *sc.Config, configAccount *sc.ConfigAccount, profileConfig *sc.AccessProfileConfig, siaDir, siaVersion string, useRegionalSTS bool, region string) (*sc.Options, error) { opts, err := setOptions(config, configAccount, profileConfig, siaDir, siaVersion) if err != nil { diff --git a/libs/go/sia/aws/options/options_test.go b/libs/go/sia/aws/options/options_test.go index a6a1407132e..332392c6c8d 100644 --- a/libs/go/sia/aws/options/options_test.go +++ b/libs/go/sia/aws/options/options_test.go @@ -32,9 +32,9 @@ import ( "testing" "github.com/AthenZ/athenz/libs/go/sia/access/config" + sc "github.com/AthenZ/athenz/libs/go/sia/config" "github.com/AthenZ/athenz/libs/go/sia/ssh/hostkey" "github.com/AthenZ/athenz/libs/go/sia/util" - "github.com/dimfeld/httptreemux" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -88,7 +88,7 @@ func (t *testServer) httpUrl() string { return fmt.Sprintf("http://%s", t.addr) } -func getConfig(fileName, roleSuffix, metaEndPoint string, useRegionalSTS bool, region string) (*Config, *ConfigAccount, error) { +func getConfig(fileName, roleSuffix, metaEndPoint string, useRegionalSTS bool, region string) (*sc.Config, *sc.ConfigAccount, error) { // Parse config bytes first, and if that fails, load values from Instance Profile and IAM info cfg, cfgAccount, err := InitFileConfig(fileName, metaEndPoint, useRegionalSTS, region, "") if err != nil { @@ -105,10 +105,10 @@ func getConfig(fileName, roleSuffix, metaEndPoint string, useRegionalSTS bool, r return cfg, cfgAccount, nil } -func getAccessProfileConfig(fileName, metaEndPoint string) (*ConfigAccount, *AccessProfileConfig, error) { +func getAccessProfileConfig(fileName, metaEndPoint string) (*sc.ConfigAccount, *sc.AccessProfileConfig, error) { // Parse config bytes first, and if that fails, load values from Instance Profile and IAM info profileConfig, err := InitAccessProfileFileConfig(fileName) - var configAccount *ConfigAccount = nil + var configAccount *sc.ConfigAccount = nil if err != nil { log.Printf("unable to parse configuration file, error: %v\n", err) log.Println("trying to determine profile name from instance profile arn...") @@ -121,7 +121,7 @@ func getAccessProfileConfig(fileName, metaEndPoint string) (*ConfigAccount, *Acc return configAccount, profileConfig, nil } -func assertService(expected Service, actual Service) bool { +func assertService(expected sc.Service, actual sc.Service) bool { log.Printf("expected: %+v\n", expected) log.Printf("actual: %+v\n", actual) return expected.Name == actual.Name && @@ -134,7 +134,7 @@ func assertService(expected Service, actual Service) bool { expected.Threshold == actual.Threshold } -func assertInServices(svcs []Service, actual Service) bool { +func assertInServices(svcs []sc.Service, actual sc.Service) bool { log.Printf("svcs passed: %+v\n", svcs) log.Printf("actual: %+v\n", actual) for _, s := range svcs { @@ -197,9 +197,9 @@ func TestOptionsNoConfig(t *testing.T) { assert.Equal(t, 1, len(opts.Services)) assert.Equal(t, "athenz", opts.Domain) assert.Equal(t, "athenz.hockey", opts.Name) - assert.True(t, assertService(opts.Services[0], Service{Name: "hockey", Uid: idCommandId("-u"), Gid: idCommandId("-g"), FileMode: 288, Threshold: DefaultThreshold})) - assert.Equal(t, DefaultThreshold, opts.Threshold) - assert.Equal(t, DefaultThreshold, opts.SshThreshold) + assert.True(t, assertService(opts.Services[0], sc.Service{Name: "hockey", Uid: idCommandId("-u"), Gid: idCommandId("-g"), FileMode: 288, Threshold: sc.DefaultThreshold})) + assert.Equal(t, sc.DefaultThreshold, opts.Threshold) + assert.Equal(t, sc.DefaultThreshold, opts.SshThreshold) assert.False(t, opts.FileDirectUpdate) assert.Equal(t, "/var/lib/sia/keys", opts.KeyDir) assert.Equal(t, "/var/lib/sia/certs", opts.CertDir) @@ -234,8 +234,8 @@ func TestOptionsNoProfileConfig(t *testing.T) { assert.True(t, opts.Name == "athenz.hockey") assert.Equal(t, opts.Profile, "test-profile") - assert.Equal(t, opts.Threshold, DefaultThreshold) - assert.Equal(t, opts.SshThreshold, DefaultThreshold) + assert.Equal(t, opts.Threshold, sc.DefaultThreshold) + assert.Equal(t, opts.SshThreshold, sc.DefaultThreshold) } // TestOptionsWithProfileConfig test the scenario when profile config file is present @@ -258,12 +258,12 @@ func TestOptionsWithProfileConfig(t *testing.T) { assert.Equal(t, "athenz.api", opts.Name) // Zeroth service should be the one from "service" key, the remaining are from "services" in no particular order - assert.True(t, assertService(opts.Services[0], Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: DefaultThreshold})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: DefaultThreshold})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: DefaultThreshold})) + assert.True(t, assertService(opts.Services[0], sc.Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: sc.DefaultThreshold})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: sc.DefaultThreshold})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: sc.DefaultThreshold})) - assert.Equal(t, DefaultThreshold, opts.SshThreshold) - assert.Equal(t, DefaultThreshold, opts.Threshold) + assert.Equal(t, sc.DefaultThreshold, opts.SshThreshold) + assert.Equal(t, sc.DefaultThreshold, opts.Threshold) assert.Equal(t, "host1.athenz.io,host2.athenz.io", opts.SshPrincipals) assert.False(t, opts.OmitDomain) @@ -292,12 +292,12 @@ func TestOptionsWithProfileConfigAndProfileRestrictTo(t *testing.T) { assert.True(t, opts.Name == "athenz.api") // Zeroth service should be the one from "service" key, the remaining are from "services" in no particular order - assert.True(t, assertService(opts.Services[0], Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: DefaultThreshold})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: DefaultThreshold})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: DefaultThreshold})) + assert.True(t, assertService(opts.Services[0], sc.Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: sc.DefaultThreshold})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: sc.DefaultThreshold})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: sc.DefaultThreshold})) - assert.Equal(t, DefaultThreshold, opts.SshThreshold) - assert.Equal(t, DefaultThreshold, opts.Threshold) + assert.Equal(t, sc.DefaultThreshold, opts.SshThreshold) + assert.Equal(t, sc.DefaultThreshold, opts.Threshold) } func TestOptionsWithRoleThreshold(t *testing.T) { @@ -320,12 +320,12 @@ func TestOptionsWithRoleThreshold(t *testing.T) { assert.Equal(t, "athenz.api", opts.Name) // Zeroth service should be the one from "service" key, the remaining are from "services" in no particular order - assert.True(t, assertService(opts.Services[0], Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: 20})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: 20})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: 20})) + assert.True(t, assertService(opts.Services[0], sc.Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: 20})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: 20})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: 20})) assert.Equal(t, float64(25), opts.Roles[0].Threshold) - assert.Equal(t, DefaultThreshold, opts.SshThreshold) + assert.Equal(t, sc.DefaultThreshold, opts.SshThreshold) assert.Equal(t, float64(20), opts.Threshold) } @@ -347,9 +347,9 @@ func TestOptionsWithServiceAccountThreshold(t *testing.T) { assert.True(t, opts.Name == "athenz.api") // Zeroth service should be the one from "service" key, the remaining are from "services" in no particular order - assert.True(t, assertService(opts.Services[0], Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: 35})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: 35})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: 25})) + assert.True(t, assertService(opts.Services[0], sc.Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: 35})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: 35})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: 25})) assert.Equal(t, float64(60), opts.SshThreshold) assert.Equal(t, float64(35), opts.Threshold) @@ -380,12 +380,12 @@ func TestOptionsWithConfig(t *testing.T) { assert.Equal(t, "athenz.api", opts.Name) // Zeroth service should be the one from "service" key, the remaining are from "services" in no particular order - assert.True(t, assertService(opts.Services[0], Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: DefaultThreshold})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: DefaultThreshold})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: DefaultThreshold})) + assert.True(t, assertService(opts.Services[0], sc.Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: sc.DefaultThreshold})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: sc.DefaultThreshold})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: sc.DefaultThreshold})) - assert.Equal(t, DefaultThreshold, opts.Threshold) - assert.Equal(t, DefaultThreshold, opts.SshThreshold) + assert.Equal(t, sc.DefaultThreshold, opts.Threshold) + assert.Equal(t, sc.DefaultThreshold, opts.SshThreshold) } // TestOptionsNoService test the scenario when /etc/sia/sia_config is present, but service is not repeated in services @@ -411,7 +411,7 @@ func TestOptionsNoServices(t *testing.T) { assert.Equal(t, 1, len(opts.Services)) assert.Equal(t, "athenz", opts.Domain) assert.Equal(t, "athenz.api", opts.Name) - assert.True(t, assertService(opts.Services[0], Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: DefaultThreshold})) + assert.True(t, assertService(opts.Services[0], sc.Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: sc.DefaultThreshold})) assert.True(t, opts.FileDirectUpdate) } @@ -564,7 +564,7 @@ func TestGetRunsAsUidGid(t *testing.T) { } } -func toServiceNames(services []Service) []string { +func toServiceNames(services []sc.Service) []string { var serviceNames []string for _, srv := range services { @@ -595,7 +595,7 @@ func TestOptionsWithAccessToken(t *testing.T) { Service: "api", Domain: "athenz.demo", Roles: []string{"reader"}, - Expiry: DefaultTokenExpiry, + Expiry: sc.DefaultTokenExpiry, User: "nobody", ProxyPrincipalSpiffeUris: "", })) @@ -615,7 +615,7 @@ func TestOptionsWithAccessToken(t *testing.T) { Service: "api", Domain: "athenz.demo", Roles: []string{"reader", "reader-admin"}, - Expiry: DefaultTokenExpiry, + Expiry: sc.DefaultTokenExpiry, User: "nobody", ProxyPrincipalSpiffeUris: "", })) @@ -635,7 +635,7 @@ func TestOptionsWithAccessToken(t *testing.T) { Service: "logger", Domain: "athenz.demo", Roles: []string{"splunk"}, - Expiry: DefaultTokenExpiry, + Expiry: sc.DefaultTokenExpiry, User: "nobody", ProxyPrincipalSpiffeUris: "", })) @@ -645,7 +645,7 @@ func TestOptionsWithAccessToken(t *testing.T) { Service: "api", Domain: "athenz.demo", Roles: []string{"*"}, - Expiry: DefaultTokenExpiry, + Expiry: sc.DefaultTokenExpiry, User: "nobody", ProxyPrincipalSpiffeUris: "", })) @@ -680,7 +680,7 @@ func TestInvalidAccessTokenDomain(t *testing.T) { } func TestGetRoleServiceOwner(t *testing.T) { - services := []Service{ + services := []sc.Service{ { Name: "svc1", }, @@ -897,6 +897,6 @@ func TestOptionsWithServiceOnlySetup(t *testing.T) { assert.Equal(t, opts.Name, "athenz.hockey") assert.Equal(t, opts.Profile, "test-profile") - assert.Equal(t, opts.Threshold, DefaultThreshold) - assert.Equal(t, opts.SshThreshold, DefaultThreshold) + assert.Equal(t, opts.Threshold, sc.DefaultThreshold) + assert.Equal(t, opts.SshThreshold, sc.DefaultThreshold) } diff --git a/libs/go/sia/aws/sds/creds.go b/libs/go/sia/aws/sds/creds.go deleted file mode 100644 index 8e694e235e5..00000000000 --- a/libs/go/sia/aws/sds/creds.go +++ /dev/null @@ -1,78 +0,0 @@ -// -// Copyright The Athenz Authors -// -// 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 sds - -import ( - "context" - "errors" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/peer" - "net" -) - -type grpcCredentials struct{} - -func NewCredentials() credentials.TransportCredentials { - return &grpcCredentials{} -} - -func AthenzGrpcServerName() string { - return "athenz-sia-sds" -} - -func (creds *grpcCredentials) ClientHandshake(_ context.Context, _ string, conn net.Conn) (net.Conn, credentials.AuthInfo, error) { - conn.Close() - return conn, ClientInfo{}, errors.New("client handshake not expected") -} - -func (creds *grpcCredentials) ServerHandshake(conn net.Conn) (net.Conn, credentials.AuthInfo, error) { - udsConn, ok := conn.(*UdsConn) - if !ok { - udsConn.Close() - return conn, ClientInfo{}, errors.New("connection type is not uds") - } - - return udsConn, udsConn.ClientInfo, nil -} - -func (creds *grpcCredentials) Info() credentials.ProtocolInfo { - return credentials.ProtocolInfo{ - SecurityProtocol: ClientAuthType(), - ServerName: AthenzGrpcServerName(), - } -} - -func (creds *grpcCredentials) Clone() credentials.TransportCredentials { - clone := *creds - return &clone -} - -func (creds *grpcCredentials) OverrideServerName(_ string) error { - return nil -} - -func ClientInfoFromContext(ctx context.Context) ClientInfo { - peer, ok := peer.FromContext(ctx) - if !ok { - return ClientInfo{} - } - clientInfo, ok := peer.AuthInfo.(ClientInfo) - if !ok { - return ClientInfo{} - } - return clientInfo -} diff --git a/libs/go/sia/aws/sds/data/athenz.api.cert.pem b/libs/go/sia/aws/sds/data/athenz.api.cert.pem deleted file mode 100644 index f2484ccf0d5..00000000000 --- a/libs/go/sia/aws/sds/data/athenz.api.cert.pem +++ /dev/null @@ -1 +0,0 @@ -x509-certificate \ No newline at end of file diff --git a/libs/go/sia/aws/sds/data/athenz.api.key.pem b/libs/go/sia/aws/sds/data/athenz.api.key.pem deleted file mode 100644 index b54c93f1ef8..00000000000 --- a/libs/go/sia/aws/sds/data/athenz.api.key.pem +++ /dev/null @@ -1 +0,0 @@ -private-key \ No newline at end of file diff --git a/libs/go/sia/aws/sds/data/ca.cert.pem b/libs/go/sia/aws/sds/data/ca.cert.pem deleted file mode 100644 index 73f609ad69b..00000000000 --- a/libs/go/sia/aws/sds/data/ca.cert.pem +++ /dev/null @@ -1 +0,0 @@ -ca-certs \ No newline at end of file diff --git a/libs/go/sia/aws/sds/handler.go b/libs/go/sia/aws/sds/handler.go deleted file mode 100644 index 94342043770..00000000000 --- a/libs/go/sia/aws/sds/handler.go +++ /dev/null @@ -1,386 +0,0 @@ -// -// Copyright The Athenz Authors -// -// 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 sds - -import ( - "context" - "errors" - "fmt" - "github.com/AthenZ/athenz/libs/go/sia/aws/options" - "github.com/AthenZ/athenz/libs/go/sia/util" - envoyCore "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - envoyTls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - envoyDiscovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - envoySecret "github.com/envoyproxy/go-control-plane/envoy/service/secret/v3" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/types/known/anypb" - "io" - "log" - "os" - "sync" -) - -type ServerHandler struct { - Mutex sync.RWMutex - Options *options.Options - Subscribers map[string]*Subscriber -} - -func NewServerHandler(opts *options.Options) *ServerHandler { - return &ServerHandler{ - Options: opts, - Subscribers: make(map[string]*Subscriber), - Mutex: sync.RWMutex{}, - } -} - -func (handler *ServerHandler) StreamSecrets(stream envoySecret.SecretDiscoveryService_StreamSecretsServer) error { - - sub := handler.subscribeToCertUpdates() - defer handler.removeSubscriber(sub) - - clientInfo := ClientInfoFromContext(stream.Context()) - log.Printf("StreamSecrets: %s: client info: %v\n", sub.GetId(), clientInfo) - - reqChan := make(chan *envoyDiscovery.DiscoveryRequest, 1) - errChan := make(chan error, 1) - - go func() { - for { - req, err := stream.Recv() - if err != nil { - log.Printf("StreamSecrets: %s: receiving error: %v\n", sub.GetId(), err) - if status.Code(err) == codes.Canceled || errors.Is(err, io.EOF) { - log.Printf("StreamSecrets: %s: resetting error...\n", sub.GetId()) - err = nil - } - errChan <- err - return - } - reqChan <- req - } - }() - - var curReq *envoyDiscovery.DiscoveryRequest - for { - select { - case newReq := <-reqChan: - - if curReq == nil { - log.Printf("StreamSecrets: %s: processing new request\n", sub.GetId()) - } else { - log.Printf("StreamSecrets: %s: processing request: version: %s, nonce: %s\n", sub.GetId(), newReq.GetVersionInfo(), newReq.GetResponseNonce()) - } - - // if envoy reported any errors then we're just going to log them, - // but we won't stop processing any requests or close connections - if newReq.ErrorDetail != nil { - log.Printf("StreamSecrets: %s: envoy reported error: %s\n", sub.GetId(), newReq.ErrorDetail.Message) - } - - // validate the request nonce and if mismatch, ignore the request - if !sub.ValidateResponseNonce(newReq.ResponseNonce) { - continue - } - - // validate version information - if !sub.ValidateVersionInfo(newReq.VersionInfo) { - continue - } - - // after the first request we should only process the requests - // if the list of requested resource names has changed - if curReq != nil && !resourceNamesChanged(curReq.ResourceNames, newReq.ResourceNames) { - continue - } - - // set and process the new request - curReq = newReq - - case <-sub.GetCertUpdates(): - sub.IncrementVersion() - // in case we receive an update before an actual request is processed - // we should ignore the update - if curReq == nil { - continue - } - log.Printf("StreamSecrets: %s: pushing updated certificates to envoy...\n", sub.GetId()) - - case err := <-errChan: - return err - } - - resp, err := handler.getStreamResponse(sub, clientInfo, curReq) - if err != nil { - log.Printf("StreamSecrets: %s: unable to generate stream response: %v\n", sub.GetId(), err) - return err - } - - if err := stream.Send(resp); err != nil { - log.Printf("StreamSecrets: %s: secret send error: %v\n", sub.GetId(), err) - return err - } - - // update the last nonce successfully sent to client - sub.SetResponseNonce(resp.GetNonce()) - } -} - -func (handler *ServerHandler) DeltaSecrets(envoySecret.SecretDiscoveryService_DeltaSecretsServer) error { - return status.Error(codes.Unimplemented, "Unimplemented Method") -} - -func (handler *ServerHandler) FetchSecrets(ctx context.Context, req *envoyDiscovery.DiscoveryRequest) (*envoyDiscovery.DiscoveryResponse, error) { - - clientInfo := ClientInfoFromContext(ctx) - log.Printf("FetchSecrets: client info: %v\n", clientInfo) - - resp, err := handler.getFetchResponse(clientInfo, req) - if err != nil { - log.Printf("FetchSecrets: unable to generate response: %v\n", err) - return nil, err - } - - return resp, nil -} - -func resourceNamesChanged(currentResources []string, newResources []string) bool { - // if the length of arrays are different, then we know there is a change - if len(currentResources) != len(newResources) { - return true - } - // typically, we'll only get a small number of resources, - // but we'll use a map/set to see if there is a change - var resourceMap = make(map[string]bool) - for _, resource := range currentResources { - resourceMap[resource] = true - } - for _, resource := range newResources { - if !resourceMap[resource] { - return true - } - } - return false -} - -func (handler *ServerHandler) getFetchResponse(info ClientInfo, req *envoyDiscovery.DiscoveryRequest) (*envoyDiscovery.DiscoveryResponse, error) { - - resp := &envoyDiscovery.DiscoveryResponse{ - TypeUrl: req.TypeUrl, - } - - err := handler.getResponse(req, info, "", resp) - if err != nil { - return nil, err - } - return resp, nil -} - -func (handler *ServerHandler) getStreamResponse(sub *Subscriber, info ClientInfo, req *envoyDiscovery.DiscoveryRequest) (*envoyDiscovery.DiscoveryResponse, error) { - - resp := &envoyDiscovery.DiscoveryResponse{ - TypeUrl: req.TypeUrl, - VersionInfo: sub.GetVersionInfo(), - } - - // provide a nonce for streaming requests - var err error - if resp.Nonce, err = util.Nonce(); err != nil { - return nil, err - } - - err = handler.getResponse(req, info, sub.GetId(), resp) - if err != nil { - return nil, err - } - return resp, nil -} - -func (handler *ServerHandler) getResponse(req *envoyDiscovery.DiscoveryRequest, info ClientInfo, subId string, resp *envoyDiscovery.DiscoveryResponse) error { - - for _, resource := range req.ResourceNames { - log.Printf("Response: %s: requesting secret: %s\n", subId, resource) - } - - // parse the requested resource name - for _, spiffeUri := range req.ResourceNames { - // let's check if this is a CA Bundle certificate spiffe uri - _, namespace, name := util.ParseCASpiffeUri(spiffeUri) - if namespace != "" && name != "" { - tlsCABundle, err := handler.getTLSCABundleSecret(spiffeUri, namespace, name) - if err != nil { - return err - } - resp.Resources = append(resp.Resources, tlsCABundle) - } else { - _, _, domain, service := util.ParseServiceSpiffeUri(spiffeUri) - if domain == "" || service == "" { - log.Printf("Response: %s: unable to parse spiffe uri: %s\n", subId, spiffeUri) - continue - } - // authenticate the request - svc, err := handler.authenticateRequest(info, req.GetNode(), domain, service) - if err != nil { - log.Printf("Response: %s: unable to authenticate the request: %v\n", subId, err) - continue - } - tlsCertificate, err := handler.getTLSCertificateSecret(spiffeUri, svc) - if err != nil { - log.Printf("Response: %s: unable to build envoyTls certificate: %v\n", subId, err) - continue - } - resp.Resources = append(resp.Resources, tlsCertificate) - } - } - return nil -} - -func (handler *ServerHandler) subscribeToCertUpdates() *Subscriber { - - handler.Mutex.Lock() - subscriber := NewSubscriber() - handler.Subscribers[subscriber.GetId()] = subscriber - handler.Mutex.Unlock() - - log.Printf("Subscription: %s: registering new subscriber\n", subscriber.GetId()) - return subscriber -} - -func (handler *ServerHandler) removeSubscriber(subscriber *Subscriber) { - - log.Printf("Subscription: %s: removing subscriber\n", subscriber.GetId()) - - handler.Mutex.Lock() - delete(handler.Subscribers, subscriber.GetId()) - defer handler.Mutex.Unlock() - - subscriber.Close() -} - -func (handler *ServerHandler) NotifySubscribers() { - - handler.Mutex.RLock() - for _, subscriber := range handler.Subscribers { - subscriber.Notify() - } - handler.Mutex.RUnlock() -} - -func (handler *ServerHandler) authenticateRequest(info ClientInfo, node *envoyCore.Node, domain, service string) (*options.Service, error) { - - if domain != handler.Options.Domain { - return nil, fmt.Errorf("invalid domain name: %s, expected: %s", domain, handler.Options.Domain) - } - for _, svc := range handler.Options.Services { - if svc.Name == service { - if svc.SDSUdsUid != info.UserID { - return nil, fmt.Errorf("invalid uid: %d, expected: %d", info.UserID, svc.SDSUdsUid) - } - nodeId := "" - if node != nil { - nodeId = node.GetId() - } - if svc.SDSNodeId != "" && svc.SDSNodeId != nodeId { - return nil, fmt.Errorf("invalid node id: %s, expected: %s", nodeId, svc.SDSNodeId) - } - nodeCluster := "" - if node != nil { - nodeCluster = node.GetCluster() - } - if svc.SDSNodeCluster != "" && svc.SDSNodeCluster != nodeCluster { - return nil, fmt.Errorf("invalid node cluster: %s, expected: %s", nodeCluster, svc.SDSNodeCluster) - } - return &svc, nil - } - } - return nil, fmt.Errorf("unknown service: %s", service) -} - -func (handler *ServerHandler) getTLSCertificateSecret(spiffeUri string, svc *options.Service) (*anypb.Any, error) { - - keyFile := util.GetSvcKeyFileName(handler.Options.KeyDir, svc.KeyFilename, handler.Options.Domain, svc.Name) - keyPEM, err := os.ReadFile(keyFile) - if err != nil { - return nil, err - } - - certFile := util.GetSvcCertFileName(handler.Options.CertDir, svc.CertFilename, handler.Options.Domain, svc.Name) - certPEM, err := os.ReadFile(certFile) - if err != nil { - return nil, err - } - - return anypb.New(&envoyTls.Secret{ - Name: spiffeUri, - Type: &envoyTls.Secret_TlsCertificate{ - TlsCertificate: &envoyTls.TlsCertificate{ - CertificateChain: &envoyCore.DataSource{ - Specifier: &envoyCore.DataSource_InlineBytes{ - InlineBytes: certPEM, - }, - }, - PrivateKey: &envoyCore.DataSource{ - Specifier: &envoyCore.DataSource_InlineBytes{ - InlineBytes: keyPEM, - }, - }, - }, - }, - }) -} - -func (handler *ServerHandler) getTLSCABundleSecret(spiffeUri, caNamespace, caName string) (*anypb.Any, error) { - - //we support a single namespace athenz with bundle name default - if caNamespace != "athenz" || caName != "default" { - return nil, fmt.Errorf("unknown TLS CA Bundle: %s\n", spiffeUri) - } - caCertsPEM, err := os.ReadFile(handler.Options.AthenzCACertFile) - if err != nil { - return nil, err - } - configTrustDomains := []*envoyTls.SPIFFECertValidatorConfig_TrustDomain{ - { - Name: caName, - TrustBundle: &envoyCore.DataSource{ - Specifier: &envoyCore.DataSource_InlineBytes{ - InlineBytes: caCertsPEM, - }, - }, - }, - } - - typedConfig, err := anypb.New(&envoyTls.SPIFFECertValidatorConfig{ - TrustDomains: configTrustDomains, - }) - if err != nil { - return nil, err - } - - return anypb.New(&envoyTls.Secret{ - Name: spiffeUri, - Type: &envoyTls.Secret_ValidationContext{ - ValidationContext: &envoyTls.CertificateValidationContext{ - CustomValidatorConfig: &envoyCore.TypedExtensionConfig{ - Name: "envoy.tls.cert_validator.spiffe", - TypedConfig: typedConfig, - }, - }, - }, - }) -} diff --git a/libs/go/sia/aws/sds/handler_test.go b/libs/go/sia/aws/sds/handler_test.go deleted file mode 100644 index b13d6a55fdd..00000000000 --- a/libs/go/sia/aws/sds/handler_test.go +++ /dev/null @@ -1,279 +0,0 @@ -// -// Copyright The Athenz Authors -// -// 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 sds - -import ( - "github.com/AthenZ/athenz/libs/go/sia/aws/options" - envoyCore "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - envoyDiscovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - "strings" - "testing" -) - -func TestResourceNamesChanged(test *testing.T) { - curList := []string{"abc", "bcd", "cde"} - newList := []string{"abc", "bcd", "cde"} - if resourceNamesChanged(curList, newList) { - test.Errorf("identical resource lists marked as changed") - } - - curList = []string{"abc", "bcd", "cde"} - newList = []string{"abc", "bcd"} - if !resourceNamesChanged(curList, newList) { - test.Errorf("different size resource lists marked as not changed") - } - - curList = []string{"abc", "bcd", "cde"} - newList = []string{"abc", "bcd", "def"} - if !resourceNamesChanged(curList, newList) { - test.Errorf("different value / same size resource lists marked as not changed") - } -} - -func TestGetResponseInvalidUri(test *testing.T) { - handler := NewServerHandler(&options.Options{}) - req := envoyDiscovery.DiscoveryRequest{ - ResourceNames: []string{"spiffe://athenz/invalid"}, - } - resp := envoyDiscovery.DiscoveryResponse{} - err := handler.getResponse(&req, ClientInfo{}, "", &resp) - if err != nil { - test.Errorf("unexpected error returned: %v", err) - } - if len(resp.Resources) != 0 { - test.Errorf("unexpected response objects created") - } -} - -func TestSubscriptionChanges(test *testing.T) { - handler := NewServerHandler(&options.Options{}) - if len(handler.Subscribers) != 0 { - test.Errorf("new handler has some subscribers") - } - sub := handler.subscribeToCertUpdates() - if handler.Subscribers[sub.GetId()] == nil { - test.Errorf("new subscriber is not in the list") - } - id := sub.GetId() - handler.removeSubscriber(sub) - if handler.Subscribers[id] != nil { - test.Errorf("removed subscriber is still in the list") - } -} - -func TestSubscriptionNotifications(test *testing.T) { - handler := NewServerHandler(&options.Options{}) - sub := handler.subscribeToCertUpdates() - handler.NotifySubscribers() - updates := <-sub.GetCertUpdates() - if !updates { - test.Errorf("subscriber update flag is not set correctly") - } - handler.removeSubscriber(sub) -} - -func TestAuthenticateRequestMismatchDomain(test *testing.T) { - handler := NewServerHandler(&options.Options{ - Domain: "athenz", - }) - _, err := handler.authenticateRequest(ClientInfo{}, nil, "sports", "api") - if err == nil { - test.Errorf("invalid domain was correctly authenticated") - } - if !strings.Contains(err.Error(), "invalid domain name") { - test.Errorf("error does not include expected invalid domain message: %s", err.Error()) - } -} - -func TestAuthenticateRequestUnknownService(test *testing.T) { - handler := NewServerHandler(&options.Options{ - Domain: "athenz", - Services: []options.Service{{Name: "backend"}}, - }) - _, err := handler.authenticateRequest(ClientInfo{}, nil, "athenz", "api") - if err == nil { - test.Errorf("unknown service was correctly authenticated") - } - if !strings.Contains(err.Error(), "unknown service") { - test.Errorf("error does not include expected unknown service message: %s", err.Error()) - } -} - -func TestAuthenticateRequestMismatchUid(test *testing.T) { - handler := NewServerHandler(&options.Options{ - Domain: "athenz", - Services: []options.Service{{Name: "api", SDSUdsUid: 124}}, - }) - _, err := handler.authenticateRequest(ClientInfo{UserID: 123}, nil, "athenz", "api") - if err == nil { - test.Errorf("mismatched uid was correctly authenticated") - } - if !strings.Contains(err.Error(), "invalid uid") { - test.Errorf("error does not include expected invalid uid message: %s", err.Error()) - } -} - -func TestAuthenticateRequestMismatchNodeId(test *testing.T) { - handler := NewServerHandler(&options.Options{ - Domain: "athenz", - Services: []options.Service{{Name: "api", SDSUdsUid: 123, SDSNodeId: "id1"}}, - }) - // first try with nil node - _, err := handler.authenticateRequest(ClientInfo{UserID: 123}, nil, "athenz", "api") - if err == nil { - test.Errorf("mismatched node id was correctly authenticated") - } - if !strings.Contains(err.Error(), "invalid node id: ,") { - test.Errorf("error does not include expected invalid node id message: %s", err.Error()) - } - // now with a node with mismatched nodeid - _, err = handler.authenticateRequest(ClientInfo{UserID: 123}, &envoyCore.Node{Id: "id2"}, "athenz", "api") - if err == nil { - test.Errorf("mismatched node id was correctly authenticated") - } - if !strings.Contains(err.Error(), "invalid node id: id2,") { - test.Errorf("error does not include expected invalid node id message: %s", err.Error()) - } -} - -func TestAuthenticateRequestMismatchNodeCluster(test *testing.T) { - handler := NewServerHandler(&options.Options{ - Domain: "athenz", - Services: []options.Service{{Name: "api", SDSUdsUid: 123, SDSNodeCluster: "cluster1"}}, - }) - // first try with nil node - _, err := handler.authenticateRequest(ClientInfo{UserID: 123}, nil, "athenz", "api") - if err == nil { - test.Errorf("mismatched node cluster was correctly authenticated") - } - if !strings.Contains(err.Error(), "invalid node cluster: ,") { - test.Errorf("error does not include expected invalid node cluster message: %s", err.Error()) - } - // now with a node with mismatched node cluster - _, err = handler.authenticateRequest(ClientInfo{UserID: 123}, &envoyCore.Node{Id: "id1", Cluster: "cluster2"}, "athenz", "api") - if err == nil { - test.Errorf("mismatched node cluster was correctly authenticated") - } - if !strings.Contains(err.Error(), "invalid node cluster: cluster2,") { - test.Errorf("error does not include expected invalid node cluster message: %s", err.Error()) - } -} - -func TestAuthenticateRequestMatchNilNode(test *testing.T) { - handler := NewServerHandler(&options.Options{ - Domain: "athenz", - Services: []options.Service{{Name: "api", SDSUdsUid: 123}}, - }) - _, err := handler.authenticateRequest(ClientInfo{UserID: 123}, nil, "athenz", "api") - if err != nil { - test.Errorf("valid requested was not correctly authenticated: %v", err) - } -} - -func TestAuthenticateRequestMatchWithNode(test *testing.T) { - handler := NewServerHandler(&options.Options{ - Domain: "athenz", - Services: []options.Service{{Name: "api", SDSUdsUid: 123, SDSNodeId: "id1", SDSNodeCluster: "cluster1"}}, - }) - _, err := handler.authenticateRequest(ClientInfo{UserID: 123}, &envoyCore.Node{Id: "id1", Cluster: "cluster1"}, "athenz", "api") - if err != nil { - test.Errorf("valid requested was not correctly authenticated: %v", err) - } -} - -func TestGetTLSCertificateSecretUnknownPrivateKey(test *testing.T) { - handler := NewServerHandler(&options.Options{ - KeyDir: "data", - CertDir: "data", - Domain: "athenz", - }) - svc := options.Service{ - Name: "unknown", - } - _, err := handler.getTLSCertificateSecret("spiffe://athenz/sa/api", &svc) - if err == nil { - test.Errorf("was able to generate certificate for unknown key file") - } -} - -func TestGetTLSCertificateSecretUnknownCertificate(test *testing.T) { - handler := NewServerHandler(&options.Options{ - KeyDir: "data", - CertDir: "data", - Domain: "athenz", - }) - svc := options.Service{ - Name: "api", - KeyFilename: "unknown", - CertFilename: "unknown", - } - _, err := handler.getTLSCertificateSecret("spiffe://athenz/sa/api", &svc) - if err == nil { - test.Errorf("was able to generate certificate for unknown cert file") - } -} - -func TestGetTLSCertificateSecret(test *testing.T) { - handler := NewServerHandler(&options.Options{ - KeyDir: "data", - CertDir: "data", - Domain: "athenz", - }) - svc := options.Service{ - Name: "api", - } - _, err := handler.getTLSCertificateSecret("spiffe://athenz/sa/api", &svc) - if err != nil { - test.Errorf("unable to generate certificate secret: %v", err) - } -} - -func TestGetTLSCABundleSecretInvalidNamespace(test *testing.T) { - handler := NewServerHandler(&options.Options{}) - _, err := handler.getTLSCABundleSecret("spiffe://athenz/ca/default", "coretech", "default") - if err == nil { - test.Errorf("certificate generated for invalid namespace") - } -} - -func TestGetTLSCABundleSecretInvalidName(test *testing.T) { - handler := NewServerHandler(&options.Options{}) - _, err := handler.getTLSCABundleSecret("spiffe://athenz/ca/default", "athenz", "primary") - if err == nil { - test.Errorf("certificate generated for invalid name") - } -} - -func TestGetTLSCABundleSecretInvalidFile(test *testing.T) { - handler := NewServerHandler(&options.Options{ - AthenzCACertFile: "unknown-file", - }) - _, err := handler.getTLSCABundleSecret("spiffe://athenz/ca/default", "athenz", "default") - if err == nil { - test.Errorf("certificate generated for invalid filename") - } -} - -func TestGetTLSCABundleSecret(test *testing.T) { - handler := NewServerHandler(&options.Options{ - AthenzCACertFile: "data/ca.cert.pem", - }) - _, err := handler.getTLSCABundleSecret("spiffe://athenz/ca/default", "athenz", "default") - if err != nil { - test.Errorf("unable to generate valid bundle: %v", err) - } -} diff --git a/libs/go/sia/aws/sds/info.go b/libs/go/sia/aws/sds/info.go deleted file mode 100644 index ad671c06e28..00000000000 --- a/libs/go/sia/aws/sds/info.go +++ /dev/null @@ -1,30 +0,0 @@ -// -// Copyright The Athenz Authors -// -// 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 sds - -type ClientInfo struct { - UserID int - PID int -} - -func (ClientInfo) AuthType() string { - return ClientAuthType() -} - -func ClientAuthType() string { - return "athenz-uds" -} diff --git a/libs/go/sia/aws/sds/info_test.go b/libs/go/sia/aws/sds/info_test.go deleted file mode 100644 index 14901a47b09..00000000000 --- a/libs/go/sia/aws/sds/info_test.go +++ /dev/null @@ -1,36 +0,0 @@ -// -// Copyright The Athenz Authors -// -// 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 sds - -import "testing" - -func TestAuthType(test *testing.T) { - info := ClientInfo{ - UserID: 101, - PID: 102, - } - - if info.AuthType() != "athenz-uds" { - test.Errorf("Unexpected auth-type: '%s', expected 'athenz-uds'", info.AuthType()) - } -} - -func TestClientAuthType(test *testing.T) { - if ClientAuthType() != "athenz-uds" { - test.Errorf("Unexpected client-auth-type: '%s', expected 'athenz-uds'", ClientAuthType()) - } -} diff --git a/libs/go/sia/aws/sds/sds.go b/libs/go/sia/aws/sds/sds.go deleted file mode 100644 index dbf57487892..00000000000 --- a/libs/go/sia/aws/sds/sds.go +++ /dev/null @@ -1,73 +0,0 @@ -// -// Copyright The Athenz Authors -// -// 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 sds - -import ( - "errors" - "fmt" - "github.com/AthenZ/athenz/libs/go/sia/aws/options" - envoySecret "github.com/envoyproxy/go-control-plane/envoy/service/secret/v3" - "google.golang.org/grpc" - "log" - "os" -) - -func StartGrpcServer(opts *options.Options, certUpdates chan bool) error { - - listener, err := StartUdsListener(opts.SDSUdsPath) - if err != nil { - return fmt.Errorf("unable to start uds listener for %s, error: %v", opts.SDSUdsPath, err) - } - defer listener.Close() - - grpcServer := grpc.NewServer( - grpc.Creds(NewCredentials()), - ) - - serverHandler := NewServerHandler(opts) - go notifyCertificateUpdates(serverHandler, certUpdates) - - envoySecret.RegisterSecretDiscoveryServiceServer(grpcServer, serverHandler) - - errChan := make(chan error) - go func() { - errChan <- grpcServer.Serve(listener) - }() - - select { - case err = <-errChan: - log.Println("Stopping GRPC SDS server...") - grpcServer.Stop() - if _, err := os.Stat(opts.SDSUdsPath); err == nil { - os.Remove(opts.SDSUdsPath) - } - if errors.Is(err, grpc.ErrServerStopped) { - err = nil - } - } - - return err -} - -func notifyCertificateUpdates(serverHandler *ServerHandler, updates <-chan bool) { - for { - select { - case <-updates: - serverHandler.NotifySubscribers() - } - } -} diff --git a/libs/go/sia/aws/sds/subscriber.go b/libs/go/sia/aws/sds/subscriber.go deleted file mode 100644 index ea7e4921735..00000000000 --- a/libs/go/sia/aws/sds/subscriber.go +++ /dev/null @@ -1,80 +0,0 @@ -// -// Copyright The Athenz Authors -// -// 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 sds - -import ( - "github.com/google/uuid" - "log" - "strconv" -) - -type Subscriber struct { - id string - certUpdChan chan bool - responseNonce string - versionNumber int -} - -func NewSubscriber() *Subscriber { - return &Subscriber{ - id: uuid.New().String(), - certUpdChan: make(chan bool, 1), - } -} - -func (subscriber *Subscriber) Close() { - close(subscriber.certUpdChan) -} - -func (subscriber *Subscriber) GetId() string { - return subscriber.id -} - -func (subscriber *Subscriber) ValidateResponseNonce(responseNonce string) bool { - if subscriber.responseNonce != "" && subscriber.responseNonce != responseNonce { - log.Printf("ValidateResponseNonce: %s: nonce mismatch: subscriber: %s, request: %s\n", subscriber.id, subscriber.responseNonce, responseNonce) - return false - } - return true -} - -func (subscriber *Subscriber) ValidateVersionInfo(versionInfo string) bool { - if subscriber.versionNumber != 0 && strconv.Itoa(subscriber.versionNumber) != versionInfo { - log.Printf("ValidateVersionInfo: %s: version info mismatch: subscriber: %d, request: %s\n", subscriber.id, subscriber.versionNumber, versionInfo) - } - return true -} - -func (subscriber *Subscriber) IncrementVersion() { - subscriber.versionNumber++ -} - -func (subscriber *Subscriber) GetVersionInfo() string { - return strconv.Itoa(subscriber.versionNumber) -} - -func (subscriber *Subscriber) SetResponseNonce(nonce string) { - subscriber.responseNonce = nonce -} - -func (subscriber *Subscriber) GetCertUpdates() chan bool { - return subscriber.certUpdChan -} - -func (subscriber *Subscriber) Notify() { - subscriber.certUpdChan <- true -} diff --git a/libs/go/sia/aws/sds/subscriber_test.go b/libs/go/sia/aws/sds/subscriber_test.go deleted file mode 100644 index 44f65bae75d..00000000000 --- a/libs/go/sia/aws/sds/subscriber_test.go +++ /dev/null @@ -1,68 +0,0 @@ -// -// Copyright The Athenz Authors -// -// 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 sds - -import ( - "testing" -) - -func TestNewSubscriber(test *testing.T) { - sub := NewSubscriber() - if sub.GetId() == "" { - test.Errorf("new subscriber does not have id") - } - if sub.certUpdChan == nil { - test.Errorf("new subscriber does not update channel") - } - sub.Close() -} - -func TestSubscriberNonce(test *testing.T) { - sub := NewSubscriber() - if sub.responseNonce != "" { - test.Errorf("new subscriber has an expected nonce: %s", sub.responseNonce) - } - //without the nonce we should all nonce responses - if !sub.ValidateResponseNonce("abc") { - test.Errorf("subscriber empty nonce value was not validated as expected") - } - sub.SetResponseNonce("abcd") - if sub.responseNonce != "abcd" { - test.Errorf("subscriber does not have expected 'abcd' nonce: %s", sub.responseNonce) - } - if !sub.ValidateResponseNonce("abcd") { - test.Errorf("subscriber 'abcd' nonce value was not validated as expected") - } - if sub.ValidateResponseNonce("xyz") { - test.Errorf("subscriber mismatched 'xyz' nonce value was validated incorrectly") - } - sub.Close() -} - -func TestSubscriberVersionInfo(test *testing.T) { - sub := NewSubscriber() - if sub.GetVersionInfo() != "0" { - test.Errorf("new subscriber does not have an expected version info of 0: %s", sub.GetVersionInfo()) - } - //for now validate does not return any failures, just logs - sub.ValidateVersionInfo("0") - sub.IncrementVersion() - if sub.GetVersionInfo() != "1" { - test.Errorf("new subscriber does not have an expected version info of 1: %s", sub.GetVersionInfo()) - } - sub.Close() -} diff --git a/libs/go/sia/aws/sds/uds.go b/libs/go/sia/aws/sds/uds.go deleted file mode 100644 index 843cb42629c..00000000000 --- a/libs/go/sia/aws/sds/uds.go +++ /dev/null @@ -1,120 +0,0 @@ -// -// Copyright The Athenz Authors -// -// 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 sds - -import ( - "fmt" - "log" - "net" - "os" - "path/filepath" - "strconv" - - "github.com/tailscale/peercred" -) - -type Listener struct { - net.Listener -} - -type UdsConn struct { - net.Conn - ClientInfo ClientInfo -} - -// StartUdsListener Start a Unix-Domain-Socket listener. We're going to create a simple -// wrapper struct for the Listener object since we want to intercept Accept calls -// and extract the caller's user and process ids. The client info object will then -// be passed to grpc as credentials.AuthInfo which can be accessed later from the -// stream context -func StartUdsListener(udsPath string) (net.Listener, error) { - - err := os.MkdirAll(filepath.Dir(udsPath), 0755) - if err != nil { - return nil, fmt.Errorf("failed to make the directory for the Unix-Domain-Socket listener on path %q: %v", udsPath, err) - } - - // Prepare: delete any existing Unix-Domain-Socket. - os.Remove(udsPath) - - // Listen on the Unix-Domain-Socket. - udsListener, err := net.Listen("unix", udsPath) - if err != nil { - return nil, fmt.Errorf("failed to listed to Unix-Domain-Socket listener on path %q: %v", udsPath, err) - } - - err = os.Chmod(udsPath, os.ModePerm) - if err != nil { - udsListener.Close() - return nil, fmt.Errorf("failed to set permissions to Unix-Domain-Socket on path %q: %v", udsPath, err) - } - - // Server is ready to accept UDS requests. - log.Printf("Unix-Domain-Socket listener is ready on path: %s\n", udsPath) - return &Listener{ - Listener: udsListener, - }, nil -} - -func (listener *Listener) Accept() (net.Conn, error) { - for { - conn, err := listener.Listener.Accept() - if err != nil { - return conn, err - } - return &UdsConn{ - Conn: conn, - ClientInfo: getUdsUserDetails(conn), - }, nil - } -} - -func (listener *Listener) Close() error { - return listener.Listener.Close() -} - -func (listener *Listener) Addr() net.Addr { - return listener.Listener.Addr() -} - -// Get the Unix-Domain-Socket's user and process ids -func getUdsUserDetails(connection net.Conn) ClientInfo { - - var clientInfo ClientInfo - - // Get uid from UDS connection. - credentials, err := peercred.Get(connection) - if err != nil { - return clientInfo - } - userId, ok := credentials.UserID() - if !ok { - log.Println("unable to obtain connection user id") - } else { - uid, err := strconv.Atoi(userId) - if err != nil { - log.Printf("unable to obtain convert user id: %s, %v\n", userId, err) - } else { - clientInfo.UserID = uid - } - } - clientInfo.PID, ok = credentials.PID() - if !ok { - log.Println("unable to obtain connection pid") - } - return clientInfo -} diff --git a/libs/go/sia/config/config.go b/libs/go/sia/config/config.go new file mode 100644 index 00000000000..486ed3ce574 --- /dev/null +++ b/libs/go/sia/config/config.go @@ -0,0 +1,228 @@ +// +// Copyright The Athenz Authors +// +// 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 config + +import ( + "time" + + ac "github.com/AthenZ/athenz/libs/go/sia/access/config" + "github.com/AthenZ/athenz/libs/go/sia/host/provider" + "github.com/AthenZ/athenz/libs/go/sia/ssh/hostkey" +) + +// package options contains types for parsing sia_config file and options to carry those config values + +// ConfigService represents a service to be specified by user, and specify User/Group attributes for the service +type ConfigService struct { + KeyFilename string `json:"key_filename,omitempty"` + CertFilename string `json:"cert_filename,omitempty"` + User string `json:"user,omitempty"` + Group string `json:"group,omitempty"` + ExpiryTime int `json:"expiry_time,omitempty"` + SDSUdsUid int `json:"sds_uds_uid,omitempty"` + SDSNodeId string `json:"sds_node_id,omitempty"` + SDSNodeCluster string `json:"sds_node_cluster,omitempty"` + Threshold float64 `json:"cert_threshold_to_check,omitempty"` +} + +// ConfigRole represents a role to be specified by user, and specify attributes for the role +type ConfigRole struct { + Filename string `json:"filename,omitempty"` //filename for the generated role certificate file + ExpiryTime int `json:"expiry_time,omitempty"` //requested expiry time for the role certificate + Service string `json:"service,omitempty"` //principal with role access + User string `json:"user,omitempty"` //user owner on the role identity key + Group string `json:"group,omitempty"` //group owner on the role identity key + Threshold float64 `json:"cert_threshold_to_check,omitempty"` +} + +// ConfigAccount represents each of the accounts that can be specified in the config file +type ConfigAccount struct { + Name string `json:"name,omitempty"` //name of the service identity + User string `json:"user,omitempty"` //the username to chown the cert/key dirs to. If absent, then root. + Group string `json:"group,omitempty"` //the group name to chown the cert/key dirs to. If absent, then athenz. + Domain string `json:"domain,omitempty"` //name of the domain for the identity + Account string `json:"account,omitempty"` //name of the account + Service string `json:"service,omitempty"` //name of the service for the identity + Zts string `json:"zts,omitempty"` //the ZTS to contact + Roles map[string]ConfigRole `json:"roles,omitempty"` //map of roles to retrieve certificates for + Version string `json:"version,omitempty"` //sia version number + Threshold float64 `json:"cert_threshold_to_check,omitempty"` //Threshold to verify for all certs + SshThreshold float64 `json:"sshcert_threshold_to_check,omitempty"` //Threshold to verify for ssh certs + OmitDomain bool `json:"omit_domain,omitempty"` //attestation role only includes service name +} + +// Config represents entire sia_config file +type Config struct { + Version string `json:"version,omitempty"` //config version + Domain string `json:"domain,omitempty"` //name of the domain for the identity + Service string `json:"service,omitempty"` //name of the service for the identity + Services map[string]ConfigService `json:"services,omitempty"` //names of the multiple services for the identity + Ssh *bool `json:"ssh,omitempty"` //ssh certificate support + SshHostKeyType hostkey.KeyType `json:"ssh_host_key_type,omitempty"` //ssh host key type - rsa, ecdsa, etc + SshPrincipals string `json:"ssh_principals,omitempty"` //ssh additional principals + SanDnsWildcard bool `json:"sandns_wildcard,omitempty"` //san dns wildcard support + SanDnsHostname bool `json:"sandns_hostname,omitempty"` //san dns hostname support + SanDnsX509Cnames string `json:"sandns_x509_cnames,omitempty"` //additional san dns entries to be added to the CSR + UseRegionalSTS bool `json:"regionalsts,omitempty"` //whether to use a regional STS endpoint (default is false) + Account string `json:"aws_account,omitempty"` //name of the AWS account for the identity ( only applicable in AWS environment ) + Accounts []ConfigAccount `json:"accounts,omitempty"` //array of configured accounts ( kept for backward compatibility sake ) + GenerateRoleKey bool `json:"generate_role_key,omitempty"` //private key to be generated for role certificate + RotateKey bool `json:"rotate_key,omitempty"` //rotate private key support + User string `json:"user,omitempty"` //the username to chown the cert/key dirs to. If absent, then root + Group string `json:"group,omitempty"` //the group name to chown the cert/key dirs to. If absent, then athenz + SDSUdsPath string `json:"sds_uds_path,omitempty"` //uds path if the agent should support uds connections + SDSUdsUid int `json:"sds_uds_uid,omitempty"` //uds connections must be from the given user uid + ExpiryTime int `json:"expiry_time,omitempty"` //service and role certificate expiry in minutes + RefreshInterval int `json:"refresh_interval,omitempty"` //specifies refresh interval in minutes + ZTSRegion string `json:"zts_region,omitempty"` //specifies zts region for the requests + DropPrivileges bool `json:"drop_privileges,omitempty"` //drop privileges to configured user instead of running as root + AccessTokens map[string]ac.Role `json:"access_tokens,omitempty"` //map of role name to token attributes + FileDirectUpdate bool `json:"file_direct_update,omitempty"` //update key/cert files directly instead of using rename + SiaKeyDir string `json:"sia_key_dir,omitempty"` //sia keys directory to override /var/lib/sia/keys + SiaCertDir string `json:"sia_cert_dir,omitempty"` //sia certs directory to override /var/lib/sia/certs + SiaTokenDir string `json:"sia_token_dir,omitempty"` //sia tokens directory to override /var/lib/sia/tokens + SiaBackupDir string `json:"sia_backup_dir,omitempty"` //sia backup directory to override /var/lib/sia/backup + HostnameSuffix string `json:"hostname_suffix,omitempty"` //hostname suffix in case we need to auto-generate hostname + Zts string `json:"zts,omitempty"` //the ZTS to contact + Roles map[string]ConfigRole `json:"roles,omitempty"` //map of roles to retrieve certificates for + Threshold float64 `json:"cert_threshold_to_check,omitempty"` //threshold to verify for all certs + SshThreshold float64 `json:"sshcert_threshold_to_check,omitempty"` //threshold to verify for ssh certs + AccessManagement bool `json:"access_management,omitempty"` //access management support + FailCountForExit int `json:"fail_count_for_exit,omitempty"` //number of failed counts before exiting program + RunAfterCerts string `json:"run_after,omitempty"` //execute the command mentioned after certs are created + RunAfterCertsErr string `json:"run_after_certs_err,omitempty"` //execute the command mentioned after role certs fail to refresh + RunAfterTokens string `json:"run_after_tokens,omitempty"` //execute the command mentioned after tokens are created + RunAfterTokensErr string `json:"run_after_tokens_err,omitempty"` //execute the command mentioned after tokens fail to refresh + SpiffeTrustDomain string `json:"spiffe_trust_domain,omitempty"` //spiffe trust domain - if configured generate full spiffe uri with namespace + StoreTokenOption *int `json:"store_token_option,omitempty"` //store access token option + RunAfterFailExit bool `json:"run_after_fail_exit,omitempty"` //exit process if run_after script fails +} + +type AccessProfileConfig struct { + Profile string `json:"profile,omitempty"` + ProfileRestrictTo string `json:"profile_restrict_to,omitempty"` +} + +// Role contains role details. Attributes are set based on the config values +type Role struct { + Name string + Service string + SvcKeyFilename string + SvcCertFilename string + ExpiryTime int + RoleCertFilename string + RoleKeyFilename string + User string + Uid int + Gid int + FileMode int + Threshold float64 +} + +// Service represents service details. Attributes are filled in based on the config values +type Service struct { + Name string + KeyFilename string + CertFilename string + User string + Group string + Uid int + Gid int + FileMode int + ExpiryTime int + SDSUdsUid int + SDSNodeId string + SDSNodeCluster string + Threshold float64 +} + +// Options represents settings that are derived from config file and application defaults +type Options struct { + Provider provider.Provider //provider instance + MetaEndPoint string //meta data service endpoint + Name string //name of the service identity + User string //the username to chown the cert/key dirs to. If absent, then root + Group string //the group name to chown the cert/key dirs to. If absent, then athenz + Domain string //name of the domain for the identity + Account string //name of the account + Service string //name of the service for the identity + Zts string //the ZTS to contact + InstanceId string //instance id if ec2/vm, task id if running within eks/ecs/gke + InstanceName string //instance name if ec2/vm + Roles []Role //map of roles to retrieve certificates for + Region string //region name + SanDnsWildcard bool //san dns wildcard support + SanDnsHostname bool //san dns hostname support + Version string //sia version number + ZTSDomains []string //zts domain prefixes + Services []Service //array of configured services + Ssh bool //ssh certificate support + UseRegionalSTS bool //use regional sts endpoint + KeyDir string //private key directory path + CertDir string //x.509 certificate directory path + AthenzCACertFile string //filename to store Athenz CA certs + ZTSCACertFile string //filename for CA certs when communicating with ZTS + ZTSServerName string //ZTS server name, if necessary for tls + ZTSAWSDomains []string //list of domain prefixes for sanDNS entries + GenerateRoleKey bool //option to generate a separate key for role certificates + RotateKey bool //rotate the private key when refreshing certificates + BackupDir string //backup directory for key/cert rotation + CertCountryName string //generated x.509 certificate country name + CertOrgName string //generated x.509 certificate organization name + SshPubKeyFile string //ssh host public key file path + SshCertFile string //ssh host certificate file path + SshConfigFile string //sshd config file path + SshHostKeyType hostkey.KeyType //ssh host key type - rsa or ecdsa + PrivateIp string //instance private ip + EC2Document string //EC2 instance identity document + EC2Signature string //EC2 instance identity document pkcs7 signature + EC2StartTime *time.Time //EC2 instance start time + InstanceIdSanDNS bool //include instance id in a san dns entry (backward compatible option) + RolePrincipalEmail bool //include role principal in a san email field (backward compatible option) + SDSUdsPath string //UDS path if the agent should support uds connections + SDSUdsUid int //UDS connections must be from the given user uid + RefreshInterval int //refresh interval for certificates - default 24 hours + ZTSRegion string //ZTS region in case the client needs this information + DropPrivileges bool //Drop privileges to configured user instead of running as root + TokenDir string //Access tokens directory + AccessTokens []ac.AccessToken //Access tokens object + Profile string //Access profile name + ProfileRestrictTo string //Tag associated with access profile roles + Threshold float64 //threshold in number of days for cert expiry checks + SshThreshold float64 //threshold in number of days for ssh cert expiry checks + FileDirectUpdate bool //update key/cert files directly instead of using rename + HostnameSuffix string //hostname suffix in case we need to auto-generate hostname + SshPrincipals string //ssh additional principals + AccessManagement bool //access management support + ZTSCloudDomains []string //list of domain prefixes for sanDNS entries + AddlSanDNSEntries []string //additional san dns entries to be added to the CSR + FailCountForExit int //number of failed counts before exiting program + RunAfterCertsOkParts []string //run after certificate parsed parts for success + RunAfterCertsErrParts []string //run after certificate parsed parts for errors + RunAfterTokensOkParts []string //run after token parsed parts for success + RunAfterTokensErrParts []string //run after token parsed parts for errors + SpiffeTrustDomain string //spiffe uri trust domain + SpiffeNamespace string //spiffe uri namespace + OmitDomain bool //attestation role only includes service name + StoreTokenOption *int //store access token option + RunAfterFailExit bool //exit process if run_after script fails +} + +const ( + DefaultTokenExpiry = 28800 // 8 hrs + DefaultThreshold = float64(15) // 15 days +) diff --git a/libs/go/sia/options/options.go b/libs/go/sia/options/options.go index 04b6f05de5b..be199ea4b96 100644 --- a/libs/go/sia/options/options.go +++ b/libs/go/sia/options/options.go @@ -21,225 +21,21 @@ import ( "encoding/json" "errors" "fmt" - legacy "github.com/AthenZ/athenz/libs/go/sia/aws/options" "log" "os" "strings" "syscall" - "time" ac "github.com/AthenZ/athenz/libs/go/sia/access/config" "github.com/AthenZ/athenz/libs/go/sia/aws/doc" "github.com/AthenZ/athenz/libs/go/sia/aws/meta" "github.com/AthenZ/athenz/libs/go/sia/aws/stssession" + sc "github.com/AthenZ/athenz/libs/go/sia/config" "github.com/AthenZ/athenz/libs/go/sia/host/provider" "github.com/AthenZ/athenz/libs/go/sia/ssh/hostkey" "github.com/AthenZ/athenz/libs/go/sia/util" ) -// package options contains types for parsing sia_config file and options to carry those config values - -// ConfigService represents a service to be specified by user, and specify User/Group attributes for the service -type ConfigService struct { - KeyFilename string `json:"key_filename,omitempty"` - CertFilename string `json:"cert_filename,omitempty"` - User string `json:"user,omitempty"` - Group string `json:"group,omitempty"` - ExpiryTime int `json:"expiry_time,omitempty"` - SDSUdsUid int `json:"sds_uds_uid,omitempty"` - SDSNodeId string `json:"sds_node_id,omitempty"` - SDSNodeCluster string `json:"sds_node_cluster,omitempty"` - Threshold float64 `json:"cert_threshold_to_check,omitempty"` -} - -// ConfigRole represents a role to be specified by user, and specify attributes for the role -type ConfigRole struct { - Filename string `json:"filename,omitempty"` //filename for the generated role certificate file - ExpiryTime int `json:"expiry_time,omitempty"` //requested expiry time for the role certificate - Service string `json:"service,omitempty"` //principal with role access - User string `json:"user,omitempty"` //user owner on the role identity key - Group string `json:"group,omitempty"` //group owner on the role identity key - Threshold float64 `json:"cert_threshold_to_check,omitempty"` -} - -// ConfigAccount represents each of the accounts that can be specified in the config file -type ConfigAccount struct { - Name string `json:"name,omitempty"` //name of the service identity - User string `json:"user,omitempty"` //the username to chown the cert/key dirs to. If absent, then root. - Group string `json:"group,omitempty"` //the group name to chown the cert/key dirs to. If absent, then athenz. - Domain string `json:"domain,omitempty"` //name of the domain for the identity - Account string `json:"account,omitempty"` //name of the account - Service string `json:"service,omitempty"` //name of the service for the identity - Zts string `json:"zts,omitempty"` //the ZTS to contact - Roles map[string]ConfigRole `json:"roles,omitempty"` //map of roles to retrieve certificates for - Version string `json:"version,omitempty"` //sia version number - Threshold float64 `json:"cert_threshold_to_check,omitempty"` //Threshold to verify for all certs - SshThreshold float64 `json:"sshcert_threshold_to_check,omitempty"` //Threshold to verify for ssh certs - OmitDomain bool `json:"omit_domain,omitempty"` //attestation role only includes service name -} - -// Config represents entire sia_config file -type Config struct { - Version string `json:"version,omitempty"` //config version - Domain string `json:"domain,omitempty"` //name of the domain for the identity - Service string `json:"service,omitempty"` //name of the service for the identity - Services map[string]ConfigService `json:"services,omitempty"` //names of the multiple services for the identity - Ssh *bool `json:"ssh,omitempty"` //ssh certificate support - SshHostKeyType hostkey.KeyType `json:"ssh_host_key_type,omitempty"` //ssh host key type - rsa, ecdsa, etc - SshPrincipals string `json:"ssh_principals,omitempty"` //ssh additional principals - SanDnsWildcard bool `json:"sandns_wildcard,omitempty"` //san dns wildcard support - SanDnsHostname bool `json:"sandns_hostname,omitempty"` //san dns hostname support - SanDnsX509Cnames string `json:"sandns_x509_cnames,omitempty"` //additional san dns entries to be added to the CSR - UseRegionalSTS bool `json:"regionalsts,omitempty"` //whether to use a regional STS endpoint (default is false) - Account string `json:"aws_account,omitempty"` //name of the AWS account for the identity ( only applicable in AWS environment ) - Accounts []ConfigAccount `json:"accounts,omitempty"` //array of configured accounts ( kept for backward compatibility sake ) - GenerateRoleKey bool `json:"generate_role_key,omitempty"` //private key to be generated for role certificate - RotateKey bool `json:"rotate_key,omitempty"` //rotate private key support - User string `json:"user,omitempty"` //the username to chown the cert/key dirs to. If absent, then root - Group string `json:"group,omitempty"` //the group name to chown the cert/key dirs to. If absent, then athenz - SDSUdsPath string `json:"sds_uds_path,omitempty"` //uds path if the agent should support uds connections - SDSUdsUid int `json:"sds_uds_uid,omitempty"` //uds connections must be from the given user uid - ExpiryTime int `json:"expiry_time,omitempty"` //service and role certificate expiry in minutes - RefreshInterval int `json:"refresh_interval,omitempty"` //specifies refresh interval in minutes - ZTSRegion string `json:"zts_region,omitempty"` //specifies zts region for the requests - DropPrivileges bool `json:"drop_privileges,omitempty"` //drop privileges to configured user instead of running as root - AccessTokens map[string]ac.Role `json:"access_tokens,omitempty"` //map of role name to token attributes - FileDirectUpdate bool `json:"file_direct_update,omitempty"` //update key/cert files directly instead of using rename - SiaKeyDir string `json:"sia_key_dir,omitempty"` //sia keys directory to override /var/lib/sia/keys - SiaCertDir string `json:"sia_cert_dir,omitempty"` //sia certs directory to override /var/lib/sia/certs - SiaTokenDir string `json:"sia_token_dir,omitempty"` //sia tokens directory to override /var/lib/sia/tokens - SiaBackupDir string `json:"sia_backup_dir,omitempty"` //sia backup directory to override /var/lib/sia/backup - HostnameSuffix string `json:"hostname_suffix,omitempty"` //hostname suffix in case we need to auto-generate hostname - Zts string `json:"zts,omitempty"` //the ZTS to contact - Roles map[string]ConfigRole `json:"roles,omitempty"` //map of roles to retrieve certificates for - Threshold float64 `json:"cert_threshold_to_check,omitempty"` //threshold to verify for all certs - SshThreshold float64 `json:"sshcert_threshold_to_check,omitempty"` //threshold to verify for ssh certs - AccessManagement bool `json:"access_management,omitempty"` //access management support - FailCountForExit int `json:"fail_count_for_exit,omitempty"` //number of failed counts before exiting program - RunAfterCerts string `json:"run_after,omitempty"` //execute the command mentioned after certs are created - RunAfterCertsErr string `json:"run_after_certs_err,omitempty"` //execute the command mentioned after role certs fail to refresh - RunAfterTokens string `json:"run_after_tokens,omitempty"` //execute the command mentioned after tokens are created - RunAfterTokensErr string `json:"run_after_tokens_err,omitempty"` //execute the command mentioned after tokens fail to refresh - SpiffeTrustDomain string `json:"spiffe_trust_domain,omitempty"` //spiffe trust domain - if configured generate full spiffe uri with namespace - StoreTokenOption *int `json:"store_token_option,omitempty"` //store access token option - RunAfterFailExit bool `json:"run_after_fail_exit,omitempty"` //exit process if run_after script fails -} - -type AccessProfileConfig struct { - Profile string `json:"profile,omitempty"` - ProfileRestrictTo string `json:"profile_restrict_to,omitempty"` -} - -// Role contains role details. Attributes are set based on the config values -type Role struct { - Name string - Service string - SvcKeyFilename string - SvcCertFilename string - ExpiryTime int - RoleCertFilename string - RoleKeyFilename string - User string - Uid int - Gid int - FileMode int - Threshold float64 -} - -// Service represents service details. Attributes are filled in based on the config values -type Service struct { - Name string - KeyFilename string - CertFilename string - User string - Group string - Uid int - Gid int - FileMode int - ExpiryTime int - SDSUdsUid int - SDSNodeId string - SDSNodeCluster string - Threshold float64 -} - -// Options represents settings that are derived from config file and application defaults -type Options struct { - Provider provider.Provider //provider instance - MetaEndPoint string //meta data service endpoint - Name string //name of the service identity - User string //the username to chown the cert/key dirs to. If absent, then root - Group string //the group name to chown the cert/key dirs to. If absent, then athenz - Domain string //name of the domain for the identity - Account string //name of the account - Service string //name of the service for the identity - Zts string //the ZTS to contact - InstanceId string //instance id if ec2/vm, task id if running within eks/ecs/gke - InstanceName string //instance name if ec2/vm - Roles []Role //map of roles to retrieve certificates for - Region string //region name - SanDnsWildcard bool //san dns wildcard support - SanDnsHostname bool //san dns hostname support - Version string //sia version number - ZTSDomains []string //zts domain prefixes - Services []Service //array of configured services - Ssh bool //ssh certificate support - UseRegionalSTS bool //use regional sts endpoint - KeyDir string //private key directory path - CertDir string //x.509 certificate directory path - AthenzCACertFile string //filename to store Athenz CA certs - ZTSCACertFile string //filename for CA certs when communicating with ZTS - ZTSServerName string //ZTS server name, if necessary for tls - ZTSAWSDomains []string //list of domain prefixes for sanDNS entries - GenerateRoleKey bool //option to generate a separate key for role certificates - RotateKey bool //rotate the private key when refreshing certificates - BackupDir string //backup directory for key/cert rotation - CertCountryName string //generated x.509 certificate country name - CertOrgName string //generated x.509 certificate organization name - SshPubKeyFile string //ssh host public key file path - SshCertFile string //ssh host certificate file path - SshConfigFile string //sshd config file path - SshHostKeyType hostkey.KeyType //ssh host key type - rsa or ecdsa - PrivateIp string //instance private ip - EC2Document string //EC2 instance identity document - EC2Signature string //EC2 instance identity document pkcs7 signature - EC2StartTime *time.Time //EC2 instance start time - InstanceIdSanDNS bool //include instance id in a san dns entry (backward compatible option) - RolePrincipalEmail bool //include role principal in a san email field (backward compatible option) - SDSUdsPath string //UDS path if the agent should support uds connections - SDSUdsUid int //UDS connections must be from the given user uid - RefreshInterval int //refresh interval for certificates - default 24 hours - ZTSRegion string //ZTS region in case the client needs this information - DropPrivileges bool //Drop privileges to configured user instead of running as root - TokenDir string //Access tokens directory - AccessTokens []ac.AccessToken //Access tokens object - Profile string //Access profile name - ProfileRestrictTo string //Tag associated with access profile roles - Threshold float64 //threshold in number of days for cert expiry checks - SshThreshold float64 //threshold in number of days for ssh cert expiry checks - FileDirectUpdate bool //update key/cert files directly instead of using rename - HostnameSuffix string //hostname suffix in case we need to auto-generate hostname - SshPrincipals string //ssh additional principals - AccessManagement bool //access management support - ZTSCloudDomains []string //list of domain prefixes for sanDNS entries - AddlSanDNSEntries []string //additional san dns entries to be added to the CSR - FailCountForExit int //number of failed counts before exiting program - RunAfterCertsOkParts []string //run after certificate parsed parts for success - RunAfterCertsErrParts []string //run after certificate parsed parts for errors - RunAfterTokensOkParts []string //run after token parsed parts for success - RunAfterTokensErrParts []string //run after token parsed parts for errors - SpiffeTrustDomain string //spiffe uri trust domain - SpiffeNamespace string //spiffe uri namespace - OmitDomain bool //attestation role only includes service name - StoreTokenOption *int //store access token option - RunAfterFailExit bool //exit process if run_after script fails -} - -const ( - DefaultTokenExpiry = 28800 // 8 hrs - DefaultThreshold = float64(15) // 15 days -) - func GetInstanceTagValue(metaEndPoint, tagKey string) (string, error) { tagValue, err := meta.GetData(metaEndPoint, "/latest/meta-data/tags/instance/"+tagKey) return string(tagValue), err @@ -259,25 +55,25 @@ func GetAccountId(metaEndPoint string, useRegionalSTS bool, region string) (stri return doc.GetDocumentEntry(document, "accountId") } -func InitCredsConfig(roleSuffix, accessProfileSeparator string, useRegionalSTS bool, region string) (*ConfigAccount, *AccessProfileConfig, error) { +func InitCredsConfig(roleSuffix, accessProfileSeparator string, useRegionalSTS bool, region string) (*sc.ConfigAccount, *sc.AccessProfileConfig, error) { account, domain, service, profile, err := stssession.GetMetaDetailsFromCreds(roleSuffix, accessProfileSeparator, useRegionalSTS, region) if err != nil { return nil, nil, fmt.Errorf("unable to parse role arn: %v", err) } - return &ConfigAccount{ + return &sc.ConfigAccount{ Domain: domain, Service: service, Account: account, Name: fmt.Sprintf("%s.%s", domain, service), - Threshold: DefaultThreshold, - SshThreshold: DefaultThreshold, - }, &AccessProfileConfig{ + Threshold: sc.DefaultThreshold, + SshThreshold: sc.DefaultThreshold, + }, &sc.AccessProfileConfig{ Profile: profile, ProfileRestrictTo: "", }, nil } -func InitProfileConfig(metaEndPoint, roleSuffix, accessProfileSeparator string) (*ConfigAccount, *AccessProfileConfig, error) { +func InitProfileConfig(metaEndPoint, roleSuffix, accessProfileSeparator string) (*sc.ConfigAccount, *sc.AccessProfileConfig, error) { info, err := meta.GetData(metaEndPoint, "/latest/meta-data/iam/info") if err != nil { @@ -298,21 +94,21 @@ func InitProfileConfig(metaEndPoint, roleSuffix, accessProfileSeparator string) domain = string(athenzDomain) omitDomain = true } - return &ConfigAccount{ + return &sc.ConfigAccount{ Domain: domain, Service: service, Account: account, Name: fmt.Sprintf("%s.%s", domain, service), - Threshold: DefaultThreshold, - SshThreshold: DefaultThreshold, + Threshold: sc.DefaultThreshold, + SshThreshold: sc.DefaultThreshold, OmitDomain: omitDomain, - }, &AccessProfileConfig{ + }, &sc.AccessProfileConfig{ Profile: profile, ProfileRestrictTo: "", }, nil } -func InitGenericProfileConfig(metaEndPoint, roleSuffix, accessProfileSeparator string, provider provider.Provider) (*Config, *AccessProfileConfig, error) { +func InitGenericProfileConfig(metaEndPoint, roleSuffix, accessProfileSeparator string, provider provider.Provider) (*sc.Config, *sc.AccessProfileConfig, error) { account, domain, service, err := provider.GetAccountDomainServiceFromMeta(metaEndPoint) if err != nil { @@ -321,23 +117,23 @@ func InitGenericProfileConfig(metaEndPoint, roleSuffix, accessProfileSeparator s profile, err := provider.GetAccessManagementProfileFromMeta(metaEndPoint) if err != nil { // access profile error can be ignored for now. - return &Config{ + return &sc.Config{ Account: account, Domain: domain, Service: service, }, nil, nil } - return &Config{ + return &sc.Config{ Account: account, Domain: domain, Service: service, - }, &AccessProfileConfig{ + }, &sc.AccessProfileConfig{ Profile: profile, ProfileRestrictTo: "", }, nil } -func InitFileConfig(fileName, metaEndPoint string, useRegionalSTS bool, region, account string, provider provider.Provider) (*Config, *ConfigAccount, error) { +func InitFileConfig(fileName, metaEndPoint string, useRegionalSTS bool, region, account string, provider provider.Provider) (*sc.Config, *sc.ConfigAccount, error) { confBytes, err := os.ReadFile(fileName) if err != nil { return nil, nil, err @@ -345,7 +141,7 @@ func InitFileConfig(fileName, metaEndPoint string, useRegionalSTS bool, region, if len(confBytes) == 0 { return nil, nil, errors.New("empty config bytes") } - var config Config + var config sc.Config err = json.Unmarshal(confBytes, &config) if err != nil { return nil, nil, err @@ -371,21 +167,21 @@ func InitFileConfig(fileName, metaEndPoint string, useRegionalSTS bool, region, } configAccount.Service = config.Service configAccount.Name = fmt.Sprintf("%s.%s", configAccount.Domain, configAccount.Service) - configAccount.Threshold = nonZeroValue(configAccount.Threshold, DefaultThreshold) - configAccount.SshThreshold = nonZeroValue(configAccount.SshThreshold, DefaultThreshold) + configAccount.Threshold = nonZeroValue(configAccount.Threshold, sc.DefaultThreshold) + configAccount.SshThreshold = nonZeroValue(configAccount.SshThreshold, sc.DefaultThreshold) return &config, &configAccount, nil } } return nil, nil, fmt.Errorf("missing account %s details from config file", account) } - config.Threshold = nonZeroValue(config.Threshold, DefaultThreshold) - config.SshThreshold = nonZeroValue(config.SshThreshold, DefaultThreshold) + config.Threshold = nonZeroValue(config.Threshold, sc.DefaultThreshold) + config.SshThreshold = nonZeroValue(config.SshThreshold, sc.DefaultThreshold) return &config, nil, nil } -func InitAccessProfileFileConfig(fileName string) (*AccessProfileConfig, error) { +func InitAccessProfileFileConfig(fileName string) (*sc.AccessProfileConfig, error) { confBytes, err := os.ReadFile(fileName) if err != nil { return nil, err @@ -393,7 +189,7 @@ func InitAccessProfileFileConfig(fileName string) (*AccessProfileConfig, error) if len(confBytes) == 0 { return nil, errors.New("empty config bytes") } - var config AccessProfileConfig + var config sc.AccessProfileConfig err = json.Unmarshal(confBytes, &config) if err != nil { return nil, err @@ -402,18 +198,18 @@ func InitAccessProfileFileConfig(fileName string) (*AccessProfileConfig, error) return nil, fmt.Errorf("missing required Profile field from the config file") } - return &AccessProfileConfig{ + return &sc.AccessProfileConfig{ Profile: config.Profile, ProfileRestrictTo: config.ProfileRestrictTo, }, nil } -func InitEnvConfig(config *Config, provider provider.Provider) (*Config, *ConfigAccount, error) { +func InitEnvConfig(config *sc.Config, provider provider.Provider) (*sc.Config, *sc.ConfigAccount, error) { // it is possible that the config object was already created the // config file in which case we're not going to override any // of the settings. if config == nil { - config = &Config{} + config = &sc.Config{} } if !config.SanDnsWildcard { config.SanDnsWildcard = util.ParseEnvBooleanFlag("ATHENZ_SIA_SANDNS_WILDCARD") @@ -515,8 +311,8 @@ func InitEnvConfig(config *Config, provider provider.Provider) (*Config, *Config config.RunAfterFailExit = util.ParseEnvBooleanFlag("ATHENZ_SIA_RUN_AFTER_FAIL_EXIT") } - config.Threshold = util.ParseEnvFloatFlag("ATHENZ_SIA_ACCOUNT_THRESHOLD", DefaultThreshold) - config.SshThreshold = util.ParseEnvFloatFlag("ATHENZ_SIA_ACCOUNT_SSH_THRESHOLD", DefaultThreshold) + config.Threshold = util.ParseEnvFloatFlag("ATHENZ_SIA_ACCOUNT_THRESHOLD", sc.DefaultThreshold) + config.SshThreshold = util.ParseEnvFloatFlag("ATHENZ_SIA_ACCOUNT_SSH_THRESHOLD", sc.DefaultThreshold) omitDomain := util.ParseEnvBooleanFlag("ATHENZ_SIA_OMIT_DOMAIN") acEnv := os.Getenv("ATHENZ_SIA_ACCESS_TOKENS") @@ -533,7 +329,7 @@ func InitEnvConfig(config *Config, provider provider.Provider) (*Config, *Config } } - var configRoles map[string]ConfigRole + var configRoles map[string]sc.ConfigRole rolesEnv := os.Getenv("ATHENZ_SIA_ACCOUNT_ROLES") if rolesEnv != "" { err := json.Unmarshal([]byte(rolesEnv), &configRoles) @@ -543,7 +339,7 @@ func InitEnvConfig(config *Config, provider provider.Provider) (*Config, *Config } config.Roles = configRoles - var configAccount *ConfigAccount + var configAccount *sc.ConfigAccount if isAWSEnvironment(provider) { roleArn := os.Getenv("ATHENZ_SIA_IAM_ROLE_ARN") @@ -567,7 +363,7 @@ func InitEnvConfig(config *Config, provider provider.Provider) (*Config, *Config config.Service = service } - configAccount = &ConfigAccount{ + configAccount = &sc.ConfigAccount{ Account: account, Domain: domain, Service: service, @@ -594,14 +390,14 @@ func InitEnvConfig(config *Config, provider provider.Provider) (*Config, *Config return config, configAccount, nil } -func InitAccessProfileEnvConfig() (*AccessProfileConfig, error) { +func InitAccessProfileEnvConfig() (*sc.AccessProfileConfig, error) { accessProfile := os.Getenv("ATHENZ_SIA_ACCESS_PROFILE") if accessProfile == "" { return nil, fmt.Errorf("athenz accessProfile variable not configured") } - return &AccessProfileConfig{ + return &sc.AccessProfileConfig{ Profile: accessProfile, ProfileRestrictTo: "", }, nil @@ -609,7 +405,7 @@ func InitAccessProfileEnvConfig() (*AccessProfileConfig, error) { // setOptions takes in sia_config objects and returns a pointer to Options after parsing and initializing the defaults // It uses profile arn for defaults when sia_config is empty or non-parsable. It populates "services" array -func setOptions(config *Config, account *ConfigAccount, profileConfig *AccessProfileConfig, siaDir, version string) (*Options, error) { +func setOptions(config *sc.Config, account *sc.ConfigAccount, profileConfig *sc.AccessProfileConfig, siaDir, version string) (*sc.Options, error) { //update regional sts and wildcard settings based on config settings useRegionalSTS := false @@ -721,11 +517,11 @@ func setOptions(config *Config, account *ConfigAccount, profileConfig *AccessPro } } - var services []Service + var services []sc.Service if config == nil || len(config.Services) == 0 { //There is no sia_config, or multiple services are not configured. //Populate services with the account information we gathered - s := Service{ + s := sc.Service{ Name: account.Service, User: account.User, Threshold: account.Threshold, @@ -740,12 +536,12 @@ func setOptions(config *Config, account *ConfigAccount, profileConfig *AccessPro return nil, fmt.Errorf("services: %+v mentioned, service: %q needs to be part of services", config.Services, config.Service) } // Populate config.Service into first - first := Service{ + first := sc.Service{ Name: config.Service, } // Populate the remaining into tail - var tail []Service + var tail []sc.Service for name, s := range config.Services { svcExpiryTime := expiryTime if s.ExpiryTime > 0 { @@ -776,7 +572,7 @@ func setOptions(config *Config, account *ConfigAccount, profileConfig *AccessPro first.SDSUdsUid = svcSDSUdsUid first.Threshold = nonZeroValue(s.Threshold, account.Threshold) } else { - ts := Service{ + ts := sc.Service{ Name: name, KeyFilename: s.KeyFilename, CertFilename: s.CertFilename, @@ -802,7 +598,7 @@ func setOptions(config *Config, account *ConfigAccount, profileConfig *AccessPro return nil, err } - var roles []Role + var roles []sc.Role if config != nil { if account.Roles != nil { @@ -815,7 +611,7 @@ func setOptions(config *Config, account *ConfigAccount, profileConfig *AccessPro rotateKey = false } roleService := getRoleServiceOwner(r.Service, services) - role := Role{ + role := sc.Role{ Name: name, Service: roleService.Name, SvcKeyFilename: util.GetSvcKeyFileName(keyDir, roleService.KeyFilename, account.Domain, roleService.Name), @@ -848,7 +644,7 @@ func setOptions(config *Config, account *ConfigAccount, profileConfig *AccessPro profileRestrictTo = profileConfig.ProfileRestrictTo } - return &Options{ + return &sc.Options{ Name: account.Name, User: account.User, Group: account.Group, @@ -895,7 +691,7 @@ func setOptions(config *Config, account *ConfigAccount, profileConfig *AccessPro }, nil } -func getRoleServiceOwner(serviceName string, services []Service) Service { +func getRoleServiceOwner(serviceName string, services []sc.Service) sc.Service { if serviceName == "" { return services[0] } @@ -908,7 +704,7 @@ func getRoleServiceOwner(serviceName string, services []Service) Service { return services[0] } -func processAccessTokens(config *Config, processedSvcs []Service) ([]ac.AccessToken, error) { +func processAccessTokens(config *sc.Config, processedSvcs []sc.Service) ([]ac.AccessToken, error) { if config == nil || config.AccessTokens == nil { return nil, nil } @@ -926,7 +722,7 @@ func processAccessTokens(config *Config, processedSvcs []Service) ([]ac.AccessTo if len(roles) == 0 { roles = []string{fileName} } - expiry := DefaultTokenExpiry + expiry := sc.DefaultTokenExpiry if t.Expiry != 0 { expiry = t.Expiry } @@ -957,17 +753,17 @@ func processAccessTokens(config *Config, processedSvcs []Service) ([]ac.AccessTo return accessTokens, nil } -func getSvc(name string, services []Service) (Service, error) { +func getSvc(name string, services []sc.Service) (sc.Service, error) { for _, s := range services { if s.Name == name { return s, nil } } - return Service{}, fmt.Errorf("%q not found in processed services", name) + return sc.Service{}, fmt.Errorf("%q not found in processed services", name) } // GetSvcNames returns comma separated list of service names -func GetSvcNames(svcs []Service) string { +func GetSvcNames(svcs []sc.Service) string { var b bytes.Buffer for _, svc := range svcs { b.WriteString(fmt.Sprintf("%s,", svc.Name)) @@ -981,7 +777,7 @@ func GetSvcNames(svcs []Service) string { // for keys and certs, then the tool can drop its access from root // to the specified user. If they're multiple users defined then // the return values would be -1/-1 -func GetRunsAsUidGid(opts *Options) (int, int) { +func GetRunsAsUidGid(opts *sc.Options) (int, int) { // first we want to check if the caller has specifically indicated // that they want to keep the privileges and not drop to another user if !opts.DropPrivileges { @@ -1030,7 +826,7 @@ func GetRunsAsUidGid(opts *Options) (int, int) { return uid, gid } -func NewOptions(config *Config, configAccount *ConfigAccount, profileConfig *AccessProfileConfig, siaDir, siaVersion string, useRegionalSTS bool, region string) (*Options, error) { +func NewOptions(config *sc.Config, configAccount *sc.ConfigAccount, profileConfig *sc.AccessProfileConfig, siaDir, siaVersion string, useRegionalSTS bool, region string) (*sc.Options, error) { opts, err := setOptions(config, configAccount, profileConfig, siaDir, siaVersion) if err != nil { @@ -1052,99 +848,6 @@ func nonZeroValue(t, base float64) float64 { return base } -func LegacyOptions(opts *Options) *legacy.Options { - lopts := &legacy.Options{ - Provider: opts.Provider, - Name: opts.Name, - User: opts.User, - Group: opts.Group, - Domain: opts.Domain, - Account: opts.Account, - Service: opts.Service, - Zts: opts.Zts, - InstanceId: opts.InstanceId, - Region: opts.Region, - SanDnsWildcard: opts.SanDnsWildcard, - SanDnsHostname: opts.SanDnsHostname, - Version: opts.Version, - ZTSDomains: opts.ZTSDomains, - Ssh: opts.Ssh, - UseRegionalSTS: opts.UseRegionalSTS, - KeyDir: opts.KeyDir, - CertDir: opts.CertDir, - AthenzCACertFile: opts.AthenzCACertFile, - ZTSCACertFile: opts.ZTSCACertFile, - ZTSServerName: opts.ZTSServerName, - ZTSAWSDomains: opts.ZTSAWSDomains, - GenerateRoleKey: opts.GenerateRoleKey, - RotateKey: opts.RotateKey, - BackupDir: opts.BackupDir, - CertCountryName: opts.CertCountryName, - CertOrgName: opts.CertOrgName, - SshPubKeyFile: opts.SshPubKeyFile, - SshCertFile: opts.SshCertFile, - SshConfigFile: opts.SshConfigFile, - PrivateIp: opts.PrivateIp, - EC2Document: opts.EC2Document, - EC2Signature: opts.EC2Signature, - EC2StartTime: opts.EC2StartTime, - InstanceIdSanDNS: opts.InstanceIdSanDNS, - RolePrincipalEmail: opts.RolePrincipalEmail, - SDSUdsPath: opts.SDSUdsPath, - SDSUdsUid: opts.SDSUdsUid, - RefreshInterval: opts.RefreshInterval, - ZTSRegion: opts.ZTSRegion, - DropPrivileges: opts.DropPrivileges, - TokenDir: opts.TokenDir, - AccessTokens: opts.AccessTokens, - Profile: opts.Profile, - ProfileRestrictTo: opts.ProfileRestrictTo, - Threshold: opts.Threshold, - SshThreshold: opts.SshThreshold, - FileDirectUpdate: opts.FileDirectUpdate, - HostnameSuffix: opts.HostnameSuffix, - } - - for _, s := range opts.Services { - svc := legacy.Service{ - Name: s.Name, - KeyFilename: s.KeyFilename, - CertFilename: s.CertFilename, - User: s.User, - Group: s.Group, - Uid: s.Uid, - Gid: s.Gid, - FileMode: s.FileMode, - ExpiryTime: s.ExpiryTime, - SDSUdsUid: s.SDSUdsUid, - SDSNodeId: s.SDSNodeId, - SDSNodeCluster: s.SDSNodeCluster, - Threshold: s.Threshold, - } - lopts.Services = append(lopts.Services, svc) - } - - for _, r := range opts.Roles { - role := legacy.Role{ - Name: r.Name, - Service: r.Service, - RoleKeyFilename: r.RoleKeyFilename, - RoleCertFilename: r.RoleCertFilename, - SvcKeyFilename: r.SvcKeyFilename, - SvcCertFilename: r.SvcCertFilename, - ExpiryTime: r.ExpiryTime, - User: r.User, - Uid: r.Uid, - Gid: r.Gid, - FileMode: r.FileMode, - Threshold: r.Threshold, - } - lopts.Roles = append(lopts.Roles, role) - } - - return lopts -} - func isAWSEnvironment(provider provider.Provider) bool { return strings.Contains(provider.GetName(), "aws") } diff --git a/libs/go/sia/options/options_test.go b/libs/go/sia/options/options_test.go index 3f35d856f85..f4e765f73bf 100644 --- a/libs/go/sia/options/options_test.go +++ b/libs/go/sia/options/options_test.go @@ -18,7 +18,6 @@ package options import ( "fmt" - "github.com/dimfeld/httptreemux" "io" "log" "net" @@ -33,9 +32,10 @@ import ( "testing" "github.com/AthenZ/athenz/libs/go/sia/access/config" + sc "github.com/AthenZ/athenz/libs/go/sia/config" "github.com/AthenZ/athenz/libs/go/sia/ssh/hostkey" "github.com/AthenZ/athenz/libs/go/sia/util" - + "github.com/dimfeld/httptreemux" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -88,7 +88,7 @@ func (t *testServer) httpUrl() string { return fmt.Sprintf("http://%s", t.addr) } -func getConfig(fileName, roleSuffix, metaEndPoint string, useRegionalSTS bool, region string) (*Config, *ConfigAccount, error) { +func getConfig(fileName, roleSuffix, metaEndPoint string, useRegionalSTS bool, region string) (*sc.Config, *sc.ConfigAccount, error) { provider := MockAWSProvider{ Name: fmt.Sprintf("athenz.aws.us-west-2"), @@ -110,10 +110,10 @@ func getConfig(fileName, roleSuffix, metaEndPoint string, useRegionalSTS bool, r return cfg, cfgAccount, nil } -func getAccessProfileConfig(fileName, metaEndPoint string) (*ConfigAccount, *AccessProfileConfig, error) { +func getAccessProfileConfig(fileName, metaEndPoint string) (*sc.ConfigAccount, *sc.AccessProfileConfig, error) { // Parse config bytes first, and if that fails, load values from Instance Profile and IAM info profileConfig, err := InitAccessProfileFileConfig(fileName) - var configAccount *ConfigAccount = nil + var configAccount *sc.ConfigAccount = nil if err != nil { log.Printf("unable to parse configuration file, error: %v\n", err) log.Println("trying to determine profile name from instance profile arn...") @@ -126,7 +126,7 @@ func getAccessProfileConfig(fileName, metaEndPoint string) (*ConfigAccount, *Acc return configAccount, profileConfig, nil } -func assertService(expected Service, actual Service) bool { +func assertService(expected sc.Service, actual sc.Service) bool { log.Printf("expected: %+v\n", expected) log.Printf("actual: %+v\n", actual) return expected.Name == actual.Name && @@ -139,7 +139,7 @@ func assertService(expected Service, actual Service) bool { expected.Threshold == actual.Threshold } -func assertInServices(svcs []Service, actual Service) bool { +func assertInServices(svcs []sc.Service, actual sc.Service) bool { log.Printf("svcs passed: %+v\n", svcs) log.Printf("actual: %+v\n", actual) for _, s := range svcs { @@ -202,9 +202,9 @@ func TestOptionsNoConfig(t *testing.T) { assert.Equal(t, 1, len(opts.Services)) assert.Equal(t, "athenz", opts.Domain) assert.Equal(t, "athenz.hockey", opts.Name) - assert.True(t, assertService(opts.Services[0], Service{Name: "hockey", Uid: idCommandId("-u"), Gid: idCommandId("-g"), FileMode: 288, Threshold: DefaultThreshold})) - assert.Equal(t, DefaultThreshold, opts.Threshold) - assert.Equal(t, DefaultThreshold, opts.SshThreshold) + assert.True(t, assertService(opts.Services[0], sc.Service{Name: "hockey", Uid: idCommandId("-u"), Gid: idCommandId("-g"), FileMode: 288, Threshold: sc.DefaultThreshold})) + assert.Equal(t, sc.DefaultThreshold, opts.Threshold) + assert.Equal(t, sc.DefaultThreshold, opts.SshThreshold) assert.False(t, opts.FileDirectUpdate) assert.Equal(t, "/var/lib/sia/keys", opts.KeyDir) assert.Equal(t, "/var/lib/sia/certs", opts.CertDir) @@ -239,8 +239,8 @@ func TestOptionsNoProfileConfig(t *testing.T) { assert.True(t, opts.Name == "athenz.hockey") assert.Equal(t, opts.Profile, "test-profile") - assert.Equal(t, opts.Threshold, DefaultThreshold) - assert.Equal(t, opts.SshThreshold, DefaultThreshold) + assert.Equal(t, opts.Threshold, sc.DefaultThreshold) + assert.Equal(t, opts.SshThreshold, sc.DefaultThreshold) } // TestOptionsWithProfileConfig test the scenario when profile config file is present @@ -263,12 +263,12 @@ func TestOptionsWithProfileConfig(t *testing.T) { assert.Equal(t, "athenz.api", opts.Name) // Zeroth service should be the one from "service" key, the remaining are from "services" in no particular order - assert.True(t, assertService(opts.Services[0], Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: DefaultThreshold})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: DefaultThreshold})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: DefaultThreshold})) + assert.True(t, assertService(opts.Services[0], sc.Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: sc.DefaultThreshold})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: sc.DefaultThreshold})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: sc.DefaultThreshold})) - assert.Equal(t, DefaultThreshold, opts.SshThreshold) - assert.Equal(t, DefaultThreshold, opts.Threshold) + assert.Equal(t, sc.DefaultThreshold, opts.SshThreshold) + assert.Equal(t, sc.DefaultThreshold, opts.Threshold) assert.Equal(t, "host1.athenz.io,host2.athenz.io", opts.SshPrincipals) } @@ -292,12 +292,12 @@ func TestOptionsWithProfileConfigAndProfileRestrictTo(t *testing.T) { assert.True(t, opts.Name == "athenz.api") // Zeroth service should be the one from "service" key, the remaining are from "services" in no particular order - assert.True(t, assertService(opts.Services[0], Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: DefaultThreshold})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: DefaultThreshold})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: DefaultThreshold})) + assert.True(t, assertService(opts.Services[0], sc.Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: sc.DefaultThreshold})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: sc.DefaultThreshold})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: sc.DefaultThreshold})) - assert.Equal(t, DefaultThreshold, opts.SshThreshold) - assert.Equal(t, DefaultThreshold, opts.Threshold) + assert.Equal(t, sc.DefaultThreshold, opts.SshThreshold) + assert.Equal(t, sc.DefaultThreshold, opts.Threshold) } func TestOptionsWithRoleThreshold(t *testing.T) { @@ -320,12 +320,12 @@ func TestOptionsWithRoleThreshold(t *testing.T) { assert.Equal(t, "athenz.api", opts.Name) // Zeroth service should be the one from "service" key, the remaining are from "services" in no particular order - assert.True(t, assertService(opts.Services[0], Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: 20})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: 20})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: 20})) + assert.True(t, assertService(opts.Services[0], sc.Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: 20})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: 20})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: 20})) assert.Equal(t, float64(25), opts.Roles[0].Threshold) - assert.Equal(t, DefaultThreshold, opts.SshThreshold) + assert.Equal(t, sc.DefaultThreshold, opts.SshThreshold) assert.Equal(t, float64(20), opts.Threshold) } @@ -347,9 +347,9 @@ func TestOptionsWithServiceAccountThreshold(t *testing.T) { assert.True(t, opts.Name == "athenz.api") // Zeroth service should be the one from "service" key, the remaining are from "services" in no particular order - assert.True(t, assertService(opts.Services[0], Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: 35})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: 35})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: 25})) + assert.True(t, assertService(opts.Services[0], sc.Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: 35})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: 35})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: 25})) assert.Equal(t, float64(60), opts.SshThreshold) assert.Equal(t, float64(35), opts.Threshold) @@ -384,12 +384,12 @@ func TestOptionsWithConfig(t *testing.T) { assert.Equal(t, "athenz.api", opts.Name) // Zeroth service should be the one from "service" key, the remaining are from "services" in no particular order - assert.True(t, assertService(opts.Services[0], Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: DefaultThreshold})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: DefaultThreshold})) - assert.True(t, assertInServices(opts.Services[1:], Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: DefaultThreshold})) + assert.True(t, assertService(opts.Services[0], sc.Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: sc.DefaultThreshold})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "ui", User: "root", Uid: 0, Gid: 0, FileMode: 288, Threshold: sc.DefaultThreshold})) + assert.True(t, assertInServices(opts.Services[1:], sc.Service{Name: "yamas", User: "nobody", Uid: getUid("nobody"), Group: "sys", Gid: getGid(t, "sys"), Threshold: sc.DefaultThreshold})) - assert.Equal(t, DefaultThreshold, opts.Threshold) - assert.Equal(t, DefaultThreshold, opts.SshThreshold) + assert.Equal(t, sc.DefaultThreshold, opts.Threshold) + assert.Equal(t, sc.DefaultThreshold, opts.SshThreshold) } // TestOptionsNoService test the scenario when /etc/sia/sia_config is present, but service is not repeated in services @@ -415,7 +415,7 @@ func TestOptionsNoServices(t *testing.T) { assert.Equal(t, 1, len(opts.Services)) assert.Equal(t, "athenz", opts.Domain) assert.Equal(t, "athenz.api", opts.Name) - assert.True(t, assertService(opts.Services[0], Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: DefaultThreshold})) + assert.True(t, assertService(opts.Services[0], sc.Service{Name: "api", User: "nobody", Uid: getUid("nobody"), Gid: getUserGid("nobody"), FileMode: 288, Threshold: sc.DefaultThreshold})) assert.True(t, opts.FileDirectUpdate) } @@ -540,7 +540,7 @@ func TestGetRunsAsUidGid(t *testing.T) { } } -func toServiceNames(services []Service) []string { +func toServiceNames(services []sc.Service) []string { var serviceNames []string for _, srv := range services { @@ -571,7 +571,7 @@ func TestOptionsWithAccessToken(t *testing.T) { Service: "api", Domain: "athenz.demo", Roles: []string{"reader"}, - Expiry: DefaultTokenExpiry, + Expiry: sc.DefaultTokenExpiry, User: "nobody", ProxyPrincipalSpiffeUris: "", })) @@ -591,7 +591,7 @@ func TestOptionsWithAccessToken(t *testing.T) { Service: "api", Domain: "athenz.demo", Roles: []string{"reader", "reader-admin"}, - Expiry: DefaultTokenExpiry, + Expiry: sc.DefaultTokenExpiry, User: "nobody", ProxyPrincipalSpiffeUris: "", })) @@ -611,7 +611,7 @@ func TestOptionsWithAccessToken(t *testing.T) { Service: "logger", Domain: "athenz.demo", Roles: []string{"splunk"}, - Expiry: DefaultTokenExpiry, + Expiry: sc.DefaultTokenExpiry, User: "nobody", ProxyPrincipalSpiffeUris: "", })) @@ -621,7 +621,7 @@ func TestOptionsWithAccessToken(t *testing.T) { Service: "api", Domain: "athenz.demo", Roles: []string{"*"}, - Expiry: DefaultTokenExpiry, + Expiry: sc.DefaultTokenExpiry, User: "nobody", ProxyPrincipalSpiffeUris: "", })) @@ -656,7 +656,7 @@ func TestInvalidAccessTokenDomain(t *testing.T) { } func TestGetRoleServiceOwner(t *testing.T) { - services := []Service{ + services := []sc.Service{ { Name: "svc1", }, @@ -951,6 +951,6 @@ func TestOptionsWithServiceOnlySetup(t *testing.T) { assert.Equal(t, opts.Name, "athenz.hockey") assert.Equal(t, opts.Profile, "test-profile") - assert.Equal(t, opts.Threshold, DefaultThreshold) - assert.Equal(t, opts.SshThreshold, DefaultThreshold) + assert.Equal(t, opts.Threshold, sc.DefaultThreshold) + assert.Equal(t, opts.SshThreshold, sc.DefaultThreshold) } diff --git a/libs/go/sia/sds/handler.go b/libs/go/sia/sds/handler.go index 08bca4858f6..fe01162478a 100644 --- a/libs/go/sia/sds/handler.go +++ b/libs/go/sia/sds/handler.go @@ -20,7 +20,7 @@ import ( "context" "errors" "fmt" - "github.com/AthenZ/athenz/libs/go/sia/options" + sc "github.com/AthenZ/athenz/libs/go/sia/config" "github.com/AthenZ/athenz/libs/go/sia/util" envoyCore "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" envoyTls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" @@ -37,11 +37,11 @@ import ( type ServerHandler struct { Mutex sync.RWMutex - Options *options.Options + Options *sc.Options Subscribers map[string]*Subscriber } -func NewServerHandler(opts *options.Options) *ServerHandler { +func NewServerHandler(opts *sc.Options) *ServerHandler { return &ServerHandler{ Options: opts, Subscribers: make(map[string]*Subscriber), @@ -281,7 +281,7 @@ func (handler *ServerHandler) NotifySubscribers() { handler.Mutex.RUnlock() } -func (handler *ServerHandler) authenticateRequest(info ClientInfo, node *envoyCore.Node, domain, service string) (*options.Service, error) { +func (handler *ServerHandler) authenticateRequest(info ClientInfo, node *envoyCore.Node, domain, service string) (*sc.Service, error) { if domain != handler.Options.Domain { return nil, fmt.Errorf("invalid domain name: %s, expected: %s", domain, handler.Options.Domain) @@ -311,7 +311,7 @@ func (handler *ServerHandler) authenticateRequest(info ClientInfo, node *envoyCo return nil, fmt.Errorf("unknown service: %s", service) } -func (handler *ServerHandler) getTLSCertificateSecret(spiffeUri string, svc *options.Service) (*anypb.Any, error) { +func (handler *ServerHandler) getTLSCertificateSecret(spiffeUri string, svc *sc.Service) (*anypb.Any, error) { keyFile := util.GetSvcKeyFileName(handler.Options.KeyDir, svc.KeyFilename, handler.Options.Domain, svc.Name) keyPEM, err := os.ReadFile(keyFile) diff --git a/libs/go/sia/sds/handler_test.go b/libs/go/sia/sds/handler_test.go index df33a4d9247..6aca89eacf0 100644 --- a/libs/go/sia/sds/handler_test.go +++ b/libs/go/sia/sds/handler_test.go @@ -17,7 +17,7 @@ package sds import ( - "github.com/AthenZ/athenz/libs/go/sia/options" + sc "github.com/AthenZ/athenz/libs/go/sia/config" envoyCore "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" envoyDiscovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" "strings" @@ -45,7 +45,7 @@ func TestResourceNamesChanged(test *testing.T) { } func TestGetResponseInvalidUri(test *testing.T) { - handler := NewServerHandler(&options.Options{}) + handler := NewServerHandler(&sc.Options{}) req := envoyDiscovery.DiscoveryRequest{ ResourceNames: []string{"spiffe://athenz/invalid"}, } @@ -60,7 +60,7 @@ func TestGetResponseInvalidUri(test *testing.T) { } func TestSubscriptionChanges(test *testing.T) { - handler := NewServerHandler(&options.Options{}) + handler := NewServerHandler(&sc.Options{}) if len(handler.Subscribers) != 0 { test.Errorf("new handler has some subscribers") } @@ -76,7 +76,7 @@ func TestSubscriptionChanges(test *testing.T) { } func TestSubscriptionNotifications(test *testing.T) { - handler := NewServerHandler(&options.Options{}) + handler := NewServerHandler(&sc.Options{}) sub := handler.subscribeToCertUpdates() handler.NotifySubscribers() updates := <-sub.GetCertUpdates() @@ -87,7 +87,7 @@ func TestSubscriptionNotifications(test *testing.T) { } func TestAuthenticateRequestMismatchDomain(test *testing.T) { - handler := NewServerHandler(&options.Options{ + handler := NewServerHandler(&sc.Options{ Domain: "athenz", }) _, err := handler.authenticateRequest(ClientInfo{}, nil, "sports", "api") @@ -100,9 +100,9 @@ func TestAuthenticateRequestMismatchDomain(test *testing.T) { } func TestAuthenticateRequestUnknownService(test *testing.T) { - handler := NewServerHandler(&options.Options{ + handler := NewServerHandler(&sc.Options{ Domain: "athenz", - Services: []options.Service{{Name: "backend"}}, + Services: []sc.Service{{Name: "backend"}}, }) _, err := handler.authenticateRequest(ClientInfo{}, nil, "athenz", "api") if err == nil { @@ -114,9 +114,9 @@ func TestAuthenticateRequestUnknownService(test *testing.T) { } func TestAuthenticateRequestMismatchUid(test *testing.T) { - handler := NewServerHandler(&options.Options{ + handler := NewServerHandler(&sc.Options{ Domain: "athenz", - Services: []options.Service{{Name: "api", SDSUdsUid: 124}}, + Services: []sc.Service{{Name: "api", SDSUdsUid: 124}}, }) _, err := handler.authenticateRequest(ClientInfo{UserID: 123}, nil, "athenz", "api") if err == nil { @@ -128,9 +128,9 @@ func TestAuthenticateRequestMismatchUid(test *testing.T) { } func TestAuthenticateRequestMismatchNodeId(test *testing.T) { - handler := NewServerHandler(&options.Options{ + handler := NewServerHandler(&sc.Options{ Domain: "athenz", - Services: []options.Service{{Name: "api", SDSUdsUid: 123, SDSNodeId: "id1"}}, + Services: []sc.Service{{Name: "api", SDSUdsUid: 123, SDSNodeId: "id1"}}, }) // first try with nil node _, err := handler.authenticateRequest(ClientInfo{UserID: 123}, nil, "athenz", "api") @@ -151,9 +151,9 @@ func TestAuthenticateRequestMismatchNodeId(test *testing.T) { } func TestAuthenticateRequestMismatchNodeCluster(test *testing.T) { - handler := NewServerHandler(&options.Options{ + handler := NewServerHandler(&sc.Options{ Domain: "athenz", - Services: []options.Service{{Name: "api", SDSUdsUid: 123, SDSNodeCluster: "cluster1"}}, + Services: []sc.Service{{Name: "api", SDSUdsUid: 123, SDSNodeCluster: "cluster1"}}, }) // first try with nil node _, err := handler.authenticateRequest(ClientInfo{UserID: 123}, nil, "athenz", "api") @@ -174,9 +174,9 @@ func TestAuthenticateRequestMismatchNodeCluster(test *testing.T) { } func TestAuthenticateRequestMatchNilNode(test *testing.T) { - handler := NewServerHandler(&options.Options{ + handler := NewServerHandler(&sc.Options{ Domain: "athenz", - Services: []options.Service{{Name: "api", SDSUdsUid: 123}}, + Services: []sc.Service{{Name: "api", SDSUdsUid: 123}}, }) _, err := handler.authenticateRequest(ClientInfo{UserID: 123}, nil, "athenz", "api") if err != nil { @@ -185,9 +185,9 @@ func TestAuthenticateRequestMatchNilNode(test *testing.T) { } func TestAuthenticateRequestMatchWithNode(test *testing.T) { - handler := NewServerHandler(&options.Options{ + handler := NewServerHandler(&sc.Options{ Domain: "athenz", - Services: []options.Service{{Name: "api", SDSUdsUid: 123, SDSNodeId: "id1", SDSNodeCluster: "cluster1"}}, + Services: []sc.Service{{Name: "api", SDSUdsUid: 123, SDSNodeId: "id1", SDSNodeCluster: "cluster1"}}, }) _, err := handler.authenticateRequest(ClientInfo{UserID: 123}, &envoyCore.Node{Id: "id1", Cluster: "cluster1"}, "athenz", "api") if err != nil { @@ -196,12 +196,12 @@ func TestAuthenticateRequestMatchWithNode(test *testing.T) { } func TestGetTLSCertificateSecretUnknownPrivateKey(test *testing.T) { - handler := NewServerHandler(&options.Options{ + handler := NewServerHandler(&sc.Options{ KeyDir: "data", CertDir: "data", Domain: "athenz", }) - svc := options.Service{ + svc := sc.Service{ Name: "unknown", } _, err := handler.getTLSCertificateSecret("spiffe://athenz/sa/api", &svc) @@ -211,12 +211,12 @@ func TestGetTLSCertificateSecretUnknownPrivateKey(test *testing.T) { } func TestGetTLSCertificateSecretUnknownCertificate(test *testing.T) { - handler := NewServerHandler(&options.Options{ + handler := NewServerHandler(&sc.Options{ KeyDir: "data", CertDir: "data", Domain: "athenz", }) - svc := options.Service{ + svc := sc.Service{ Name: "api", KeyFilename: "unknown", CertFilename: "unknown", @@ -228,12 +228,12 @@ func TestGetTLSCertificateSecretUnknownCertificate(test *testing.T) { } func TestGetTLSCertificateSecret(test *testing.T) { - handler := NewServerHandler(&options.Options{ + handler := NewServerHandler(&sc.Options{ KeyDir: "data", CertDir: "data", Domain: "athenz", }) - svc := options.Service{ + svc := sc.Service{ Name: "api", } _, err := handler.getTLSCertificateSecret("spiffe://athenz/sa/api", &svc) @@ -243,7 +243,7 @@ func TestGetTLSCertificateSecret(test *testing.T) { } func TestGetTLSCABundleSecretInvalidNamespace(test *testing.T) { - handler := NewServerHandler(&options.Options{}) + handler := NewServerHandler(&sc.Options{}) _, err := handler.getTLSCABundleSecret("spiffe://athenz/ca/default", "coretech", "default") if err == nil { test.Errorf("certificate generated for invalid namespace") @@ -251,7 +251,7 @@ func TestGetTLSCABundleSecretInvalidNamespace(test *testing.T) { } func TestGetTLSCABundleSecretInvalidName(test *testing.T) { - handler := NewServerHandler(&options.Options{}) + handler := NewServerHandler(&sc.Options{}) _, err := handler.getTLSCABundleSecret("spiffe://athenz/ca/default", "athenz", "primary") if err == nil { test.Errorf("certificate generated for invalid name") @@ -259,7 +259,7 @@ func TestGetTLSCABundleSecretInvalidName(test *testing.T) { } func TestGetTLSCABundleSecretInvalidFile(test *testing.T) { - handler := NewServerHandler(&options.Options{ + handler := NewServerHandler(&sc.Options{ AthenzCACertFile: "unknown-file", }) _, err := handler.getTLSCABundleSecret("spiffe://athenz/ca/default", "athenz", "default") @@ -269,7 +269,7 @@ func TestGetTLSCABundleSecretInvalidFile(test *testing.T) { } func TestGetTLSCABundleSecret(test *testing.T) { - handler := NewServerHandler(&options.Options{ + handler := NewServerHandler(&sc.Options{ AthenzCACertFile: "data/ca.cert.pem", }) _, err := handler.getTLSCABundleSecret("spiffe://athenz/ca/default", "athenz", "default") diff --git a/libs/go/sia/sds/sds.go b/libs/go/sia/sds/sds.go index ec9f9d4c67f..4eb76498127 100644 --- a/libs/go/sia/sds/sds.go +++ b/libs/go/sia/sds/sds.go @@ -19,14 +19,14 @@ package sds import ( "errors" "fmt" - "github.com/AthenZ/athenz/libs/go/sia/options" + sc "github.com/AthenZ/athenz/libs/go/sia/config" envoySecret "github.com/envoyproxy/go-control-plane/envoy/service/secret/v3" "google.golang.org/grpc" "log" "os" ) -func StartGrpcServer(opts *options.Options, certUpdates chan bool) error { +func StartGrpcServer(opts *sc.Options, certUpdates chan bool) error { listener, err := StartUdsListener(opts.SDSUdsPath) if err != nil { diff --git a/provider/aws/sia-ec2/authn.go b/provider/aws/sia-ec2/authn.go index 36ca6f0138e..3c14fe8fb4a 100644 --- a/provider/aws/sia-ec2/authn.go +++ b/provider/aws/sia-ec2/authn.go @@ -18,13 +18,14 @@ package sia import ( "encoding/json" - "github.com/AthenZ/athenz/libs/go/sia/aws/options" - "github.com/AthenZ/athenz/libs/go/sia/util" "log" "os" "time" "github.com/AthenZ/athenz/libs/go/sia/aws/meta" + "github.com/AthenZ/athenz/libs/go/sia/aws/options" + sc "github.com/AthenZ/athenz/libs/go/sia/config" + "github.com/AthenZ/athenz/libs/go/sia/util" ) func getDocValue(docMap map[string]interface{}, key string) string { @@ -93,7 +94,7 @@ func GetECSOnEC2TaskId() string { return taskId } -func GetEC2Config(configFile, profileConfigFile, profileRestrictToKey, metaEndpoint string, useRegionalSTS bool, region, account string) (*options.Config, *options.ConfigAccount, *options.AccessProfileConfig, error) { +func GetEC2Config(configFile, profileConfigFile, profileRestrictToKey, metaEndpoint string, useRegionalSTS bool, region, account string) (*sc.Config, *sc.ConfigAccount, *sc.AccessProfileConfig, error) { config, configAccount, err := options.InitFileConfig(configFile, metaEndpoint, useRegionalSTS, region, account) if err != nil { log.Printf("Unable to process configuration file '%s': %v\n", configFile, err) @@ -124,7 +125,7 @@ func GetEC2Config(configFile, profileConfigFile, profileRestrictToKey, metaEndpo return config, configAccount, profileConfig, nil } -func GetEC2AccessProfile(configFile, profileRestrictToKey, metaEndpoint string, useRegionalSTS bool, region string) (*options.AccessProfileConfig, error) { +func GetEC2AccessProfile(configFile, profileRestrictToKey, metaEndpoint string, useRegionalSTS bool, region string) (*sc.AccessProfileConfig, error) { accessProfileConfig, err := options.InitAccessProfileFileConfig(configFile) if err != nil { log.Printf("Unable to process configuration file '%s': %v\n", configFile, err) diff --git a/provider/aws/sia-eks/authn.go b/provider/aws/sia-eks/authn.go index 7c0ade4629b..db587e47da3 100644 --- a/provider/aws/sia-eks/authn.go +++ b/provider/aws/sia-eks/authn.go @@ -21,6 +21,7 @@ import ( "os" "github.com/AthenZ/athenz/libs/go/sia/aws/options" + sc "github.com/AthenZ/athenz/libs/go/sia/config" ) func GetEKSPodId() string { @@ -31,7 +32,7 @@ func GetEKSPodId() string { return podId } -func GetEKSConfig(configFile, profileConfigFile, metaEndpoint string, useRegionalSTS bool, region string) (*options.Config, *options.ConfigAccount, *options.AccessProfileConfig, error) { +func GetEKSConfig(configFile, profileConfigFile, metaEndpoint string, useRegionalSTS bool, region string) (*sc.Config, *sc.ConfigAccount, *sc.AccessProfileConfig, error) { config, configAccount, err := options.InitFileConfig(configFile, metaEndpoint, useRegionalSTS, region, "") if err != nil { @@ -66,7 +67,7 @@ func GetEKSConfig(configFile, profileConfigFile, metaEndpoint string, useRegiona return config, configAccount, nil, nil } -func GetEKSAccessProfile(configFile, metaEndpoint string, useRegionalSTS bool, region string) (*options.AccessProfileConfig, error) { +func GetEKSAccessProfile(configFile, metaEndpoint string, useRegionalSTS bool, region string) (*sc.AccessProfileConfig, error) { accessProfileConfig, err := options.InitAccessProfileFileConfig(configFile) if err != nil { log.Printf("Unable to process user access management configuration file '%s': %v\n", configFile, err) diff --git a/provider/aws/sia-fargate/authn.go b/provider/aws/sia-fargate/authn.go index 9d1df9e39b1..f303a809aa8 100644 --- a/provider/aws/sia-fargate/authn.go +++ b/provider/aws/sia-fargate/authn.go @@ -24,6 +24,7 @@ import ( "github.com/AthenZ/athenz/libs/go/sia/aws/doc" "github.com/AthenZ/athenz/libs/go/sia/aws/meta" "github.com/AthenZ/athenz/libs/go/sia/aws/options" + sc "github.com/AthenZ/athenz/libs/go/sia/config" "github.com/AthenZ/athenz/libs/go/sia/util" ) @@ -49,7 +50,7 @@ func GetFargateData(metaEndPoint string) (string, string, string, error) { return util.ParseTaskArn(taskArn) } -func initTaskConfig(config *options.Config, metaEndpoint string) (*options.Config, *options.ConfigAccount, error) { +func initTaskConfig(config *sc.Config, metaEndpoint string) (*sc.Config, *sc.ConfigAccount, error) { uri := os.Getenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI") if uri == "" { return nil, nil, fmt.Errorf("cannot fetch AWS_CONTAINER_CREDENTIALS_RELATIVE_URI env variable") @@ -73,10 +74,10 @@ func initTaskConfig(config *options.Config, metaEndpoint string) (*options.Confi // config file in which case we're not going to override any // of the settings. if config == nil { - config = &options.Config{} + config = &sc.Config{} } config.Service = service - return config, &options.ConfigAccount{ + return config, &sc.ConfigAccount{ Account: account, Domain: domain, Service: service, @@ -84,7 +85,7 @@ func initTaskConfig(config *options.Config, metaEndpoint string) (*options.Confi }, nil } -func GetFargateConfig(configFile, metaEndpoint string, useRegionalSTS bool, account, region string) (*options.Config, *options.ConfigAccount, error) { +func GetFargateConfig(configFile, metaEndpoint string, useRegionalSTS bool, account, region string) (*sc.Config, *sc.ConfigAccount, error) { config, configAccount, err := options.InitFileConfig(configFile, metaEndpoint, useRegionalSTS, account, region) if err != nil { diff --git a/provider/gcp/sia-gce/authn.go b/provider/gcp/sia-gce/authn.go index e5ec60bd194..ce3ebcb2af1 100644 --- a/provider/gcp/sia-gce/authn.go +++ b/provider/gcp/sia-gce/authn.go @@ -17,14 +17,16 @@ package sia import ( + "log" + + sc "github.com/AthenZ/athenz/libs/go/sia/config" "github.com/AthenZ/athenz/libs/go/sia/host/provider" "github.com/AthenZ/athenz/libs/go/sia/options" - "log" ) -func GetGCEConfig(configFile, profileConfigFile, metaEndpoint, region string, provider provider.Provider) (*options.Config, *options.AccessProfileConfig, error) { +func GetGCEConfig(configFile, profileConfigFile, metaEndpoint, region string, provider provider.Provider) (*sc.Config, *sc.AccessProfileConfig, error) { - var profileConfig *options.AccessProfileConfig + var profileConfig *sc.AccessProfileConfig config, _, err := options.InitFileConfig(configFile, metaEndpoint, false, region, "", provider) if err != nil { log.Printf("Unable to process configuration file '%s': %v\n", configFile, err) @@ -51,7 +53,7 @@ func GetGCEConfig(configFile, profileConfigFile, metaEndpoint, region string, pr return config, profileConfig, nil } -func GetGCEAccessProfile(configFile, metaEndpoint string, provider provider.Provider) (*options.AccessProfileConfig, error) { +func GetGCEAccessProfile(configFile, metaEndpoint string, provider provider.Provider) (*sc.AccessProfileConfig, error) { accessProfileConfig, err := options.InitAccessProfileFileConfig(configFile) if err != nil { log.Printf("Unable to process user access management configuration file '%s': %v\n", configFile, err) diff --git a/provider/gcp/sia-gce/cmd/siad/main.go b/provider/gcp/sia-gce/cmd/siad/main.go index 9176b015801..3a4be678961 100644 --- a/provider/gcp/sia-gce/cmd/siad/main.go +++ b/provider/gcp/sia-gce/cmd/siad/main.go @@ -19,15 +19,16 @@ package main import ( "flag" "fmt" - "github.com/AthenZ/athenz/libs/go/sia/ssh/hostkey" - "github.com/AthenZ/athenz/libs/go/sia/util" "log" "os" "strings" "github.com/AthenZ/athenz/libs/go/sia/agent" + sc "github.com/AthenZ/athenz/libs/go/sia/config" "github.com/AthenZ/athenz/libs/go/sia/gcp/meta" "github.com/AthenZ/athenz/libs/go/sia/options" + "github.com/AthenZ/athenz/libs/go/sia/ssh/hostkey" + "github.com/AthenZ/athenz/libs/go/sia/util" "github.com/AthenZ/athenz/provider/gcp/sia-gce" ) @@ -99,7 +100,7 @@ func main() { } // backward compatibility sake, keeping the ConfigAccount struct - configAccount := &options.ConfigAccount{ + configAccount := &sc.ConfigAccount{ Name: fmt.Sprintf("%s.%s", config.Domain, config.Service), User: config.User, Group: config.Group, diff --git a/provider/gcp/sia-gke/authn.go b/provider/gcp/sia-gke/authn.go index 89c477f34ae..10162243b42 100644 --- a/provider/gcp/sia-gke/authn.go +++ b/provider/gcp/sia-gke/authn.go @@ -20,6 +20,7 @@ import ( "log" "os" + sc "github.com/AthenZ/athenz/libs/go/sia/config" "github.com/AthenZ/athenz/libs/go/sia/host/provider" "github.com/AthenZ/athenz/libs/go/sia/options" ) @@ -37,7 +38,7 @@ func GetGKEPodId() string { return podId } -func GetGKEConfig(configFile, profileConfigFile, metaEndpoint, region string, provider provider.Provider) (*options.Config, *options.AccessProfileConfig, error) { +func GetGKEConfig(configFile, profileConfigFile, metaEndpoint, region string, provider provider.Provider) (*sc.Config, *sc.AccessProfileConfig, error) { config, _, err := options.InitFileConfig(configFile, metaEndpoint, false, region, "", provider) if err != nil { @@ -66,7 +67,7 @@ func GetGKEConfig(configFile, profileConfigFile, metaEndpoint, region string, pr return config, nil, nil } -func GetGKEAccessProfile(configFile, metaEndpoint string, provider provider.Provider) (*options.AccessProfileConfig, error) { +func GetGKEAccessProfile(configFile, metaEndpoint string, provider provider.Provider) (*sc.AccessProfileConfig, error) { accessProfileConfig, err := options.InitAccessProfileFileConfig(configFile) if err != nil { log.Printf("Unable to process user access management configuration file '%s': %v\n", configFile, err) diff --git a/provider/gcp/sia-gke/cmd/siad/main.go b/provider/gcp/sia-gke/cmd/siad/main.go index c1d4e4ef4c2..03cceb390e1 100644 --- a/provider/gcp/sia-gke/cmd/siad/main.go +++ b/provider/gcp/sia-gke/cmd/siad/main.go @@ -24,6 +24,7 @@ import ( "strings" "github.com/AthenZ/athenz/libs/go/sia/agent" + sc "github.com/AthenZ/athenz/libs/go/sia/config" "github.com/AthenZ/athenz/libs/go/sia/gcp/meta" "github.com/AthenZ/athenz/libs/go/sia/host/utils" "github.com/AthenZ/athenz/libs/go/sia/options" @@ -85,7 +86,7 @@ func main() { } // backward compatibility sake, keeping the ConfigAccount struct - configAccount := &options.ConfigAccount{ + configAccount := &sc.ConfigAccount{ Name: fmt.Sprintf("%s.%s", config.Domain, config.Service), User: config.User, Group: config.Group, diff --git a/provider/gcp/sia-run/authn.go b/provider/gcp/sia-run/authn.go index 8b66ac80659..10fda418383 100644 --- a/provider/gcp/sia-run/authn.go +++ b/provider/gcp/sia-run/authn.go @@ -17,12 +17,14 @@ package sia import ( + "log" + + sc "github.com/AthenZ/athenz/libs/go/sia/config" "github.com/AthenZ/athenz/libs/go/sia/host/provider" "github.com/AthenZ/athenz/libs/go/sia/options" - "log" ) -func GetRunConfig(configFile, metaEndpoint, region string, provider provider.Provider) (*options.Config, error) { +func GetRunConfig(configFile, metaEndpoint, region string, provider provider.Provider) (*sc.Config, error) { config, _, err := options.InitFileConfig(configFile, metaEndpoint, false, region, "", provider) if err != nil { diff --git a/provider/gcp/sia-run/cmd/siad/main.go b/provider/gcp/sia-run/cmd/siad/main.go index d0c7a7c2ce7..3906e536eef 100644 --- a/provider/gcp/sia-run/cmd/siad/main.go +++ b/provider/gcp/sia-run/cmd/siad/main.go @@ -24,6 +24,7 @@ import ( "strings" "github.com/AthenZ/athenz/libs/go/sia/agent" + sc "github.com/AthenZ/athenz/libs/go/sia/config" "github.com/AthenZ/athenz/libs/go/sia/gcp/meta" "github.com/AthenZ/athenz/libs/go/sia/options" "github.com/AthenZ/athenz/provider/gcp/sia-run" @@ -82,7 +83,7 @@ func main() { } // backward compatibility sake, keeping the ConfigAccount struct - configAccount := &options.ConfigAccount{ + configAccount := &sc.ConfigAccount{ Name: fmt.Sprintf("%s.%s", config.Domain, config.Service), User: config.User, Group: config.Group, diff --git a/utils/msd-agent/svc/service_aws_ec2.go b/utils/msd-agent/svc/service_aws_ec2.go index cdfa7089f31..d8e2500562b 100644 --- a/utils/msd-agent/svc/service_aws_ec2.go +++ b/utils/msd-agent/svc/service_aws_ec2.go @@ -8,6 +8,7 @@ import ( "github.com/AthenZ/athenz/libs/go/sia/aws/doc" "github.com/AthenZ/athenz/libs/go/sia/aws/meta" "github.com/AthenZ/athenz/libs/go/sia/aws/options" + sc "github.com/AthenZ/athenz/libs/go/sia/config" "github.com/AthenZ/athenz/provider/aws/sia-ec2" ) @@ -39,7 +40,7 @@ func (fetcher *EC2Fetcher) Fetch(host MsdHost, accountId string) (ServicesData, }, nil } -func ec2ToMsdService(services []options.Service) []Service { +func ec2ToMsdService(services []sc.Service) []Service { srv := make([]Service, 0) for _, service := range services { s := Service{