action.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. package forms
  2. import (
  3. "encoding/base64"
  4. "strings"
  5. "github.com/porter-dev/porter/internal/kubernetes"
  6. "github.com/porter-dev/porter/internal/models"
  7. "github.com/porter-dev/porter/internal/repository"
  8. )
  9. // ActionResolver exposes an interface for resolving an action as a ServiceAccount.
  10. // So that actions can be chained together, a pointer to a serviceAccount can be
  11. // used -- if this points to nil, a new service account is created
  12. type ActionResolver interface {
  13. PopulateServiceAccount(repo repository.ServiceAccountRepository) error
  14. }
  15. // ServiceAccountActionResolver is the base type for resolving a ServiceAccountAction
  16. // that belongs to a given ServiceAccountCandidate
  17. type ServiceAccountActionResolver struct {
  18. ServiceAccountCandidateID uint `json:"sa_candidate_id" form:"required"`
  19. SA *models.ServiceAccount
  20. SACandidate *models.ServiceAccountCandidate
  21. }
  22. // PopulateServiceAccount will create a service account if it does not exist,
  23. // or will append a new cluster given by a ServiceAccountCandidate to the
  24. // ServiceAccount
  25. func (sar *ServiceAccountActionResolver) PopulateServiceAccount(
  26. repo repository.ServiceAccountRepository,
  27. ) error {
  28. var err error
  29. id := sar.ServiceAccountCandidateID
  30. if sar.SACandidate == nil {
  31. sar.SACandidate, err = repo.ReadServiceAccountCandidate(id)
  32. if err != nil {
  33. return err
  34. }
  35. }
  36. rawConf, err := kubernetes.GetRawConfigFromBytes(sar.SACandidate.Kubeconfig)
  37. if err != nil {
  38. return err
  39. }
  40. context := rawConf.Contexts[rawConf.CurrentContext]
  41. authInfoName := context.AuthInfo
  42. authInfo := rawConf.AuthInfos[authInfoName]
  43. clusterName := context.Cluster
  44. cluster := rawConf.Clusters[clusterName]
  45. modelCluster := models.Cluster{
  46. Name: clusterName,
  47. LocationOfOrigin: cluster.LocationOfOrigin,
  48. Server: cluster.Server,
  49. TLSServerName: cluster.TLSServerName,
  50. InsecureSkipTLSVerify: cluster.InsecureSkipTLSVerify,
  51. }
  52. if len(cluster.CertificateAuthorityData) > 0 {
  53. modelCluster.CertificateAuthorityData = cluster.CertificateAuthorityData
  54. }
  55. if sar.SA == nil {
  56. sar.SA = &models.ServiceAccount{
  57. ProjectID: sar.SACandidate.ProjectID,
  58. Kind: sar.SACandidate.Kind,
  59. Clusters: []models.Cluster{modelCluster},
  60. AuthMechanism: sar.SACandidate.AuthMechanism,
  61. LocationOfOrigin: authInfo.LocationOfOrigin,
  62. Impersonate: authInfo.Impersonate,
  63. ImpersonateGroups: strings.Join(authInfo.ImpersonateGroups, ","),
  64. }
  65. } else {
  66. doesClusterExist := false
  67. for _, cluster := range sar.SA.Clusters {
  68. if cluster.Name == sar.SACandidate.ClusterName && cluster.Server == sar.SACandidate.ClusterEndpoint {
  69. doesClusterExist = true
  70. }
  71. }
  72. if !doesClusterExist {
  73. sar.SA.Clusters = append(sar.SA.Clusters, modelCluster)
  74. }
  75. }
  76. if len(authInfo.ClientCertificateData) > 0 {
  77. sar.SA.ClientCertificateData = authInfo.ClientCertificateData
  78. }
  79. if len(authInfo.ClientKeyData) > 0 {
  80. sar.SA.ClientKeyData = authInfo.ClientKeyData
  81. }
  82. if authInfo.Token != "" {
  83. sar.SA.Token = authInfo.Token
  84. }
  85. if authInfo.Username != "" {
  86. sar.SA.Username = authInfo.Username
  87. }
  88. if authInfo.Password != "" {
  89. sar.SA.Password = authInfo.Password
  90. }
  91. if authInfo.AuthProvider != nil && authInfo.AuthProvider.Name == "oidc" {
  92. if url, ok := authInfo.AuthProvider.Config["idp-issuer-url"]; ok {
  93. sar.SA.OIDCIssuerURL = url
  94. }
  95. if clientID, ok := authInfo.AuthProvider.Config["client-id"]; ok {
  96. sar.SA.OIDCClientID = clientID
  97. }
  98. if clientSecret, ok := authInfo.AuthProvider.Config["client-secret"]; ok {
  99. sar.SA.OIDCClientSecret = clientSecret
  100. }
  101. if caData, ok := authInfo.AuthProvider.Config["idp-certificate-authority-data"]; ok {
  102. // based on the implementation, the oidc plugin expects the data to be base64 encoded,
  103. // which means we will not decode it here
  104. // reference: https://github.com/kubernetes/kubernetes/blob/9dfb4c876bfca7a5ae84259fae2bc337ed90c2d7/staging/src/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc.go#L135
  105. sar.SA.OIDCCertificateAuthorityData = caData
  106. }
  107. if idToken, ok := authInfo.AuthProvider.Config["id-token"]; ok {
  108. sar.SA.OIDCIDToken = idToken
  109. }
  110. if refreshToken, ok := authInfo.AuthProvider.Config["refresh-token"]; ok {
  111. sar.SA.OIDCRefreshToken = refreshToken
  112. }
  113. }
  114. return nil
  115. }
  116. // ClusterCADataAction contains the base64 encoded cluster CA data
  117. type ClusterCADataAction struct {
  118. *ServiceAccountActionResolver
  119. ClusterCAData string `json:"cluster_ca_data" form:"required"`
  120. }
  121. // PopulateServiceAccount will add cluster ca data to a cluster in the ServiceAccount's
  122. // list of clusters
  123. func (cda *ClusterCADataAction) PopulateServiceAccount(
  124. repo repository.ServiceAccountRepository,
  125. ) error {
  126. err := cda.ServiceAccountActionResolver.PopulateServiceAccount(repo)
  127. if err != nil {
  128. return err
  129. }
  130. saCandidate := cda.ServiceAccountActionResolver.SACandidate
  131. for i, cluster := range cda.ServiceAccountActionResolver.SA.Clusters {
  132. if cluster.Name == saCandidate.ClusterName && cluster.Server == saCandidate.ClusterEndpoint {
  133. decoded, err := base64.StdEncoding.DecodeString(cda.ClusterCAData)
  134. // skip if decoding error
  135. if err != nil {
  136. return err
  137. }
  138. (&cluster).CertificateAuthorityData = decoded
  139. cda.ServiceAccountActionResolver.SA.Clusters[i] = cluster
  140. }
  141. }
  142. return nil
  143. }
  144. // ClientCertDataAction contains the base64 encoded cluster cert data
  145. type ClientCertDataAction struct {
  146. *ServiceAccountActionResolver
  147. ClientCertData string `json:"client_cert_data" form:"required"`
  148. }
  149. // PopulateServiceAccount will add client CA data to a ServiceAccount
  150. func (ccda *ClientCertDataAction) PopulateServiceAccount(
  151. repo repository.ServiceAccountRepository,
  152. ) error {
  153. err := ccda.ServiceAccountActionResolver.PopulateServiceAccount(repo)
  154. if err != nil {
  155. return err
  156. }
  157. decoded, err := base64.StdEncoding.DecodeString(ccda.ClientCertData)
  158. // skip if decoding error
  159. if err != nil {
  160. return err
  161. }
  162. ccda.ServiceAccountActionResolver.SA.ClientCertificateData = decoded
  163. return nil
  164. }
  165. // ClientKeyDataAction contains the base64 encoded cluster key data
  166. type ClientKeyDataAction struct {
  167. *ServiceAccountActionResolver
  168. ClientKeyData string `json:"client_key_data" form:"required"`
  169. }
  170. // PopulateServiceAccount will add client CA data to a ServiceAccount
  171. func (ckda *ClientKeyDataAction) PopulateServiceAccount(
  172. repo repository.ServiceAccountRepository,
  173. ) error {
  174. err := ckda.ServiceAccountActionResolver.PopulateServiceAccount(repo)
  175. if err != nil {
  176. return err
  177. }
  178. decoded, err := base64.StdEncoding.DecodeString(ckda.ClientKeyData)
  179. // skip if decoding error
  180. if err != nil {
  181. return err
  182. }
  183. ckda.ServiceAccountActionResolver.SA.ClientKeyData = decoded
  184. return nil
  185. }
  186. // OIDCIssuerDataAction contains the base64 encoded IDP issuer CA data
  187. type OIDCIssuerDataAction struct {
  188. *ServiceAccountActionResolver
  189. OIDCIssuerCAData string `json:"oidc_idp_issuer_ca_data" form:"required"`
  190. }
  191. // PopulateServiceAccount will add OIDC issuer CA data to a ServiceAccount
  192. func (oida *OIDCIssuerDataAction) PopulateServiceAccount(
  193. repo repository.ServiceAccountRepository,
  194. ) error {
  195. err := oida.ServiceAccountActionResolver.PopulateServiceAccount(repo)
  196. if err != nil {
  197. return err
  198. }
  199. // based on the implementation, the oidc plugin expects the data to be base64 encoded,
  200. // which means we will not decode it here
  201. // reference: https://github.com/kubernetes/kubernetes/blob/9dfb4c876bfca7a5ae84259fae2bc337ed90c2d7/staging/src/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc.go#L135
  202. oida.ServiceAccountActionResolver.SA.OIDCCertificateAuthorityData = oida.OIDCIssuerCAData
  203. return nil
  204. }
  205. // TokenDataAction contains the token data to use
  206. type TokenDataAction struct {
  207. *ServiceAccountActionResolver
  208. TokenData string `json:"token_data" form:"required"`
  209. }
  210. // PopulateServiceAccount will add bearer token data to a ServiceAccount
  211. func (tda *TokenDataAction) PopulateServiceAccount(
  212. repo repository.ServiceAccountRepository,
  213. ) error {
  214. err := tda.ServiceAccountActionResolver.PopulateServiceAccount(repo)
  215. if err != nil {
  216. return err
  217. }
  218. tda.ServiceAccountActionResolver.SA.Token = tda.TokenData
  219. return nil
  220. }
  221. // GCPKeyDataAction contains the GCP key data
  222. type GCPKeyDataAction struct {
  223. *ServiceAccountActionResolver
  224. GCPKeyData string `json:"gcp_key_data" form:"required"`
  225. }
  226. // PopulateServiceAccount will add GCP key data to a ServiceAccount
  227. func (gkda *GCPKeyDataAction) PopulateServiceAccount(
  228. repo repository.ServiceAccountRepository,
  229. ) error {
  230. err := gkda.ServiceAccountActionResolver.PopulateServiceAccount(repo)
  231. if err != nil {
  232. return err
  233. }
  234. gkda.ServiceAccountActionResolver.SA.GCPKeyData = []byte(gkda.GCPKeyData)
  235. return nil
  236. }
  237. // AWSDataAction contains the AWS data (access id, key)
  238. type AWSDataAction struct {
  239. *ServiceAccountActionResolver
  240. AWSAccessKeyID string `json:"aws_access_key_id" form:"required"`
  241. AWSSecretAccessKey string `json:"aws_secret_access_key" form:"required"`
  242. AWSClusterID string `json:"aws_cluster_id" form:"required"`
  243. }
  244. // PopulateServiceAccount will add GCP key data to a ServiceAccount
  245. func (akda *AWSDataAction) PopulateServiceAccount(
  246. repo repository.ServiceAccountRepository,
  247. ) error {
  248. err := akda.ServiceAccountActionResolver.PopulateServiceAccount(repo)
  249. if err != nil {
  250. return err
  251. }
  252. akda.ServiceAccountActionResolver.SA.AWSAccessKeyID = akda.AWSAccessKeyID
  253. akda.ServiceAccountActionResolver.SA.AWSSecretAccessKey = akda.AWSSecretAccessKey
  254. akda.ServiceAccountActionResolver.SA.AWSClusterID = akda.AWSClusterID
  255. return nil
  256. }