github.com/outbrain/consul@v1.4.5/website/source/docs/guides/acl-migrate-tokens.html.md (about)

     1  ---
     2  layout: "docs"
     3  page_title: "ACL Token Migration"
     4  sidebar_current: "docs-guides-acl-migrate-tokens"
     5  description: |-
     6    Consul 1.4.0 introduces a new ACL system with improvements for the security and
     7    management of ACL tokens and policies. This guide documents how to upgrade
     8    existing (now called "legacy") tokens after upgrading to 1.4.0.
     9  ---
    10  
    11  # ACL Token Migration
    12  
    13  Consul 1.4.0 introduces a new ACL system with improvements for the security and
    14  management of ACL tokens and policies. This guide documents how to upgrade
    15  existing (now called "legacy") tokens after upgrading to 1.4.0.
    16  
    17  Since the policy syntax changed to be more precise and flexible to manage, it's
    18  necessary to manually translate old tokens into new ones to take advantage of
    19  the new ACL system features. Tooling is provided to help automate this and this
    20  guide describes the overall process.
    21  
    22  ~> **Note:** **1.4.0 retains full support for "legacy" ACL tokens** so upgrades
    23  from Consul 1.3.0 are safe. Existing tokens will continue to work in the same
    24  way for at least two "major" releases (1.5.x, 1.6.x, etc; note HashiCorp does
    25  not use SemVer for our products).
    26  
    27  This document will briefly describe [what changed](#what-changed), and then walk
    28  through the [high-level migration process options](#migration-process), finally
    29  giving some [specific examples](#migration-examples) of migration strategies.
    30  
    31  ## New ACL System Differences
    32  
    33  The [ACL guide](/docs/guides/acl.html) and [legacy ACL
    34  guide](/docs/guides/acl-legacy.html) describes the new and old systems in
    35  detail. Below is a summary of the changes that need to be considered when
    36  migrating legacy tokens to the new system.
    37  
    38  ### Token and Policy Separation
    39  
    40  You can use a single policy in the new system for all tokens that share access
    41  rules. For example, all tokens created using the clone endpoint in the legacy
    42  system can be represented with a single policy and a set of tokens that map to
    43  that policy.
    44  
    45  ### Rule Syntax Changes
    46  
    47  The most significant change is that rules with selectors _no longer prefix match
    48  by default_. In the legacy system the following rules would grant access to
    49  nodes, services and keys _prefixed_ with foo.
    50  
    51  ```
    52  node "foo" { policy = "write" }
    53  service "foo" { policy = "write" }
    54  key "foo" { policy = "write" }
    55  ```
    56  
    57  In the new system the same syntax will only perform _exact_ match on the whole
    58  node name, service name or key.
    59  
    60  In general, exact match is what most operators intended most of the time so the
    61  same policy can be kept, however if you rely on prefix match behavior then using
    62  the same syntax will break behavior.
    63  
    64  Prefix matching can be expressed in the new ACL system explicitly, making the
    65  following rules in the new system exactly the same as the rules above in the
    66  old.
    67  
    68  ```
    69  node_prefix "foo" { policy = "write" }
    70  service_prefix "foo" { policy = "write" }
    71  key_prefix "foo" { policy = "write" }
    72  ```
    73  
    74  ### API Separation
    75  
    76  The "old" API endpoints below continue to work for backwards compatibility but
    77  will continue to create or show only "legacy" tokens that can't take full
    78  advantage of the new ACL system improvements. They are documented fully under
    79  [Legacy Tokens](/api/acl/legacy.html).
    80  
    81  - [`PUT /acl/create` - Create Legacy Token](/api/acl/legacy.html#create-acl-token)
    82  - [`PUT /acl/update` - Update Legacy Token](/api/acl/legacy.html#update-acl-token)
    83  - [`PUT /acl/destroy/:uuid` - Delete Legacy Token](/api/acl/legacy.html#delete-acl-token)
    84  - [`GET /acl/info/:uuid` - Read Legacy Token](/api/acl/legacy.html#read-acl-token)
    85  - [`PUT /acl/clone/:uuid` - Clone Legacy Token](/api/acl/legacy.html#clone-acl-token)
    86  - [`GET /acl/list` - List Legacy Tokens](/api/acl/legacy.html#list-acls)
    87  
    88  The new ACL system includes new API endpoints to manage
    89  the [ACL System](/api/acl/acl.html), [Tokens](/api/acl/tokens.html)
    90  and [Policies](/api/acl/policies.html).
    91  
    92  ## Migration Process
    93  
    94  While "legacy" tokens will continue to work for several major releases, it's
    95  advisable to plan on migrating existing tokens as soon as is convenient.
    96  Migrating also enables using the new policy management improvements, stricter
    97  policy syntax rules and other features of the new system without
    98  re-issuing all the secrets in use.
    99  
   100  The high-level process for migrating a legacy token is as follows:
   101  
   102   1. Create a new policy or policies that grant the required access
   103   2. Update the existing token to use those policies
   104  
   105  ### Prerequisites
   106  
   107  This process assumes that the 1.4.0 upgrade is complete including all legacy
   108  ACLs having their accessor IDs populated. This might take up to several minutes
   109  after the servers upgrade in the primary datacenter. You can tell if this is the
   110  case by using `consul acl token list` and checking that no tokens exist with a
   111  blank `AccessorID`.
   112  
   113  In addition, it is assumed that all clients that might _create_ ACL tokens (e.g.
   114  Vault's Consul secrets engine) have been updated to use the [new ACL
   115  APIs](/docs/guides/acl-migrate-tokens.html#api-separation).
   116  
   117  Specifically if you are using Vault's Consul secrets engine you need to be
   118  running Vault 1.0.0 or higher, _and_ you must update all roles defined in Vault
   119  to specify a list of policy names rather than an inline policy (which causes
   120  Vault to use the legacy API).
   121  
   122  ~> **Note:** if you have systems still creating "legacy" tokens with the old
   123  APIs, the migration steps below will still work, however you'll have to keep
   124  re-running them until nothing is creating legacy tokens to ensure all tokens are
   125  migrated.
   126  
   127  ### Creating Policies
   128  
   129  There are a range of different strategies for creating new policies from existing
   130  tokens. Two high-level strategies are described here although others or a
   131  mixture of these may be most appropriate depending on the ACL tokens you already
   132  have.
   133  
   134  #### Strategy 1: Simple Policy Mapping
   135  
   136  The simplest and most automatic strategy is to create one new policy for every
   137  existing token. This is easy to automate, but may result in a lot of policies
   138  with exactly the same rules and with non-human-readable names which will make
   139  managing policies harder. This approach can be accomplished using the [`consul
   140  acl policy create`](/docs/commands/acl/acl-policy.html#create) command with
   141  `-from-token` option.
   142  
   143  | Pros | Cons |
   144  | ---- | ---- |
   145  | ✅ Simple           | ❌ May leave many duplicated policies |
   146  | ✅ Easy to automate | ❌ Policy names not human-readable |
   147  
   148  A detailed example of using this approach is [given
   149  below](#simple-policy-mapping).
   150  
   151  #### Strategy 2: Combining Policies
   152  
   153  This strategy takes a more manual approach to create a more manageable set of
   154  policies. There are a spectrum of options for how to do this which tradeoff
   155  increasing human involvement for increasing clarity and re-usability of the
   156  resulting policies.
   157  
   158  For example you could use hashes of the policy rules to de-duplicate identical
   159  token policies automatically, however naming them something meaningful for
   160  humans would likely still need manual intervention.
   161  
   162  Toward the other end of the spectrum it might be beneficial for security to
   163  translate prefix matches into exact matches. This however requires the operator
   164  knowing that clients using the token really doesn't rely on the prefix matching
   165  semantics of the old ACL system.
   166  
   167  To assist with this approach, there is a CLI tool and corresponding API that can
   168  translate a legacy ACL token's rules into a new ACL policy that is exactly
   169  equivalent. See [`consul acl
   170  translate-rules`](/docs/commands/acl/acl-translate-rules.html).
   171  
   172  | Pros | Cons |
   173  | ---- | ---- |
   174  | ✅ Clearer, more manageable policies | ❌ Requires more manual effort |
   175  | ✅ Policies can be re-used by new ACL tokens | ❌ May take longer for large or complex existing policy sets |
   176  
   177  A detailed example of using this approach is  [given below](#combining-policies).
   178  
   179  ### Updating Existing Tokens
   180  
   181  Once you have created one or more policies that adequately express the rules
   182  needed for a legacy token, you can update the token via the CLI or API to use
   183  those policies.
   184  
   185  After updating, the token is no longer considered "legacy" and will have all the
   186  properties of a new token, however it keeps it's `SecretID` (the secret part of
   187  the token used in API calls) so clients already using that token will continue
   188  to work. It is assumed that the policies you attach continue to grant the
   189  necessary access for existing clients; this is up to the operator to ensure.
   190  
   191  #### Update via API
   192  
   193  Use the [`PUT /v1/acl/token/:AccessorID`](/api/acl/tokens.html#update-a-token)
   194  endpoint. Specifically, ensure that the `Rules` field is omitted or empty. Empty
   195  `Rules` indicates that this is now treated as a new token.
   196  
   197  #### Update via CLI
   198  
   199  Use the [`consul acl token update`](/docs/commands/acl/acl-token.html#update)
   200  command to update the token. Specifically you need to use `-upgrade-legacy`
   201  which will ensure that legacy rules are removed as well as the new policies
   202  added.
   203  
   204  ## Migration Examples
   205  
   206  Below are two detailed examples of the two high-level strategies for creating
   207  polices discussed above. It should be noted these are intended to clarify the
   208  concrete steps you might take. **We don't recommend you perform production
   209  migrations with ad-hoc terminal commands**. Combining these or something similar
   210  into a script might be appropriate.
   211  
   212  ### Simple Policy Mapping
   213  
   214  This strategy uses the CLI to create a new policy for every existing legacy
   215  token with exactly equivalent rules. It's easy to automate and clients will see
   216  no change in behavior for their tokens, but it does leave you with a lot of
   217  potentially identical policies to manage or clean up later.
   218  
   219  #### Create Policies
   220  
   221  You can get the AccessorID of every legacy token from the API. For example,
   222  using `curl` and `jq` in bash:
   223  
   224  ```sh
   225  $ LEGACY_IDS=$(curl -sH "X-Consul-Token: $CONSUL_HTTP_TOKEN" \
   226     'localhost:8500/v1/acl/tokens' | jq -r '.[] | select (.Legacy) | .AccessorID')
   227  $ echo "$LEGACY_IDS"
   228  621cbd12-dde7-de06-9be0-e28d067b5b7f
   229  65cecc86-eb5b-ced5-92dc-f861cf7636fe
   230  ba464aa8-d857-3d26-472c-4d49c3bdae72
   231  ```
   232  
   233  To create a policy for each one we can use something like:
   234  
   235  ```sh
   236  for id in $LEGACY_IDS; do \
   237    consul acl policy create -name "migrated-$id" -from-token $id \
   238      -description "Migrated from legacy ACL token"; \
   239  done
   240  ```
   241  
   242  Each policy now has an identical set of rules to the original token. You can
   243  inspect these:
   244  
   245  ```sh
   246  $ consul acl policy read -name migrated-621cbd12-dde7-de06-9be0-e28d067b5b7f
   247  ID:           573d84bd-8b08-3061-e391-d2602e1b4947
   248  Name:         migrated-621cbd12-dde7-de06-9be0-e28d067b5b7f
   249  Description:  Migrated from legacy ACL token
   250  Datacenters:
   251  Rules:
   252  service_prefix "" {
   253    policy = "write"
   254  }
   255  ```
   256  
   257  Notice how the policy here is `service_prefix` and not `service` since the old
   258  ACL syntax was an implicit prefix match. This ensures any clients relying on
   259  prefix matching behavior will still work.
   260  
   261  #### Update Tokens
   262  
   263  With the policies created as above, we can automatically upgrade all legacy
   264  tokens.
   265  
   266  ```sh
   267  for id in $LEGACY_IDS; do \
   268    consul acl token update -id $id -policy-name "migrated-$id" -upgrade-legacy; \
   269  done
   270  ```
   271  
   272  The update is now complete, all legacy tokens are now new tokens with identical
   273  secrets and enforcement rules.
   274  
   275  ### Combining Policies
   276  
   277  This strategy has more manual elements but results in a cleaner and more
   278  manageable set of policies than the fully automatic solutions. Note that this is
   279  **just an example** to illustrate a few ways you may choose to merge or
   280  manipulate policies.
   281  
   282  #### Find All Unique Policies
   283  
   284  You can get the AccessorID of every legacy token from the API. For example,
   285  using `curl` and `jq` in bash:
   286  
   287  ```sh
   288  $ LEGACY_IDS=$(curl -sH "X-Consul-Token: $CONSUL_HTTP_TOKEN" \
   289     'localhost:8500/v1/acl/tokens' | jq -r '.[] | select (.Legacy) | .AccessorID')
   290  $ echo "$LEGACY_IDS"
   291  8b65fdf9-303e-0894-9f87-e71b3273600c
   292  d9deb39b-1b30-e100-b9c5-04aba3f593a1
   293  f2bce42e-cdcc-848d-28ca-cfd0556a22e3
   294  ```
   295  
   296  Now we want to read the actual policy for each legacy token and de-duplicate
   297  them. We can use the `translate-rules` helper sub-command which will read the
   298  token's policy and return a new ACL policy that is exactly equivalent.
   299  
   300  ```sh
   301  $ for id in $LEGACY_IDS; do \
   302    echo "Policy for $id:"
   303    consul acl translate-rules -token-accessor "$id"; \
   304  done
   305  Policy for 8b65fdf9-303e-0894-9f87-e71b3273600c:
   306  service_prefix "bar" {
   307    policy = "write"
   308  }
   309  Policy for d9deb39b-1b30-e100-b9c5-04aba3f593a1:
   310  service_prefix "foo" {
   311    policy = "write"
   312  }
   313  Policy for f2bce42e-cdcc-848d-28ca-cfd0556a22e3:
   314  service_prefix "bar" {
   315    policy = "write"
   316  }
   317  ```
   318  
   319  Notice that two policies are the same and one different.
   320  
   321  We can change the loop above to take a hash of this policy definition to
   322  de-duplicate the policies into a set of files locally. This example uses command
   323  available on macOS but equivalents for other platforms should be easy to find.
   324  
   325  ```sh
   326  $ mkdir policies
   327  $ for id in $LEGACY_IDS; do \
   328    # Fetch the equivalent new policy rules based on the legacy token rules
   329    NEW_POLICY=$(consul acl translate-rules -token-accessor "$id"); \
   330    # Sha1 hash the rules
   331    HASH=$(echo -n "$NEW_POLICY" | shasum | awk '{ print $1 }'); \
   332    # Write rules to a policy file named with the hash to de-duplicated
   333    echo "$NEW_POLICY" > policies/$HASH.hcl; \
   334  done
   335  $ tree policies
   336  policies
   337  ├── 024ce11f26f59436c518fb31f0999d1400485c17.hcl
   338  └── 501b787c9444fbd62f346ab257eeb27197be2444.hcl
   339  ```
   340  
   341  #### Cleaning Up Policies
   342  
   343  You can now manually inspect and potentially edit these policies. For example we
   344  could rename them according to their intended use. In this case we maintain the
   345  hash as it will allow us to match tokens to policies later.
   346  
   347  ```sh
   348  $ cat policies/024ce11f26f59436c518fb31f0999d1400485c17.hcl
   349  service_prefix "bar" {
   350    policy = "write"
   351  }
   352  $ # Add human-readable suffix to the file name so policies end up clearly named
   353  $ mv policies/024ce11f26f59436c518fb31f0999d1400485c17.hcl \
   354    policies/024ce11f26f59436c518fb31f0999d1400485c17-bar-service.hcl
   355  ```
   356  
   357  You might also choose to tighten up the rules, for example if you know you never
   358  rely on prefix-matching the service name `foo` you might choose to modify the
   359  policy to use exact match.
   360  
   361  ```sh
   362  $ cat policies/501b787c9444fbd62f346ab257eeb27197be2444.hcl
   363  service_prefix "foo" {
   364    policy = "write"
   365  }
   366  $ echo 'service "foo" { policy = "write" }' > policies/501b787c9444fbd62f346ab257eeb27197be2444.hcl
   367  $ # Add human-readable suffix to the file name so policies end up clearly named
   368  $ mv policies/501b787c9444fbd62f346ab257eeb27197be2444.hcl \
   369    policies/501b787c9444fbd62f346ab257eeb27197be2444-foo-service.hcl
   370  ```
   371  
   372  #### Creating Policies
   373  
   374  We now have a minimal set of policies to create, with human-readable names. We
   375  can create each one with something like the following.
   376  
   377  ```sh
   378  $ for p in $(ls policies | grep ".hcl"); do \
   379    # Extract the hash part of the file name
   380    HASH=$(echo "$p" | cut -d - -f 1); \
   381    # Extract the name suffix without .hcl
   382    NAME=$(echo "$p" | cut -d - -f 2- | cut -d . -f 1); \
   383    # Create new policy based on the rules in the file and the name we gave
   384    consul acl policy create -name $NAME \
   385      -rules "@policies/$p" \
   386      -description "Migrated from legacy token"; \
   387  done
   388  ID:           da2a9f9b-4e44-13f8-e308-76ce7a8dcb21
   389  Name:         bar-service
   390  Description:  Migrated from legacy token
   391  Datacenters:
   392  Rules:
   393  service_prefix "bar" {
   394    policy = "write"
   395  }
   396  
   397  ID:           9fbded86-9140-efe4-b661-c8bd07b6c584
   398  Name:         foo-service
   399  Description:  Migrated from legacy token
   400  Datacenters:
   401  Rules:
   402  service "foo" { policy = "write" }
   403  
   404  ```
   405  
   406  #### Upgrading Tokens
   407  
   408  Finally we can map our existing tokens to those policies using the hash in the
   409  policy file names. The `-upgrade-legacy` flag removes the token's legacy
   410  embedded rules at the same time as associating them with the new policies
   411  created from those rules.
   412  
   413  ```sh
   414  $ for id in $LEGACY_IDS; do \
   415    NEW_POLICY=$(consul acl translate-rules -token-accessor "$id"); \
   416    HASH=$(echo -n "$NEW_POLICY" | shasum | awk '{ print $1 }'); \
   417    # Lookup the hash->new policy mapping from the policy file names
   418    POLICY_FILE=$(ls policies | grep "^$HASH"); \
   419    POLICY_NAME=$(echo "$POLICY_FILE" | cut -d - -f 2- | cut -d . -f 1); \
   420    echo "==> Mapping token $id to policy $POLICY_NAME"; \
   421    consul acl token update -id $id -policy-name $POLICY_NAME -upgrade-legacy; \
   422  done
   423  ==> Mapping token 8b65fdf9-303e-0894-9f87-e71b3273600c to policy bar-service
   424  Token updated successfully.
   425  AccessorID:   8b65fdf9-303e-0894-9f87-e71b3273600c
   426  SecretID:     3dbb3981-7654-733a-3475-5ce20fc5a7b9
   427  Description:
   428  Local:        false
   429  Create Time:  0001-01-01 00:00:00 +0000 UTC
   430  Policies:
   431     da2a9f9b-4e44-13f8-e308-76ce7a8dcb21 - bar-service
   432  ==> Mapping token d9deb39b-1b30-e100-b9c5-04aba3f593a1 to policy foo-service
   433  Token updated successfully.
   434  AccessorID:   d9deb39b-1b30-e100-b9c5-04aba3f593a1
   435  SecretID:     5f54733b-4c76-eb74-8781-3550c20f4969
   436  Description:
   437  Local:        false
   438  Create Time:  0001-01-01 00:00:00 +0000 UTC
   439  Policies:
   440     9fbded86-9140-efe4-b661-c8bd07b6c584 - foo-service
   441  ==> Mapping token f2bce42e-cdcc-848d-28ca-cfd0556a22e3 to policy bar-service
   442  Token updated successfully.
   443  AccessorID:   f2bce42e-cdcc-848d-28ca-cfd0556a22e3
   444  SecretID:     f3aaa3e2-2c6f-cf3c-1e86-454de728e8ab
   445  Description:
   446  Local:        false
   447  Create Time:  0001-01-01 00:00:00 +0000 UTC
   448  Policies:
   449     da2a9f9b-4e44-13f8-e308-76ce7a8dcb21 - bar-service
   450  ```
   451  
   452  At this point all tokens are upgraded and can use new ACL features while
   453  retaining the same secret clients are already using.