Auomatic deployment of a jump host on OpenStack
In this sub-section, is shown how to automatically deploy a bastion host in an OpenStack environment using Terraform, followed by direct configuration through an Ansible role. The repository documenting all steps is available at this GitHub repository link.
The Ansible role you’ll find can be used standalone or as part of an automated deployment pipeline together with the Terraform module in the terraform directory, which handles the infrastructure provisioning on OpenStack.
Tip
Choose this installation method only if you already have some knowledge of Terraform and Ansible. Otherwise, follow the full guide.
Start by cloning the repository on any VM you want:
git clone https://github.com/Laniakea-elixir-it/ansible-role-vpn-bastion
Note
If you plan to use the full automated workflow (Terraform + Ansible), you can skip this Ansible-only section and jump directly to the Terraform part. If instead you want to configure an existing VM manually or test the PAM/OIDC setup, run this Ansible role as described below.
Ansible configuration
This playbook turns an Ubuntu 22.04 VM into a bastion that accepts SSH logins via OpenID Connect (device code flow) using the pam_oauth2_device module.
Note
If you are manually configuring the VM to act as a bastion host, ensure that the instance meets all the specified requirements. However, if you are using the Terraform integration, these manual steps are not necessary as the configuration is handled automatically.
The specific version of the PAM module used is: pam_oauth2_device.
The playbook performs the following tasks:
Builds and installs the
pam_oauth2_devicePAM module.Writes
/etc/pam_oauth2_device/config.jsonfor your chosen IdP.Sends the device-code URL via SMTP (disabled by default).
Creates
~/.ssh/authorized_keysfor theimuser if a public key is provided.
By cloning the repository, the Ansible section contains:
├─ inventory
├─ site.yml
├─ group_vars/
| ├─ bastion.yml # public settings (non-secret)
| └─ bastion.vault.yml # secrets (managed through Ansible Vault)
├─ templates/
| └─ pam_config.json.j2
└─ .gitignore
Inside the directory you will find:
an
inventoryfilea
site.ymlfor installation and PAM configurationa
group_varsdirectory containing settings and secretstemplates used to configure your IdP
Note
Make sure the following requirements are met:
Target host (can be your vm): Ubuntu 22.04 VM reachable via SSH (with sudo-capable user, e.g.
ubuntu).Controller: Ansible ≥ 2.15.
OIDC client:
client_idandclient_secretregistered at your IdP.(Optional) SMTP credentials for delivering device-code URLs by email.
Ansible configuration
First edit the inventory file and set your bastion’s public IP and SSH user:
[bastion]
bastion1 ansible_host=BASTION_PUBLIC_IP ansible_user=ubuntu
Then choose your IdP and fill the provider endpoints. Open group_vars/bastion.yml. IAM endpoints are already filled in; for other IdPs, replace the placeholders:
idp_provider: "iam" # or lifescience | egi
...
oidc_providers:
iam:
device_endpoint: "https://.../devicecode"
token_endpoint: "https://.../token"
userinfo_endpoint: "https://.../userinfo"
lifescience:
device_endpoint: "FILL_ME"
token_endpoint: "FILL_ME"
userinfo_endpoint: "FILL_ME"
egi:
device_endpoint: "FILL_ME"
token_endpoint: "FILL_ME"
userinfo_endpoint: "FILL_ME"
Then is important to modify the bastion.vault.yml and insert your sensible data.
Warning
Put secrets into the Vault file. Never commit the Vault file.
# OIDC client (confidential)
client_id: "YOUR_OIDC_CLIENT_ID"
client_secret: "YOUR_OIDC_CLIENT_SECRET"
# SMTP password (only if enable_email: true in bastion.yml)
smtp:
smtp_password: "YOUR_SMTP_PASSWORD"
# Create local UNIX users before enabling PAM (must include the OIDC preferred_username)
preferred_username: "your_oidc_username"
extra_local_users:
- "im" # technical jump user (optional)
# - "anotheruser" # add more if needed
# SSH public key for the 'im' user (optional)
jump_user_pubkey: "ssh-rsa AAAA... comment"
Once configured, run the playbook:
(Optional) enable email for device code/URL:
enable_email: true
smtp:
smtp_server_url: "smtps://smtp.gmail.com:465"
smtp_username: "your-smtp-user"
# smtp_password goes in bastion.vault.yml
Then encrypt your valut and run the playbook:
ansible-playbook -i inventory site.yml
Now you should have a fully functional and configured bastion host on your BASTION_PUBLIC_IP.
Create the Jump Host with Terraform
This procedure defines and deploys a Virtual Machine (VM) on OpenStack, configured to act as a Bastion Host (jump host). It serves as a secure SSH entry point to access resources in private networks.
Running the configuration will create:
An OpenStack keypair for SSH access.
A bastion VM in OpenStack with both public and private NICs.
An Ansible inventory file pointing to the VM with the correct SSH key and IP.
A fully configured bastion host (via Ansible).
Note
Ensure the following requirements:
Terraform: ≥ 1.14.0
Ansible: ≥ 2.15
OIDC client:
client_id+client_secretfrom your IdP(Optional) SMTP credentials
Structure of the repository
Cloning the repository gives the following Terraform structure:
terraform_bastion/
├─ main.tf
├─ terraform.tfvars
└─ variables.tf
main.tfcontains the configuration and all required fields.variables.tfdefines and documents all variables.terraform.tfvarscontains sensitive values and must be kept private.
Warning
If you fork the repository, never commit terraform.tfvars.
Add it to .gitignore or encrypt and use a vault.
Terraform configuration
The main.tf and variables.tf files are already setted, you need to modify the terraform.tfvars with your sensible configurations:
When you set all the configuration run the commant for terraform, and it will create and configure the bastion host for you:
terraform init
terraform apply