diff --git a/api/agent/api.go b/api/agent/api.go index 0a6f9287..f3bcf74d 100644 --- a/api/agent/api.go +++ b/api/agent/api.go @@ -11,6 +11,7 @@ import ( bundle "github.com/spiffe/spire-api-sdk/proto/spire/api/server/bundle/v1" debugServer "github.com/spiffe/spire-api-sdk/proto/spire/api/server/debug/v1" entry "github.com/spiffe/spire-api-sdk/proto/spire/api/server/entry/v1" + trustdomain "github.com/spiffe/spire-api-sdk/proto/spire/api/server/trustdomain/v1" types "github.com/spiffe/spire-api-sdk/proto/spire/api/types" "google.golang.org/grpc/health/grpc_health_v1" @@ -322,6 +323,92 @@ func (s *Server) DeleteFederatedBundle(inp DeleteFederatedBundleRequest) (*Delet return (*DeleteFederatedBundleResponse)(bundle), nil } +// Federation APIs +type ListFederationRelationshipsRequest trustdomain.ListFederationRelationshipsRequest +type ListFederationRelationshipsResponse trustdomain.ListFederationRelationshipsResponse + +func (s *Server) ListFederationRelationships(inp ListFederationRelationshipsRequest) (*ListFederationRelationshipsResponse, error) { //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet + inpReq := trustdomain.ListFederationRelationshipsRequest(inp) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet + var conn *grpc.ClientConn + conn, err := grpc.Dial(s.SpireServerAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return nil, err + } + defer conn.Close() + client := trustdomain.NewTrustDomainClient(conn) + + bundle, err := client.ListFederationRelationships(context.Background(), &inpReq) + if err != nil { + return nil, err + } + + return (*ListFederationRelationshipsResponse)(bundle), nil +} + +type CreateFederationRelationshipRequest trustdomain.BatchCreateFederationRelationshipRequest +type CreateFederationRelationshipResponse trustdomain.BatchCreateFederationRelationshipResponse + +func (s *Server) CreateFederationRelationship(inp CreateFederationRelationshipRequest) (*CreateFederationRelationshipResponse, error) { //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet + inpReq := trustdomain.BatchCreateFederationRelationshipRequest(inp) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet + var conn *grpc.ClientConn + conn, err := grpc.Dial(s.SpireServerAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return nil, err + } + defer conn.Close() + client := trustdomain.NewTrustDomainClient(conn) + + bundle, err := client.BatchCreateFederationRelationship(context.Background(), &inpReq) + if err != nil { + return nil, err + } + + return (*CreateFederationRelationshipResponse)(bundle), nil +} + +type UpdateFederationRelationshipRequest trustdomain.BatchUpdateFederationRelationshipRequest +type UpdateFederationRelationshipResponse trustdomain.BatchUpdateFederationRelationshipResponse + +func (s *Server) UpdateFederationRelationship(inp UpdateFederationRelationshipRequest) (*UpdateFederationRelationshipResponse, error) { //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet + inpReq := trustdomain.BatchUpdateFederationRelationshipRequest(inp) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet + var conn *grpc.ClientConn + conn, err := grpc.Dial(s.SpireServerAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return nil, err + } + defer conn.Close() + client := trustdomain.NewTrustDomainClient(conn) + + bundle, err := client.BatchUpdateFederationRelationship(context.Background(), &inpReq) + if err != nil { + return nil, err + } + + return (*UpdateFederationRelationshipResponse)(bundle), nil +} + +type DeleteFederationRelationshipRequest trustdomain.BatchDeleteFederationRelationshipRequest +type DeleteFederationRelationshipResponse trustdomain.BatchDeleteFederationRelationshipResponse + +func (s *Server) DeleteFederationRelationship(inp DeleteFederationRelationshipRequest) (*DeleteFederationRelationshipResponse, error) { //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet + inpReq := trustdomain.BatchDeleteFederationRelationshipRequest(inp) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet + var conn *grpc.ClientConn + conn, err := grpc.Dial(s.SpireServerAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return nil, err + } + defer conn.Close() + client := trustdomain.NewTrustDomainClient(conn) + + bundle, err := client.BatchDeleteFederationRelationship(context.Background(), &inpReq) + if err != nil { + return nil, err + } + + return (*DeleteFederationRelationshipResponse)(bundle), nil +} + + /* Agent diff --git a/api/agent/server.go b/api/agent/server.go index cc5a9e85..198a5257 100644 --- a/api/agent/server.go +++ b/api/agent/server.go @@ -20,6 +20,9 @@ import ( "github.com/hashicorp/hcl/hcl/token" "github.com/pkg/errors" + "google.golang.org/protobuf/encoding/protojson" + trustdomain "github.com/spiffe/spire-api-sdk/proto/spire/api/server/trustdomain/v1" + "github.com/spiffe/tornjak/pkg/agent/authentication/authenticator" "github.com/spiffe/tornjak/pkg/agent/authorization" agentdb "github.com/spiffe/tornjak/pkg/agent/db" @@ -607,6 +610,174 @@ func (s *Server) federatedBundleDelete(w http.ResponseWriter, r *http.Request) { } } +// Federation APIs +func (s *Server) federationList(w http.ResponseWriter, r *http.Request) { + var input ListFederationRelationshipsRequest + buf := new(strings.Builder) + + n, err := io.Copy(buf, r.Body) + if err != nil { + emsg := fmt.Sprintf("Error parsing data: %v", err.Error()) + retError(w, emsg, http.StatusBadRequest) + return + } + data := buf.String() + + if n == 0 { + input = ListFederationRelationshipsRequest{} + } else { + err := json.Unmarshal([]byte(data), &input) + if err != nil { + emsg := fmt.Sprintf("Error parsing data: %v", err.Error()) + retError(w, emsg, http.StatusBadRequest) + return + } + } + + ret, err := s.ListFederationRelationships(input) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet + if err != nil { + emsg := fmt.Sprintf("Error: %v", err.Error()) + retError(w, emsg, http.StatusInternalServerError) + return + } + + cors(w, r) + je := json.NewEncoder(w) + err = je.Encode(ret) + if err != nil { + emsg := fmt.Sprintf("Error: %v", err.Error()) + retError(w, emsg, http.StatusBadRequest) + return + } +} + +func (s *Server) federationCreate(w http.ResponseWriter, r *http.Request) { + var input CreateFederationRelationshipRequest + var rawInput trustdomain.BatchCreateFederationRelationshipRequest + buf := new(strings.Builder) + + n, err := io.Copy(buf, r.Body) + if err != nil { + emsg := fmt.Sprintf("Error parsing data: %v", err.Error()) + retError(w, emsg, http.StatusBadRequest) + return + } + data := buf.String() + + if n == 0 { + input = CreateFederationRelationshipRequest{} + } else { + // required to use protojson because of oneof field + err := protojson.Unmarshal([]byte(data), &rawInput) + if err != nil { + emsg := fmt.Sprintf("Error parsing data: %v", err.Error()) + retError(w, emsg, http.StatusBadRequest) + return + } + input = CreateFederationRelationshipRequest(rawInput) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet + } + + ret, err := s.CreateFederationRelationship(input) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet + if err != nil { + emsg := fmt.Sprintf("Error: %v", err.Error()) + retError(w, emsg, http.StatusInternalServerError) + return + } + + cors(w, r) + je := json.NewEncoder(w) + err = je.Encode(ret) + if err != nil { + emsg := fmt.Sprintf("Error: %v", err.Error()) + retError(w, emsg, http.StatusBadRequest) + return + } +} + +func (s *Server) federationUpdate(w http.ResponseWriter, r *http.Request) { + var input UpdateFederationRelationshipRequest + var rawInput trustdomain.BatchUpdateFederationRelationshipRequest + buf := new(strings.Builder) + + n, err := io.Copy(buf, r.Body) + if err != nil { + emsg := fmt.Sprintf("Error parsing data: %v", err.Error()) + retError(w, emsg, http.StatusBadRequest) + return + } + data := buf.String() + + if n == 0 { + input = UpdateFederationRelationshipRequest{} + } else { + err := protojson.Unmarshal([]byte(data), &rawInput) + if err != nil { + emsg := fmt.Sprintf("Error parsing data: %v", err.Error()) + retError(w, emsg, http.StatusBadRequest) + return + } + input = UpdateFederationRelationshipRequest(rawInput) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet + + } + + ret, err := s.UpdateFederationRelationship(input) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet + if err != nil { + emsg := fmt.Sprintf("Error: %v", err.Error()) + retError(w, emsg, http.StatusInternalServerError) + return + } + + cors(w, r) + je := json.NewEncoder(w) + err = je.Encode(ret) + if err != nil { + emsg := fmt.Sprintf("Error: %v", err.Error()) + retError(w, emsg, http.StatusBadRequest) + return + } +} + +func (s *Server) federationDelete(w http.ResponseWriter, r *http.Request) { + var input DeleteFederationRelationshipRequest + buf := new(strings.Builder) + + n, err := io.Copy(buf, r.Body) + if err != nil { + emsg := fmt.Sprintf("Error parsing data: %v", err.Error()) + retError(w, emsg, http.StatusBadRequest) + return + } + data := buf.String() + + if n == 0 { + input = DeleteFederationRelationshipRequest{} + } else { + err := json.Unmarshal([]byte(data), &input) + if err != nil { + emsg := fmt.Sprintf("Error parsing data: %v", err.Error()) + retError(w, emsg, http.StatusBadRequest) + return + } + } + + ret, err := s.DeleteFederationRelationship(input) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet + if err != nil { + emsg := fmt.Sprintf("Error: %v", err.Error()) + retError(w, emsg, http.StatusInternalServerError) + return + } + + cors(w, r) + je := json.NewEncoder(w) + err = je.Encode(ret) + if err != nil { + emsg := fmt.Sprintf("Error: %v", err.Error()) + retError(w, emsg, http.StatusBadRequest) + return + } +} + + func cors(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json;charset=UTF-8") w.Header().Set("Access-Control-Allow-Origin", "*") @@ -813,6 +984,10 @@ func (s *Server) GetRouter() http.Handler { apiRtr.HandleFunc("/api/v1/spire/federations/bundles", s.federatedBundleCreate).Methods("POST") apiRtr.HandleFunc("/api/v1/spire/federations/bundles", s.federatedBundleUpdate).Methods("PATCH") apiRtr.HandleFunc("/api/v1/spire/federations/bundles", s.federatedBundleDelete).Methods("DELETE") + apiRtr.HandleFunc("/api/v1/spire/federations", s.federationList).Methods("GET") + apiRtr.HandleFunc("/api/v1/spire/federations", s.federationCreate).Methods("POST") + apiRtr.HandleFunc("/api/v1/spire/federations", s.federationUpdate).Methods("PATCH") + apiRtr.HandleFunc("/api/v1/spire/federations", s.federationDelete).Methods("DELETE") // Tornjak specific apiRtr.HandleFunc("/api/v1/tornjak/serverinfo", s.tornjakGetServerInfo).Methods("GET") diff --git a/cmd/agent/main.go b/cmd/agent/main.go index f62f2ff0..7b5471d2 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -15,7 +15,7 @@ import ( type cliOptions struct { genericOptions struct { - spireFile string + spireFile string tornjakFile string expandEnv bool } @@ -124,7 +124,6 @@ func GetServerInfo(configData string) (agentapi.TornjakSpireServerInfo, error) { return agentapi.TornjakSpireServerInfo{}, errors.Errorf("Could not parse SPIRE Config: %v", err) } - if config.Plugins == nil { return agentapi.TornjakSpireServerInfo{}, errors.New("config plugins map should not be nil") } diff --git a/docs/conf/agent/full.conf b/docs/conf/agent/full.conf index c236a202..3d8bae30 100644 --- a/docs/conf/agent/full.conf +++ b/docs/conf/agent/full.conf @@ -1,125 +1,126 @@ -server { - # location of SPIRE socket - # here, set to default SPIRE socket path - spire_socket_path = "unix:///tmp/spire-server/private/api.sock" - - ### BEGIN SERVER CONNECTION CONFIGURATION ### - # Note: at least one of http, tls, and mtls must be configured - # The server can open multiple if multiple sections included - # The server only ends when all connections error - - # [required] configure HTTP connection to Tornjak server - http { - port = 10000 # container port for HTTP connection - } - - # [optional, recommended] configure HTTPS connection to Tornjak server - https { - port = 10443 # [required for HTTPS] container port for HTTPS connection - cert = "sample-keys/tls.pem" # [required for HTTPS] TLS cert - key = "sample-keys/key.pem" # [required for HTTPS] TLS key - client_ca = "sample-keys/rootCA.pem" # enables mTLS connection for HTTPS port - } - - ### END SERVER CONNECTION CONFIGURATION ### -} - -plugins { - - ### BEGIN DATASTORE PLUGIN CONFIGURATION ### - - # Configure SQL local database for Tornjak - DataStore "sql" { - plugin_data { - drivername = "sqlite3" - filename = "tornjak.sqlite3" # location of database - } - } - - ### END DATASTORE PLUGIN CONFIGURATION - - ### BEGIN IAM PLUGIN CONFIGURATION ### - # Note: if no UserManagement configuration included, authentication treated as noop - - # This plugin will extract roles from `realm_access.roles` in the JWT and pass - # to the authorization layer as user roles. - Authenticator "Keycloak" { - plugin_data { - # issuer - Issuer URL for OIDC - # here is a sample for Keycloak running locally on Minikube - issuer = "http://host.docker.internal:8080/realms/tornjak" - # for cloud deployment it would be something like: - # issuer = "http:///realms/tornjak" - - # audience - expected value for aud claim in JWT - # if not included or set, there will be no audience check - # recommended to ensure JWT was meant for Tornjak Backend resource server - audience = "tornjak-backend" - } - } - - # This policy requires admin role for all write calls, viewer role for all read calls - # and authentication success for the "/" api - Authorizer "RBAC" { - plugin_data { - name = "Admin Viewer Policy" - role "admin" { desc = "admin person" } - role "viewer" { desc = "viewer person" } - # this special character role is reserved for allowing all authenticated persons - role "" { desc = "authenticated person" } - - # home tornjak backend api allowed with any successful authentication - API "/" { allowed_roles = [""] } - # allowed with successful authentication and either admin or viewer role - API "/api/healthcheck" { allowed_roles = ["admin", "viewer"] } - API "/api/debugserver" { allowed_roles = ["admin", "viewer"] } - API "/api/agent/list" { allowed_roles = ["admin", "viewer"] } - API "/api/entry/list" { allowed_roles = ["admin", "viewer"] } - API "/api/tornjak/serverinfo" { allowed_roles = ["admin", "viewer"] } - API "/api/tornjak/selectors/list" { allowed_roles = ["admin", "viewer"] } - API "/api/tornjak/agents/list" { allowed_roles = ["admin", "viewer"] } - API "/api/tornjak/clusters/list" { allowed_roles = ["admin", "viewer"] } - # allowed with successful authentication and admin role - API "/api/agent/ban" { allowed_roles = ["admin"] } - API "/api/agent/delete" { allowed_roles = ["admin"] } - API "/api/agent/createjointoken" { allowed_roles = ["admin"] } - API "/api/entry/create" { allowed_roles = ["admin"] } - API "/api/entry/delete" { allowed_roles = ["admin"] } - API "/api/tornjak/selectors/register" { allowed_roles = ["admin"] } - API "/api/tornjak/clusters/create" { allowed_roles = ["admin"] } - API "/api/tornjak/clusters/edit" { allowed_roles = ["admin"] } - API "/api/tornjak/clusters/delete" { allowed_roles = ["admin"] } - - APIv1 "GET /api/v1/spire/serverinfo" { allowed_roles = ["admin", "viewer"] } - APIv1 "GET /api/v1/spire/healthcheck" { allowed_roles = ["admin", "viewer"] } - APIv1 "GET /api/v1/spire/agents" { allowed_roles = ["admin", "viewer"] } - APIv1 "DELETE /api/v1/spire/agents" { allowed_roles = ["admin"] } - APIv1 "POST /api/v1/spire/agents/ban" { allowed_roles = ["admin"] } - APIv1 "POST /api/v1/spire/agents/jointoken" { allowed_roles = ["admin"] } - APIv1 "GET /api/v1/spire/entries" { allowed_roles = ["admin", "viewer"] } - APIv1 "POST /api/v1/spire/entries" { allowed_roles = ["admin"] } - APIv1 "DELETE /api/v1/spire/entries" { allowed_roles = ["admin"] } - - # SPIRE Federation API calls - APIv1 "GET /api/v1/spire/bundle" { allowed_roles = ["admin", "viewer"] } - APIv1 "GET /api/v1/spire/federations/bundles" { allowed_roles = ["admin", "viewer"] } - APIv1 "POST /api/v1/spire/federations/bundles" { allowed_roles = ["admin"] } - APIv1 "PATCH /api/v1/spire/federations/bundles" { allowed_roles = ["admin"] } - APIv1 "DELETE /api/v1/spire/federations/bundles" { allowed_roles = ["admin"] } - - # Tornjak API calls - APIv1 "GET /api/v1/tornjak/serverinfo" { allowed_roles = ["admin", "viewer"] } - APIv1 "GET /api/v1/tornjak/agents" { allowed_roles = ["admin", "viewer"] } - APIv1 "POST /api/v1/tornjak/selectors" { allowed_roles = ["admin"] } - APIv1 "GET /api/v1/tornjak/selectors" { allowed_roles = ["admin", "viewer"] } - APIv1 "GET /api/v1/tornjak/clusters" { allowed_roles = ["admin", "viewer"] } - APIv1 "POST /api/v1/tornjak/clusters" { allowed_roles = ["admin"] } - APIv1 "PATCH /api/v1/tornjak/clusters" { allowed_roles = ["admin"] } - APIv1 "DELETE /api/v1/tornjak/clusters" { allowed_roles = ["admin"] } - } - } - - ### END IAM PLUGIN CONFIGURATION - - -} +server { + # location of SPIRE socket + # here, set to default SPIRE socket path + spire_socket_path = "unix:///tmp/spire-server/private/api.sock" + + ### BEGIN SERVER CONNECTION CONFIGURATION ### + # Note: at least one of http, tls, and mtls must be configured + # The server can open multiple if multiple sections included + # The server only ends when all connections error + + # [required] configure HTTP connection to Tornjak server + http { + port = 10000 # container port for HTTP connection + } + + # [optional, recommended] configure HTTPS connection to Tornjak server + https { + port = 10443 # [required for HTTPS] container port for HTTPS connection + cert = "sample-keys/tls.pem" # [required for HTTPS] TLS cert + key = "sample-keys/key.pem" # [required for HTTPS] TLS key + client_ca = "sample-keys/rootCA.pem" # enables mTLS connection for HTTPS port + } + + ### END SERVER CONNECTION CONFIGURATION ### +} + +plugins { + + ### BEGIN DATASTORE PLUGIN CONFIGURATION ### + + # Configure SQL local database for Tornjak + DataStore "sql" { + plugin_data { + drivername = "sqlite3" + filename = "tornjak.sqlite3" # location of database + } + } + + ### END DATASTORE PLUGIN CONFIGURATION + + ### BEGIN IAM PLUGIN CONFIGURATION ### + # Note: if no UserManagement configuration included, authentication treated as noop + + # This plugin will extract roles from `realm_access.roles` in the JWT and pass + # to the authorization layer as user roles. + Authenticator "Keycloak" { + plugin_data { + # issuer - Issuer URL for OIDC + # here is a sample for Keycloak running locally on Minikube + issuer = "http://host.docker.internal:8080/realms/tornjak" + # for cloud deployment it would be something like: + # issuer = "http:///realms/tornjak" + + # audience - expected value for aud claim in JWT + # if not included or set, there will be no audience check + # recommended to ensure JWT was meant for Tornjak Backend resource server + audience = "tornjak-backend" + } + } + + # This policy requires admin role for all write calls, viewer role for all read calls + # and authentication success for the "/" api + Authorizer "RBAC" { + plugin_data { + name = "Admin Viewer Policy" + role "admin" { desc = "admin person" } + role "viewer" { desc = "viewer person" } + # this special character role is reserved for allowing all authenticated persons + role "" { desc = "authenticated person" } + + # home tornjak backend api allowed with any successful authentication + API "/" { allowed_roles = [""] } + # allowed with successful authentication and either admin or viewer role + API "/api/healthcheck" { allowed_roles = ["admin", "viewer"] } + API "/api/debugserver" { allowed_roles = ["admin", "viewer"] } + API "/api/agent/list" { allowed_roles = ["admin", "viewer"] } + API "/api/entry/list" { allowed_roles = ["admin", "viewer"] } + API "/api/tornjak/serverinfo" { allowed_roles = ["admin", "viewer"] } + API "/api/tornjak/selectors/list" { allowed_roles = ["admin", "viewer"] } + API "/api/tornjak/agents/list" { allowed_roles = ["admin", "viewer"] } + API "/api/tornjak/clusters/list" { allowed_roles = ["admin", "viewer"] } + # allowed with successful authentication and admin role + API "/api/agent/ban" { allowed_roles = ["admin"] } + API "/api/agent/delete" { allowed_roles = ["admin"] } + API "/api/agent/createjointoken" { allowed_roles = ["admin"] } + API "/api/entry/create" { allowed_roles = ["admin"] } + API "/api/entry/delete" { allowed_roles = ["admin"] } + API "/api/tornjak/selectors/register" { allowed_roles = ["admin"] } + API "/api/tornjak/clusters/create" { allowed_roles = ["admin"] } + API "/api/tornjak/clusters/edit" { allowed_roles = ["admin"] } + API "/api/tornjak/clusters/delete" { allowed_roles = ["admin"] } + + # v1 API + APIv1 "GET /api/v1/spire/serverinfo" { allowed_roles = ["admin", "viewer"] } + APIv1 "GET /api/v1/spire/healthcheck" { allowed_roles = ["admin", "viewer"] } + APIv1 "GET /api/v1/spire/agents" { allowed_roles = ["admin", "viewer"] } + APIv1 "DELETE /api/v1/spire/agents" { allowed_roles = ["admin"] } + APIv1 "POST /api/v1/spire/agents/ban" { allowed_roles = ["admin"] } + APIv1 "POST /api/v1/spire/agents/jointoken" { allowed_roles = ["admin"] } + APIv1 "GET /api/v1/spire/entries" { allowed_roles = ["admin", "viewer"] } + APIv1 "POST /api/v1/spire/entries" { allowed_roles = ["admin"] } + APIv1 "DELETE /api/v1/spire/entries" { allowed_roles = ["admin"] } + + # SPIRE Federation API calls + APIv1 "GET /api/v1/spire/bundle" { allowed_roles = ["admin", "viewer"] } + APIv1 "GET /api/v1/spire/federations/bundles" { allowed_roles = ["admin", "viewer"] } + APIv1 "POST /api/v1/spire/federations/bundles" { allowed_roles = ["admin"] } + APIv1 "PATCH /api/v1/spire/federations/bundles" { allowed_roles = ["admin"] } + APIv1 "DELETE /api/v1/spire/federations/bundles" { allowed_roles = ["admin"] } + + # Tornjak API calls + APIv1 "GET /api/v1/tornjak/serverinfo" { allowed_roles = ["admin", "viewer"] } + APIv1 "GET /api/v1/tornjak/agents" { allowed_roles = ["admin", "viewer"] } + APIv1 "POST /api/v1/tornjak/selectors" { allowed_roles = ["admin"] } + APIv1 "GET /api/v1/tornjak/selectors" { allowed_roles = ["admin", "viewer"] } + APIv1 "GET /api/v1/tornjak/clusters" { allowed_roles = ["admin", "viewer"] } + APIv1 "POST /api/v1/tornjak/clusters" { allowed_roles = ["admin"] } + APIv1 "PATCH /api/v1/tornjak/clusters" { allowed_roles = ["admin"] } + APIv1 "DELETE /api/v1/tornjak/clusters" { allowed_roles = ["admin"] } + } + } + + ### END IAM PLUGIN CONFIGURATION + + +} diff --git a/go.mod b/go.mod index 8d9451d7..76cb820c 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/spiffe/spire-api-sdk v1.2.5-0.20230413135745-699e242b965d github.com/urfave/cli/v2 v2.3.0 google.golang.org/grpc v1.56.3 + google.golang.org/protobuf v1.34.2 ) require ( @@ -57,5 +58,4 @@ require ( golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.19.0 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect - google.golang.org/protobuf v1.33.0 // indirect ) diff --git a/go.sum b/go.sum index e81a7ed1..5df51d15 100644 --- a/go.sum +++ b/go.sum @@ -396,8 +396,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/openapi.yaml b/openapi.yaml index f1be170e..06d5e66d 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -4,7 +4,7 @@ info: description: | This API allows management of SPIRE server via the Tornjak server, comprised of SPIRE server API calls and Tornjak-specific API calls. - version: 1.3.0 + version: "1.8.0" paths: /api/v1/spire/healthcheck: get: @@ -107,6 +107,23 @@ paths: "uptime": 333, "federated_bundles_count": 1 } + /api/v1/spire/bundle: + get: + summary: Get current SPIRE server bundle + description: Retrieves SPIRE server bundle + responses: + default: + description: "Unexpected error" + content: + application/json: + schema: + $ref: '#/components/schemas/error' + "200": + description: "OK" + content: + application/json: + schema: + $ref: '#/components/schemas/bundle' /api/v1/spire/agents: get: summary: Calls SPIRE server `spire-server agent list` command @@ -129,30 +146,7 @@ paths: type: array items: $ref: '#/components/schemas/agent' - example: # Example response - { - "agents": [ - { - "id": { - "trust_domain": "example.org", - "path": "/spire/agent" - }, - "attestation_type": "k8s_sat", - "x509svid_serial_number": "111", - "x509svid_expires_at": 222, - "selectors": [ - { - "type": "k8s_sat", - "value": "agent_ns:spire" - }, - { - "type": "k8s_sat", - "value": "agent_sa:spire-agent" - } - ] - } - ] - } + delete: summary: Calls SPIRE server `spire-server agent evict` command @@ -192,13 +186,10 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/spiffe_id' - examples: - example_request: - value: - id: - path: "/spire/agent/" - trust_domain: "example.org" + type: object + properties: + id: + $ref: '#/components/schemas/spiffe_id' responses: default: description: "Unexpected error" @@ -236,13 +227,6 @@ paths: ttl: type: integer examples: [500] - examples: - example1: - value: - path: "/sample/spiffe/id" - token: 1111 - trust_domain: "example.org" - ttl: 500 responses: default: description: "Unexpected error" @@ -263,11 +247,6 @@ paths: expires_at: type: integer examples: [555] - examples: - example1: - value: - value: 1111 - expires_at: 555 /api/v1/spire/entries: get: summary: Calls SPIRE server `spire-server entry show` @@ -284,26 +263,13 @@ paths: content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/entry' - examples: - example_response: - summary: "A sample response showing entries" - value: - entries: - - id: "id1" - spiffe_id: - trust_domain: "example.org" - path: "/spire/agent/" - parent_id: - trust_domain: "example.org" - path: "/spire/agent/" - selectors: - - type: "k8s_sat" - value: "agent_ns:spire" - - type: "k8s_sat" - value: "agent_sa:spire-agent" + type: object + properties: + entries: + type: array + items: + $ref: '#/components/schemas/entry' + post: summary: Calls SPIRE server `spire-server entry create` description: Create registration entries @@ -312,43 +278,13 @@ paths: content: application/json: schema: - allOf: - - $ref: '#/components/schemas/entry' - - type: object - properties: - admin: - type: string - dns_names: - type: string - downstream: - type: string - expires_at: - type: string - federates_with: - type: array - items: - type: string - examples: [] - examples: - example_request: - summary: "Example request for creating a registration entry" - value: - admin: "/spire/agent/" - dns_names: "/spire/agent/" - downstream: "/spire/agent/" - expires_at: "/spire/agent/" - federates_with: [] - parent_id: - trust_domain: "example.org" - path: "/spire/agent/" - selectors: - - type: "k8s_sat" - value: "agent_ns:spire" - - type: "k8s_sat" - value: "agent_sa:spire-agent" - spiffe_id: - trust_domain: "example.org" - path: "/spire/agent/" + type: object + properties: + entries: + type: array + items: + $ref: '#/components/schemas/entry' + responses: default: description: "Unexpected error" @@ -367,34 +303,12 @@ paths: type: array items: type: object - properties: - status: - type: object + allOf: + - $ref: '#/components/schemas/spire_status' + - type: object properties: - message: - type: string - examples: ["OK"] - entry: - $ref: '#/components/schemas/entry' - examples: - example_response: - summary: "Example response after creating a registration entry" - value: - results: - - status: - message: "OK" - entry: - spiffe_id: - trust_domain: "example.org" - path: "/spire/agent/" - parent_id: - trust_domain: "example.org" - path: "/spire/agent/" - selectors: - - type: "k8s_sat" - value: "agent_ns:spire" - - type: "k8s_sat" - value: "agent_sa:spire-agent" + entry: + $ref: '#/components/schemas/entry' delete: summary: Calls SPIRE server `spire-server entry delete` command description: Deletes a specified registration entry @@ -409,11 +323,7 @@ paths: type: array items: type: string - examples: - - summary: "Example request to delete a registration entry" - value: - ids: - - "111" + examples: ["858da-3d-40-b7-caea9"] responses: default: description: "Unexpected error" @@ -428,31 +338,293 @@ paths: schema: type: object properties: - Results: + results: type: array items: type: object - properties: - status: - type: object + allOf: + - $ref: '#/components/schemas/spire_status' + - type: object properties: - message: + id: type: string examples: - - "OK" - id: - type: string - examples: - - "111" - examples: - example_response: - summary: "Example response after deleting a registration entry" - value: - Results: - - status: - message: "OK" - id: "111" - + - "858da-3d-40-b7-caea9" + /api/v1/spire/federations: + get: + summary: Lists all federations configured on SPIRE Server + description: Lists all federations configured on SPIRE Server + responses: + default: + description: "Unexpected error" + content: + application/json: + schema: + $ref: '#/components/schemas/error' + "200": + description: "OK" + content: + application/json: + schema: + type: object + properties: + federation_relationships: + type: array + items: + $ref: '#/components/schemas/federation_response' + post: + summary: Creates federation relationship on SPIRE server + description: Calls `spire-server federation create` + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + federation_relationships: + type: array + items: + $ref: '#/components/schemas/federation_request' + responses: + default: + description: "Unexpected error" + content: + application/json: + schema: + $ref: '#/components/schemas/error' + "200": + description: "OK" + content: + application/json: + schema: + type: object + properties: + results: + type: array + items: + type: object + allOf: + - $ref: '#/components/schemas/spire_status' + - type: object + properties: + federation_relationship: + $ref: '#/components/schemas/federation_response' + patch: + summary: Creates federation relationship on SPIRE server + description: Calls `spire-server federation create` + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + federation_relationships: + type: array + items: + $ref: '#/components/schemas/federation_request' + responses: + default: + description: "Unexpected error" + content: + application/json: + schema: + $ref: '#/components/schemas/error' + "200": + description: "OK" + content: + application/json: + schema: + type: object + properties: + results: + type: array + items: + type: object + allOf: + - $ref: '#/components/schemas/spire_status' + - type: object + properties: + federation_relationship: + $ref: '#/components/schemas/federation_response' + delete: + summary: Deletes federation relationship on SPIRE server + description: Calls `spire-server federation delete` + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + trust_domains: + type: array + items: + type: string + examples: ["example.org"] + responses: + default: + description: "Unexpected error" + content: + application/json: + schema: + $ref: '#/components/schemas/error' + "200": + description: "OK" + content: + application/json: + schema: + type: object + properties: + results: + type: array + items: + type: object + allOf: + - $ref: '#/components/schemas/spire_status' + - type: object + properties: + trust_domain: + type: string + examples: ["example.org"] + /api/v1/spire/federations/bundles: + get: + summary: Lists federation bundles + description: Call `spire-server bundle list` + responses: + default: + description: "Unexpected error" + content: + application/json: + schema: + $ref: '#/components/schemas/error' + "200": + description: "OK" + content: + application/json: + schema: + oneOf: + - type: object + properties: + bundles: + type: array + items: + $ref: '#/components/schemas/bundle' + - type: object # if no bundles, it is empty + post: + summary: Sets federation bundles + description: Call `spire-server bundle set` + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + bundle: + type: array + items: + $ref: '#/components/schemas/bundle' + responses: + default: + description: "Unexpected error" + content: + application/json: + schema: + $ref: '#/components/schemas/error' + "200": + description: "OK" + content: + application/json: + schema: + type: object + properties: + results: + type: array + items: + type: object + allOf: + - $ref: '#/components/schemas/spire_status' + - type: object + properties: + bundle: + $ref: '#/components/schemas/bundle' + patch: + summary: Sets federation bundles + description: Updates if bundle found, else returns error + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + bundle: + type: array + items: + $ref: '#/components/schemas/bundle' + responses: + default: + description: "Unexpected error" + content: + application/json: + schema: + $ref: '#/components/schemas/error' + "200": + description: "OK" + content: + application/json: + schema: + type: object + properties: + results: + type: array + items: + type: object + allOf: + - $ref: '#/components/schemas/spire_status' + - type: object + properties: + bundle: + $ref: '#/components/schemas/bundle' + delete: + requestBody: + content: + application/json: + schema: + type: object + properties: + trust_domains: + type: array + items: + type: string + examples: ["example.org"] + responses: + default: + description: "Unexpected error" + content: + application/json: + schema: + $ref: '#/components/schemas/error' + "200": + description: "OK" + content: + application/json: + schema: + type: object + properties: + results: + type: array + items: + type: object + allOf: + - $ref: '#/components/schemas/spire_status' + - type: object + properties: + trust_domain: + type: string + examples: ["trust_domain"] /api/v1/tornjak/serverinfo: get: summary: Get general Tornjak server information. @@ -517,14 +689,19 @@ paths: type: object properties: agents: - type: object - properties: - spiffeid: - type: string - examples: ["spiffe://example.org/spire/agent/"] - plugin: - type: string - examples: ["plugin1"] + type: array + items: + type: object + properties: + spiffeid: + type: string + examples: ["spiffe://example.org/spire/agent/"] + plugin: + type: string + examples: ["plugin1"] + cluster: + type: string + examples: [""] post: summary: Post Tornjak selectors. description: Submits a selector to the Tornjak server. @@ -535,15 +712,12 @@ paths: schema: type: object properties: - id: - type: object - properties: - plugin: - type: string - examples: ["plugin1"] - spiffe id: - type: string - examples: ["spiffe://example.org/spire/agent"] + plugin: + type: string + examples: ["plugin1"] + spiffe id: + type: string + examples: ["spiffe://example.org/spire/agent"] responses: default: description: "Unexpected error" @@ -581,33 +755,10 @@ paths: type: array items: type: object - properties: - name: - type: string - examples: ["clustername"] - editedName: - type: string - examples: [""] - creationTime: - type: string - examples: ["Feb 08 2023 21:02:10"] - domainName: - type: string - examples: [""] - managedBy: - type: string - examples: [""] - platformType: - type: string - examples: ["Docker"] - agentsList: - type: array - items: - type: string - examples: ["agent1"] + $ref: '#/components/schemas/tornjak_cluster' post: - summary: Create a Tornjak selector. - description: Creates a new Tornjak selector with the specified cluster details. + summary: Create a Tornjak cluster + description: Creates a new Tornjak cluster. requestBody: required: true content: @@ -617,22 +768,7 @@ paths: properties: cluster: type: object - properties: - name: - type: string - examples: ["clusterName"] - platformType: - type: string - examples: ["Docker"] - agentsList: - type: array - items: - type: string - examples: - - ["agent1", "agent2"] - domainName: - type: string - examples: ["example.org"] + $ref: '#/components/schemas/tornjak_cluster' responses: default: description: "Unexpected error" @@ -724,29 +860,55 @@ paths: components: schemas: + spire_status_ok: + type: object + properties: + message: + type: string + examples: ["OK"] + spire_status_error: + type: object + properties: + code: + type: integer + minimum: 0 + message: + type: string + examples: ["failed to ..."] + spire_status: + type: object + properties: + status: + oneOf: + - $ref: '#/components/schemas/spire_status_ok' + - $ref: '#/components/schemas/spire_status_error' spiffe_id: type: object properties: path: type: string + examples: ["/ns/default"] trust_domain: type: string + examples: ["example.org"] selector: type: object properties: type: type: string - examples: [k8s_sat] + examples: ["k8s_sat"] value: type: string + examples: ["agent_ns:spire", "agent_sa:spire-agent"] entry: type: object properties: id: type: string + examples: ["858da-34-50-b7-cacd98"] spiffe_id: $ref: '#/components/schemas/spiffe_id' parent_id: @@ -755,7 +917,33 @@ components: type: array items: $ref: '#/components/schemas/selector' - + x509_svid_ttl: + type: integer + minimum: 0 + examples: [12345] + created_at: + type: integer + minimum: 0 + examples: [1725556278] + admin: + type: boolean + examples: [false] + dns_names: + type: array + items: + type: string + examples: ["example1.org"] + downstream: + type: boolean + examples: [false] + expires_at: + type: integer + examples: [12345] + federates_with: + type: array + items: + type: string + examples: ["example1.org", "example2.org"] agent: type: object @@ -764,7 +952,7 @@ components: $ref: '#/components/schemas/spiffe_id' attestation_type: type: string - examples: [k8s_sat] + examples: ["k8s_sat"] x509svid_serial_number: type: integer x509svid_expires_at: @@ -773,6 +961,144 @@ components: type: array items: $ref: '#/components/schemas/selector' + + federation_profile_web: + type: object + properties: + https_web: + type: object + federation_profile_web_response: + type: object + properties: + HttpsWeb: + type: object + federation_profile_spiffe: + type: object + properties: + https_spiffe: + type: object + properties: + endpoint_spiffe_id: + type: string + examples: ["spiffe://example1.org/spire-server"] + federation_profile_spiffe_response: + type: object + properties: + HttpsSpiffe: + type: object + properties: + endpoint_spiffe_id: + type: string + examples: ["spiffe://example1.org/spire-server"] + federation_request: + type: object + required: + - trust_domain + - bundle_endpoint_url + allOf: + - type: object + properties: + trust_domain: + type: string + examples: ["example1.org"] + bundle_endpoint_url: + type: string + examples: ["https://bundle.example1.org"] + trust_domain_bundle: + type: object + $ref: '#/components/schemas/bundle' + - oneOf: + - $ref: '#/components/schemas/federation_profile_spiffe' + - $ref: '#/components/schemas/federation_profile_web' + examples: + - { + "trust_domain": "example1.org", + "bundle_endpoint_url": "https://bundle.example1.org", + "https_spiffe": { + "endpoint_spiffe_id": "spiffe://example1.org/spire-server" + } + } + - {"trust_domain": "example2.org", + "bundle_endpoint_url": "https://bundle.example2.org", + "https_web": {} + } + federation_response: + type: object + allOf: + - type: object + properties: + trust_domain: + type: string + examples: ["example1.org"] + bundle_endpoint_url: + type: string + examples: ["https://bundle.example1.org"] + BundleEndpointProfile: + type: object + oneOf: + - $ref: '#/components/schemas/federation_profile_spiffe_response' + - $ref: '#/components/schemas/federation_profile_web_response' + bundle: + type: object + properties: + trust_domain: + type: string + examples: ["example.org"] + x509_authorities: + type: array + items: + type: object + properties: + asn1: + type: string + examples: ["MIIDiz..."] + jwt_authorities: + type: array + items: + type: object + properties: + public_key: + type: string + examples: ["MIIBIj..."] + key_id: + type: string + examples: ["Elaub..."] + expires_at: + type: integer + minimum: 0 + examples: [1723062174] + sequence_number: + type: integer + minimum: 0 + examples: [3] + tornjak_cluster: + type: object + properties: + name: + type: string + examples: ["clusterName"] + platformType: + type: string + examples: ["Docker"] + agentsList: + type: array + items: + type: string + examples: + - "agent1" + - "agent2" + domainName: + type: string + examples: ["example.org"] + editedName: + type: string + examples: [""] + creationTime: + type: string + examples: ["Feb 08 2023 21:02:10"] + managedBy: + type: string + examples: [""] error: type: string - examples: ["Bad request"] \ No newline at end of file + examples: ["Bad request"] diff --git a/pkg/agent/authentication/authenticator/authenticator.go b/pkg/agent/authentication/authenticator/authenticator.go index fc6210fc..aebf4d67 100644 --- a/pkg/agent/authentication/authenticator/authenticator.go +++ b/pkg/agent/authentication/authenticator/authenticator.go @@ -10,5 +10,5 @@ type Authenticator interface { // AuthenticateRequest takes request, verifies certain properties // and returns relevant UserInfo to be interpreted by the Authorizer // or error upon verification error - AuthenticateRequest(r *http.Request) (*user.UserInfo) + AuthenticateRequest(r *http.Request) *user.UserInfo } diff --git a/pkg/agent/authentication/authenticator/keycloak.go b/pkg/agent/authentication/authenticator/keycloak.go index 0a7571fa..6f4ec43b 100644 --- a/pkg/agent/authentication/authenticator/keycloak.go +++ b/pkg/agent/authentication/authenticator/keycloak.go @@ -26,9 +26,9 @@ type KeycloakClaim struct { } type KeycloakAuthenticator struct { - jwks *keyfunc.JWKS - jwksURL string - audience string + jwks *keyfunc.JWKS + jwksURL string + audience string } func getJWKeyFunc(httpjwks bool, jwksInfo string) (*keyfunc.JWKS, error) { @@ -57,7 +57,8 @@ func getJWKeyFunc(httpjwks bool, jwksInfo string) (*keyfunc.JWKS, error) { } // newKeycloakAuthenticator (https bool, jwks string, redirect string) -// get keyfunc based on https +// +// get keyfunc based on https func NewKeycloakAuthenticator(httpjwks bool, issuerURL string, audience string) (*KeycloakAuthenticator, error) { // perform OIDC discovery oidcClient, err := discovery.NewClient(context.Background(), issuerURL) @@ -73,9 +74,9 @@ func NewKeycloakAuthenticator(httpjwks bool, issuerURL string, audience string) return nil, err } return &KeycloakAuthenticator{ - jwks: jwks, - audience: audience, - jwksURL: jwksURL, + jwks: jwks, + audience: audience, + jwksURL: jwksURL, }, nil } @@ -96,13 +97,13 @@ func getToken(r *http.Request, redirectURL string) (string, error) { } -func wrapAuthenticationError(err error) (*user.UserInfo) { +func wrapAuthenticationError(err error) *user.UserInfo { return &user.UserInfo{ AuthenticationError: err, } } -func (a *KeycloakAuthenticator) AuthenticateRequest(r *http.Request)(*user.UserInfo) { +func (a *KeycloakAuthenticator) AuthenticateRequest(r *http.Request) *user.UserInfo { token, err := getToken(r, a.jwksURL) if err != nil { return wrapAuthenticationError(err) diff --git a/pkg/agent/authentication/authenticator/null_authenticator.go b/pkg/agent/authentication/authenticator/null_authenticator.go index 9fadd254..81e8fdcb 100644 --- a/pkg/agent/authentication/authenticator/null_authenticator.go +++ b/pkg/agent/authentication/authenticator/null_authenticator.go @@ -11,6 +11,6 @@ type NullAuthenticator struct{} func NewNullAuthenticator() *NullAuthenticator { return &NullAuthenticator{} } -func (a *NullAuthenticator) AuthenticateRequest(r *http.Request) (*user.UserInfo) { +func (a *NullAuthenticator) AuthenticateRequest(r *http.Request) *user.UserInfo { return nil } diff --git a/pkg/agent/authentication/user/user.go b/pkg/agent/authentication/user/user.go index c5d1c562..765b29f3 100644 --- a/pkg/agent/authentication/user/user.go +++ b/pkg/agent/authentication/user/user.go @@ -2,5 +2,5 @@ package user type UserInfo struct { AuthenticationError error - Roles []string + Roles []string } diff --git a/pkg/agent/authorization/authorization.go b/pkg/agent/authorization/authorization.go index 3bf833e3..0844a418 100644 --- a/pkg/agent/authorization/authorization.go +++ b/pkg/agent/authorization/authorization.go @@ -9,4 +9,4 @@ import ( type Authorizer interface { // Authorize Request AuthorizeRequest(r *http.Request, u *user.UserInfo) error -} \ No newline at end of file +} diff --git a/pkg/agent/authorization/null_authorizer.go b/pkg/agent/authorization/null_authorizer.go index 13a57dde..811f4c43 100644 --- a/pkg/agent/authorization/null_authorizer.go +++ b/pkg/agent/authorization/null_authorizer.go @@ -11,6 +11,6 @@ type NullAuthorizer struct{} func NewNullAuthorizer() *NullAuthorizer { return &NullAuthorizer{} } -func (a *NullAuthorizer) AuthorizeRequest(r *http.Request, u *user.UserInfo) (error) { +func (a *NullAuthorizer) AuthorizeRequest(r *http.Request, u *user.UserInfo) error { return nil -} \ No newline at end of file +} diff --git a/pkg/agent/authorization/rbac.go b/pkg/agent/authorization/rbac.go index be1e9edf..df01b469 100644 --- a/pkg/agent/authorization/rbac.go +++ b/pkg/agent/authorization/rbac.go @@ -1,31 +1,32 @@ package authorization import ( - "net/http" "github.com/pkg/errors" "fmt" "strings" + "net/http" + "github.com/spiffe/tornjak/pkg/agent/authentication/user" ) type RBACAuthorizer struct { - name string - roleList map[string]string + name string + roleList map[string]string apiMapping map[string][]string apiV1Mapping map[string]map[string][]string } // TODO put this in a common constants file var staticAPIList = map[string]struct{}{ - "/": {}, - "/api/healthcheck": {}, - "/api/debugserver": {}, - "/api/agent/list": {}, - "/api/entry/list": {}, - "/api/tornjak/serverinfo": {}, - "/api/tornjak/selectors/list": {}, - "/api/tornjak/agents/list": {}, - "/api/tornjak/clusters/list": {}, + "/": {}, + "/api/healthcheck": {}, + "/api/debugserver": {}, + "/api/agent/list": {}, + "/api/entry/list": {}, + "/api/tornjak/serverinfo": {}, + "/api/tornjak/selectors/list": {}, + "/api/tornjak/agents/list": {}, + "/api/tornjak/clusters/list": {}, "/api/agent/ban": {}, "/api/agent/delete": {}, "/api/agent/createjointoken": {}, @@ -93,8 +94,8 @@ func NewRBACAuthorizer(policyName string, roleList map[string]string, apiMapping } fmt.Printf("apiV1Mapping: %v\n", apiV1Mapping) return &RBACAuthorizer{ - name: policyName, - roleList: roleList, + name: policyName, + roleList: roleList, apiMapping: apiMapping, apiV1Mapping: apiV1Mapping, }, nil