Add resume pdf & html
This commit is contained in:
10
.env.example
10
.env.example
@ -1,5 +1,5 @@
|
|||||||
{
|
MFA_IDENTIFIER="ARN",
|
||||||
"MFA_IDENTIFIER": "ARN",
|
S3_ROLE="ARN",
|
||||||
"S3_ROLE": "ARN",
|
SESSION_TYPE=""
|
||||||
"SESSION_TYPE": ""
|
BW_AWS_ACCOUNT_SECRET_ID=""
|
||||||
}
|
BW_SESSION=""
|
||||||
@ -7,10 +7,12 @@
|
|||||||
- [x] Secrets/Token Management
|
- [x] Secrets/Token Management
|
||||||
- [x] Consider secret-scanning
|
- [x] Consider secret-scanning
|
||||||
- [x] Added git-leaks on pre-commit hook
|
- [x] Added git-leaks on pre-commit hook
|
||||||
- [x] Create & Connect to a Git*** repository
|
- [x] Create & Connect to a Git repository
|
||||||
- [x] https://git.dropbear-minnow.ts.net/
|
- [x] https://code.wizards.cafe
|
||||||
- [x] Modify and make a second commit
|
- [x] Modify and make a second commit
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
- [x] Test to see if gitea actions works
|
- [x] Test to see if gitea actions works
|
||||||
- [x] Have an existing s3 bucket
|
- [x] Have an existing s3 bucket
|
||||||
|
|
||||||
@ -23,51 +25,82 @@
|
|||||||
## Lab
|
## Lab
|
||||||
- [x] create a custom IAM Policy
|
- [x] create a custom IAM Policy
|
||||||
- [x] create an IAM Role for EC2
|
- [x] create an IAM Role for EC2
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
- [x] Attach the Role to your EC2 Instance
|
- [x] Attach the Role to your EC2 Instance
|
||||||
- [x] Verify is3 access from the EC2 Instance
|
- [x] Verify is3 access from the EC2 Instance
|
||||||
* HTTPS outbound was not set up
|
* HTTPS outbound was not set up
|
||||||
* I did not check outbound rules (even when the lab explicitly called this out)
|
* I did not check outbound rules (even when the lab explicitly called this out)
|
||||||
because it mentioned lab 2, so my assumption was that it had already been set up
|
because it mentioned lab 2, so my assumption was that it had already been set up
|
||||||
(it was not). When connection to s3 failed I double checked lab 3 instructions
|
(it was not). When connection to s3 failed I double checked lab 3 instructions
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Stretch
|
### Stretch
|
||||||
- [x] Create a bucket policy that blocks all public access but allows your IAM role
|
- [x] Create a bucket policy that blocks all public access but allows your IAM role
|
||||||
- [ ] Implmented: [guide](https://aws.amazon.com/blogs/security/how-to-restrict-amazon-s3-bucket-access-to-a-specific-iam-role/)
|
- [ ] Implmented: [guide](https://aws.amazon.com/blogs/security/how-to-restrict-amazon-s3-bucket-access-to-a-specific-iam-role/)
|
||||||
|
|
||||||

|

|
||||||
- [ ] **Experiment** with requiring MFA or VPC conditions.
|
|
||||||
- [ ] MFA conditions
|
- [x] **Experiment** with requiring MFA or VPC conditions.
|
||||||
|
- [x] MFA conditions
|
||||||
* MFA did not work out of the box after setting it in the s3 bucket policy.
|
* MFA did not work out of the box after setting it in the s3 bucket policy.
|
||||||
The ways I found you can configure MFA:
|
The ways I found you can configure MFA:
|
||||||
* [stackoverflow](https://stackoverflow.com/questions/34795780/how-to-use-mfa-with-aws-cli)
|
* [stackoverflow](https://stackoverflow.com/questions/34795780/how-to-use-mfa-with-aws-cli)
|
||||||
* [official guide](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html)
|
* [official guide](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html)
|
||||||
* via cli roles
|
* [x] via cli roles - I set up a new set of role-trust relationships.
|
||||||
|
Update s3 Role:
|
||||||
|
Update action: sts:assumerole
|
||||||
|
Update principle (for user -- could not target group)
|
||||||
|
Add condition (MFA bool must be true)
|
||||||
|
* Commands referenced: I set up a script that looks like this
|
||||||
|
|
||||||
|
```bash
|
||||||
|
MFA_TOKEN=$1
|
||||||
|
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "Error: Run with MFA token!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z $BW_AWS_ACCOUNT_SECRET_ID ]; then
|
||||||
|
echo "env var BW_AWS_ACCOUNT_SECRET_ID must be set!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
AWS_SECRETS=$(bw get item $BW_AWS_ACCOUNT_SECRET_ID)
|
||||||
|
|
||||||
|
export AWS_ACCESS_KEY_ID=$(echo "$AWS_SECRETS" | jq -r '.fields[0].value')
|
||||||
|
export AWS_SECRET_ACCESS_KEY=$(echo "$AWS_SECRETS" | jq '.fields[1].value' | tr -d '"')
|
||||||
|
|
||||||
|
SESSION_OUTPUT=$(aws sts assume-role --role-arn $S3_ROLE --role-session-name $SESSION_TYPE --serial-number $MFA_IDENTIFIER --token-code $MFA_TOKEN)
|
||||||
|
#echo $SESSION_OUTPUT
|
||||||
|
export AWS_SESSION_TOKEN=$(echo "$SESSION_OUTPUT" | jq '.Credentials.SessionToken' | tr -d '"')
|
||||||
|
export AWS_ACCESS_KEY_ID=$(echo "$SESSION_OUTPUT" | jq '.Credentials.AccessKeyId' | tr -d '"')
|
||||||
|
export AWS_SECRET_ACCESS_KEY=$(echo "$SESSION_OUTPUT" | jq '.Credentials.SecretAccessKey' | tr -d '"')
|
||||||
|
#echo $AWS_SESSION_TOKEN
|
||||||
|
#echo $AWS_ACCESS_KEY_ID
|
||||||
|
#echo $AWS_SECRET_ACCESS_KEY
|
||||||
|
aws s3 ls s3://witch-lab-3
|
||||||
|
```
|
||||||
|
|
||||||
* configuration via ~/.aws/credentials
|
* configuration via ~/.aws/credentials
|
||||||
* 1Password CLI with AWS Plugin
|
* 1Password CLI with AWS Plugin
|
||||||
* I use bitwarden, which also has an AWS Plugin
|
* I use bitwarden, which also has an AWS Plugin
|
||||||
* This is probably what I will gravitate towards for a more
|
|
||||||
long-term setup, because having all of these credentials
|
|
||||||
floating around in various areas on my computer/virtualbox
|
|
||||||
envs gets confusing. Not a fan.
|
|
||||||
* I've seen a lot more recommendations (TBH it's more like 2 vs 0)
|
* I've seen a lot more recommendations (TBH it's more like 2 vs 0)
|
||||||
for 1password for password credential setup. Wonder why?
|
for 1password for password credential setup. Wonder why?
|
||||||
* other apps that handle this
|
|
||||||
- [ ] VPC
|
|
||||||
|
|
||||||
- [ ] **Host a static site**
|
- [x] **Host a static site**
|
||||||
- [ ] Enable a static website hosting (`index.html`)
|
- [x] Enable a static website hosting (`index.html`)
|
||||||
- [ ] Configure route 53 alias or CNAME for `resume.<yourdomain>` to the bucket endpoint.
|
- [x] Configure route 53 alias or CNAME for `resume.<yourdomain>` to the bucket endpoint.
|
||||||
- [ ] Deploy CloudFront with ACM certificate for HTTPS
|
- [x] Deploy CloudFront with ACM certificate for HTTPS
|
||||||
#### Private "Innvite-Only" Resume Hosting
|
* see: [resume](https://resume.wizards.cafe)
|
||||||
1. **Pre-signed URLs**
|
- [ ] **Private "Invite-Only" Resume Hosting**
|
||||||
|
1. [ ] **Pre-signed URLs**
|
||||||
`aws s3 presign s3://<YOUR_BUCKET_NAME>/resume.pdf --expires-in 3600`
|
`aws s3 presign s3://<YOUR_BUCKET_NAME>/resume.pdf --expires-in 3600`
|
||||||
2. **IAM-only access**
|
|
||||||
- [ ] Store under `private/`
|
|
||||||
- [ ] Write a bucket policy allowing only the role `EC2-S3-Access-Role-daphodell` to `GetObject`
|
|
||||||
3. **Restrict to IP address**
|
|
||||||
- [ ] copy pasta json into bucket policy
|
|
||||||
|
|
||||||
### Further Exploration
|
### Further Exploration
|
||||||
1. [ ] Snapshots & AMIs
|
1. [ ] Snapshots & AMIs
|
||||||
@ -80,7 +113,6 @@
|
|||||||
- [ ] Bash: report world-writable files
|
- [ ] Bash: report world-writable files
|
||||||
- [ ] Python with boto3: list snapshots, start/stop instances
|
- [ ] Python with boto3: list snapshots, start/stop instances
|
||||||
|
|
||||||
|
|
||||||
## Further Reading
|
## Further Reading
|
||||||
- [ ]
|
- [ ]
|
||||||
- [ ]
|
- [ ]
|
||||||
@ -89,6 +121,14 @@
|
|||||||
## Reflection
|
## Reflection
|
||||||
* What I built
|
* What I built
|
||||||
* Challenges
|
* Challenges
|
||||||
|
* Groups cannot be used as the principal in a trust relationship
|
||||||
|
* The stretch goal for setting up s3 + mfa was a bit of a pain:
|
||||||
|
* The earlier lab had me set up a trust relationship on the role to allow EC2 as a principal
|
||||||
|
on the role
|
||||||
|
When I later updated IAM permissions to include MFA, I promptly forgot about this detail
|
||||||
|
and had chatgpt help me with troubleshooting. It was pretty good at helping me figure out
|
||||||
|
the issue
|
||||||
|
|
||||||
* Security concerns
|
* Security concerns
|
||||||
On scale and security at scale
|
On scale and security at scale
|
||||||
|
|
||||||
@ -107,5 +147,4 @@ classDef aside stroke-dasharray: 5 5, stroke-width:2px;
|
|||||||
- [ ] Clean up
|
- [ ] Clean up
|
||||||
- [ ] Custom roles
|
- [ ] Custom roles
|
||||||
- [ ] Custom policies
|
- [ ] Custom policies
|
||||||
- [ ] Stop ec2 Instance
|
- [ ] Shut down ec2 Instance
|
||||||
- [ ] Remove s3 bucket
|
|
||||||
BIN
lab-3/LAB-REPORT.pdf
Normal file
BIN
lab-3/LAB-REPORT.pdf
Normal file
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 69 KiB |
22
lab-resume/LAB-REPORT.md
Normal file
22
lab-resume/LAB-REPORT.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# [daphodell]
|
||||||
|
- [git](https://code.wizards.cafe)
|
||||||
|
|
||||||
|
### Projects
|
||||||
|
- Placeholder
|
||||||
|
|
||||||
|
## Skills
|
||||||
|
- Cloud Platforms & Security: AWS (IAM)
|
||||||
|
- Infrastructure as Code: Terraform, CloudFormation
|
||||||
|
- Programming and Scripting: JavaScript, Bash, Go
|
||||||
|
- DevOps & Automation: CI/CD Pipelines (GitHub Actions, Gitlab, Gitea Actions), Docker
|
||||||
|
|
||||||
|
## Professional Experience
|
||||||
|
### Senior Software Engineer
|
||||||
|
[Company A], 2025
|
||||||
|
- Automated data pipeline to ingest and transform data from unstructured municipal websites
|
||||||
|
- Leveraged LLMs to parse and normalize data
|
||||||
|
- Improved update frequency and data accuracy via integration and baseline testing.
|
||||||
|
|
||||||
|
### Senior Software Engineer
|
||||||
|
[Company B], 2022-2024
|
||||||
|
- Placeholder text
|
||||||
BIN
lab-resume/LAB-REPORT.pdf
Normal file
BIN
lab-resume/LAB-REPORT.pdf
Normal file
Binary file not shown.
157
lab-resume/index.html
Normal file
157
lab-resume/index.html
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>daphodell Resume</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<style>
|
||||||
|
/* LaTeX-like styling */
|
||||||
|
body {
|
||||||
|
font-family: 'Computer Modern', 'Latin Modern Roman', 'Times New Roman', Times, serif;
|
||||||
|
background: #f9f9f9;
|
||||||
|
color: #222;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 40px auto;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||||||
|
padding: 40px 48px;
|
||||||
|
border-radius: 8px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
h1, h2, h3 {
|
||||||
|
font-family: 'Latin Modern Roman', 'Computer Modern', 'Times New Roman', Times, serif;
|
||||||
|
font-weight: normal;
|
||||||
|
margin-top: 1.5em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 2.2em;
|
||||||
|
border-bottom: 2px solid #222;
|
||||||
|
padding-bottom: 0.2em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.3em;
|
||||||
|
color: #333;
|
||||||
|
margin-top: 1.5em;
|
||||||
|
border-bottom: 1px solid #bbb;
|
||||||
|
padding-bottom: 0.1em;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.1em;
|
||||||
|
color: #444;
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 0.2em;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
margin-top: 0.2em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
padding-left: 1.2em;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
margin-bottom: 0.3em;
|
||||||
|
}
|
||||||
|
.section {
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
.job-title {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.05em;
|
||||||
|
}
|
||||||
|
.job-company {
|
||||||
|
font-style: italic;
|
||||||
|
color: #555;
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
|
.job-dates {
|
||||||
|
float: right;
|
||||||
|
color: #888;
|
||||||
|
font-size: 0.95em;
|
||||||
|
}
|
||||||
|
.header-links {
|
||||||
|
position: absolute;
|
||||||
|
right: 48px;
|
||||||
|
top: 48px;
|
||||||
|
font-size: 0.92em;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
.header-links a {
|
||||||
|
color: #888;
|
||||||
|
text-decoration: none;
|
||||||
|
margin-left: 0;
|
||||||
|
font-size: 1em;
|
||||||
|
padding: 0;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
.header-links a:hover {
|
||||||
|
color: #0072b1;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.container { padding: 16px 6vw; }
|
||||||
|
.job-dates { float: none; display: block; margin-top: 0.2em; }
|
||||||
|
.header-links {
|
||||||
|
position: static;
|
||||||
|
display: block;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
right: auto;
|
||||||
|
top: auto;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1 style="margin-bottom:0.5em;">
|
||||||
|
daphodell
|
||||||
|
</h1>
|
||||||
|
<span class="header-links">
|
||||||
|
<a href="https://code.wizards.cafe" title="Git profile" target="_blank" rel="noopener">
|
||||||
|
git
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h2>Skills</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Cloud Platforms & Security: AWS (IAM)</li>
|
||||||
|
<li>Infrastructure as Code: Terraform, CloudFormation</li>
|
||||||
|
<li>Programming and Scripting: JavaScript, Bash, Go</li>
|
||||||
|
<li>DevOps & Automation: CI/CD Pipelines (GitHub Actions, Gitlab, Gitea Actions), Docker</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h2>Professional Experience</h2>
|
||||||
|
<div>
|
||||||
|
<span class="job-title">Senior Software Engineer</span>
|
||||||
|
<span class="job-company">[Company A]</span>
|
||||||
|
<span class="job-dates">2025</span>
|
||||||
|
<ul>
|
||||||
|
<li>Automated data pipeline to ingest and transform data from unstructured municipal websites</li>
|
||||||
|
<li>Leveraged LLMs to parse and normalize data</li>
|
||||||
|
<li>Improved update frequency and data accuracy via integration and baseline testing.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="job-title">Senior Software Engineer</span>
|
||||||
|
<span class="job-company">[Company B]</span>
|
||||||
|
<span class="job-dates">2022-2024</span>
|
||||||
|
<ul>
|
||||||
|
<li>Placeholder text</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
18
mise.toml
18
mise.toml
@ -1,22 +1,20 @@
|
|||||||
[tools]
|
[tools]
|
||||||
aws-cli = 'latest'
|
aws-cli = 'latest'
|
||||||
bitwarden = 'latest'
|
pandoc = 'latest'
|
||||||
jq = 'latest'
|
# Also required:
|
||||||
bw = 'latest'
|
# - bitwarden CLI
|
||||||
|
# - jq
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
_.file = '.env.json'
|
_.file = '.env'
|
||||||
BLAH = "{{ exec(command=\"bw get item $BW_AWS_ACCOUNT_SECRET_ID\") }}"
|
|
||||||
|
|
||||||
[tasks.ssh]
|
[tasks.ssh]
|
||||||
run = "ssh -p 5679 vboxuser@127.0.0.1"
|
run = "ssh -p 5679 vboxuser@127.0.0.1"
|
||||||
|
|
||||||
[tasks.generate]
|
[tasks.labgen]
|
||||||
run = "./utilities/pdf_make/labs.sh"
|
run = "./utilities/pdf_make/labs.sh"
|
||||||
|
|
||||||
[tasks.setup-aws]
|
[tasks.poke-s3]
|
||||||
run = """
|
run = """
|
||||||
export SECRETS_OBJECT=$(bw get item $BW_AWS_ACCOUNT_SECRET_ID)
|
./utilities/setup_aws/use-s3.sh
|
||||||
export AWS_ACCESS_KEY_ID=$(echo "$SECRETS_OBJECT" | jq -r '.fields[0].value')
|
|
||||||
export AWS_SECRET_ACCESS_KEY=$(echo "$SECRETS_OBJECT" | jq '.fields[1].value')
|
|
||||||
"""
|
"""
|
||||||
@ -21,7 +21,7 @@ find . -maxdepth 1 -type d -name "lab-*" | while read lab_dir; do
|
|||||||
# Generate PDF using pandoc
|
# Generate PDF using pandoc
|
||||||
# Make sure 'pandoc' command is available in the image, which it is for pandoc/latex
|
# Make sure 'pandoc' command is available in the image, which it is for pandoc/latex
|
||||||
image_dir="$lab_dir"
|
image_dir="$lab_dir"
|
||||||
pandoc "$markdown_file" -s -o "$pdf_file" --pdf-engine=pdflatex --resource-path "$image_dir"
|
pandoc "$markdown_file" -s -o "$pdf_file" --pdf-engine=pdflatex --resource-path "$image_dir" -V geometry:margin=0.5in
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
echo "Successfully generated $pdf_file"
|
echo "Successfully generated $pdf_file"
|
||||||
|
|||||||
29
utilities/setup_aws/use-s3.sh
Executable file
29
utilities/setup_aws/use-s3.sh
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
MFA_TOKEN=$1
|
||||||
|
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "Error: Run with MFA token!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z $BW_AWS_ACCOUNT_SECRET_ID ]; then
|
||||||
|
echo "env var BW_AWS_ACCOUNT_SECRET_ID must be set!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
AWS_SECRETS=$(bw get item $BW_AWS_ACCOUNT_SECRET_ID)
|
||||||
|
|
||||||
|
export AWS_ACCESS_KEY_ID=$(echo "$AWS_SECRETS" | jq -r '.fields[0].value')
|
||||||
|
export AWS_SECRET_ACCESS_KEY=$(echo "$AWS_SECRETS" | jq '.fields[1].value' | tr -d '"')
|
||||||
|
|
||||||
|
SESSION_OUTPUT=$(aws sts assume-role --role-arn $S3_ROLE --role-session-name $SESSION_TYPE --serial-number $MFA_IDENTIFIER --token-code $MFA_TOKEN)
|
||||||
|
#echo $SESSION_OUTPUT
|
||||||
|
export AWS_SESSION_TOKEN=$(echo "$SESSION_OUTPUT" | jq '.Credentials.SessionToken' | tr -d '"')
|
||||||
|
export AWS_ACCESS_KEY_ID=$(echo "$SESSION_OUTPUT" | jq '.Credentials.AccessKeyId' | tr -d '"')
|
||||||
|
export AWS_SECRET_ACCESS_KEY=$(echo "$SESSION_OUTPUT" | jq '.Credentials.SecretAccessKey' | tr -d '"')
|
||||||
|
#echo $AWS_SESSION_TOKEN
|
||||||
|
#echo $AWS_ACCESS_KEY_ID
|
||||||
|
#echo $AWS_SECRET_ACCESS_KEY
|
||||||
|
aws s3 ls s3://witch-lab-3
|
||||||
|
echo "finished!"
|
||||||
Reference in New Issue
Block a user