Programmatic Provisioning
Posit Connect supports the programmatic provisioning of an initial administrator user with an API key. This API key can then be used for scripted user provisioning and content deployment processes. This may be useful when, for example, Connect is deployed in an “immutable” environment where no content publication will take place once Connect is running, or for other automated deployment workflows.
Example Workflow
The following section describes an example workflow that creates the first admin API key programmatically using the rsconnect-python
CLI.
Prerequisites
- A fresh instance of Posit Connect with no pre-existing user accounts.
- The ability to generate a secret key for JSON Web Token (JWT) signing/verification.
- Access to the
rsconnect-python
CLI.
Limitations
This workflow will fail if Connect is configured with password authentication without email sending. Password authentication without email sending relies on the initial administrator user to act as the account confirmation intermediary. This conflicts with the goal of the bootstrapped initial administrator, which is only intended to be an anchor for the first API key. The bootstrapped administrator account is not intended to have or provide UI access. This means that you will be unable to confirm any subsequent user accounts that are created on the Connect instance.
If this is problematic for your workflow, please reach out to your customer success representative to talk through possible options.
Generate a Secret Key
Posit Connect currently supports HS256 as its signing/verification algorithm, with a required minimum secret length of 32 bytes.
You can create a valid keyfile with the following command:
head -c 32 /dev/random | base64 > /path/to/secret.key
Save this secret key to a file. Both rsconnect-python
and Posit Connect will need to be able to access this keyfile in the following steps. This secret is a one-time key that holds no authority after this workflow is finished. We recommend deleting it after provisioning is complete. There are no strict requirements around the keyfile location on the filesystem.
You can verify the length of your secret key with the following commmand:
cat /path/to/secret.key | base64 -d | wc -c
Currently, the only JWT signing algorithm supported is HMAC-SHA-256 (HS256), which is a symmetric-key algorithm. This means that the same secret needs to be shared between rsconnect-python
and Posit Connect. Asymmetric-key algorithms, which use public/private keypairs, are not currently supported.
This signing algorithm may not meet your needs if you do not have a secure way to share the generated secret between your client tool and Posit Connect.
Take care to ensure that you follow best practices around key generation and secret management.
Posit Connect expects the secret key to be base64-encoded. If Connect is failing to load your secret correctly, verify that the secret is base64-encoded.
Configure Posit Connect
Update your Posit Connect configuation to include the following:
;; /etc/rstudio-connect/rstudio-connect.gcfg
[Bootstrap]
Enabled = true
SecretKeyFile = /path/to/secret.key
This property allows Connect to read the secret from a file.
A secret key file may be the simplest approach if Connect and the client tool both have access to the same file system. For example, common access can be provided by a mounted file system in a cluster environment, or by running Connect and the client tool on the same host.
;; /etc/rstudio-connect/rstudio-connect.gcfg
[Bootstrap]
Enabled = true
SecretKey = "<SECRET_KEY>"
This property facilitates secret key injection with an environment variable.
Do not store the secret key itself in your configuration file.
Environment variables may aid simple and secure secret key transfer in certain environments. For example, you might use this property if you’re running Connect in a Kubernetes cluster or similar environment where applications are commonly configured using environment variables.
This approach could also be useful if you control the “parent” system that is configuring your Connect environment. Environment variables may allow you to ephemerally inject the secret into your Connect environment without having to engage the filesystem.
The following example would inject the same configuration properties through environment variables:
export CONNECT_BOOTSTRAP_ENABLED=true
export CONNECT_BOOTSTRAP_SECRETKEY="<SECRET_KEY>"
This configuration change:
- Exposes the bootstrapping API endpoint which is used to provision the admin-scoped API key
- Provides Posit Connect with the secret key required to verify the JWT
If Posit Connect is currently running, restart the server.
Request an API key
The two approaches for configuring the JWT secret described below are mutually exclusive. Attempting to configure the secret using both an environment variable and a keyfile will fail.
After installing rsconnect-python
, run the following command:
rsconnect bootstrap \
--server <server_address> \
--jwt-keypath </path/to/secret.key>
rsconnect bootstrap --server <server_address>
This approach facilitates secret key injection with an environment variable. It uses the same environment variable which configures Bootstrap.SecretKey
in Connect: CONNECT_BOOTSTRAP_SECRETKEY
.
The following example would configure the environment variable expected by rsconnect-python
:
export CONNECT_BOOTSTRAP_SECRETKEY="<SECRET_KEY>"
If successful, rsconnect-python
will return an API key embedded in a JSON object:
{
"status": 200,
"api_key": <the_api_key>,
"message": "Success."
}
If an error occurs, this command will return a JSON object with a non-200 status, an empty string for api_key
, and an error message:
{
"status": <error_status>,
"api_key": "",
"message": <error_message>
}
The API Key will only be returned ONCE by Posit Connect. After the first successful response from the bootstrapping endpoint, subsequent requests will fail. If you lose access to the API key but have not successfully provisioned the Posit Connect instance, you will need to start over from the beginning with a fresh Connect instance.
If this command is embedded in a larger provisioning script, it may be useful to insert the API key directly into an environment variable. The rsconnect bootstrap
command provides an optional --raw
flag which extracts the API key from the surrounding json response before returning it. This allows a more complex script to do something like the following:
export API_KEY=$(rsconnect bootstrap --raw --jwt-keypath </path/to/bootstrap.key> --server <server_address>)
Cleanup
After provisioning, assuming that you have created a non-bootstrap administrator account with UI access, it is recommended that you:
- Lock the bootstrap admin account and prohibit use of its API key.
- Disable the
Bootstrap
section of your Connect config and restart Connect. - Delete the secret keyfile from your filesystem.
The easiest way to prohibit use of the bootstrap API key is by locking the bootstrap admin account through the Connect API. This action can be performed using the bootstrap API key itself.
The example below simplifies JSON response processing with a command-line tool called jq
. Details on jq
and installation information can be found on its project webpage.
# Assume we have already completed provisioning using the bootstrapped api key
export BOOTSTRAP_API_KEY=bootstrapped_api_key
export CONNECT_SERVER=http://connect:8000/
# Use the bootstrap API key to get the GUID of the bootstrapped admin account
export GUID=$(curl -H "Authorization: KEY ${BOOTSTRAP_API_KEY}" \
${CONNECT_SERVER}/__api__/v1/user | jq -r ' .guid')
export DATA='{
"locked": true
}'
# Locking the bootstrapped admin account will also prohibit use of its API key
curl -X POST -H "Authorization: Key ${API_KEY}" \
--data-raw "${DATA}" \
${GUID}/lock http://localhost:3939/__api__/v1/users/
Troubleshooting
Common rsconnect-python Errors
Invalid secret key length
Error: Secret key expected to be at least 32 bytes in length.
Your secret is too short. Use a secret key that is at least 32 bytes in length.
Because the secret is base64-encoded, you will not be able to review the key length in the secret file directly. You need to decode the key first.
To check the decoded secret length, you can use a command like:
cat /path/to/secret.key | base64 --decode | wc -c
Common Error Responses
401
{
"status": 401,
"api_key": "",
"message": "JWT authorization failed."
}
Posit Connect was unable to verify the JWT. Confirm that you are using the same secret keyfile in both rsconnect-python
and Connect. If you are constructing your JWT by hand, verify the payload contents and signature algorithm.
403
{
"status": 403,
"api_key": "",
"message": "Unable to provision initial admin. Please check status of Connect database."
}
Check if there are any users already provisioned for your Connect instance. The endpoint will only provision an admin user if the database contains no other users. This response will be sent if you try to reuse the bootstrap endpoint after a successful response.
Remember that you can only successfully request the API key from the endpoint once.
404
{
"status": 404,
"api_key": "",
"message": "Unable to find provisioning endpoint. Please check your 'rsconnect bootstrap --server' parameter and your Connect configuration."
}
Confirm that Boostrap.Enabled
is set to true
in your config. Verify that the rsconnect bootstrap --server
parameter is correct.
FAQ
What if I want to use a different client-side tool than rsconnect-python
? rsconnect-python
can be replaced with custom clients, which will need to be able to produce a JWT and use it in a request against the Posit Connect API.
See the bootstrap endpoint documentation for more details on the Posit Connect API, and the “Custom Clients” section in the appendix for additional implementation details.
Who (or what) is the returned API key attached to? The API key is attached to an ephemeral administrator user. This admin user is not intended to provide access to the UI and can be disabled after provisioning is complete. If you provision another admin user and use it to log into the UI, you will see the bootstrapped admin in the list of registered users. It will have the username __bootstrap_admin__
.