Skip to content

Commit

Permalink
Merge pull request #5833 from EnterpriseDB/release-2024-07-03a
Browse files Browse the repository at this point in the history
Release 2024-07-03a
  • Loading branch information
djw-m authored Jul 3, 2024
2 parents 9e9a522 + 8a6e1e2 commit 643e29d
Show file tree
Hide file tree
Showing 11 changed files with 276 additions and 252 deletions.
306 changes: 136 additions & 170 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion product_docs/docs/pgd/5/ddl/ddl-locking.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ can be reached and are keeping up reasonably well with the current write rate.
If such DDL commands must be performed while a node is down, first remove the
down node from the configuration.

All eligible data notes must agree to grant a global DML lock before the lock is granted.
All eligible data nodes must agree to grant a global DML lock before the lock is granted.
Witness and subscriber-only nodes aren't eligible to participate.

If a DDL statement isn't replicated, no global locks are acquired.
Expand Down
75 changes: 61 additions & 14 deletions product_docs/docs/postgres_for_kubernetes/1/security.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,17 @@ the cluster (PostgreSQL included).

### Role Based Access Control (RBAC)

The operator interacts with the Kubernetes API server with a dedicated service
account called `postgresql-operator-manager`. In Kubernetes this is installed
by default in the `postgresql-operator-system` namespace, with a cluster role
binding between this service account and the `postgresql-operator-manager`
cluster role which defines the set of rules/resources/verbs granted to the operator.
The operator interacts with the Kubernetes API server using a dedicated service
account named `postgresql-operator-manager`. This service account is typically installed in
the operator namespace, commonly `postgresql-operator-system`. However, the namespace may vary
based on the deployment method (see the subsection below).

In the same namespace, there is a binding between the `postgresql-operator-manager` service
account and a role. The specific name and type of this role (either `Role` or
`ClusterRole`) also depend on the deployment method. This role defines the
necessary permissions required by the operator to function correctly. To learn
more about these roles, you can use the `kubectl describe clusterrole` or
`kubectl describe role` commands, depending on the deployment method.
For OpenShift specificities on this matter, please consult the
["Red Hat OpenShift" section](openshift.md#predefined-rbac-objects), in particular
["Pre-defined RBAC objects" section](openshift.md#predefined-rbac-objects).
Expand All @@ -118,7 +124,7 @@ For OpenShift specificities on this matter, please consult the

Below we provide some examples and, most importantly, the reasons why
EDB Postgres for Kubernetes requires full or partial management of standard Kubernetes
namespaced resources.
namespaced or non-namespaced resources.

`configmaps`
: The operator needs to create and manage default config maps for
Expand Down Expand Up @@ -171,14 +177,55 @@ namespaced resources.
validate them before starting the restore process.

`nodes`
: The operator needs to get the labels for Affinity and AntiAffinity, so it can
decide in which nodes a pod can be scheduled preventing the replicas to be
in the same node, specially if nodes are in different availability zones. This
permission is also used to determine if a node is schedule or not, avoiding
the creation of pods that cannot be created at all.

To see all the permissions required by the operator, you can run `kubectl
describe clusterrole postgresql-operator-manager`.
: The operator needs to get the labels for Affinity and AntiAffinity so it can
decide in which nodes a pod can be scheduled. This is useful, for example, to
prevent the replicas from being scheduled in the same node - especially
important if nodes are in different availability zones. This
permission is also used to determine whether a node is scheduled, preventing
the creation of pods on unscheduled nodes, or triggering a switchover if
the primary lives in an unscheduled node.

#### Deployments and `ClusterRole` Resources

As mentioned above, each deployment method may have variations in the namespace
location of the service account, as well as the names and types of role
bindings and respective roles.

##### Via Kubernetes Manifest

When installing EDB Postgres for Kubernetes using the Kubernetes manifest, permissions are
set to `ClusterRoleBinding` by default. You can inspect the permissions
required by the operator by running:

```sh
kubectl describe clusterrole postgresql-operator-manager
```

##### Via OLM

From a security perspective, the Operator Lifecycle Manager (OLM) provides a
more flexible deployment method. It allows you to configure the operator to
watch either all namespaces or specific namespaces, enabling more granular
permission management.

!!!Info
OLM allows you to deploy the operator in its own namespace and configure it
to watch specific namespaces used for EDB Postgres for Kubernetes clusters. This setup helps
to contain permissions and restrict access more effectively.

#### Why Are ClusterRole Permissions Needed?

The operator currently requires `ClusterRole` permissions just to read `nodes`
objects. All other permissions can be namespace-scoped (i.e., `Role`) or
cluster-wide (i.e., `ClusterRole`).

Even with these permissions, if someone gains access to the `ServiceAccount`,
they will only have `get`, `list`, and `watch` permissions, which are limited
to viewing resources. However, if an unauthorized user gains access to the
`ServiceAccount`, it indicates a more significant security issue.

Therefore, it's crucial to prevent users from accessing the operator's
`ServiceAccount` and any other `ServiceAccount` with elevated permissions.

### Calls to the API server made by the instance manager

Expand Down
2 changes: 1 addition & 1 deletion product_docs/docs/tde/15/affected_commands.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ When TDE is enabled, the following commands have TDE-specific options or read TD
- [pg_resetwal](/tde/latest/troubleshooting/#resetting-a-corrupt-tde-encrypted-wal-file)
- [pg_verifybackup](/tde/latest/backups/#verify-a-backup-of-a-tde-system)
- [pg_rewind](/tde/latest/backups/#resynchronize-timelines-in-a-tde-system)
- [pg_upgrade](/tde/latest/pg_upgrade/)
- [pg_upgrade](pg_upgrade_arguments)
- [postgres](/tde/latest/single_user/)

4 changes: 2 additions & 2 deletions product_docs/docs/tde/15/pg_upgrade_arguments.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: "TDE upgrade arguments"
navTitle: TDE upgrade arguments
---

These arguments to [pg_upgrade](https://www.postgresql.org/docs/latest/pgupgrade.html) help with upgrading encrypted clusters.
These arguments to [pg_upgrade](https://www.postgresql.org/docs/current/pgupgrade.html) help with upgrading encrypted clusters.

## `--copy-by-block`

Expand All @@ -15,7 +15,7 @@ For added certainty, if the old cluster is encrypted and the new cluster was ini

See the description of the [initdb --copy-key-from=<file> option](enabling_tde/#using-initdb-tde-options) for information on copying a key from an existing cluster when preparing a new cluster as a target for `pg_upgrade`.

See [Tutorials](/upgrade_overview/#tutorials) for `--copy-by-block` usage examples.
See [Tutorials](upgrade_overview/#tutorials) for `--copy-by-block` usage examples.

## `--key-unwrap-command=<command>`

Expand Down
2 changes: 1 addition & 1 deletion product_docs/docs/tde/15/upgrade_overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: "TDE pg_upgrade use cases"
navTitle: TDE pg_upgrade use cases
---

EDB supports using [pg_upgrade](pg_upgrade_arguments) to add encryption to unencrypted systems.
EDB supports using [pg_upgrade](https://www.postgresql.org/docs/current/pgupgrade.html) with additional [EDB upgrade arguments](pg_upgrade_arguments) to add encryption to unencrypted systems.
This table provides an overview of supported use cases.

| Use case | Source unencrypted server | Target encrypted server |
Expand Down
84 changes: 46 additions & 38 deletions src/components/advanced-search/filtering.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useCallback, useEffect } from "react";
import { Badge } from "react-bootstrap";
import {
useClearRefinements,
Expand Down Expand Up @@ -59,10 +59,20 @@ const RadioRefinement = ({
items,
refine,
showBadge,
show,
sortFunction,
translation = {},
}) => {
const radioRefine = useCallback(
(refinement) => {
// toggle all current refinements, add new one
for (let item of items) {
if (item !== refinement && item.isRefined) refine(item.label);
}
if (refinement) refine(refinement.label);
},
[items, refine],
);

if (items == null) {
return null;
}
Expand All @@ -80,16 +90,8 @@ const RadioRefinement = ({
}),
);

const radioRefine = (refinement) => {
// toggle all current refinements, add new one
for (let item of items) {
if (item !== refinement && item.isRefined) refine(item.label);
}
if (refinement) refine(refinement.label);
};

return (
<div className={`mb-4 ps-1 ${!show && "d-none"}`}>
<div className={`mb-4 ps-1`}>
<div className="mb-2 fw-bold text-muted text-uppercase small">
{heading || capitalize(attribute)}
</div>
Expand Down Expand Up @@ -121,28 +123,23 @@ const RadioRefinement = ({
const SingleFacetRefinement = ({
attribute,
limit,
show,
showBadge,
hideIfEmpty = false,
sortBy,
}) => {
const { items, refine } = useRefinementList({ attribute, limit, sortBy });
const empty = !items || items.length === 0;
const { algoliaIndex } = useSiteMetadata();

const { uiState } = useInstantSearch();
const query = uiState[algoliaIndex].query;

return (
<RadioRefinement
attribute={attribute}
items={items}
refine={refine}
showBadge={query && query.length}
show={show && !(hideIfEmpty && empty)}
translation={products}
sortFunction={null}
/>
);
if (!hideIfEmpty || items?.length)
return (
<RadioRefinement
attribute={attribute}
items={items}
refine={refine}
showBadge={showBadge}
translation={products}
sortFunction={null}
/>
);
};

const ClearRefinements = () => {
Expand All @@ -164,6 +161,9 @@ const ClearRefinements = () => {

export const AdvancedSearchFiltering = () => {
const { items } = useCurrentRefinements();
const { algoliaIndex } = useSiteMetadata();
const { uiState } = useInstantSearch();
const query = uiState[algoliaIndex].query;

const productFilterApplied = items.some(
(item) => item.attribute === "product",
Expand All @@ -173,23 +173,31 @@ export const AdvancedSearchFiltering = () => {
);

// if we don't have a product filter applied, wipe any version filters
if (versionFilterApplied && !productFilterApplied) {
const versionFilter = items.find((item) => item.attribute === "version");
for (let refinement of versionFilter.refinements) {
versionFilter.refine(refinement);
useEffect(() => {
if (versionFilterApplied && !productFilterApplied) {
const versionFilter = items.find((item) => item.attribute === "version");
for (let refinement of versionFilter.refinements) {
versionFilter.refine(refinement);
}
}
}
}, [versionFilterApplied, productFilterApplied, items]);

return (
<>
<SingleFacetRefinement attribute="product" limit={30} show={true} />
<SingleFacetRefinement
attribute="version"
attribute="product"
limit={30}
show={productFilterApplied}
hideIfEmpty={!versionFilterApplied}
sortBy={versionSort}
showBadge={query?.length}
/>
{productFilterApplied && (
<SingleFacetRefinement
attribute="version"
limit={30}
showBadge={query?.length}
hideIfEmpty={!versionFilterApplied}
sortBy={versionSort}
/>
)}
<ClearRefinements />
</>
);
Expand Down
28 changes: 12 additions & 16 deletions src/components/advanced-search/form.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,26 @@
import React, {
useLayoutEffect,
useEffect,
useCallback,
useRef,
useState,
} from "react";
import React, { useLayoutEffect, useCallback, useRef, useState } from "react";
import { useSearchBox } from "react-instantsearch";
import Icon, { iconNames } from "../icon";
import { SlashIndicator, ClearButton } from "../search/formComps";

export const AdvancedSearchForm = () => {
const { clear, refine, query } = useSearchBox();
export const AdvancedSearchForm = ({ initialQuery }) => {
const { clear, refine } = useSearchBox();
const inputRef = useRef(null);
const [inputValue, setInputValue] = useState(query);
const [inputValue, setInputValue] = useState(initialQuery);
const queryLength = (inputValue || "").length;

useEffect(() => {
setInputValue(query);
}, [query]);
const [lastSetQuery, setLastSetQuery] = useState(initialQuery);

const onInputChange = useCallback(
(e) => {
let newValue = e.currentTarget.value;
setInputValue(newValue);
refine(newValue);
// todo: debounce
if (newValue !== lastSetQuery) {
setLastSetQuery(newValue);
refine(newValue);
}
},
[refine],
[refine, lastSetQuery],
);

const onClearSearch = useCallback(
Expand Down Expand Up @@ -81,6 +76,7 @@ export const AdvancedSearchForm = () => {
aria-label="search"
placeholder="Search"
value={inputValue}
defaultValue={initialQuery}
onChange={onInputChange}
ref={inputRef}
/>
Expand Down
4 changes: 3 additions & 1 deletion src/components/search/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { SlashIndicator, ClearButton, SearchPane } from "./formComps";
import useSiteMetadata from "../../hooks/use-sitemetadata";
import { products } from "../../constants/products";
import { Dropdown, DropdownButton } from "react-bootstrap";
// loaded client-side for use by Algolia
import aa from "search-insights"; // eslint-disable-line no-unused-vars

const searchClient = algoliasearch(
"HXNAF5X3I8",
Expand Down Expand Up @@ -253,7 +255,7 @@ const SearchBar = ({ searchProduct, searchVersion }) => {
<InstantSearch
searchClient={searchClient}
indexName={algoliaIndex}
insights={false}
insights={true}
className="dropdown"
>
<Configure {...searchConfig} />
Expand Down
4 changes: 3 additions & 1 deletion src/pages/404.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import Icon, { iconNames } from "../components/icon";
import useSiteMetadata from "../hooks/use-sitemetadata";
import usePathPrefix from "../hooks/use-path-prefix";
import { products } from "../constants/products";
// loaded client-side for use by Algolia
import aa from "search-insights"; // eslint-disable-line no-unused-vars

const searchClient = algoliasearch(
"HXNAF5X3I8",
Expand Down Expand Up @@ -89,7 +91,7 @@ const SuggestedLinksSearch = ({ queryParams }) => {
searchClient={searchClient}
indexName={algoliaIndex}
initialUiState={{ [algoliaIndex]: queryParams }}
insights={false}
insights={true}
>
<SuggestedLinks />
<Configure
Expand Down
Loading

2 comments on commit 643e29d

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸŽ‰ Published on https://edb-docs.netlify.app as production
πŸš€ Deployed on https://668520cd990b89c8f623800d--edb-docs.netlify.app

Please sign in to comment.