Connecting to a Cloud Platform database
If you have an RDS instance on Cloud Platform and a namespace on a Container Platform cluster, you can use a port-forward pod on the Container Platform to reach that database and prove connectivity.
There are two parts to it:
- Allow your cluster’s address range through the database’s security group. This is a Terraform change in your Cloud Platform namespace.
- Connect to the database. To do this from your own machine, use a port-forward pod.
Before you start
You need:
- a namespace on a Container Platform nonlive cluster. See Deploying an application to the Container Platform
- an RDS instance in a Cloud Platform namespace, created with the RDS module. See Cloud Platform namespace user guide and RDS user guide
- the ability to raise a pull request against cloud-platform-environments
Step 1: allow your cluster to reach the database
In your Cloud Platform namespace, add a security-group.tf file to the resources directory:
data "aws_vpc" "selected" {
filter {
name = "tag:Name"
values = [var.vpc_name == "live" ? "live-1" : var.vpc_name]
}
}
resource "aws_security_group" "rds" {
name = "${var.namespace}-rds-sg"
description = "Allow Container Platform access to this database"
vpc_id = data.aws_vpc.selected.id
lifecycle {
create_before_destroy = true
}
}
resource "aws_security_group_rule" "rds_inbound" {
type = "ingress"
description = "Container Platform nonlive"
from_port = 5432
to_port = 5432
protocol = "tcp"
security_group_id = aws_security_group.rds.id
cidr_blocks = ["10.195.48.0/20"] # your cluster, see the table below
}
You must change cidr_blocks to your Container Platform cluster:
| Cluster | Address range |
|---|---|
container-platform-octo-nonlive |
10.195.48.0/20 |
container-platform-hmpps-nonlive |
10.195.64.0/20 |
container-platform-laa-nonlive |
10.195.80.0/20 |
container-platform-cd-nonlive |
10.195.96.0/20 |
These are the node ranges. Your pods have their own addresses, but traffic leaving the cluster appears to come from the node it is running on, so the node range is what the security group needs to allow.
More clusters will be added over time. The ranges for every cluster are defined in the
vpc_cidr map in
network/locals.tf,
where primary is the node range you need and secondary is the pod range.
If your cluster is not listed above, use the primary value for it from that file. Ask in
#container-platform-alpha-users
if you are not sure which applies to you.
Then attach the security group to your RDS instance. In the file where you call the RDS module, add:
module "rds" {
source = "github.com/ministryofjustice/cloud-platform-terraform-rds-instance?ref=<version>"
vpc_name = var.vpc_name
vpc_security_group_ids = [aws_security_group.rds.id]
# ...the rest of your configuration
}
Creating the security group is not enough on its own. If you do not add
vpc_security_group_ids, the rule has no effect and your connection will time out.
Raise a pull request with both changes. Once it is merged, the pipeline applies it.
Use port 1433 for MS-SQL and 3306 for MySQL.
Step 2: run a port-forward pod
The remaining steps use two different clusters. Steps 2, 3 and 5 run against your Container Platform cluster. Step 4 reads a secret from your Cloud Platform namespace. Check which cluster you are pointed at before each step:
kubectl config current-context
Run this in your Container Platform namespace, replacing [your container platform namespace]
and [your database hostname]. The hostname appears in both REMOTE_HOST and
overrides sections. You can find the hostname in the rds_instance_address field of your
RDS secret in your Cloud Platform namespace.
kubectl -n [your container platform namespace] run port-forward-pod \
--image=ministryofjustice/port-forward \
--port=5432 \
--env="REMOTE_HOST=[your database hostname]" \
--env="LOCAL_PORT=5432" \
--env="REMOTE_PORT=5432" \
--overrides='{"spec":{"securityContext":{"runAsNonRoot":true,"runAsUser":1001,"seccompProfile":{"type":"RuntimeDefault"}},"containers":[{"name":"port-forward-pod","image":"ministryofjustice/port-forward","ports":[{"containerPort":5432}],"env":[{"name":"REMOTE_HOST","value":"[your database hostname]"},{"name":"LOCAL_PORT","value":"5432"},{"name":"REMOTE_PORT","value":"5432"}],"securityContext":{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]}}}]}}'
This command differs from the equivalent one in the Cloud Platform user guide. Container Platform enforces the
restrictedPod Security Standard, so the security settings have to be set explicitly. Without the--overridesthe pod is rejected.
Check it started:
kubectl -n [your container platform namespace] logs port-forward-pod
You should see Socat started listening on 5432.
Step 3: forward traffic from your machine
kubectl -n [your container platform namespace] port-forward port-forward-pod 5432:5432
Leave this running while you use the database.
Step 4: connect
Get the credentials from the RDS secret in your Cloud Platform namespace:
kubectl -n [your cloud platform namespace] get secret [your rds secret] -o go-template='
dbname: {{index .data "database_name" | base64decode}}
username: {{index .data "database_username" | base64decode}}
password: {{index .data "database_password" | base64decode}}
'
Then connect as if the database were local:
psql --host localhost --port 5432 \
--dbname [your database name] \
--username [your database username] \
--password
You will be prompted for the password.
Step 5: delete the port-forward pod
kubectl -n [your container platform namespace] delete pod port-forward-pod
Please delete it when you have finished. Pods created this way are not managed by a deployment and will be removed when the node they run on is replaced, so do not rely on one staying up.
Connecting from an application
The steps above are for reaching a database from your own machine.
A deployed application connects over the same network path, so no further network changes are needed. What it also needs is the database credentials, and those are held in your Cloud Platform namespace.
Do not commit credentials to a repository or put them in plain manifests. The Container Platform does not yet provide a mechanism for secrets management, and it is being tracked separately. If you need to connect an application to a database, talk to us first so we can deliberate on an approach in the #container-platform-alpha-users channel.
Troubleshooting
The connection times out.
Check the security group is attached to the instance, not just created. This is the most
common cause. Confirm vpc_security_group_ids is set on your RDS module and that the change
has been applied:
aws rds describe-db-instances --db-instance-identifier [your database identifier] --query 'DBInstances[0].VpcSecurityGroups[*].VpcSecurityGroupId' --output text
aws ec2 describe-security-groups --group-ids sg-xxxxxxxxxxxxxxxxx sg-yyyyyyyyyyyyyyyyy --output table
Then check you used the node range for your cluster from the table above, rather than a pod address.
The pod is rejected with a violates PodSecurity "restricted:latest" error.
You have run the command without the --overrides section. Use the full command in step 2.
socat fails to start, or the pod crashes.
LOCAL_PORT must be above 1024. The container does not run as root and cannot bind to
privileged ports.
Getting help
Ask in #container-platform-alpha-users.