From 09d18e5d3ab13060436318d70aebc00551eeef3f Mon Sep 17 00:00:00 2001 From: Adam Mohammed Date: Wed, 18 Sep 2024 12:34:32 -0400 Subject: [PATCH] Permissions docs --- equinix/design/permissions-migration/home.org | 11 + .../permissions-initial-design.org | 88 ++++++ .../design/permissions-migration/policy.org | 286 ++++++++++++++++++ .../permissions-migration/test-plan.org | 23 ++ 4 files changed, 408 insertions(+) create mode 100644 equinix/design/permissions-migration/home.org create mode 100644 equinix/design/permissions-migration/permissions-initial-design.org create mode 100644 equinix/design/permissions-migration/policy.org create mode 100644 equinix/design/permissions-migration/test-plan.org diff --git a/equinix/design/permissions-migration/home.org b/equinix/design/permissions-migration/home.org new file mode 100644 index 0000000..f4c5d75 --- /dev/null +++ b/equinix/design/permissions-migration/home.org @@ -0,0 +1,11 @@ +#+TITLE: Permissions Migration Home +#+AUTHOR: Adam Mohammed +#+DATE: September 18, 2024 + +* Initial Design Doc + +* Test Plan + +* Architecture Design Review doc + +* Permissions Overview for Handbook diff --git a/equinix/design/permissions-migration/permissions-initial-design.org b/equinix/design/permissions-migration/permissions-initial-design.org new file mode 100644 index 0000000..7da4d3c --- /dev/null +++ b/equinix/design/permissions-migration/permissions-initial-design.org @@ -0,0 +1,88 @@ +#+TITLE: Permissions Redesign +#+AUTHOR: Adam Mohammed + +* Overview + +This document describes what granularity we'll have available for MVP +when using permissions-api as the policy decision point (PDP). + +* Top-Level Resources + +** User Based Resources + +- User +- APIKeys (bound to user) + +** Project Level Resources + +- Project (Read/update/delete) +- Instances +- Appliances +- Reservations (aka Hardware Reservations) +- Document +- IP Reservation +- IP Address +- IP Assignment +- Virtual Network +- Virtual Circuit +- Interconnection (Read/update) +- VRF +- Membership +- Invitations +- BGP Sessions +- BGP Configs +- Project API Keys + +*** Lower-tier resources + +- BGPDynamicNeighbors authorizes through MetalGateway +- ElasticIps authorizes through MetalGateway + +- VRFIPReservation authorizes through VRF +- VRFLearnedRoutes authorizes through VRF +- VRFBGPNeighbors authorizes through VRF +- VRFStaticRoutes authorizes Through VRF + +- Authorizes through Instance: + - Actions (reboot/power-cycle) (create, list) + - Ip Assignments (create, list only) + - Traffic (index only) + - Termination (POST only) + - BGPSessions (CRUD) + - BGPNeighbors (index only) + - Bandwidth (index only) + - SSH-keys (index only) + - Diagnostics (Read only) + - Metadata (read only) + - Userdata (read only) + - Error reports (create, read) + + + +** Organization Level Resources + +- Organization +- Project (create-only) +- Interconnection (create/delete) + + + +** Weird ones + +BGP Config Requestss +2FA enforce + +* Phase 2 + + +We decided to just throw actions on organizations/projects/user + + +ok, so I can configure the check access to dump out the context I need. + +For every controller + action, I need: +The resource type the permission check is on +The action name that the check requires + + +With that I can produce the policy that we need on the Permissions API side diff --git a/equinix/design/permissions-migration/policy.org b/equinix/design/permissions-migration/policy.org new file mode 100644 index 0000000..60f60ef --- /dev/null +++ b/equinix/design/permissions-migration/policy.org @@ -0,0 +1,286 @@ +#+TITLE: Metal API Policy +#+AUTHOR: Adam Mohammed +* How to produce this information? +Using this snippet placed in =config/initializers/packet.rb= +#+begin_src ruby + def permission_logger + Class.new do + def initialize(db) + @db = db + end + + def permissions_sql + <<-SQL + INSERT INTO metal_permissions ( + controller_name, path, resource, action + ) VALUES ( ?, ?, ?, ?); + SQL + end + + def check_access(*args, **kwargs) + context = kwargs[:context] + controller = context[:controller] + path = controller.request.path + controller_action = "#{controller.class.name}##{controller.action_name}" + @db.execute(permissions_sql, [controller_action, path, args[1], args[2]]) + true + end + end + end + + def permissions_checker + ::Authorization::PolicyEngine::IAMChecker.new(client: permission_logger.new(sqlite_db)) + end + +#+end_src + +I then pushed a branch so the standard CI pipeline build would spit out +a DB with the results: + +#+begin_src diff +modified .buildkite/pipeline.yaml +@@ -96,6 +96,8 @@ steps: + commands: + - /home/packet/api/.buildkite/script/parallel_test_setup.sh + - /home/packet/api/.buildkite/script/cucumber-container.sh ++ artifact_paths: ++ - 'test.db' + retry: + automatic: + - exit_status: "*" +@@ -109,6 +111,7 @@ steps: + env: + BUILD_NUMBER: ${BUILDKITE_BUILD_NUMBER} + API_BUILD_IMAGE: ${API_BUILD_IMAGE} ++ POLICY_ENGINE: "cancancan_wins" + + - label: Rspec + key: "rspec" +@@ -117,6 +120,8 @@ steps: + commands: + - /home/packet/api/.buildkite/script/parallel_test_setup.sh + - /home/packet/api/.buildkite/script/rspec-container.sh ++ artifact_paths: ++ - 'test.db' + retry: + automatic: + - exit_status: "*" +@@ -132,6 +137,7 @@ steps: + env: + BUILD_NUMBER: ${BUILDKITE_BUILD_NUMBER} + API_BUILD_IMAGE: ${API_BUILD_IMAGE} ++ POLICY_ENGINE: "cancancan_wins" + + - label: Build spec image + key: "spec-build" + +#+end_src + +If later you want to combine these dbs you can do so as follows: + +1. Download =test.db= from the rspec step and name it =test-rspec.db= +2. Download =test.db= from the cucumber step and name it =test-cucumber.db= +3. Create the merged =test-full-suite.db= +#+begin_src bash + $ cp test-rspec.db test-full-suite.db + $ sqlite3 'test-full-suite.db' + sqlite> ATTACH 'test-cucumber' AS cuke + sqlite> BEGIN; + sqlite> INSERT INTO metal_permissions SELECT * FROM cuke.metal_permissions; + sqlite> COMMIT; + sqlite> DETACH cuke; + sqlite> .quit +#+end_src + + +* Organization actions +#+begin_src +metal_billing_information_get +metal_billing_information_update +metal_capability_list +metal_coupon_usage_list +metal_coupon_usage_redeem +metal_credit_create +metal_credit_delete +metal_credit_list +metal_discount_create +metal_enforce_2fa_create +metal_instances_listing_list +metal_interconnection_create +metal_interconnection_delete +metal_interconnection_get +metal_interconnection_list +metal_interconnection_port_get +metal_interconnection_port_list +metal_interconnection_update +metal_interconnection_virtual_circuit_create +metal_interconnection_virtual_circuit_list +metal_interconnection_virtual_circuit_update +metal_invitation_create +metal_invitation_delete +metal_invitation_get +metal_invitation_list +metal_invitation_resend +metal_invitation_update +metal_ip_address_delete +metal_ip_address_get +metal_lab_get +metal_leave_organization_create +metal_member_delete +metal_member_list +metal_member_update +metal_membership_delete +metal_membership_update +metal_organization_create +metal_organization_delete +metal_organization_get +metal_organization_logos +metal_organization_update +metal_payment_get +metal_payment_method_create +metal_payment_method_delete +metal_payment_method_get +metal_payment_method_list +metal_payment_method_update +metal_project_create +metal_search_search_plans +metal_tier_inquiry_create +metal_vendor_list +#+end_src + +* Project actions + +#+begin_src +metal_acl_list +metal_activate_create +metal_allocation_list +metal_batch_delete +metal_batch_get +metal_batch_list +metal_bgp_config_delete +metal_bgp_config_request_create +metal_bgp_config_update +metal_bgp_config_view +metal_bgp_dynamic_neighbor_create +metal_bgp_dynamic_neighbor_list +metal_bgp_neighbor_list +metal_bgp_session_create +metal_bgp_session_delete +metal_bgp_session_get +metal_bgp_session_list +metal_bgp_session_update +metal_discount_create +metal_dn_create +metal_dn_list +metal_ecx_connection_create +metal_ecx_connection_list +metal_error_report_create +metal_error_report_get +metal_event_alert_configuration_create +metal_event_alert_configuration_get +metal_event_alert_configuration_update +metal_firmware_set_get +metal_global_bgp_range_list +metal_hardware_reservation_get +metal_health_get +metal_instance_action_create +metal_instance_action_list +metal_instance_batch_create +metal_instance_create +metal_instance_delete +metal_instance_get +metal_instance_list +metal_instance_metadatum_show_by_ip +metal_instance_password_create +metal_instance_update +metal_instances_listing_list +metal_interconnection_create +metal_interconnection_list +metal_interconnection_virtual_circuit_create +metal_interconnection_virtual_circuit_list +metal_interconnection_virtual_circuit_update +metal_invitation_create +metal_invitation_list +metal_ip_address_delete +metal_ip_address_get +metal_ip_address_update +metal_ip_assignment_create +metal_ip_assignment_list +metal_ip_availability_available +metal_ip_reservation_create +metal_ip_reservation_list +metal_ip_reservation_request_create +metal_ip_reservation_update +metal_leave_project_create +metal_license_activation_get +metal_license_create +metal_license_delete +metal_license_get +metal_license_list +metal_license_update +metal_membership_delete +metal_membership_get +metal_membership_list +metal_membership_update +metal_metal_gateway_create +metal_metal_gateway_delete +metal_metal_gateway_elastic_ip_create +metal_metal_gateway_elastic_ip_list +metal_metal_gateway_get +metal_metal_gateway_list +metal_metering_limit_create +metal_move_create +metal_project_api_key_create +metal_project_api_key_list +metal_project_create +metal_project_delete +metal_project_get +metal_project_update +metal_reservation_create +metal_reservation_get +metal_reservation_list +metal_screenshot_get +metal_spot_market_request_create +metal_spot_market_request_delete +metal_spot_market_request_get +metal_spot_market_request_list +metal_subscribed_event_create +metal_subscribed_event_delete +metal_subscribed_event_get +metal_subscribed_event_list +metal_subscribed_events_all_create +metal_subscribed_events_all_delete +metal_traffic_list +metal_transfer_request_create +metal_transfer_request_delete +metal_transfer_request_get +metal_transfer_request_update +metal_userdatum_show_by_ip +metal_virtual_network_create +metal_virtual_network_delete +metal_virtual_network_get +metal_virtual_network_list +metal_virtual_network_update +metal_vrf_create +metal_vrf_delete +metal_vrf_get +metal_vrf_list +metal_vrf_route_create +metal_vrf_route_delete +metal_vrf_route_get +metal_vrf_route_list +metal_vrf_route_update +metal_vrf_update +#+end_src + +* User actions + +#+begin_src +metal_discount_create +metal_metering_limit_create +metal_sales_report_get +metal_user_avatars +metal_user_force_verify +metal_user_get +metal_user_update +#+end_src diff --git a/equinix/design/permissions-migration/test-plan.org b/equinix/design/permissions-migration/test-plan.org new file mode 100644 index 0000000..7fa22e3 --- /dev/null +++ b/equinix/design/permissions-migration/test-plan.org @@ -0,0 +1,23 @@ +#+TITLE: Testing IAM-Runtime checks for Metal API +#+AUTHOR: Adam Mohammed + +* What's changed + +* Stages of testing +- Initial Canary + - Run terraform against internal canary URL +- Slow roll to production + - Watch for errors +- In-production warn mode + - Observe for discrepancies between cancancan/iam-runtime +- Runtime winning mode +- Completed + +* Monitoring +- Trace attributes that are relevant + +- Dashboards +- Create dashboard around cancancan disagreements +- Create dashboard where resource was not metal org/project/user + +* Handling broken cases