Skip to content

Commit

Permalink
move token bootstrapping to Go
Browse files Browse the repository at this point in the history
  • Loading branch information
divolgin committed Aug 26, 2020
1 parent 0b6efeb commit 618bd0a
Show file tree
Hide file tree
Showing 11 changed files with 223 additions and 26 deletions.
6 changes: 0 additions & 6 deletions kotsadm/api/kustomize/overlays/dev/deployment.yaml
Expand Up @@ -41,12 +41,6 @@ spec:
value: http://127.0.0.1:30065
- name: GRAPHQL_PREM_ENDPOINT
value: http://graphql-api-prem:3000/graphql
- name: AUTO_CREATE_CLUSTER
value: "1"
- name: AUTO_CREATE_CLUSTER_NAME
value: "microk8s"
- name: AUTO_CREATE_CLUSTER_TOKEN
value: this-is-definitely-not-a-secret
- name: ENABLE_KURL
value: "1"
- name: SHARED_PASSWORD_BCRYPT
Expand Down
16 changes: 0 additions & 16 deletions kotsadm/api/src/server/server.ts
Expand Up @@ -110,22 +110,6 @@ export class Server extends ServerLoader {
paramsStore: new ParamsStore(pool, params),
};

logger.info({msg: "ensuring a local cluster exists"});
if (!process.env["AUTO_CREATE_CLUSTER_TOKEN"]) {
logger.error({msg: "you must set AUTO_CREATE_CLUSTER_TOKEN"});
process.exit(1);
return;
}
const cluster = await stores.clusterStore.maybeGetClusterWithTypeNameAndToken("ship", "this-cluster", process.env["AUTO_CREATE_CLUSTER_TOKEN"]!);
if (!cluster) {
await stores.clusterStore.createNewShipCluster(undefined, true, "this-cluster", process.env["AUTO_CREATE_CLUSTER_TOKEN"]);
}

if (process.env["SHARED_PASSWORD_BCRYPT"]) {
logger.info({msg: "ensuring that shared admin console password is provisioned"});
await stores.userStore.createAdminConsolePassword(process.env["SHARED_PASSWORD_BCRYPT"]!);
}

const setContext = async (req: Request, res: Response, next: NextFunction) => {
let token = req.get("Authorization") || "";

Expand Down
6 changes: 6 additions & 0 deletions kotsadm/kustomize/overlays/dev/deployment.yaml
Expand Up @@ -24,6 +24,12 @@ spec:
- name: KOTSADM_TARGET_NAMESPACE
value: "test"
valueFrom: ~
- name: AUTO_CREATE_CLUSTER
value: "1"
- name: AUTO_CREATE_CLUSTER_NAME
value: "microk8s"
- name: AUTO_CREATE_CLUSTER_TOKEN
value: this-is-definitely-not-a-secret
- name: POD_NAMESPACE
valueFrom:
fieldRef:
Expand Down
45 changes: 45 additions & 0 deletions kotsadm/pkg/apiserver/bootstrap.go
@@ -1,6 +1,8 @@
package apiserver

import (
"os"

"github.com/pkg/errors"
"github.com/replicatedhq/kots/kotsadm/pkg/store"
)
Expand All @@ -10,5 +12,48 @@ func bootstrap() error {
return errors.Wrap(err, "failed to init store")
}

if err := bootstrapClusterToken(); err != nil {
return errors.Wrap(err, "failed to bootstrap cluster token")
}

if err := bootstrapSharedPassword(); err != nil {
return errors.Wrap(err, "failed to bootstrap shared password")
}

return nil
}

func bootstrapClusterToken() error {
if os.Getenv("AUTO_CREATE_CLUSTER_TOKEN") == "" {
return errors.New("AUTO_CREATE_CLUSTER_TOKEN is not set")
}

_, err := store.GetStore().LookupClusterID("ship", "this-cluster", os.Getenv("AUTO_CREATE_CLUSTER_TOKEN"))
if err == nil {
return nil
}

if err != nil && !store.GetStore().IsNotFound(err) {
return errors.Wrap(err, "failed to lookup cluster ID")
}

_, err = store.GetStore().CreateNewCluster("", true, "this-cluster", os.Getenv("AUTO_CREATE_CLUSTER_TOKEN"))
if err != nil {
return errors.Wrap(err, "failed to create cluster")
}

return nil
}

func bootstrapSharedPassword() error {
if os.Getenv("SHARED_PASSWORD_BCRYPT") == "" {
return nil
}

_, err := store.GetStore().CreateAdminConsolePassword(os.Getenv("SHARED_PASSWORD_BCRYPT"))
if err != nil {
return errors.New("failed to admin password")
}

return nil
}
8 changes: 4 additions & 4 deletions kotsadm/pkg/apiserver/server.go
Expand Up @@ -24,16 +24,16 @@ import (
func Start() {
log.Printf("kotsadm version %s\n", os.Getenv("VERSION"))

if err := bootstrap(); err != nil {
panic(err)
}

ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
if err := store.GetStore().WaitForReady(ctx); err != nil {
panic(err)
}
cancel()

if err := bootstrap(); err != nil {
panic(err)
}

if err := informers.Start(); err != nil {
log.Println("Failed to start informers", err)
}
Expand Down
22 changes: 22 additions & 0 deletions kotsadm/pkg/rand/rand.go
@@ -0,0 +1,22 @@
package rand

import (
"math/rand"
"time"
)

const LOWER_CASE = "abcdefghijklmnopqrstuvwxyz"
const UPPER_CASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
const NUMERIC = "1234567890"

func init() {
rand.Seed(time.Now().UnixNano())
}

func StringWithCharset(length int, charset string) string {
b := make([]byte, length)
for i := range b {
b[i] = charset[rand.Intn(len(charset))]
}
return string(b)
}
8 changes: 8 additions & 0 deletions kotsadm/pkg/store/ocistore/cluster_store.go
Expand Up @@ -34,3 +34,11 @@ func (s OCIStore) GetClusterIDFromDeployToken(deployToken string) (string, error

return string(clusterID), nil
}

func (s OCIStore) LookupClusterID(clusterType string, title string, token string) (string, error) {
return "", ErrNotImplemented
}

func (s OCIStore) CreateNewCluster(userId string, isAllUsers bool, title string, token string) (string, error) {
return "", ErrNotImplemented
}
5 changes: 5 additions & 0 deletions kotsadm/pkg/store/ocistore/user_store.go
@@ -0,0 +1,5 @@
package ocistore

func (s OCIStore) CreateAdminConsolePassword(passwordBcrypt string) (string, error) {
return "", ErrNotImplemented
}
76 changes: 76 additions & 0 deletions kotsadm/pkg/store/s3pg/cluster_store.go
@@ -1,8 +1,14 @@
package s3pg

import (
"database/sql"
"fmt"
"time"

"github.com/gosimple/slug"
"github.com/pkg/errors"
"github.com/replicatedhq/kots/kotsadm/pkg/persistence"
"github.com/replicatedhq/kots/kotsadm/pkg/rand"
)

func (s S3PGStore) ListClusters() (map[string]string, error) {
Expand Down Expand Up @@ -52,3 +58,73 @@ func (s S3PGStore) GetClusterIDFromDeployToken(deployToken string) (string, erro

return clusterID, nil
}

func (s S3PGStore) LookupClusterID(clusterType string, title string, token string) (string, error) {
db := persistence.MustGetPGSession()
query := `select id from cluster where cluster_type = $1 and title = $2 and token = $3`
row := db.QueryRow(query, clusterType, title, token)

var clusterID string
if err := row.Scan(&clusterID); err != nil {
return "", errors.Wrap(err, "failed to scan")
}

return clusterID, nil
}

func (s S3PGStore) CreateNewCluster(userID string, isAllUsers bool, title string, token string) (string, error) {
clusterID := rand.StringWithCharset(32, rand.LOWER_CASE)
clusterSlug := slug.Make(title)

db := persistence.MustGetPGSession()

foundUniqueSlug := false
for i := 0; !foundUniqueSlug; i++ {
slugProposal := clusterSlug
if i > 0 {
slugProposal = fmt.Sprintf("%s-%d", clusterSlug, i)
}
query := `select count(1) as count from cluster where slug = $1`
row := db.QueryRow(query, slugProposal)

var count int
if err := row.Scan(&count); err != nil {
if err == sql.ErrNoRows {
clusterSlug = slugProposal
foundUniqueSlug = true
break
}
return "", errors.Wrap(err, "failed to scan")
}
}

if token == "" {
token = rand.StringWithCharset(32, rand.LOWER_CASE)
}

tx, err := db.Begin()
if err != nil {
return "", errors.Wrap(err, "failed to begin transaction")
}
defer tx.Rollback()

query := `insert into cluster (id, title, slug, created_at, updated_at, cluster_type, is_all_users, token) values ($1, $2, $3, $4, $5, $6, $7, $8)`
_, err = db.Exec(query, clusterID, title, clusterSlug, time.Now(), nil, "ship", isAllUsers, token)
if err != nil {
return "", errors.Wrap(err, "failed to insert cluster row")
}

if userID != "" {
query := `insert into user_cluster (user_id, cluster_id) values ($1, $2)`
_, err := db.Exec(query, userID, clusterID)
if err != nil {
return "", errors.Wrap(err, "failed to insert user_cluster row")
}
}

if err := tx.Commit(); err != nil {
return "", errors.Wrap(err, "failed to commit transaction")
}

return clusterID, nil
}
50 changes: 50 additions & 0 deletions kotsadm/pkg/store/s3pg/user_store.go
@@ -0,0 +1,50 @@
package s3pg

import (
"database/sql"
"time"

"github.com/pkg/errors"
"github.com/replicatedhq/kots/kotsadm/pkg/persistence"
"github.com/replicatedhq/kots/kotsadm/pkg/rand"
)

func (s S3PGStore) CreateAdminConsolePassword(passwordBcrypt string) (string, error) {
db := persistence.MustGetPGSession()
tx, err := db.Begin()
if err != nil {
return "", errors.Wrap(err, "failed to begin transaction")
}
defer tx.Rollback()

query := `select user_id from ship_user_local where email = $1`
row := tx.QueryRow(query, "default-user@none.com")

var userID string
if err := row.Scan(&userID); err != nil {
if err != sql.ErrNoRows {
return "", errors.Wrap(err, "failed lookup existing user")
}

userID = rand.StringWithCharset(32, rand.LOWER_CASE)
query := `insert into ship_user (id, created_at, last_login) values ($1, $2, $3)`

_, err := tx.Exec(query, userID, time.Now(), time.Now())
if err != nil {
return "", errors.Wrap(err, "failed to create ship user")
}
}

query = `insert into ship_user_local (user_id, password_bcrypt, first_name, last_name, email)
values ($1, $2, $3, $4, $5) ON CONFLICT (email) do update set password_bcrypt = EXCLUDED.password_bcrypt`
_, err = tx.Exec(query, userID, passwordBcrypt, "Default", "User", "default-user@none.com")
if err != nil {
return "", errors.Wrap(err, "failed to create ship user local")
}

if err := tx.Commit(); err != nil {
return "", errors.Wrap(err, "failed to commit transaction")
}

return userID, nil
}
7 changes: 7 additions & 0 deletions kotsadm/pkg/store/store_interface.go
Expand Up @@ -27,6 +27,7 @@ type KOTSStore interface {
AirgapStore
TaskStore
SessionStore
UserStore
AppStatusStore
AppStore
VersionStore
Expand Down Expand Up @@ -93,6 +94,10 @@ type SessionStore interface {
GetSession(id string) (*sessiontypes.Session, error)
}

type UserStore interface {
CreateAdminConsolePassword(string) (string, error)
}

type AppStatusStore interface {
GetAppStatus(string) (*appstatustypes.AppStatus, error)
}
Expand Down Expand Up @@ -137,6 +142,8 @@ type ClusterStore interface {
ListClusters() (map[string]string, error)
GetClusterIDFromSlug(slug string) (string, error)
GetClusterIDFromDeployToken(string) (string, error)
LookupClusterID(clusterType string, title string, token string) (string, error)
CreateNewCluster(userId string, isAllUsers bool, title string, token string) (string, error)
}

type InstallationStore interface {
Expand Down

0 comments on commit 618bd0a

Please sign in to comment.