Skip to content

Latest commit

 

History

History
120 lines (94 loc) · 8.48 KB

token_management.md

File metadata and controls

120 lines (94 loc) · 8.48 KB

How is token/credential generated and managed in etcd?

ahrtr@github
January 23, 2023

Table of Contents

Background

etcd needs to handle token/credentials only when auth is enabled.

This post describes how etcd generates and manages the tokens. There are two kinds of tokens, which are simple and JWT. The simple token is designed for development testing, so it isn't recommended to use simple token in production use cases. Please use JWT token in production. Use the flag --auth-token to specify the token type, and the valid options are "simple" or "jwt".

How is each user credentials persisted?

No matter which token type you are going to use, you need to setup RBAC firstly. Creating users using command etcdctl user add or client SDK. Specifically, you need to set both username and password when creating a user.

etcd uses bcrypt.GenerateFromPassword (see also v3_server.go#L490) to generate a bcrypt hash of the password at the given cost specified in --bcrypt-cost. Eventually the hashed password is persisted in the bbolt database.

Note if using TLS CN based auth, then no password is needed; accordingly nothing to save in this case.

How is each token generated?

etcd server generates a token when it successfully authenticates a user. The client side needs to provide both username and password to authenticate a user.

When authenticating a user, etcd server uses bcrypt.CompareHashAndPassword (see also store.go#L375) to compare a bcrypt hashed password with the given plaintext password. If both the username and password are correct, then etcd server generates a token per the token type you choose.

Simple Token

If simple token is configured, etcd server generates a simple token in the format fmt.Sprintf("%s.%d", simpleTokenPrefix, index). The simpleTokenPrefix is a random string of 16 bytes, and the index is just the current consistent_index.

Note simple tokens are not cryptographically signed.

JWT Token

Note you need to start etcd with flags something like below to use JWT token,

--auth-token=jwt,ttl=300s,pub-key=/srv/jwt_RS256.pub,priv-key=/srv/jwt_RS256,sign-method=RS256

If JWT token is configured, etcd server generates a JWT token, which is cryptographically signed by the provided private key. Note etcd depends on golang-jwt/jwt to generate JWT tokens.

Refer to #signing-methods-and-key-types to learn more about Signing Methods and Key Types.

How should client side configure credentials

There are two ways for the client side to configure the credentials. The first way is to configure username and password. Note that it's independent of how the token is generated on server side. In other words, the server side can generate a simple token or JWT based on the value configured for --auth-token.

The second way is to use TLS Common Name with the option --client-cert-auth=true. In this case, the client doesn't need a password for a user, accordingly the client doesn't need to authenticate the user to get a token either. The server side will try to get the username from the field of Common Name (CN) from the client's certificate.

How is the credentials and token exchanged/transported?

When adding a user, the client side populates AuthUserAddRequest, and the request is marshaled at client side and unmarshalled at server side by gRPC automatically.

When authenticating a user, the client side populates AuthenticateRequest, and the request is marshaled at client side and unmarshalled at server side by gRPC automatically. If the user is successfully authenticated, then the generated token is returned to the client side via AuthenticateResponse, which is marshaled at server side and unmarshalled at client side by gRPC automatically.

The transport layer is secured by TLS if configured, see client.go#L352 and client.go#L234.

How is the token used and managed?

When auth is enabled and the client already gets a valid token, it needs to get the token included in the context for each following gRPC request. See credentials.go#L118 and client.go#L296 for client side. See store.go#L1044-L1058 for server side.

Note if using TLS CN based auth, etcd server gets the username from the Common Name in peer's certificate.

etcd server parses and manages the token differently per the token type.

Simple Token

Note the simple tokens are stateful, etcd caches all token-username mappings in memory. The lifetime of each simple token is specified by --auth-token-ttl, which defaults to 300 seconds. etcd removes a token from the cache when it expires.

When etcd server receives a simple token, it just checks whether the token exists in the cache, and regards it as valid if it exists.

JWT Token

The JWT tokens are stateless, and etcd depends on jwt.Parse to parse, validate, verify the signature of a JWT token and return the parsed token. Each JWT token's lifetime is specified in --auth-token=jwt,ttl=300s,..., and defaults to 300 seconds.

Possible minor improvement

Note --auth-token-ttl is only used by simple tokens. Personally I think it should be used by the JWT token as well. In order to be backward compatible, JWT should use TTL below in descending priority order,

  • --auth-token=jwt,ttl=300s,...
  • --auth-token-ttl
  • The default value 300s.

Please anyone feel free to raise an issue in etcd community and gets it resolved.