From cecd2341609a2baddc6f6fe291b081c5dac086f2 Mon Sep 17 00:00:00 2001 From: ducoterra Date: Thu, 11 Jul 2024 12:35:17 -0400 Subject: [PATCH] add ipv4 support --- ansible/inventory.yaml | 1 + aws/README.md | 16 +++- ddns/ddns.service | 2 +- ddns/ddns.sh | 22 +++-- ddns/install_ddns.yaml | 18 ++-- ddns/reeseapps_record_template.json | 83 +++++++++++++++++++ ...te.json => reeselink_record_template.json} | 4 +- ddns/vars.yaml | 2 - dns/README.md | 2 +- dns/reeselink.json | 13 +++ external-dns/deploy.yaml | 1 + gitea/gitea-values.yaml | 2 +- ingress-nginx/values.yaml | 1 + ipv4-proxy/README.md | 24 ++++++ ipv4-proxy/nginx.conf | 53 ++++++++++++ ipv4-proxy/nginx.yaml | 42 ++++++++++ ipv4-proxy/vars.yaml | 37 +++++++++ .../{jellyfin.yaml => deployment.yaml} | 82 +----------------- jellyfin/templates/ingress.yaml | 25 ++++++ jellyfin/templates/pvc.yaml | 27 ++++++ jellyfin/templates/service.yaml | 17 ++++ k3s/README.md | 1 + 22 files changed, 374 insertions(+), 101 deletions(-) create mode 100644 ddns/reeseapps_record_template.json rename ddns/{record_template.json => reeselink_record_template.json} (86%) create mode 100644 ipv4-proxy/README.md create mode 100644 ipv4-proxy/nginx.conf create mode 100644 ipv4-proxy/nginx.yaml create mode 100644 ipv4-proxy/vars.yaml rename jellyfin/templates/{jellyfin.yaml => deployment.yaml} (53%) create mode 100644 jellyfin/templates/ingress.yaml create mode 100644 jellyfin/templates/pvc.yaml create mode 100644 jellyfin/templates/service.yaml diff --git a/ansible/inventory.yaml b/ansible/inventory.yaml index 2c10ebf..2ce2aa1 100644 --- a/ansible/inventory.yaml +++ b/ansible/inventory.yaml @@ -6,6 +6,7 @@ fedora: hosts: nextcloud: kube: + yellow: ubuntu: hosts: diff --git a/aws/README.md b/aws/README.md index b432c77..fb70681 100644 --- a/aws/README.md +++ b/aws/README.md @@ -1,6 +1,20 @@ # AWS Credentials -## Aws Certbot Route53 Policies +## Credential Generation + +```bash +export AWS_USERNAME= +aws iam create-user --user-name $AWS_USERNAME +aws iam create-access-key --user-name $AWS_USERNAME + +# Allow updating reeseapps +aws iam attach-user-policy --user-name $AWS_USERNAME --policy-arn arn:aws:iam::892236928704:policy/update-reeseapps + +# Allow updating reeselink +aws iam attach-user-policy --user-name $AWS_USERNAME --policy-arn arn:aws:iam::892236928704:policy/update-reeselink +``` + +## AWS Certbot Route53 Policies Example Policy: diff --git a/ddns/ddns.service b/ddns/ddns.service index 1e6138a..da6f931 100644 --- a/ddns/ddns.service +++ b/ddns/ddns.service @@ -1,5 +1,5 @@ [Unit] -Description=Updates the {{ fqdn }} record with the current public IPV4 address +Description=Updates the IPv4 records with the current public IPV4 address [Service] ExecStart=/usr/local/scripts/ddns.sh diff --git a/ddns/ddns.sh b/ddns/ddns.sh index aaaa53f..36eeb36 100755 --- a/ddns/ddns.sh +++ b/ddns/ddns.sh @@ -2,11 +2,19 @@ # Get public IP address (there are many ways to do it, I picked this way) PUBLIC_IP=$(curl -4 ifconfig.me) -# Update *.{{ fqdn }} and {{ fqdn }} -cat /etc/ddns/record_template.json \ - | jq '.Changes[0].ResourceRecordSet.ResourceRecords[0].Value = "'$PUBLIC_IP'"' \ - > /etc/ddns/record.json -# aws cli to update a record -aws route53 change-resource-record-sets --hosted-zone-id {{ hosted_zone_id }} --change-batch file:///etc/ddns/record.json -PUBLIC_IPV6=$(dig -t aaaa +short myip.opendns.com @resolver1.opendns.com) +# Update reeselink records +cat /etc/ddns/reeselink_record_template.json \ + | jq '.Changes[0].ResourceRecordSet.ResourceRecords[0].Value = "'$PUBLIC_IP'"' \ + > /etc/ddns/reeselink_record.json + +# Update reeseapps records +cat /etc/ddns/reeseapps_record_template.json \ + | jq '.Changes[].ResourceRecordSet.ResourceRecords[0].Value = "'$PUBLIC_IP'"' \ + > /etc/ddns/reeseapps_record.json + +# Update reeselink records +aws route53 change-resource-record-sets --hosted-zone-id Z0092652G7L97DSINN18 --change-batch file:///etc/ddns/reeselink_record.json + +# Update reeseapps records +aws route53 change-resource-record-sets --hosted-zone-id Z012820733346FJ0U4FUF --change-batch file:///etc/ddns/reeseapps_record.json diff --git a/ddns/install_ddns.yaml b/ddns/install_ddns.yaml index 00099b6..7741a85 100644 --- a/ddns/install_ddns.yaml +++ b/ddns/install_ddns.yaml @@ -1,8 +1,5 @@ - name: Update nginx stream configuration - hosts: colors - become: true - become_user: root - become_method: sudo + hosts: yellow vars_files: - vars.yaml tasks: @@ -29,10 +26,17 @@ path: /etc/ddns state: directory mode: '0755' - - name: Copy record_template.json + - name: Copy reeseapps_record_template.json template: - src: record_template.json - dest: /etc/ddns/record_template.json + src: reeseapps_record_template.json + dest: /etc/ddns/reeseapps_record_template.json + owner: root + group: root + mode: '0644' + - name: Copy reeselink_record_template.json + template: + src: reeselink_record_template.json + dest: /etc/ddns/reeselink_record_template.json owner: root group: root mode: '0644' diff --git a/ddns/reeseapps_record_template.json b/ddns/reeseapps_record_template.json new file mode 100644 index 0000000..3f93672 --- /dev/null +++ b/ddns/reeseapps_record_template.json @@ -0,0 +1,83 @@ +{ + "Comment": "Update Public IPV4 Address", + "Changes": [ + { + "Action": "UPSERT", + "ResourceRecordSet": { + "Name": "homeassistant.reeseapps.com", + "Type": "A", + "TTL": 300, + "ResourceRecords": [ + { + "Value": "" + } + ] + } + }, + { + "Action": "UPSERT", + "ResourceRecordSet": { + "Name": "nextcloud.reeseapps.com", + "Type": "A", + "TTL": 300, + "ResourceRecords": [ + { + "Value": "" + } + ] + } + }, + { + "Action": "UPSERT", + "ResourceRecordSet": { + "Name": "gitea.reeseapps.com", + "Type": "A", + "TTL": 300, + "ResourceRecords": [ + { + "Value": "" + } + ] + } + }, + { + "Action": "UPSERT", + "ResourceRecordSet": { + "Name": "git.reeseapps.com", + "Type": "A", + "TTL": 300, + "ResourceRecords": [ + { + "Value": "" + } + ] + } + }, + { + "Action": "UPSERT", + "ResourceRecordSet": { + "Name": "jellyfin.reeseapps.com", + "Type": "A", + "TTL": 300, + "ResourceRecords": [ + { + "Value": "" + } + ] + } + }, + { + "Action": "UPSERT", + "ResourceRecordSet": { + "Name": "snapdrop.reeseapps.com", + "Type": "A", + "TTL": 300, + "ResourceRecords": [ + { + "Value": "" + } + ] + } + } + ] +} diff --git a/ddns/record_template.json b/ddns/reeselink_record_template.json similarity index 86% rename from ddns/record_template.json rename to ddns/reeselink_record_template.json index 14ff31b..17f95e6 100644 --- a/ddns/record_template.json +++ b/ddns/reeselink_record_template.json @@ -4,7 +4,7 @@ { "Action": "UPSERT", "ResourceRecordSet": { - "Name": "{{ fqdn }}.", + "Name": "ipv4.reeselink.com", "Type": "A", "TTL": 300, "ResourceRecords": [ @@ -13,6 +13,6 @@ } ] } - } + } ] } diff --git a/ddns/vars.yaml b/ddns/vars.yaml index 8148683..e69de29 100644 --- a/ddns/vars.yaml +++ b/ddns/vars.yaml @@ -1,2 +0,0 @@ -hosted_zone_id: Z0092652G7L97DSINN18 -fqdn: ipv4.reeselink.com diff --git a/dns/README.md b/dns/README.md index f5fe7d2..0c4ba2f 100644 --- a/dns/README.md +++ b/dns/README.md @@ -25,4 +25,4 @@ aws route53 change-resource-record-sets --hosted-zone-id Z0092652G7L97DSINN18 -- ```bash aws route53 change-resource-record-sets --hosted-zone-id Z012820733346FJ0U4FUF --change-batch file://dns/reeseapps.json -``` +``` \ No newline at end of file diff --git a/dns/reeselink.json b/dns/reeselink.json index cef4bd3..a04d6dc 100644 --- a/dns/reeselink.json +++ b/dns/reeselink.json @@ -52,6 +52,19 @@ } ] } + }, + { + "Action": "UPSERT", + "ResourceRecordSet": { + "Name": "driveripper.reeselink.com", + "Type": "AAAA", + "TTL": 300, + "ResourceRecords": [ + { + "Value": "2600:1700:1e6c:a81f:94bb:b8ff:fe9f:1c63" + } + ] + } } ] } diff --git a/external-dns/deploy.yaml b/external-dns/deploy.yaml index 142c306..775f974 100644 --- a/external-dns/deploy.yaml +++ b/external-dns/deploy.yaml @@ -54,6 +54,7 @@ spec: - --source=service - --source=ingress - --domain-filter=reeseapps.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones + - --domain-filter=reeselink.com - --provider=aws # - --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization - --aws-zone-type=public # only look at public hosted zones (valid values are public, private or no value for both) diff --git a/gitea/gitea-values.yaml b/gitea/gitea-values.yaml index 2741b8d..7f3d3c3 100644 --- a/gitea/gitea-values.yaml +++ b/gitea/gitea-values.yaml @@ -63,7 +63,7 @@ service: ipFamilies: ["IPv6"] annotations: metallb.universe.tf/address-pool: "external" - external-dns.alpha.kubernetes.io/hostname: git.reeseapps.com + external-dns.alpha.kubernetes.io/hostname: git.reeseapps.com,git.reeselink.com redis-cluster: diff --git a/ingress-nginx/values.yaml b/ingress-nginx/values.yaml index 69a2908..4ad1d8f 100644 --- a/ingress-nginx/values.yaml +++ b/ingress-nginx/values.yaml @@ -4,6 +4,7 @@ controller: annotations: metallb.universe.tf/address-pool: "external" metallb.universe.tf/allow-shared-ip: nginx + external-dns.alpha.kubernetes.io/hostname: ingress-nginx.reeselink.com ipFamilyPolicy: SingleStack ipFamilies: - IPv6 diff --git a/ipv4-proxy/README.md b/ipv4-proxy/README.md new file mode 100644 index 0000000..384fd09 --- /dev/null +++ b/ipv4-proxy/README.md @@ -0,0 +1,24 @@ +# IPv4 Proxy + +This project aims to serve those without an IPv6 ISP by forwarding IPv4 requests to the +correct destination. This is accomplished by SSL preread and port mapping. This service +is intended only for publicly accessible services. + +## DDNS + +This project pairs with the ddns service. Set that up first! + +## Updating IPv4 Proxy Records + +1. In `ddns` create a new record in the `reeseapps_record_template.json` +2. Apply the new record with ansible +3. Update `vars.yaml` in this project +4. Run the following ansible script: + +```bash +ansible-playbook -i ansible/inventory.yaml ipv4-proxy/nginx.yaml +``` + +## Logging + +You can tail all the nginx logs with `ssh yellow 'tail -f /var/log/nginx/*.log'` diff --git a/ipv4-proxy/nginx.conf b/ipv4-proxy/nginx.conf new file mode 100644 index 0000000..b96ff69 --- /dev/null +++ b/ipv4-proxy/nginx.conf @@ -0,0 +1,53 @@ +load_module /usr/lib64/nginx/modules/ngx_stream_module.so; + +worker_processes auto; + +events { + worker_connections 1024; +} + +stream { + log_format ssl '| Remote Addr: $remote_addr:$server_port | SSL Preread: $ssl_preread_server_name | Forward: $map_forward_ssl | $time_local | $protocol | $status | $bytes_sent | $bytes_received | $session_time |'; + log_format port '| Remote Addr: $remote_addr:$server_port | SSL Preread: $ssl_preread_server_name | Forward: $map_forward_port | $time_local | $protocol | $status | $bytes_sent | $bytes_received | $session_time |'; + + # Map all SSL parsed server names to hosts + map $ssl_preread_server_name $map_forward_ssl { + +{% for item in stream_ssl %} + {{ item.external.domain }} {{ item.internal.domain }}:{{ item.internal.port }}; +{% endfor %} + } + + server { + access_log /var/log/nginx/nginx_stream_access.log ssl; + error_log /var/log/nginx/nginx_stream_error.log warn; + + listen 443; + + proxy_pass $map_forward_ssl; + ssl_preread on; + proxy_socket_keepalive on; + resolver 10.1.0.1; + } + + map $server_port $map_forward_port { +{% for item in stream_ports %} + {{ item.external }} {{ item.internal }}; +{% endfor %} + } + + server { +{% for item in stream_ports %} + listen {{ item.external }}; +{% endfor %} + access_log /var/log/nginx/nginx_stream_access.log port; + error_log /var/log/nginx/nginx_stream_error.log warn; + + listen 443; + + proxy_pass $map_forward_port; + proxy_socket_keepalive on; + resolver 10.1.0.1; + + } +} diff --git a/ipv4-proxy/nginx.yaml b/ipv4-proxy/nginx.yaml new file mode 100644 index 0000000..dbe45ff --- /dev/null +++ b/ipv4-proxy/nginx.yaml @@ -0,0 +1,42 @@ +- name: Update nginx stream configuration + hosts: yellow + vars_files: + - vars.yaml + tasks: + - name: Ensure nginx, certbot, and nginx-mod-stream are installed + ansible.builtin.dnf: + name: + - nginx + - nginx-mod-stream + state: present + - name: Remove http.d dir before repopulating + file: + path: /etc/nginx/http.d/ + state: absent + - name: Remove stream.d dir before repopulating + file: + path: /etc/nginx/stream.d/ + state: absent + - name: Create stream.d dir + ansible.builtin.file: + path: /etc/nginx/stream.d + state: directory + mode: '0755' + - name: Template nginx.conf + template: + src: nginx.conf + dest: /etc/nginx/nginx.conf + owner: root + group: root + mode: '0644' + - name: Test nginx configuration + ansible.builtin.shell: /usr/sbin/nginx -t + - name: Stop nginx service + ansible.builtin.systemd_service: + state: stopped + name: nginx + - name: Reload nginx service + ansible.builtin.systemd_service: + state: started + name: nginx + enabled: true diff --git a/ipv4-proxy/vars.yaml b/ipv4-proxy/vars.yaml new file mode 100644 index 0000000..3fa8794 --- /dev/null +++ b/ipv4-proxy/vars.yaml @@ -0,0 +1,37 @@ +stream_ssl: + - external: + domain: homeassistant.reeseapps.com + internal: + domain: homeassistant.reeselink.com + port: 443 + protocol: https + - external: + domain: gitea.reeseapps.com + internal: + domain: ingress-nginx.reeselink.com + port: 443 + protocol: https + - external: + domain: nextcloud.reeseapps.com + internal: + domain: nextcloud.reeselink.com + port: 443 + protocol: https + - external: + domain: jellyfin.reeseapps.com + internal: + domain: ingress-nginx.reeselink.com + port: 443 + protocol: https + - external: + domain: snapdrop.reeseapps.com + internal: + domain: ingress-nginx.reeselink.com + port: 443 + protocol: https + +stream_ports: + - external: 2222 + internal: git.reeselink.com:22 + - external: 3478 + internal: nextcloud.reeselink.com:3478 \ No newline at end of file diff --git a/jellyfin/templates/jellyfin.yaml b/jellyfin/templates/deployment.yaml similarity index 53% rename from jellyfin/templates/jellyfin.yaml rename to jellyfin/templates/deployment.yaml index 623b01f..fa2d1ba 100644 --- a/jellyfin/templates/jellyfin.yaml +++ b/jellyfin/templates/deployment.yaml @@ -59,92 +59,16 @@ spec: claimName: {{ .Release.Name }}-cache - name: movies nfs: - server: democratic-csi-server.reeselink.com + server: driveripper.reeselink.com path: /mnt/enc0/media/Movies readOnly: true - name: shows nfs: - server: democratic-csi-server.reeselink.com + server: driveripper.reeselink.com path: /mnt/enc0/media/Shows readOnly: true - name: videos nfs: - server: democratic-csi-server.reeselink.com + server: driveripper.reeselink.com path: /mnt/enc0/media/Videos readOnly: true - ---- - -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: {{ .Release.Name }}-config - annotations: - "helm.sh/resource-policy": keep -spec: - storageClassName: zfs-iscsi-enc0 - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 8Gi - ---- - -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: {{ .Release.Name }}-cache - annotations: - "helm.sh/resource-policy": keep -spec: - storageClassName: zfs-iscsi-enc1 - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 128Gi - ---- - -apiVersion: v1 -kind: Service -metadata: - name: {{ .Release.Name }} -spec: - type: ClusterIP - selector: - app.kubernetes.io/name: jellyfin - ports: - - name: http - protocol: TCP - port: 80 - targetPort: http - ---- - -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: {{ .Release.Name }} - annotations: - cert-manager.io/cluster-issuer: letsencrypt - kubernetes.io/ingress.class: nginx - nginx.ingress.kubernetes.io/proxy-body-size: "0" - nginx.org/client-max-body-size: "0" -spec: - rules: - - host: {{ .Values.jellyfin.domain }} - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: jellyfin - port: - name: http - tls: - - hosts: - - {{ .Values.jellyfin.domain }} - secretName: jellyfin-tls-cert diff --git a/jellyfin/templates/ingress.yaml b/jellyfin/templates/ingress.yaml new file mode 100644 index 0000000..57f5cec --- /dev/null +++ b/jellyfin/templates/ingress.yaml @@ -0,0 +1,25 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ .Release.Name }} + annotations: + cert-manager.io/cluster-issuer: letsencrypt + nginx.ingress.kubernetes.io/proxy-body-size: "0" + nginx.org/client-max-body-size: "0" +spec: + ingressClassName: nginx + rules: + - host: {{ .Values.jellyfin.domain }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: jellyfin + port: + name: http + tls: + - hosts: + - {{ .Values.jellyfin.domain }} + secretName: jellyfin-tls-cert \ No newline at end of file diff --git a/jellyfin/templates/pvc.yaml b/jellyfin/templates/pvc.yaml new file mode 100644 index 0000000..faf8887 --- /dev/null +++ b/jellyfin/templates/pvc.yaml @@ -0,0 +1,27 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Release.Name }}-config + annotations: + "helm.sh/resource-policy": keep +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 8Gi + +--- + +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Release.Name }}-cache + annotations: + "helm.sh/resource-policy": keep +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 16Gi \ No newline at end of file diff --git a/jellyfin/templates/service.yaml b/jellyfin/templates/service.yaml new file mode 100644 index 0000000..e599d1c --- /dev/null +++ b/jellyfin/templates/service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }} +spec: + ipFamilyPolicy: PreferDualStack + ipFamilies: + - IPv6 + - IPv4 + type: ClusterIP + selector: + app.kubernetes.io/name: jellyfin + ports: + - name: http + protocol: TCP + port: 80 + targetPort: http \ No newline at end of file diff --git a/k3s/README.md b/k3s/README.md index a2801d5..ae82545 100644 --- a/k3s/README.md +++ b/k3s/README.md @@ -195,6 +195,7 @@ spec: ```bash aws iam create-user --user-name "externaldns" aws iam attach-user-policy --user-name "externaldns" --policy-arn arn:aws:iam::892236928704:policy/update-reeseapps +aws iam attach-user-policy --user-name "externaldns" --policy-arn arn:aws:iam::892236928704:policy/update-reeselink SECRET_ACCESS_KEY=$(aws iam create-access-key --user-name "externaldns") ACCESS_KEY_ID=$(echo $SECRET_ACCESS_KEY | jq -r '.AccessKey.AccessKeyId')