This document provides detailed security considerations and best practices for the Matomo AWS Server deployment.
This deployment implements AWS security best practices with a defense-in-depth approach:
- Zero local secrets - All credentials managed by AWS services
- Network isolation - Private subnets and security groups
- Least privilege access - Minimal IAM permissions
- Encrypted storage - All data encrypted at rest and in transit
Internet Gateway
β
βββββΌββββ
β NAT β (Public Subnet)
βGatewayβ
βββββ¬ββββ
β
ββββββββΌβββββββ βββββββββββββββββββ
β EC2 β β RDS MySQL β
β Matomo ββββββ (Private) β
β (Public) β β β
βββββββββββββββ βββββββββββββββββββ
Security Boundaries:
- Public Subnet: EC2 instance with controlled access
- Private Subnet: Database with no internet access
- Security Groups: Application-level firewall rules
| Component | Security Feature |
|---|---|
| VPC | Isolated network (10.0.0.0/16) |
| Public Subnet | Internet access via IGW |
| Private Subnet | Internet access via NAT only |
| Route Tables | Controlled traffic routing |
| Network ACLs | Subnet-level access control |
Ingress Rules:
- Port 22 (SSH): Configurable CIDR (default: 0.0.0.0/0)
- Port 80 (HTTP): 0.0.0.0/0
- Port 443 (HTTPS): 0.0.0.0/0
Egress Rules:
- All traffic: 0.0.0.0/0Ingress Rules:
- Port 3306 (MySQL): Web Security Group only
Egress Rules:
- None (default deny)Storage: AWS Systems Manager Parameter Store
- Parameter Name:
/matomo/ec2/private-key/{keypair-name} - Type: SecureString (encrypted with AWS KMS)
- Access: EC2 instance role only
Retrieval:
# Automated by get-info.sh script
aws ssm get-parameter \
--name "/matomo/ec2/private-key/matomo-keypair" \
--with-decryption \
--query 'Parameter.Value' \
--output text > matomo-key.pemStorage: AWS Secrets Manager
- Format: JSON with username/password
- Encryption: AWS KMS managed keys
- Rotation: Can be enabled for production
Access Pattern:
# EC2 instance retrieves credentials
secret = secretsmanager.get_secret_value(SecretId=secret_arn)
credentials = json.loads(secret['SecretString'])
username = credentials['username']
password = credentials['password']{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:GetParameter",
"ssm:GetParameters"
],
"Resource": "arn:aws:ssm:*:*:parameter/matomo/*"
},
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "arn:aws:secretsmanager:*:*:secret:matomo-*"
}
]
}Principle: Least privilege access - only what's needed for operation.
# /var/www/html/config/config.ini.php
[General]
trusted_hosts[] = "your-domain.com"
trusted_hosts[] = "12.34.56.78" # EC2 IP# Recommended Apache configuration
Header always set X-Content-Type-Options nosniff
Header always set X-Frame-Options DENY
Header always set X-XSS-Protection "1; mode=block"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"- Default User:
matomo(non-root) - Network: Private subnet only
- Encryption: At-rest encryption enabled
- Backups: Configurable (disabled by default for cost)
- TLS: Force encrypted connections
- Authentication: Strong auto-generated passwords
- Access: Limited to application security group
# Monitor security-related API calls
aws logs filter-log-events \
--log-group-name CloudTrail \
--filter-pattern "{ $.eventName = GetSecretValue }"# Enable VPC flow logs for network monitoring
aws ec2 create-flow-logs \
--resource-type VPC \
--resource-ids vpc-12345678 \
--traffic-type ALL \
--log-destination-type cloud-watch-logs// Update cdk.json
{
"context": {
"matomo": {
"allowedSshCidr": "YOUR.IP.ADDRESS/32"
}
}
}# SSH to server and edit config
ssh -i matomo-key.pem ec2-user@YOUR-EC2-IP
sudo nano /var/www/html/config/config.ini.php
# Add your domain/IP to trusted_hosts
[General]
trusted_hosts[] = "yourdomain.com"// Update cdk.json for production
{
"context": {
"matomo": {
"databaseConfig": {
"backupRetention": 7,
"multiAZ": true
}
}
}
}# Add Application Load Balancer with SSL
Resources:
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Scheme: internet-facing
SecurityGroups: [!Ref ALBSecurityGroup]
SSLCertificate:
Type: AWS::CertificateManager::Certificate
Properties:
DomainName: matomo.yourdomain.com# Web Application Firewall
Resources:
WebACL:
Type: AWS::WAFv2::WebACL
Properties:
Rules:
- Name: AWSManagedRulesCommonRuleSet
Priority: 1
Statement:
ManagedRuleGroupStatement:
VendorName: AWS
Name: AWSManagedRulesCommonRuleSet# Restrict HTTP/HTTPS to ALB only
web_security_group.add_ingress_rule(
peer=ec2.Peer.security_group_id(alb_security_group.security_group_id),
connection=ec2.Port.tcp(80)
)# Review IAM roles and policies
aws iam list-attached-role-policies --role-name MatomoEC2Role
# Check security group rules
aws ec2 describe-security-groups --group-ids sg-12345678# Rotate database credentials (if enabled)
aws secretsmanager rotate-secret --secret-id matomo-db-credentials
# Regenerate SSH keys
aws ec2 create-key-pair --key-name matomo-keypair-new# Check for unauthorized access attempts
sudo grep "Failed password" /var/log/secure
# Monitor Matomo access logs
sudo tail -f /var/log/httpd/matomo_access.log- SSH access restricted to known IPs
- Database in private subnet only
- Trusted hosts configured in Matomo
- SSL/TLS configured (production)
- Security groups follow least privilege
- CloudTrail logging enabled
- VPC Flow Logs enabled
- Regular security updates applied
- Backup and recovery tested
- Incident response plan documented
# Immediately isolate the instance
aws ec2 modify-instance-attribute \
--instance-id i-1234567890abcdef0 \
--groups sg-emergency-isolation
# Create forensic snapshot
aws ec2 create-snapshot \
--volume-id vol-1234567890abcdef0 \
--description "Forensic snapshot - $(date)"- Isolate affected systems
- Assess scope of compromise
- Contain the incident
- Eradicate threats
- Recover systems safely
- Document lessons learned
# Emergency key rotation
aws secretsmanager update-secret \
--secret-id matomo-db-credentials \
--secret-string '{"username":"matomo","password":"NEW-SECURE-PASSWORD"}'
# Update application configuration
ssh -i matomo-key.pem ec2-user@YOUR-EC2-IP
sudo systemctl restart httpd- AWS Config - Compliance monitoring
- AWS GuardDuty - Threat detection
- AWS Security Hub - Security posture management
Remember: Security is an ongoing process, not a one-time setup. Regularly review and update your security measures as threats evolve.