368 lines
10 KiB
Plaintext
368 lines
10 KiB
Plaintext
#!/usr/bin/env nu
|
||
|
||
# Configure access policies for OCI registry namespaces
|
||
|
||
export def main [
|
||
--registry-url: string = "localhost:5000"
|
||
--registry-type: string = "zot"
|
||
--admin-password: string = "admin"
|
||
] {
|
||
print "🔒 Configuring OCI registry access policies..."
|
||
print $" Registry: ($registry_url)"
|
||
print $" Type: ($registry_type)\n"
|
||
|
||
let policies = get-policy-definitions
|
||
|
||
match $registry_type {
|
||
"zot" => {
|
||
configure-zot-policies $registry_url $policies
|
||
}
|
||
"harbor" => {
|
||
configure-harbor-policies $registry_url $policies $admin_password
|
||
}
|
||
"distribution" => {
|
||
configure-distribution-policies $registry_url $policies
|
||
}
|
||
_ => {
|
||
print $"⚠ Unknown registry type: ($registry_type)"
|
||
}
|
||
}
|
||
|
||
print "\n✅ Policy configuration complete!"
|
||
}
|
||
|
||
def get-policy-definitions [] -> list {
|
||
[
|
||
{
|
||
namespace: "provisioning-extensions"
|
||
access: {
|
||
authenticated: {
|
||
read: true
|
||
write: true
|
||
delete: true
|
||
}
|
||
anonymous: {
|
||
read: false
|
||
write: false
|
||
delete: false
|
||
}
|
||
}
|
||
users: [
|
||
{name: "provisioning", role: "admin"}
|
||
{name: "developer", role: "developer"}
|
||
]
|
||
webhooks: [
|
||
{
|
||
event: "push"
|
||
url: "http://orchestrator:8080/registry/events/push"
|
||
}
|
||
{
|
||
event: "delete"
|
||
url: "http://orchestrator:8080/registry/events/delete"
|
||
}
|
||
]
|
||
}
|
||
{
|
||
namespace: "provisioning-kcl"
|
||
access: {
|
||
authenticated: {
|
||
read: true
|
||
write: true
|
||
delete: false
|
||
}
|
||
anonymous: {
|
||
read: false
|
||
write: false
|
||
delete: false
|
||
}
|
||
}
|
||
users: [
|
||
{name: "provisioning", role: "admin"}
|
||
{name: "developer", role: "developer"}
|
||
]
|
||
webhooks: [
|
||
{
|
||
event: "push"
|
||
url: "http://orchestrator:8080/registry/events/push"
|
||
}
|
||
]
|
||
}
|
||
{
|
||
namespace: "provisioning-platform"
|
||
access: {
|
||
authenticated: {
|
||
read: true
|
||
write: false
|
||
delete: false
|
||
}
|
||
anonymous: {
|
||
read: false
|
||
write: false
|
||
delete: false
|
||
}
|
||
}
|
||
users: [
|
||
{name: "provisioning", role: "admin"}
|
||
]
|
||
webhooks: [
|
||
{
|
||
event: "push"
|
||
url: "http://orchestrator:8080/registry/events/push"
|
||
}
|
||
]
|
||
}
|
||
{
|
||
namespace: "provisioning-test"
|
||
access: {
|
||
authenticated: {
|
||
read: true
|
||
write: true
|
||
delete: true
|
||
}
|
||
anonymous: {
|
||
read: true
|
||
write: false
|
||
delete: false
|
||
}
|
||
}
|
||
users: [
|
||
{name: "provisioning", role: "admin"}
|
||
{name: "developer", role: "developer"}
|
||
{name: "tester", role: "developer"}
|
||
]
|
||
webhooks: []
|
||
}
|
||
]
|
||
}
|
||
|
||
def configure-zot-policies [registry_url: string, policies: list] {
|
||
print "Configuring Zot policies..."
|
||
print "\nℹ Zot policies are configured in config.json"
|
||
print " The following policies are defined:\n"
|
||
|
||
for policy in $policies {
|
||
print $"📦 ($policy.namespace):"
|
||
print $" Authenticated users:"
|
||
print $" Read: ($policy.access.authenticated.read)"
|
||
print $" Write: ($policy.access.authenticated.write)"
|
||
print $" Delete: ($policy.access.authenticated.delete)"
|
||
print $" Anonymous users:"
|
||
print $" Read: ($policy.access.anonymous.read)"
|
||
print $" Users: ($policy.users | get name | str join ', ')"
|
||
if ($policy.webhooks | length) > 0 {
|
||
print $" Webhooks: ($policy.webhooks | length) configured"
|
||
}
|
||
print ""
|
||
}
|
||
|
||
print "ℹ To update policies, edit zot/config.json and restart the registry"
|
||
}
|
||
|
||
def configure-harbor-policies [
|
||
registry_url: string
|
||
policies: list
|
||
admin_password: string
|
||
] {
|
||
print "Configuring Harbor policies via API...\n"
|
||
|
||
let auth = $"admin:($admin_password)" | encode base64
|
||
|
||
for policy in $policies {
|
||
print $"📦 Configuring ($policy.namespace)..."
|
||
|
||
# Get project ID
|
||
let project_id = get-harbor-project-id $registry_url $policy.namespace $auth
|
||
|
||
if ($project_id | is-empty) {
|
||
print $" ⚠ Project not found, skipping"
|
||
continue
|
||
}
|
||
|
||
# Configure RBAC
|
||
configure-harbor-rbac $registry_url $project_id $policy.users $auth
|
||
|
||
# Configure webhooks
|
||
if ($policy.webhooks | length) > 0 {
|
||
configure-harbor-webhooks $registry_url $project_id $policy.webhooks $auth
|
||
}
|
||
|
||
print $" ✓ Policies configured"
|
||
print ""
|
||
}
|
||
}
|
||
|
||
def get-harbor-project-id [
|
||
registry_url: string
|
||
project_name: string
|
||
auth: string
|
||
] -> int {
|
||
let result = (do {
|
||
http get $"http://($registry_url)/api/v2.0/projects?name=($project_name)" --headers {
|
||
"Authorization": $"Basic ($auth)"
|
||
}
|
||
} | complete)
|
||
|
||
if $result.exit_code == 0 {
|
||
let projects = ($result.stdout | from json)
|
||
if ($projects | length) > 0 {
|
||
return ($projects | first | get project_id)
|
||
}
|
||
}
|
||
|
||
return null
|
||
}
|
||
|
||
def configure-harbor-rbac [
|
||
registry_url: string
|
||
project_id: int
|
||
users: list
|
||
auth: string
|
||
] {
|
||
print " Configuring RBAC..."
|
||
|
||
for user in $users {
|
||
let role_id = match $user.role {
|
||
"admin" => 1
|
||
"developer" => 2
|
||
"guest" => 3
|
||
_ => 2
|
||
}
|
||
|
||
let member_body = {
|
||
role_id: $role_id
|
||
member_user: {
|
||
username: $user.name
|
||
}
|
||
}
|
||
|
||
let result = (do {
|
||
http post $"http://($registry_url)/api/v2.0/projects/($project_id)/members" $member_body --headers {
|
||
"Authorization": $"Basic ($auth)"
|
||
"Content-Type": "application/json"
|
||
}
|
||
} | complete)
|
||
|
||
if $result.exit_code == 0 {
|
||
print $" ✓ User ($user.name) added as ($user.role)"
|
||
} else {
|
||
if ($result.stderr | str contains "already exists") {
|
||
print $" ℹ User ($user.name) already exists"
|
||
} else {
|
||
print $" ⚠ Error adding user ($user.name)"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
def configure-harbor-webhooks [
|
||
registry_url: string
|
||
project_id: int
|
||
webhooks: list
|
||
auth: string
|
||
] {
|
||
print " Configuring webhooks..."
|
||
|
||
for webhook in $webhooks {
|
||
let webhook_body = {
|
||
name: $"webhook-($webhook.event)"
|
||
description: $"Webhook for ($webhook.event) events"
|
||
enabled: true
|
||
event_types: [$webhook.event]
|
||
targets: [
|
||
{
|
||
type: "http"
|
||
address: $webhook.url
|
||
skip_cert_verify: true
|
||
}
|
||
]
|
||
}
|
||
|
||
let result = (do {
|
||
http post $"http://($registry_url)/api/v2.0/projects/($project_id)/webhook/policies" $webhook_body --headers {
|
||
"Authorization": $"Basic ($auth)"
|
||
"Content-Type": "application/json"
|
||
}
|
||
} | complete)
|
||
|
||
if $result.exit_code == 0 {
|
||
print $" ✓ Webhook for ($webhook.event) configured"
|
||
} else {
|
||
print $" ⚠ Error configuring webhook for ($webhook.event)"
|
||
}
|
||
}
|
||
}
|
||
|
||
def configure-distribution-policies [registry_url: string, policies: list] {
|
||
print "Configuring Distribution policies...\n"
|
||
print "ℹ Distribution uses htpasswd for authentication"
|
||
print " Generating htpasswd file...\n"
|
||
|
||
# Collect all unique users
|
||
let all_users = ($policies | each { |p| $p.users } | flatten | get name | uniq)
|
||
|
||
print "Users to create:"
|
||
for user in $all_users {
|
||
print $" - ($user)"
|
||
}
|
||
|
||
print "\nℹ To create users, run:"
|
||
print " htpasswd -Bc htpasswd <username>"
|
||
print "\nℹ Access control is enforced at the authentication layer"
|
||
print " All authenticated users can push/pull"
|
||
print " Anonymous users can only pull if allowed in config.yml"
|
||
}
|
||
|
||
# Helper: Show policy for namespace
|
||
export def "policy show" [
|
||
namespace: string
|
||
] {
|
||
let policies = get-policy-definitions
|
||
let policy = ($policies | where namespace == $namespace | first)
|
||
|
||
if ($policy | is-empty) {
|
||
print $"Policy for namespace ($namespace) not found"
|
||
return
|
||
}
|
||
|
||
print $"Policy for: ($policy.namespace)\n"
|
||
|
||
print "Access Control:"
|
||
print " Authenticated users:"
|
||
print $" Read: ($policy.access.authenticated.read)"
|
||
print $" Write: ($policy.access.authenticated.write)"
|
||
print $" Delete: ($policy.access.authenticated.delete)"
|
||
print " Anonymous users:"
|
||
print $" Read: ($policy.access.anonymous.read)"
|
||
print $" Write: ($policy.access.anonymous.write)"
|
||
print $" Delete: ($policy.access.anonymous.delete)"
|
||
|
||
print "\nUsers:"
|
||
for user in $policy.users {
|
||
print $" - ($user.name) (($user.role))"
|
||
}
|
||
|
||
if ($policy.webhooks | length) > 0 {
|
||
print "\nWebhooks:"
|
||
for webhook in $policy.webhooks {
|
||
print $" - Event: ($webhook.event)"
|
||
print $" URL: ($webhook.url)"
|
||
}
|
||
}
|
||
}
|
||
|
||
# Helper: List all policies
|
||
export def "policy list" [] -> table {
|
||
let policies = get-policy-definitions
|
||
|
||
$policies | each { |p|
|
||
{
|
||
namespace: $p.namespace
|
||
auth_read: $p.access.authenticated.read
|
||
auth_write: $p.access.authenticated.write
|
||
anon_read: $p.access.anonymous.read
|
||
users: ($p.users | length)
|
||
webhooks: ($p.webhooks | length)
|
||
}
|
||
}
|
||
}
|