Commit 7829be1c authored by Cédric OLIVIER's avatar Cédric OLIVIER
Browse files

feat: Add auth method selection

parent 23eb9afe
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ test-on-remote:
    VAULT_ROLE_ID: "$TEST_VAULT_ROLE_ID"
    VAULT_TOKEN: "$TEST_VAULT_TOKEN"
    VAULT_SECRET_ID: "$TEST_VAULT_SECRET_ID"
    VAULT_AUTH_METHOD: "auto"
  stage: acceptance
  script:
    - curl -s -S -f "http://vault-secrets-provider/health"
+22 −6
Original line number Diff line number Diff line
@@ -144,12 +144,28 @@ The tool requires the following environment variables to be set (as GitLab CI se
| `VAULT_BASE_AUTH_APPROLE_PATH`| The base [AppRole authentication](https://www.vaultproject.io/api-docs/auth/approle) API path | `/auth/approle` |
| `VAULT_BASE_AUTH_JWT_PATH`    | The base [JWT/OIDC authentication](https://www.vaultproject.io/api-docs/auth/jwt) API path | `/auth/jwt` |
| `VAULT_BASE_KV_SECRETS_PATH`  | The base [Key/Value secrets](https://www.vaultproject.io/api-docs/secret/kv/kv-v1) API path | `/secret` |
| `VAULT_AUTH_METHOD`           | Authentication method used to authenticate on Vault server (see below) | _none_ (auto-detect) |
| `VAULT_ROLE_ID`   | The [AppRole](https://www.vaultproject.io/docs/auth/approle) RoleID <br/>_Required for the [AppRole](https://www.vaultproject.io/docs/auth/approle) Auth Method_ | _none_ |
| `VAULT_SECRET_ID` | The [AppRole](https://www.vaultproject.io/docs/auth/approle) SecretID <br/>_Required for the [AppRole](https://www.vaultproject.io/docs/auth/approle) Auth Method_ | _none_ |
| `VAULT_TOKEN`     | The authentication token <br/>_Required for the [Token](https://www.vaultproject.io/docs/auth/token) Auth Method_ | _none_ |
| `VAULT_JWT_TOKEN` | The signed [JSON Web Token](https://en.wikipedia.org/wiki/JSON_Web_Token) to login <br/>_Required for the [JWT/OIDC](https://www.vaultproject.io/docs/auth/jwt) Auth Method_ | `$CI_JOB_JWT` |
| `VAULT_JWT_ROLE`  | Name of the role against which the login is being attempted  <br/>_Required for the [JWT/OIDC](https://www.vaultproject.io/docs/auth/jwt) Auth Method_ | `default_role` |

### Authentication method support

The vault-service-provider supports most vault authentication method.

By default it tries to auto-dectect the authentication method, by checking variables `VAULT_JWT_TOKEN`, `VAULT_ROLE_ID` and `VAULT_TOKEN`, but the authentication method might also be set explicitly using the $VAULT_AUTH_METHOD variable:


| Value            | Authentication Method                                      |
| ---------------- | ---------------------------------------------------------- |
| _none_ (default) or `auto` | The template tries to **auto-detect** the authentication method |
| `jwt`            | [JWT/OIDC Auth Method](https://www.vaultproject.io/api-docs/auth/jwt) |
| `approle`        | [AppRole Auth Method](https://www.vaultproject.io/api-docs/auth/approle) |
| `token`          | [Token Auth Method](https://www.vaultproject.io/api-docs/auth/token) |


If no authentication parameter is set, the image will emit an error log at startup.

### Use in GitLab CI
+31 −7
Original line number Diff line number Diff line
@@ -69,18 +69,34 @@ var (
	vaultJwtRole             = os.Getenv("VAULT_JWT_ROLE")
	vaultRoleId              = os.Getenv("VAULT_ROLE_ID")
	vaultSecretId            = os.Getenv("VAULT_SECRET_ID")
	clientToken              = os.Getenv("VAULT_TOKEN")
	vaultAuthMethod          = EnvStr("VAULT_AUTH_METHOD").Or("auto")
	vaultClientToken         = os.Getenv("VAULT_TOKEN")
	expirationTimeSec        = time.Now().Unix() + 86400 // 24 hours from start time
	kvEngineVersion          = 0
	clientToken              string
)

func DumpVaultCfg() {
func ConfigAuthMethod() {
	log.Printf("Vault server base url: '%s'\n", vaultBaseUrl)
	if len(clientToken) > 0 {
		log.Printf("Vault Auth method: Token (provided)\n")
	if vaultAuthMethod == "auto" {
		log.Printf("Vault Auth method set to 'auto': auto-detect ...\n")
		if len(vaultClientToken) > 0 {
			vaultAuthMethod = "token"
			log.Printf("... Token detected\n")
		} else if len(vaultRoleId) > 0 && len(vaultSecretId) > 0 {
		log.Printf("Vault Auth method: AppRole (path: '%s')\n", vaultBaseAuthApprolePath)
			vaultAuthMethod = "approle"
			log.Printf("... AppRole detected (path: '%s')\n", vaultBaseAuthApprolePath)
		} else if len(vaultJwtToken) > 0 {
			vaultAuthMethod = "jwt"
			log.Printf("... JWT detected (path: '%s')\n", vaultBaseAuthJwtPath)
		} else {
			log.Printf("WARN: no Vault Auth method configured\n")
		}
	} else if vaultAuthMethod == "token" && len(vaultClientToken) > 0 {
		log.Printf("Vault Auth method: Token (provided)\n")
	} else if vaultAuthMethod == "approle" && len(vaultRoleId) > 0 && len(vaultSecretId) > 0 {
		log.Printf("Vault Auth method: AppRole (path: '%s')\n", vaultBaseAuthApprolePath)
	} else if vaultAuthMethod == "jwt" && len(vaultJwtToken) > 0 {
		log.Printf("Vault Auth method: JWT (path: '%s')\n", vaultBaseAuthJwtPath)
	} else {
		log.Printf("WARN: no Vault Auth method configured\n")
@@ -125,20 +141,28 @@ func doLogin() (*Authentication, error) {
	var loginObj interface{}
	var loginUrl string

	if len(vaultRoleId) > 0 && len(vaultSecretId) > 0 {
	log.Printf("... login with AuthMethod (POST %s)\n", vaultAuthMethod)
	if vaultAuthMethod == "approle" && len(vaultRoleId) > 0 && len(vaultSecretId) > 0 {
		loginUrl = fmt.Sprintf("%s%s/login", vaultBaseUrl, vaultBaseAuthApprolePath)
		log.Printf("... login with AppRole (POST %s)\n", loginUrl)
		loginObj = AppRoleLoginBody{
			RoleId:   vaultRoleId,
			SecretId: vaultSecretId,
		}
	} else if len(vaultJwtToken) > 0 {
	} else if vaultAuthMethod == "jwt" && len(vaultJwtToken) > 0 {
		loginUrl = fmt.Sprintf("%s%s/login", vaultBaseUrl, vaultBaseAuthJwtPath)
		log.Printf("... login with JWT (POST %s)\n", loginUrl)
		loginObj = JwtLoginBody{
			Role:  vaultJwtRole,
			Token: vaultJwtToken,
		}
	} else if vaultAuthMethod == "token" && len(vaultClientToken) > 0 {
		log.Printf("... login with Token (provided)\n")
		auth := &Authentication{
			ClientToken:   vaultClientToken,
			LeaseDuration: 0,
		}
		return auth, nil
	} else {
		return nil, StatusError{
			Code:    http.StatusServiceUnavailable,
+1 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ func Test_approle_login_marshalling(t *testing.T) {
		t.Fatalf("Encoding error\nExpected:\n%s\nGot:\n%s", string(expected), string(marshalled))
	}
}

func Test_jwt_login_with_role_marshalling(t *testing.T) {
	expected := []byte(`{"role":"dev-role","jwt":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"}`)
	body := JwtLoginBody{
+1 −1
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ func main() {
	port := EnvInt("PORT").Or(8080)
	log.Printf("Launching service on port %d\n", port)

	DumpVaultCfg()
	ConfigAuthMethod()

	// health endpoint
	http.HandleFunc("/health", Health)