Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
853e72e
commit c06e767
Showing
11 changed files
with
337 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
/****************************************************************************** | ||
* | ||
* Copyright 2024 SAP SE | ||
* | ||
* 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 basic | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"os" | ||
"strings" | ||
|
||
"github.com/sapcc/keppel/internal/keppel" | ||
|
||
"gopkg.in/yaml.v2" | ||
) | ||
|
||
// AccountManagementDriver is the account management driver "basic". | ||
type AccountManagementDriver struct { | ||
configPath string | ||
config AccountConfig | ||
} | ||
|
||
type AccountConfig struct { | ||
Accounts []Accounts `yaml:"accounts"` | ||
} | ||
|
||
type Accounts struct { | ||
Name string `yaml:"name"` | ||
AuthTenantID string `yaml:"auth_tenant_id"` | ||
GCPolicies []keppel.GCPolicy `yaml:"gc_policies"` | ||
RBACPolicies []keppel.RBACPolicy `yaml:"rbac_policies"` | ||
ReplicationPolicy keppel.ReplicationPolicy `yaml:"replication_policy"` | ||
SecurityScanPolicies []keppel.SecurityScanPolicy `yaml:"security_scan_policies"` | ||
ValidationPolicy keppel.ValidationPolicy `yaml:"validation_policy"` | ||
} | ||
|
||
func init() { | ||
keppel.AccountManagementDriverRegistry.Add(func() keppel.AccountManagementDriver { | ||
return &AccountManagementDriver{} | ||
}) | ||
} | ||
|
||
// PluginTypeID implements the keppel.AccountManagementDriver interface. | ||
func (a *AccountManagementDriver) PluginTypeID() string { return "basic" } | ||
|
||
// ConfigureAccount implements the keppel.AccountManagementDriver interface. | ||
func (a *AccountManagementDriver) Init(envToConfigFile string) error { | ||
a.configPath = os.Getenv("KEPPEL_ACCOUNT_MANAGEMENT_FILE") | ||
if a.configPath == "" { | ||
return errors.New("KEPPEL_ACCOUNT_MANAGEMENT_FILE is not set") | ||
} | ||
|
||
return a.loadConfig() | ||
} | ||
|
||
// ConfigureAccount implements the keppel.AccountManagementDriver interface. | ||
func (a *AccountManagementDriver) ConfigureAccount(db *keppel.DB, account keppel.Account) (keppel.Account, error) { | ||
for _, cfgAccount := range a.config.Accounts { | ||
if cfgAccount.AuthTenantID != account.AuthTenantID { | ||
continue | ||
} | ||
|
||
account.IsManaged = true | ||
account.RequiredLabels = strings.Join(cfgAccount.ValidationPolicy.RequiredLabels, ",") | ||
|
||
gcPolicyJSON, err := json.Marshal(cfgAccount.GCPolicies) | ||
if err != nil { | ||
return keppel.Account{}, fmt.Errorf("gc_policies: %w", err) | ||
} | ||
account.GCPoliciesJSON = string(gcPolicyJSON) | ||
|
||
rbacPolicyJSON, err := json.Marshal(cfgAccount.RBACPolicies) | ||
if err != nil { | ||
return keppel.Account{}, fmt.Errorf("rbac_policies: %w", err) | ||
} | ||
account.RBACPoliciesJSON = string(rbacPolicyJSON) | ||
|
||
securityScanPoliciesJSON, err := json.Marshal(cfgAccount.SecurityScanPolicies) | ||
if err != nil { | ||
return keppel.Account{}, fmt.Errorf("security_scan_policies: %w", err) | ||
} | ||
account.SecurityScanPoliciesJSON = string(securityScanPoliciesJSON) | ||
|
||
_, err = cfgAccount.ReplicationPolicy.ApplyToAccount(db, &account) | ||
if err != nil { | ||
return keppel.Account{}, fmt.Errorf("replication_policy: %w", err) | ||
} | ||
|
||
return account, nil | ||
} | ||
|
||
// we didn't find the account, delete it | ||
return keppel.Account{}, nil | ||
} | ||
|
||
func (a *AccountManagementDriver) ManagedAccountNames() ([]string, error) { | ||
err := a.loadConfig() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var accounts []string | ||
for _, account := range a.config.Accounts { | ||
accounts = append(accounts, account.Name) | ||
} | ||
|
||
return accounts, nil | ||
} | ||
|
||
func (a *AccountManagementDriver) loadConfig() error { | ||
reader, err := os.Open(a.configPath) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
decoder := yaml.NewDecoder(reader) | ||
decoder.SetStrict(true) | ||
var config AccountConfig | ||
err = decoder.Decode(&config) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
a.config = config | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/****************************************************************************** | ||
* | ||
* Copyright 2024 SAP SE | ||
* | ||
* 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 basic | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/sapcc/keppel/internal/keppel" | ||
|
||
"github.com/sapcc/go-bits/assert" | ||
) | ||
|
||
func TestAccountManagementDriver(t *testing.T) { | ||
driver := AccountManagementDriver{ | ||
configPath: "./fixtures/account_management.yaml", | ||
} | ||
|
||
listOfAccounts, err := driver.ManagedAccountNames() | ||
if err != nil { | ||
t.Fatalf(err.Error()) | ||
} | ||
assert.DeepEqual(t, "account", listOfAccounts, []string{"abcde"}) | ||
|
||
account := keppel.Account{ | ||
IsManaged: true, | ||
Name: "abcde", | ||
AuthTenantID: "1245", | ||
} | ||
newAccount, err := driver.ConfigureAccount(nil, account) | ||
if err != nil { | ||
t.Fatalf(err.Error()) | ||
} | ||
|
||
expectedAccount := keppel.Account{ | ||
Name: "abcde", | ||
AuthTenantID: "1245", | ||
ExternalPeerURL: "registry-tertiary.example.org", | ||
RequiredLabels: "important-label,some-label", | ||
IsManaged: true, | ||
RBACPoliciesJSON: "[{\"match_repository\":\"library/.*\",\"permissions\":[\"anonymous_pull\"]},{\"match_repository\":\"library/alpine\",\"match_username\":\".*@tenant2\",\"permissions\":[\"pull\",\"push\"]}]", | ||
GCPoliciesJSON: "[{\"match_repository\":\".*/database\",\"except_repository\":\"archive/.*\",\"time_constraint\":{\"on\":\"pushed_at\",\"newer_than\":{\"value\":6,\"unit\":\"h\"}},\"action\":\"protect\"},{\"match_repository\":\".*\",\"only_untagged\":true,\"action\":\"delete\"}]", | ||
SecurityScanPoliciesJSON: "[{\"match_repository\":\".*\",\"match_vulnerability_id\":\".*\",\"except_fix_released\":true,\"action\":{\"assessment\":\"risk accepted: vulnerabilities without an available fix are not actionable\",\"ignore\":true}}]", | ||
} | ||
assert.DeepEqual(t, "account", newAccount, expectedAccount) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
accounts: | ||
- name: abcde | ||
auth_tenant_id: "1245" | ||
gc_policies: | ||
- match_repository: .*/database | ||
except_repository: archive/.* | ||
time_constraint: | ||
"on": pushed_at | ||
newer_than: 21600000000000 | ||
action: protect | ||
- match_repository: .* | ||
only_untagged: true | ||
action: delete | ||
rbac_policies: | ||
- match_repository: library/.* | ||
permissions: | ||
- anonymous_pull | ||
- match_repository: library/alpine | ||
match_username: .*@tenant2 | ||
permissions: | ||
- pull | ||
- push | ||
replication_policy: | ||
strategy: from_external_on_first_use | ||
upstream_peer_hostname: "" | ||
external_peer: | ||
url: registry-tertiary.example.org | ||
security_scan_policies: | ||
- match_repository: .* | ||
match_vulnerability_id: .* | ||
except_fix_released: true | ||
action: | ||
assessment: 'risk accepted: vulnerabilities without an available fix are not actionable' | ||
ignore: true | ||
validation_policy: | ||
required_labels: | ||
- important-label | ||
- some-label |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/****************************************************************************** | ||
* | ||
* Copyright 2024 SAP SE | ||
* | ||
* 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 keppel | ||
|
||
import ( | ||
"github.com/sapcc/go-bits/pluggable" | ||
) | ||
|
||
// AccountManagementDriver is a pluggable interface for receiving account | ||
// configuration from an external system. Accounts can either be managed by | ||
// this driver, or created and maintained by users through the Keppel API. | ||
type AccountManagementDriver interface { | ||
pluggable.Plugin | ||
// Init is called before any other interface methods, and allows the plugin to | ||
// perform first-time initialization. | ||
Init(string) error | ||
|
||
// Called by a jobloop for every account every once in a while (e.g. every hour). | ||
// | ||
// Returns an updated account object if the account is managed. | ||
// The jobloop will apply the account in the DB accordingly. | ||
// | ||
// Returns Account{} if the account was managed, and now shall be deleted. | ||
// The jobloop will clean up the manifests, blobs, repos and the account. | ||
ConfigureAccount(*DB, Account) (Account, error) | ||
|
||
// Called by a jobloop every once in a while (e.g. every hour). | ||
// | ||
// If new names appear in the list, the jobloop will create the | ||
// respective accounts as configured by ConfigureAccount(). | ||
ManagedAccountNames() ([]string, error) | ||
} | ||
|
||
// AccountManagementDriverRegistry is a pluggable.Registry for AccountManagementDriver implementations. | ||
var AccountManagementDriverRegistry pluggable.Registry[AccountManagementDriver] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.