diff --git a/cmd/flux/bootstrap_github.go b/cmd/flux/bootstrap_github.go
index 54b9694fa8ece28a7207388345f11db42a039df3..34f60f6d5709222151958840cfa2f7d4fbfd55d2 100644
--- a/cmd/flux/bootstrap_github.go
+++ b/cmd/flux/bootstrap_github.go
@@ -111,7 +111,11 @@ func init() {
 func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
 	ghToken := os.Getenv(ghTokenEnvVar)
 	if ghToken == "" {
-		return fmt.Errorf("%s environment variable not found", ghTokenEnvVar)
+		var err error
+		ghToken, err = readPasswordFromStdin("Please type your GitHub personal access token: ")
+		if err != nil {
+			return fmt.Errorf("could not read token: %w", err)
+		}
 	}
 
 	if err := bootstrapValidate(); err != nil {
diff --git a/cmd/flux/bootstrap_gitlab.go b/cmd/flux/bootstrap_gitlab.go
index 09bde7f3ebe3f0a8991d15e0d793b3f45a946a73..24baa03a9d27c5797d8d407a87a82074d8102632 100644
--- a/cmd/flux/bootstrap_gitlab.go
+++ b/cmd/flux/bootstrap_gitlab.go
@@ -108,7 +108,11 @@ func init() {
 func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
 	glToken := os.Getenv(glTokenEnvVar)
 	if glToken == "" {
-		return fmt.Errorf("%s environment variable not found", glTokenEnvVar)
+		var err error
+		glToken, err = readPasswordFromStdin("Please type your GitLab personal access token: ")
+		if err != nil {
+			return fmt.Errorf("could not read token: %w", err)
+		}
 	}
 
 	if projectNameIsValid, err := regexp.MatchString(gitlabProjectRegex, gitlabArgs.repository); err != nil || !projectNameIsValid {
diff --git a/cmd/flux/main.go b/cmd/flux/main.go
index 916c494014667121c22097100fb04aa3dbc8dfd5..86cfba769af0f81fc643a28ef3cba41b4f08286a 100644
--- a/cmd/flux/main.go
+++ b/cmd/flux/main.go
@@ -17,12 +17,15 @@ limitations under the License.
 package main
 
 import (
+	"bufio"
+	"fmt"
 	"log"
 	"os"
 	"path/filepath"
 	"time"
 
 	"github.com/spf13/cobra"
+	"golang.org/x/term"
 	corev1 "k8s.io/api/core/v1"
 	_ "k8s.io/client-go/plugin/pkg/client/auth"
 
@@ -167,3 +170,22 @@ func homeDir() string {
 	}
 	return os.Getenv("USERPROFILE") // windows
 }
+
+func readPasswordFromStdin(prompt string) (string, error) {
+	var out string
+	var err error
+	fmt.Fprint(os.Stdout, prompt)
+	stdinFD := int(os.Stdin.Fd())
+	if term.IsTerminal(stdinFD) {
+		var inBytes []byte
+		inBytes, err = term.ReadPassword(int(os.Stdin.Fd()))
+		out = string(inBytes)
+	} else {
+		out, err = bufio.NewReader(os.Stdin).ReadString('\n')
+	}
+	if err != nil {
+		return "", fmt.Errorf("could not read from stdin: %w", err)
+	}
+	fmt.Println()
+	return out, nil
+}
diff --git a/go.mod b/go.mod
index d12d179513a58110ba06debc47205382f0e2e95f..ca48bb4d3c3fa6d06ae0546fa2247ca378288150 100644
--- a/go.mod
+++ b/go.mod
@@ -29,6 +29,7 @@ require (
 	github.com/spf13/cobra v1.1.3
 	github.com/spf13/pflag v1.0.5
 	golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b
+	golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d
 	k8s.io/api v0.22.2
 	k8s.io/apiextensions-apiserver v0.22.2
 	k8s.io/apimachinery v0.22.2