pihole-kubernetes
pihole-kubernetes copied to clipboard
`serviceWeb.https` does not work
Hello,
Using/enabling serviceWeb.https does not work since the app container only listens on port 80 (even though a LoadBalancer and corresponding port definitions exist for the pod/container).
I've looked through the docs/issues/PRs and haven't come across a similar issue or seen a way of using TLS on the admin page.
Is this expected or am I missing something?
Thank you in advance for your time!
Sorry @hrpatel I missed this ticket. Did you get this working?
Came across the same issue. The container is only listening on port 80. curl -k https://localhost inside the container shows Connection refused.
Ok, i think there is a misunderstanding. Please correct me if I'm wrong. I'm not using TLS for pihole so I might be wrong on this topic.
The setting serviceWeb.https will only att the port 443 to the created service. The setting webHttps will only add the containerPort to the container
If you want to have HTTPS enabled there are differnet options. First you have to decide where the TLS termination should take place.
a) At the pihole container: There are two ways, either you create a proxy to handle the certificates (https://discourse.pi-hole.net/t/docker-ssl-certificate-to-pi-hole-web-interface/28919/3) or you configure pihole to handle the certificates (but I'm not sure how to implement this inside the docker container https://discourse.pi-hole.net/t/enabling-https-for-your-pi-hole-web-interface/5771).
b) At the Loadbalancer In this case the Loadbalancer would talk to the container unencrypted on port 80. The Loadbalancer would only expose port 443 for https traffic.
c) At the ingress I think it should be possible to add HTTPS termination to an ingress as well, but I don't know how to do this.
Maybe somebody who has implemented https to pihole could help to clarify this. @utkuozdemir implemented this feature, maybe he has https working already?
I found another tutorial on how to get TLS running with MetalLB https://blog.tekspace.io/setup-kubernetes-cluster-using-k3s-metallb-letsencrypt-on-bare-metal/
@MoJo2600 In my PR I actually didn't touch anything related to the HTTPS logic. My goal was to be able to make it configurable so that it would be possible to get the following part in the service customizable:
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
- port: 443
targetPort: https
protocol: TCP
name: https
In my case, I did not want the section with the 443 https port, because I was using Rancher and its LoadBalancer implementation exposes whatever ports it finds in the Service from the same public IP to the outside world - in my case 443 was already occupied by another process, so I couldn't use Rancher ServiceLB as-is.
With my changes, it is possible to override the used port numbers and disable individual port sections in the manifests - but it does not configure anything regarding the actual HTTPS logic.
Regarding the HTTPS topic overall:
- I do the SSL termination at the ingress controller level - as all my other applications.
- I use Rancher which takes care of the ServiceLBs + comes with Traefik ingress controller.
- I install cert-manager + configure a ClusterIssuer for let's encrypt named
lets-encrypt
My ingresses look like this:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: lets-encrypt
traefik.ingress.kubernetes.io/redirect-entry-point: https
name: pihole
spec:
rules:
- host: pihole.example.com
http: ...
tls:
- hosts:
- pihole.example.com
secretName: jellyfin-tls
Important part is the annotations and the TLS section - a matching host and particularly you need to specify a secretName that will be filled by cert-manager with the valid SSL certificate.
Of course, you'll also need a DNS record that points pihole.example.com -> YOUR_SERVICELB_IP (In my case it's a wildcard, *.example.com)
Thank you for your input @utkuozdemir. I think you explained it very well.
I'm doing HTTPS a bit differently than others. I don't want pihole to rely on my ingress. This is because, at this time, my pihole is serving the DNS for the ingress.
This is either incredibly neat or incredibly stupid. Probably both. But I do think there's a lot of value in a standalone TLS config for something like PiHole. This way my PiHole admin console doesn't rely on ingress tech of any kind.
I use MetalLB in my example, but you could totally hostPort it if you wanted, the Virtual IP (VIP) just made it easy to issue a certificate, which I use CertManager.
This means if pihole is down, I cannot get to the ingress.
Because of this I opted to just memorize an easy ip addr, (in the below example 192.168.1.10) which serves as my PiHole's VIP via MetalLB.
NOTE: My notes here do rely on my pull requests, so this won't work for you until these are merged (I'm just slamming them in my own locally versioned chart):
- https://github.com/MoJo2600/pihole-kubernetes/pull/210
- https://github.com/MoJo2600/pihole-kubernetes/pull/211
- https://github.com/MoJo2600/pihole-kubernetes/pull/229
These PRs are needed to mount in a custom external.conf file for lighthttpd and to change the probes to https. Due to the way the redirects work the default kubernetes probes get an https response when they expect an http one (in my testing).
Full Example
Before using this helm chart and kubernetes, I'd used the external.conf file to inject TLS settings like this. (NOTE: these have changed as of container versions 2022.04, so I'll include both)
values.yaml (any tag before 2022.04.01)
image:
tag: "2022.02.1" # This is the chart default at the time of posting
nodeSelector:
node-role.kubernetes.io/pihole: pihole
replicaCount: 2
DNS1: "1.1.1.1"
DNS2: "8.8.8.8"
antiaff:
enabled: true
avoidRelease: pihole
strict: true
serviceWeb:
annotations:
metallb.universe.tf/allow-shared-ip: pihole-svc
type: LoadBalancer
loadBalancerIP: 192.168.1.10
serviceDns:
annotations:
metallb.universe.tf/allow-shared-ip: pihole-svc
type: LoadBalancer
loadBalancerIP: 192.168.1.10
probes:
liveness:
port: https
scheme: HTTPS
readiness:
port: https
scheme: HTTPS
extraVolumes:
external-conf-cm:
configMap:
name: lighttpd-external-conf
external-conf:
emptyDir:
medium: Memory
tls-cert:
secret:
secretName: pihole-tls-cert
extraVolumeMounts:
external-conf:
mountPath: /etc/lighttpd/external.conf
subPath: external.conf
tls-cert:
mountPath: "/etc/ssl/lighttpd-private"
extraInitContainers:
# Pi-hole attempts to touch /etc/lighttpd/external.conf to make sure it exists
# but since we're using a configmap this fails.
#
# This way, we copy a real file that's touchable (not a read-only filesystem,
# like a configmap is). I'd still (someday) like to be able to mount the configmap
# directly.
- name: copy-config
image: busybox
args:
- sh
- -c
- |
cp /etc/lighttpd-cm/external.conf /etc/lighttpd/
ls -l /etc/lighttpd/
volumeMounts:
- name: external-conf-cm
mountPath: /etc/lighttpd-cm/
- name: external-conf
mountPath: /etc/lighttpd/
dnsmasq:
customDnsEntries:
# Server up *.lab.example.com wildcard to an ingress traefik IP
- address=/lab.example.com/192.168.1.20
# Server up *.dev.example.com wildcard to an ingress traefik IP
- address=/dev.example.com/192.168.1.21
customSettings:
- domain=example.com
- expand-hosts
- local=/example.com/
additionalHostsEntries:
- 192.168.1.30 foo.example.com
extraObjects:
- apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: pihole-tls-cert
spec:
dnsNames:
- "pihole.example.com"
- "dns.example.com"
ipAddresses:
- "192.168.1.10"
issuerRef:
group: cert-manager.io
kind: ClusterIssuer
name: my-cluster-issuer
secretName: pihole-tls-cert
- apiVersion: v1
kind: ConfigMap
metadata:
name: lighttpd-external-conf
data:
external.conf: |
# Redirect HTTP to HTTPS
$HTTP["scheme"] == "http" {
$HTTP["host"] =~ ".*" {
url.redirect = (".*" => "https://%0$0")
}
}
external.conf: |
$HTTP["host"] =~ "pihole.example.com" {
# Ensure the Pi-hole Block Page knows that this is not a blocked domain
setenv.add-environment = ("fqdn" => "true")
# Enable the SSL engine with the mapped in certs from CertManager.
$SERVER["socket"] == ":443" {
ssl.engine = "enable"
ssl.pemfile = "/etc/ssl/lighttpd-private/tls.crt"
ssl.privkey = "/etc/ssl/lighttpd-private/tls.key"
ssl.ca-file = "/etc/ssl/lighttpd-private/ca.crt"
ssl.honor-cipher-order = "enable"
ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"
ssl.use-sslv2 = "disable"
ssl.use-sslv3 = "disable"
}
}
# Redirect HTTP to HTTPS
$HTTP["scheme"] == "http" {
$HTTP["host"] =~ ".*" {
url.redirect = (".*" => "https://%0$0")
}
}
values.yaml (2022.04.01 and above, I'm using `2022.05 atm)
image:
tag: "2022.05"
nodeSelector:
node-role.kubernetes.io/pihole: pihole
replicaCount: 2
DNS1: "1.1.1.1"
DNS2: "8.8.8.8"
antiaff:
enabled: true
avoidRelease: pihole
strict: true
serviceWeb:
annotations:
metallb.universe.tf/allow-shared-ip: pihole-svc
type: LoadBalancer
loadBalancerIP: 192.168.1.10
serviceDns:
annotations:
metallb.universe.tf/allow-shared-ip: pihole-svc
type: LoadBalancer
loadBalancerIP: 192.168.1.10
probes:
liveness:
port: https
scheme: HTTPS
readiness:
port: https
scheme: HTTPS
extraVolumes:
external-conf-cm:
configMap:
name: lighttpd-external-conf
external-conf:
emptyDir:
medium: Memory
tls-cert:
secret:
secretName: pihole-tls-cert
extraVolumeMounts:
external-conf:
mountPath: /etc/lighttpd/external.conf
subPath: external.conf
tls-cert:
mountPath: "/etc/ssl/lighttpd-private"
extraInitContainers:
# Pi-hole attempts to touch /etc/lighttpd/external.conf to make sure it exists
# but since we're using a configmap this fails.
#
# This way, we copy a real file that's touchable (not a read-only filesystem,
# like a configmap is). I'd still (someday) like to be able to mount the configmap
# directly.
- name: copy-config
image: busybox
args:
- sh
- -c
- |
cp /etc/lighttpd-cm/external.conf /etc/lighttpd/
ls -l /etc/lighttpd/
volumeMounts:
- name: external-conf-cm
mountPath: /etc/lighttpd-cm/
- name: external-conf
mountPath: /etc/lighttpd/
dnsmasq:
customDnsEntries:
# Server up *.lab.example.com wildcard to an ingress traefik IP
- address=/lab.example.com/192.168.1.20
# Server up *.dev.example.com wildcard to an ingress traefik IP
- address=/dev.example.com/192.168.1.21
customSettings:
- domain=example.com
- expand-hosts
- local=/example.com/
additionalHostsEntries:
- 192.168.1.30 foo.example.com
extraObjects:
- apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: pihole-tls-cert
spec:
dnsNames:
- "pihole.example.com"
- "dns.example.com"
ipAddresses:
- "192.168.1.10"
issuerRef:
group: cert-manager.io
kind: ClusterIssuer
name: my-example-issuer
secretName: pihole-tls-cert
- apiVersion: v1
kind: ConfigMap
metadata:
name: lighttpd-external-conf
data:
external.conf: |
# New configs as of 2022.04 and newer
server.modules += ( "mod_openssl" )
setenv.add-environment = ("fqdn" => "true")
# Enable the SSL engine with the mapped in certs from CertManager.
$SERVER["socket"] == ":443" {
ssl.engine = "enable"
ssl.pemfile = "/etc/ssl/lighttpd-private/tls.crt"
ssl.privkey = "/etc/ssl/lighttpd-private/tls.key"
ssl.ca-file = "/etc/ssl/lighttpd-private/ca.crt"
ssl.openssl.ssl-conf-cmd = ("MinProtocol" => "TLSv1.3", "Options" => "-ServerPreference")
}
# Redirect HTTP to HTTPS
$HTTP["scheme"] == "http" {
$HTTP["host"] =~ ".*" {
url.redirect = (".*" => "https://%0$0")
}
}