Skip to content

Commit

Permalink
Allow setting up a DNS name for the minecloud server
Browse files Browse the repository at this point in the history
Implements VeriorPies#68

A DNS name is setup automatically when the parameter
DOMAIN_NAME is set to a name pointing to an existing
Hosted Zone. Setting up a hosted zone on AWS involves
purchasing a domain and paying a for it on a yearly
basis. This makes it hard to automate and is therefore
left out of the automation. Instead an existing
hosted zone is referenced by the value of the parameter
DOMAIN_NAME in the MineCloud-Service-Info.ts file.
When this parameter is set, an additional branch of
the CDK script gets executed, which assigns some
additional policies to the EC2 role to allow the VM
to lookup the hosted zone id to update the IP address
of the ARecord, which has been created by CDK.
The DNS name is derived by appending the value of
DOMAIN_NAME to the hostname "minecloud".
The DOMAIN_NAME is attached as a tag to the EC2 instance
to make it accessible to the start_service.sh script
which updates the ARecord with the assigned IP address.
The bot pushes the domain name to the Discord channel
only when the DOMAIN_NAME parameter has been set.

The changeset also includes a change to avoid the file
MineCloud-Service-Info.ts with all the private data
being committed to Git accidentally. Therefore the
file is renamed to MineCloud-Service-Info.ts.sample
and the README is updated to make a copy to the actual
name MineCloud-Service-Info.ts before setting all the
parameters. The MineCloud-Service-Info.ts is set to the
.gitignore list so that the actual file is ignored
upon git commits.
mirumpf committed Oct 3, 2023
1 parent dc4b7bf commit 01c07ef
Showing 5 changed files with 104 additions and 10 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -8,4 +8,8 @@ node_modules
.cdk.staging
cdk.out

dist
dist
MineCloud-Service-Info.ts
.DS_Store
MineCloud-Service-Info.ts.mine
cdk.context.json
Original file line number Diff line number Diff line change
@@ -7,3 +7,6 @@ export const DISCORD_APP_ID = '';
export const DISCORD_PUBLIC_KEY = '';
export const DISCORD_BOT_TOKEN = '';
export const DISCORD_CHANNEL_WEB_HOOK = '';

// DNS (optional)
export const DOMAIN_NAME = '';
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -127,6 +127,13 @@ If you prefer, we have a step-by-step video tutorial ↓
<p align="center">
You can also optionally set up the BOT avatar here<br>
</p>
- (Optional) Create and update a DNS record upon boot:
- Users of the server can connect to the same DNS name instead of having to update the server configuration each time a new IP gets assigned.
- The CDK infrastructure automation script will create a DNS record under a **given** Hosted zone.
- The start_server.sh script will update the DNS record every time the VM starts.
- NOTE: The script will not create a Hosted Zone for you! The domain purchasing needs to be done manually and the Hosted Zone must be available in the same AWS account where you want to setup MineCloud.
- ATTENTION: For the instance to update its own DNS record the permissions must be broader than without using a DNS name. This could be used by an attacker to takeover the DNS record.
- `DOMAIN_NAME`: contains the domain name of an existing hosted zone.
2. Deploy MineCloud
- (Optional) Customize Deployment
- Common customizable options (e.g. VM type, disk size...etc) can be found in `minecloud_configs\MineCloud-Configs.ts`
@@ -156,6 +163,14 @@ If you prefer, we have a step-by-step video tutorial ↓
If you have deployed MineCloud more than once, **THERE MIGHT BE DANGLING SPOT INSTANCE REQUESTS THAT WILL CONSTANTLY CHARGE YOU. MAKE SURE TO CHECK YOUR [EC2 SPOT REQUESTS TAB](https://console.aws.amazon.com/ec2/home#SpotInstances:) AND CANCEL THE DANGLING SPOT REQUEST IF THERE'S ANY!!**
You can also run the following commands:
```
aws ec2 describe-spot-instance-requests --query "SpotInstanceRequests[].{id:SpotInstanceRequestId,state:State,created:CreateTime}"
```
and cancel the dangling requests (the ones with older timestamps):
```
aws ec2 cancel-spot-instance-requests --spot-instance-request-ids <id>
```
## Managing the Server after Deployment
After Deployment, the server can be managed by SSH terminals and SFTP clients.
62 changes: 58 additions & 4 deletions lib/mine-cloud-stack.ts
Original file line number Diff line number Diff line change
@@ -5,7 +5,8 @@ import {
CustomResource,
Duration,
Stack,
StackProps
StackProps,
Tags
} from 'aws-cdk-lib';
import {
Effect,
@@ -53,6 +54,11 @@ import { IGNORE_FAILURE_ON_INSTANCE_INIT } from '../minecloud_configs/advanced_c

export const STACK_PREFIX = STACK_NAME;

import {
DOMAIN_NAME
} from '../MineCloud-Service-Info';
import route53 = require('aws-cdk-lib/aws-route53');

export class MineCloud extends Stack {
readonly ec2Instance;

@@ -151,7 +157,7 @@ export class MineCloud extends Stack {
keyName: `${STACK_PREFIX}_ec2_key`
});

return new SpotInstance(this, `${STACK_PREFIX}_ec2_instance`, {
const spotInstance = new SpotInstance(this, `${STACK_PREFIX}_ec2_instance`, {
vpc: defaultVPC,
keyName: sshKeyPair.keyName,
role: ec2Role,
@@ -166,8 +172,8 @@ export class MineCloud extends Stack {
}),
templateId: `${STACK_PREFIX}_ec2_launch_template`,
launchTemplateSpotOptions: {
interruptionBehavior: SpotInstanceInterruption.STOP,
requestType: SpotRequestType.PERSISTENT,
interruptionBehavior: SpotInstanceInterruption.TERMINATE,
requestType: SpotRequestType.ONE_TIME,
maxPrice: MAX_PRICE
},
initOptions: {
@@ -187,6 +193,54 @@ export class MineCloud extends Stack {
// (YOU'LL NEED TO MANUALLY CANCEL THE DANGLING SPOT REQUEST TO AVOID SPINNING UP ADDITIONAL EC2 INSTANCE)
init: getInitConfig(backupBucketName)
});

// Optional: do all the DNS related stuff only when a DOMAIN_NAME parameter is set
if (DOMAIN_NAME) {

// get a reference to the existing hosted zone
const zone = route53.HostedZone.fromLookup(this, 'Zone', { domainName: DOMAIN_NAME })

// add permission to describe tags of an EC2 instance and lookup hosted zones by DNS domain name
ec2Role.addToPolicy(
new PolicyStatement({
effect: Effect.ALLOW,
actions: [
'ec2:DescribeTags',
'route53:ListHostedZonesByName'
],
resources: ['*']
})
);
// add permission to update the DNS record
ec2Role.addToPolicy(
new PolicyStatement({
effect: Effect.ALLOW,
actions: [
"route53:ChangeResourceRecordSets"
],
resources: ['arn:aws:route53:::hostedzone/' + zone.hostedZoneId]
})
);

const DNS_NAME = 'minecloud' // variable to make it overridable in the future

// create a dummy record which we can update during server start
// TODO: https://github.com/aws/aws-cdk/issues/4155
// It seems as if the Arecord does not get deleted upon CDK destroy
// in that case we could simply re-use the old entry until the issue gets fixed
// is it possible that the record cannot be deleted when its value gets updated externally?
const aliasRecord = new route53.ARecord(this, 'MyARecord', {
target: {
values: ['192.168.0.1'],
},
recordName: DNS_NAME + '.' + DOMAIN_NAME,
zone: zone,
});

Tags.of(spotInstance).add('DOMAIN_NAME', DOMAIN_NAME);
}

return spotInstance;
}

setupDiscordCommands(): CustomResource {
28 changes: 23 additions & 5 deletions server_init_assets/start_service.sh
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
cd /opt/minecloud
echo "Server started: $(date)"
public_ip=$(dig +short myip.opendns.com @resolver1.opendns.com)
echo "new ip: $public_ip"
./send_discord_message_to_webhook.sh "The server instance is ready >w< ! Here's the IP address:\n$public_ip"
echo "Discord public IP sent"

MY_IP=$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4/)
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id/)
AZ=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone/)

DOMAIN_NAME=$(aws ec2 describe-tags --region ${AZ::-1} --filters "Name=resource-id,Values=${INSTANCE_ID}" --query 'Tags[?Key==`DOMAIN_NAME`].Value' --output text)
if [ ! -z "$DOMAIN_NAME" ]
then
DNS_NAME="minecloud.$DOMAIN_NAME"

ZONE=$(aws route53 list-hosted-zones-by-name --dns-name $DOMAIN_NAME --query "HostedZones[].Id" --output text)
ZONE_ID=${ZONE##*/}

aws route53 change-resource-record-sets --hosted-zone-id $ZONE_ID --change-batch '{"Changes":[{"Action":"UPSERT","ResourceRecordSet":{"Name":"'$DNS_NAME'","Type":"A","TTL":60,"ResourceRecords":[{"Value":"'$MY_IP'"}]}}]}'
echo "hostname (ip): $DNAME_NAME ($MY_IP)"
./send_discord_message_to_webhook.sh "The server instance is ready >w< ! Here's the hostname/IP address:\n$DNS_NAME ($MY_IP)"
echo "Discord hostname & public IP sent"
else
echo "ip: $MY_IP"
./send_discord_message_to_webhook.sh "The server instance is ready >w< ! Here's the IP address:\n$MY_IP"
echo "Discord public IP sent"
fi

#start the Minecloud server
echo "starting server"
cd server
./start_server.sh
echo "server stop"
echo "server stop"

0 comments on commit 01c07ef

Please sign in to comment.